Repository navigation
fix(memory): extract memories from sanitized response text when PII response sanitization is on - #16401
Merged
diegosouzapw merged 3 commits intoOct 13, 2026
Conversation
…ation is on runMemoryExtractionGate receives the response before PII response sanitization on both paths (non-streaming: ahead of the post-call guardrails; streaming: the body assembled upstream of the PII SSE transform). With PII_RESPONSE_SANITIZATION on, the client sees [EMAIL_REDACTED] while the raw address is extracted into memory. Fails on the base: the two 'memory gets the redacted reply' cases. The control and the flag-off / no-mutation / request-text guards pass.
…esponse sanitization is on runMemoryExtractionGate is fed the response before PII response sanitization on both completion paths, so with PII_RESPONSE_SANITIZATION on the client saw [EMAIL_REDACTED] while the raw value was extracted into durable memory. The gate now runs the extracted response text through sanitizePII, the same sanitizer the response path uses. It is a no-op while the flag is off (Hard Rule cheaper-inference#20) and works on the extracted text, so the caller's response object is not mutated. Request-derived text is left to PII_REDACTION_ENABLED, which has already masked the request body. Adds the new test to stryker tap.testFiles (memoryExtraction.ts is a mutated module).
diegosouzapw
merged commit Oct 13, 2026
3160851
into
cheaper-inference:release/v3.8.52
31 of 51 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #16400
Problem
With
PII_RESPONSE_SANITIZATIONon, the client receives redacted output, but persistent memory extracts facts from the unsanitized response. Both completion paths callrunMemoryExtractionGatebefore response sanitization runs:nonStreamingResponse.tsextracts fromtranslatedResponseat ~L715.piiMasker.postCall→sanitizePIIResponseruns later, in the post-call guardrails at ~L761.streamingTail.ts(~L556) extracts fromstreamResponseBody. That body is assembled in the provider transform, upstream of the PII SSE transform (streamingPipeline.ts~L89–99).So raw emails, SSNs and similar values are written to durable memory and can be re-injected into later prompts.
Fix
runMemoryExtractionGatenow runs the extracted response text throughsanitizePII, the same sanitizer and mode handling the response path uses (sanitizePIIResponseappliessanitizePIIto each string).sanitizePIIreturns the text unchanged.sanitizePIIResponsewould mutate in place, which is why it isn't used here.)PII_REDACTION_ENABLED's job, andpiiMasker.preCallhas already rewritten the body before extraction.I considered moving extraction after the post-call guardrails instead. That would fix only the non-streaming path, and it would mean editing frozen chat-core files.
Tests
tests/unit/memory-extraction-pii-sanitization.test.ts(6 tests):sanitizePIIredacts the sample reply, so failures can't come from a misconfigured flag.[EMAIL_REDACTED]and no raw email or SSN.output_text: no raw email in memory.Failing first: commit
de12cb1afaadds the test alone. On the base, the two "flag on" extraction cases fail with the raw email and SSN in the extracted text. The other four pass. With the fix, all 6 pass.The test is added to
stryker.conf.jsontap.testFiles, becausememoryExtraction.tsis a mutated module.Other test results:
video-bridge-memory-suppression,chatcore-memory-extraction,memory-extractionandmemory-extraction-boundary-truncation.src/lib/memory: 91/91 pass.memoryExtraction,piiSanitizer,streamingTailornonStreamingResponse: 287 of 292 pass with 1 failure. That failure isintegration-wiring.test.ts› "keeps usage quota wired through A2A and MCP tools", which fails identically on the untouched base.Local quality gates
I ran the
quality.ymlfast-gate set locally againstrelease/v3.8.52:Passing: file-size, complexity-ratchets, dead-code, duplication, error-helper, any-budget, cycles (ratchet), type-coverage, open-sse-typecheck, typecheck:core, ts7-diagnostics-ratchet, changelog-integrity, route-guard-membership, test-discovery, codeql-ratchet, ai-attribution and the rest. ESLint has 0 warnings on the changed files.
Failing, not caused by this change:
dashboard-typecheck: TS2339 indashboard/providers/[id]/components/ConnectionRow.tsx.agent-skills-sync: 20 skills to regenerate.mutation-test-coverage: lists covering tests missing for accountFallback, routeGuard,utils/errorand publicCreds.memoryExtraction.tsis no longer listed.All three fail identically on the untouched base.
Not run locally:
workflowsneeds actionlint and zizmor.secrets,vuln-ratchetandopenapi-breakingskip gracefully without their binaries.