diff --git a/agent/redact.py b/agent/redact.py index 8e5ed1eaf4379..b5dca332c4ade 100644 --- a/agent/redact.py +++ b/agent/redact.py @@ -512,14 +512,16 @@ def _mask_control_split_tokens(text: str, mask_fn) -> str: # A complete token at end-of-line followed by a word line # (``ghp_\nbutton [ref=e3]``) joins into one stripped-copy # match and the mask eats ``button``. Line structure is legitimate; - # the self-matching fragment is handled by the ordinary prefix pass. + # the self-matching fragment is handled by the ordinary prefix pass + # (any remainder past the newline is left unmasked — accepted + # residual to preserve line structure). # For NON-newline controls (ESC, ZWSP, ...) the join proceeds even # when a fragment self-matches: those bytes never legitimately sit # between a token and adjacent prose, and skipping there let the # non-matching remainder of a split token leak # (``sk-\x1b`` masked only the head). span = text[start_orig:end_orig] - if _PREFIX_RE.search(span) and ("\n" in span or "\r" in span): + if ("\n" in span or "\r" in span) and _PREFIX_RE.search(span): continue # Reject matches whose original span crosses a non-token char # (e.g. ``sk_abc…\nTAVILY_API_KEY=…`` — the ``=`` is not part of a @@ -527,7 +529,7 @@ def _mask_control_split_tokens(text: str, mask_fn) -> str: # reject when the match runs into a ``KEY=`` name: a real token value # is followed by a newline/space/end, not ``=``. if (all(c in _TOKEN_BODY_CHARS or _CONTROL_CHARS_RE.match(c) - for c in text[start_orig:end_orig]) + for c in span) and (end_orig >= len(text) or text[end_orig] != "=")): matches.append((start_orig, end_orig, mask_fn(body))) for start_orig, end_orig, replacement in reversed(matches):