Problem
The current error sanitization is scattered across multiple per-tool and per-error-type handlers (sanitizeUserFacingText, formatAssistantErrorText, individual tool error formatting). Each new tool or API integration can introduce new leak vectors that require patching individually. This doesn't scale.
PR #16653 patches three specific variants (Brave Search 429, Edit tool path leaks, generic API error JSON), but the underlying architecture remains fragile.
Proposed Solution
Add a single sanitization gate in src/infra/outbound/deliver.ts at the deliverOutboundPayloads() boundary — every payload.text gets sanitized before hitting any channel send function, regardless of source (tool error, LLM error, assistant reply, system event).
Architecture
Current: Tool errors → payloads.ts (per-tool sanitization) → deliver.ts → channels
Proposed: Tool errors → payloads.ts (raw) → deliver.ts (central sanitizer) → channels
↓
audit log (raw error for debugging)
Scope estimate (~70 net lines)
- Add
sanitizeOutboundText() in deliver.ts before channel sends (~20 lines)
- Move/refactor
sanitizeUserFacingText() to be outbound-aware (~30 lines)
- Remove redundant per-tool sanitization (~-50 lines)
- Emit raw errors to logging/audit before sanitizing (~40 lines)
- Tests for the boundary sanitizer (~30 lines)
Benefits
- Single point of control — new tools/APIs are safe by default
- Audit trail — raw errors preserved for debugging, never shown to users
- Simpler tool code — tools don't need to worry about error formatting
Related: #7867, PR #16653
Problem
The current error sanitization is scattered across multiple per-tool and per-error-type handlers (
sanitizeUserFacingText,formatAssistantErrorText, individual tool error formatting). Each new tool or API integration can introduce new leak vectors that require patching individually. This doesn't scale.PR #16653 patches three specific variants (Brave Search 429, Edit tool path leaks, generic API error JSON), but the underlying architecture remains fragile.
Proposed Solution
Add a single sanitization gate in
src/infra/outbound/deliver.tsat thedeliverOutboundPayloads()boundary — everypayload.textgets sanitized before hitting any channel send function, regardless of source (tool error, LLM error, assistant reply, system event).Architecture
Scope estimate (~70 net lines)
sanitizeOutboundText()in deliver.ts before channel sends (~20 lines)sanitizeUserFacingText()to be outbound-aware (~30 lines)Benefits
Related: #7867, PR #16653