Skip to content

fix(gateway): recover long correlated Claude results from the transcript - #59

Open
Yehonal wants to merge 1 commit into
mainfrom
pr/mcp-long-result-recovery
Open

Yehonal wants to merge 1 commit into
mainfrom
pr/mcp-long-result-recovery

Conversation

@Yehonal

@Yehonal Yehonal commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

Long Claude replies were being lost over MCP even though the agent answered correctly with the right result markers. Short tasks worked, but every review-sized fresh task failed with result_uncorrelated or result_timeout.

Two causes:

  • the terminal collector drops a reply that scrolls out of the pane, and gives up after timeoutSeconds while a long turn is still running;
  • the transcript fallback used the public search path, which clips each message to 4096 characters, so the closing marker of any longer reply was cut off.

Changes

  • Transcript result reader. New agent-session.sh --agent claude result <session> --correlation-id <id> [--wait <s>] (claude-transcript-result.mjs) returns the whole reply from the native transcript. It accepts only one assistant text block with exactly one marker pair; duplicates or an unterminated pair give exit 67, nothing found gives 124.
  • Provider recovery. After result_uncorrelated or result_timeout the provider calls that reader and waits up to agentSessions.resultRecoverySeconds (0 to 14400, default 0 = check once). The task stays working while it waits. The search-based fallback is gone.
  • 8 KiB truncation. agent-native-call.mjs wrote the result and called process.exit() right away, which cuts piped stdout at 8 KiB, so large native-session results came back truncated but marked completed. Both scripts now write synchronously. A new test with a 200 KB result fails on the old code.
  • Locale. With LANG=C (how the gateway service runs today) tmux and grep mangle the ❯/· glyphs used by readiness and trust-dialog matching. _mesh-tmux.sh now switches to C.UTF-8 when the locale isn't UTF-8. This was also why launch-options-test.sh failed locally but not in CI.
  • Fresh follow-ups. The new target is killed when writer ownership can't be proven (nothing was sent yet). --resume A --fork-session no longer counts as a writer of A. Added a test for fresh_session_unsupported raised at execution time.

Tests

  • npm run verify passes on a LANG=C host, including the full tmux-bridge harness (75/75) and the gateway suite (254/254).
  • The new reader was checked read-only against four real failed fresh sessions: it returns the complete replies (5.7 to 26 KB).

Rollout note

To make long reviews complete, the live config needs resultRecoverySeconds (e.g. 3600) after deploy. With the default of 0, only replies already in the transcript are recovered.

Long Claude replies were lost even when the agent answered correctly: the
terminal collector drops replies that scroll out of the pane or outlast
timeoutSeconds, and the transcript fallback went through the public search
path, which clips each message to 4096 characters.

- New `agent-session.sh --agent claude result` reads the whole correlated
  reply from the native transcript, optionally waiting for it.
- The provider uses it after result_uncorrelated or result_timeout, waiting
  up to agentSessions.resultRecoverySeconds (0-14400, default 0).
- agent-native-call.mjs and the new reader write stdout synchronously:
  process.exit() after an async write cut piped results at 8 KiB.
- The bridge forces a UTF-8 locale so prompt and trust-dialog matching work
  when the host runs with LANG=C.
- Fresh sessions: kill the new target when writer ownership can't be proven.
- A forked resume no longer counts as a writer of its source session.
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