fix(agent): stop redacting tool-call args in history; fix auth-header quote-eating (#43083) - #54136
Merged
Conversation
… quote-eating Two related redaction bugs from #43083: 1. build_assistant_message redacted tool-call arguments in-memory. That dict feeds both the replayed conversation history and state.db (which is itself replayed verbatim on session resume), so the model read back its own PGPASSWORD='***' psql call and copied the placeholder, breaking every credential-dependent command on the second turn. The masking gave no real protection either — the same secret still leaks through tool OUTPUT. Remove it. Keeping secrets out of the replayable store is a separate tokenization/vault concern (security.redact_secrets still governs storage-time redaction elsewhere). 2. _AUTH_HEADER_RE's greedy \S+ credential class ate a closing quote when the token sat flush against it (Authorization: Bearer sk-.."), turning value corruption into syntax corruption (unterminated quote -> shell EOF / SyntaxError). Exclude " and ' from the token class; real credentials never contain them. Closes #43083.
Contributor
🔎 Lint report:
|
19 tasks
WolframRavenwolf
added a commit
to WolframRavenwolf/hermes-agent
that referenced
this pull request
Jul 14, 2026
Keep canonical tool-call arguments byte-exact so replay and resume cannot reuse redaction placeholders. Narrow operational metadata false positives, preserve quote/backslash syntax, and align Anthropic interleaved replay with the raw canonical argument contract. Adapted from merged upstream PRs NousResearch#54061/NousResearch#54136 and a hardened subset of open PR NousResearch#47348.
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.
Summary
Credential-bearing tool calls now keep working across turns, and masking an
Authorization:header no longer eats the closing quote.Two related redaction bugs reported in #43083:
build_assistant_message()masked tool-call arguments in-memory. That dict feeds both the conversation history replayed to the model every turn and state.db, which is itself replayed verbatim on session resume (get_messages_as_conversation). So a model that ranPGPASSWORD='real' psql ...read back its ownPGPASSWORD='***'call on the next turn and copied the placeholder — every credential-dependent command broke on the second call. The masking also gave no real protection: the same secret still leaks through tool output (file contents, command output, diffs, the compaction block), which this pass never touched.@shady2k)._AUTH_HEADER_RE's greedy\S+credential class pulled in a closing quote when the token sat flush against it ("Authorization: Bearer sk-..."), turning value corruption into syntax corruption — an unterminated quote → shell EOF / PythonSyntaxError.Changes
agent/chat_completion_helpers.py: stop redacting tool-call arguments inbuild_assistant_message. Keeping secrets out of the replayable store is a separate tokenization/vault concern — it cannot be done by masking the replayed history without breaking replay. Storage-time redaction elsewhere is unchanged and still governed bysecurity.redact_secrets.agent/redact.py:_AUTH_HEADER_REcredential class now excludes"/'([^\s"']+). Real credentials never contain quotes, so the mask stops at the quote boundary.tests/agent/test_redact.py: +2 regression tests — token flush against"and'keeps both quotes balanced.tests/agent/test_tool_call_arg_no_redaction.py: +2 tests — tool-call args (PGPASSWORD, Bearer token) are preserved verbatim even with redaction forced on.Validation
PGPASSWORD='real' psqlon turn 2***copied → fails"Authorization: Bearer sk-..."maskedBearer ***(closing"eaten)Bearer ***"(quote survives)test_redact+ new)Closes #43083.
Infographic