Skip to content

fix: tolerate typo'd render-tag names in echo strip; warn on degenerate end_turn turns - #674

Merged
ranxianglei merged 1 commit into
masterfrom
2026-09-10_acip-tag-echo-tolerance
Sep 10, 2026
Merged

fix: tolerate typo'd render-tag names in echo strip; warn on degenerate end_turn turns#674
ranxianglei merged 1 commit into
masterfrom
2026-09-10_acip-tag-echo-tolerance

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Fixes

#673 — follow-up to #644/#646 (v0.1.97 incident family): a model-emitted render-tag echo whose tag name is typo'd (an acp permutation plus one stray char, e.g. acpi/acip) bypassed every exact-spelling pattern in the tag-echo filter. The 43-char fragment then became the turn's entire visible text: stop_reason=end_turn, zero tool_use, complete next-step plan sitting in the thinking block — the agent stalls mid-orchestration until manually nudged.

What changed

Gap 1 — tolerant name matching (src/loop/tag-echo-filter.ts)

  • All strip patterns (PAIRED / LONE_OPEN / LONE_CLOSE / TRUNC_* / PARTIAL_TAIL / RENDER_TAG_DETECT) now match the acplike name set: permutations of acp plus one inserted a/c/p/i char (66 members, longest-first), built programmatically so the set stays auditable.
  • Every occurrence still requires a strict name boundary (whitespace or close after the name), so legit text is untouched — verified: C includes of acpi/acpi.h, <caption>, <app id="1">, uppercase ACPI_DEVICE, underscore triggers, a < b.
  • The swallow-until-close scan now finds loose close variants (mixed correct-open / typo-close echoes from the later dumps).
  • Existing caps unchanged (hold 128 / definite drop 4096 / swallow release 80); worst-case false-positive surface bounded by them.
  • New lifetime stats() (inputChars/outputChars/dropped) so callers can do turn-level accounting across intermediate flushes.
  • src/loop/adapter-responses.ts module-level RENDER_TAG_RE loosened to the same name set.

Gap 2 — degenerate-turn detection (WARN only)

  • New src/degenerate-turn.ts: pure predicate + message builder. Fires when a terminal turn ends with zero visible text AND zero tool calls (thinking may be non-empty — that is exactly the incident shape).
  • Wired into all three compress-loop adapters (anthropicend_turn, openaistop, responsescompleted) and both plugin passthrough pipes (chat SSE covers OpenAI + Anthropic wires; Responses pipe). In the pipes, visible-text accounting counts only content/text fields — thinking/reasoning never counts, matching the client-visible definition.
  • Deliberately no auto-retry: retry interplay with billing, kernel state, and client-side retry loops needs owner sign-off. The warn gives operators a grep-able signal ([degenerate-turn]) immediately; auto-retry can land behind it as a follow-up.

Tests

  • Split-parity harness extended with typo/mixed/safe cases (every split position x seeds).
  • Gate engagement for typo'd openers/closers; safe-word survival static+streaming parity.
  • stats() accounting; degenerateTurnWarning matrix.
  • Adapter e2e: thinking-only + echo-only anthropic turn warns; clean turn does not.
  • Pipe level: strips typo'd tags on the wire; warns on degenerate turns; no warn when visible text exists or a tool call is present.

Pre-flight

  • npm run typecheck clean
  • npm test: 1279/1279 pass
  • npm run build clean (dist self-contained)
  • E2E suite not run here (needs a live Responses upstream); unit coverage exercises every changed path

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

📦 Built Package Artifact

Branch: 2026-09-10_acip-tag-echo-tolerance (da1ab44)

Option A — Install from npm PR tag (recommended)

npm install -g billion-context@pr-674

Each push to this PR publishes a new version under the pr-674 npm tag.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr674.tgz
npm install -g package

This comment is automatically updated on each push.

…te end_turn turns

Follow-up to #644/#646: a model-emitted render-tag echo with a typo'd tag
name (acpi/acip/apic-style variants of acp) bypassed every exact-spelling
pattern in the tag-echo filter and passed through as the turn's entire
visible text - a 43-char degenerate end_turn turn with zero tool calls
that stalls the agent mid-orchestration until manually nudged (#673).

- tag-echo-filter: all patterns now match acplike names (permutations of
  acp plus one inserted a/c/p/i char), still requiring a name boundary so
  legit text (acpi/acpi.h includes, caption, app, ACPI_DEVICE) is
  untouched; swallow-until-close scans loose close variants; add lifetime
  stats() for turn-level accounting
- adapters (anthropic/openai/responses): warn [degenerate-turn] when a
  terminal turn ends with zero visible text and zero tool calls (thinking
  may be non-empty); responses RENDER_TAG_RE loosened to match
- plugin passthrough pipes (openai chat / anthropic / responses): same
  degenerate-turn warn with per-field visible-text accounting (thinking
  and reasoning fields never count as visible)
- tests: split-parity typo cases, gate engagement, safe-word survival,
  stats accounting, warning matrix, adapter e2e, pipe-level warn/no-warn
@ranxianglei
ranxianglei force-pushed the 2026-09-10_acip-tag-echo-tolerance branch from d61fa2c to da1ab44 Compare September 10, 2026 10:16
@ranxianglei
ranxianglei merged commit 4f22209 into master Sep 10, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant