Skip to content

fix(redact): strip complete CSI/SGR sequences from shadow copy (#81012) - #81079

Closed
Enough1122 wants to merge 4 commits into
NousResearch:mainfrom
Enough1122:fix/81012-redact-csi-sgr-bypass
Closed

fix(redact): strip complete CSI/SGR sequences from shadow copy (#81012)#81079
Enough1122 wants to merge 4 commits into
NousResearch:mainfrom
Enough1122:fix/81012-redact-csi-sgr-bypass

Conversation

@Enough1122

Copy link
Copy Markdown
Contributor

Fixes #81012 — redact: complete CSI/SGR sequences defeat prefix masking (ESC-byte-only stripping leaves 'm'-glue).

NousResearch#81052)

When a queued-follow-up turn finishes before the stream consumer confirms
final delivery, gateway/run.py falls back to adapter.send(first_response) and
the normal completion pipeline then issues the same text a second time.

Track (chat_id, session_key) tuples delivered via the fallback path in a
set scoped to the pending-message handling block; the normal completion
send decision at the (_streamed or _content_delivered) guard now also
checks the set, so a successful fallback delivery suppresses the duplicate
send. The set keys on (chat_id, session_key) so different chats / sessions
running on the same gateway are not cross-suppressed.
…tate (NousResearch#81050)

The web dashboard's remove path only called _remove_mcp_server(name), which
deleted the server entry from config.yaml but left the OAuth token files
(.json, .client.json, .meta.json) on disk under mcp-tokens/. The CLI
`hermes mcp remove` path already routed through MCPOAuthManager.remove()
to delete all three; this commit mirrors that on the dashboard side so a
server removed via the UI cannot be revived at the next gateway restart by
leftover meta.json metadata.
…lity (NousResearch#81014)

When a memory provider (e.g. Mnemosyne) is configured but the 'memory'
toolset is not in the agent's tool surface (platform_toolsets /
disabled_toolsets), the provider's system_prompt_block() was injected
unconditionally — instructing the model to call tools that do not exist
in the tool schema. inject_memory_provider_tools() already gated
the tool injection; build_system_prompt() did not.

Two changes:
1. MemoryManager.build_system_prompt() now consults
   memory_provider_tools_enabled() once set_tool_gating() has
   recorded the agent's toolset configuration, suppressing the provider
   block when tools are gated out. Legacy callers that never call
   set_tool_gating() retain the old behavior.
2. inject_memory_provider_tools() now logs a WARNING with the
   suppressed schema count when the gate fires, so an operator reading
   agent.log can correlate the dangling instructions with the gate.

agent_init.py calls set_tool_gating() on the manager before
inject_memory_provider_tools() runs.
…esearch#81012)

A vendor-prefixed token wrapped in ANSI color codes leaked entirely
because _mask_control_split_tokens stripped only the ESC byte, leaving
[32m glued to the token head; the literal m defeated the
(?<![A-Za-z0-9_-]) lookbehind in _PREFIX_RE.

Build the shadow copy in two steps: (1) strip complete CSI sequences
(\x1b\[[0-9;?]*[A-Za-z]), reusing the pattern shape from
tools/ansi_strip.py, then (2) strip remaining bare control chars. The
order matters — stripping the bare ESC byte first would leave the same
glue.

The join guard's "original span only contains token-body or control
chars" invariant now also accepts CSI positions, so the shadow match can
extend through a CSI byte without being rejected as crossing a
non-token-character class. The line-boundary skip (\n / \r) is
preserved verbatim — that's the button [ref=e3] regression guard
(NousResearch#77484 / NousResearch#80987), not the bug being fixed.

Verified: 6 new regression tests (CSI-wrapped, 256-color, CSI+newline
split, adjacent-text not swallowed, bare-ESC, newline-only regression)
plus 98 existing redact tests all pass.
@alt-glitch alt-glitch added type/security Security vulnerability or hardening P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery comp/cli CLI entry point, hermes_cli/, setup wizard sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages duplicate This issue or pull request already exists labels Aug 7, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #81060 for the CSI/SGR redaction repair. #81060 is the focused implementation; this PR includes unrelated stacked changes.

@Enough1122

Copy link
Copy Markdown
Contributor Author

Duplicate of #81060 (same issue). Closing in favor of the earlier PR to avoid double-fixing the same issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery duplicate This issue or pull request already exists P3 Low — cosmetic, nice to have sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

redact: complete CSI/SGR sequences defeat prefix masking (ESC-byte-only stripping leaves 'm'-glue)

2 participants