From e1962480580b4b256242f239233c242a314cfa8d Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 5 Aug 2026 11:57:27 +0100 Subject: [PATCH] fix(workflows): redact CEL error values by default; tokenize attachments once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two follow-ups from PR review. _fit_attachment_text tokenized the extraction twice on the oversized path — once for the budget check and once for chars_per_token. That is a full BPE pass over the whole document (~12ms per 250k chars) on the hot path of every attachment turn. Compute it once. The CEL error summarizer kept quoted fragments that looked like identifiers, on the theory that those are variable names rather than data. They are not distinguishable: celpy interpolates state values into messages (`StringType('SECRET')`), and a one-word user query such as `invoice` is a valid identifier, so it survived redaction and reached both the log line and — since config errors now bypass sanitize_api_error — the user. The earlier test passed only because its sample value contained hyphens. Quoted fragments are now redacted by default, allowing only the two positions known to hold names rather than data: the variable in "undeclared reference to 'x'" and the class in "". Tests cover identifier-shaped values, that the exception class survives, the truncation backstop, and the empty-expression guard. --- application/agents/workflows/cel_evaluator.py | 15 ++++++-- application/llm/openai.py | 7 +++- tests/agents/test_cel_evaluator.py | 38 +++++++++++++++++-- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/application/agents/workflows/cel_evaluator.py b/application/agents/workflows/cel_evaluator.py index 2a7a1748..121b5a28 100644 --- a/application/agents/workflows/cel_evaluator.py +++ b/application/agents/workflows/cel_evaluator.py @@ -52,12 +52,19 @@ def _summarize_cel_error(exc: Exception) -> str: if marker in text: text = text.split(marker, 1)[0] text = text.strip().lstrip("(").strip() - # Quoted fragments in celpy messages are either the offending identifier - # (safe, and the useful part) or a state value (not ours to echo). Keep - # bare identifiers, redact anything else. + + # Redact quoted fragments by DEFAULT. celpy interpolates state values into + # messages (``StringType('SECRET')``), and a value that happens to look + # like an identifier is indistinguishable from a variable name — so allow + # only the two positions that are known to hold names, not data: the + # offending variable in "undeclared reference to 'x'", and the exception + # class in "". def _redact(match: "re.Match[str]") -> str: inner = match.group(1) - return f"'{inner}'" if inner.isidentifier() else "'…'" + prefix = text[max(0, match.start() - 14):match.start()] + if prefix.endswith("reference to ") or prefix.endswith("