Skip to content

fix(agent-runner): rotate oversized/old session transcripts before resume#2586

Merged
gavrielc merged 1 commit into
nanocoai:mainfrom
IamAdamJowett:fix/rotate-oversized-transcripts
May 22, 2026
Merged

fix(agent-runner): rotate oversized/old session transcripts before resume#2586
gavrielc merged 1 commit into
nanocoai:mainfrom
IamAdamJowett:fix/rotate-oversized-transcripts

Conversation

@IamAdamJowett

Copy link
Copy Markdown
Contributor

Problem

A long-lived hub/parent session never rotates its continuation, so the on-disk SDK transcript (projects/<cwd>/<session>.jsonl) grows without bound — days of history plus base64 image blocks the agent Read (e.g. screenshots routed back from sub-agents). The Claude SDK reloads the entire transcript on every --resume. Past a threshold the first turn alone exceeds the host's 30-minute absolute-ceiling (src/host-sweep.ts), so the container is SIGKILLed (exit 137) before it can produce any output — and the next inbound message wakes a fresh container that hits the same wall. The hub becomes permanently unresponsive.

Observed in the wild: a hub session responsive for days went silent on an image-heavy turn. Its transcript had reached 28 MB over 9 days (55% base64 images); each wake wrote the continuation, then went silent until the ceiling killed it. session_state had a continuation row but no current_tool — the SDK never reached a single tool call.

Fix

Before resuming, the Claude provider inspects the transcript backing the stored continuation. If it exceeds a size cap (default 12 MB) or age cap (default 14 days, from the first entry's timestamp), it archives a markdown summary to conversations/ (reusing the existing PreCompact archiver) and starts a fresh session. Durable state already lives in the workspace/memory, so little is lost.

  • New optional AgentProvider.maybeRotateContinuation(continuation, cwd) — providers without an on-disk transcript simply omit it.
  • poll-loop calls it once at startup, right before resume.
  • Existing PreCompact archiving refactored into a shared archiveTranscriptFile().
  • Operator-overridable: CLAUDE_TRANSCRIPT_ROTATE_BYTES, CLAUDE_TRANSCRIPT_ROTATE_AGE_DAYS.

Testing

  • New claude.rotate.test.ts: keeps small/recent transcripts, rotates oversized and aged ones, no-ops on unknown session ids.
  • Full provider + poll-loop suites pass; container typecheck clean.

🤖 Generated with Claude Code

…sume

A long-lived hub session never rotates its continuation, so the on-disk
.jsonl grows without bound — days of history plus base64 image blocks the
agent Read (screenshots from QA lanes, etc.). The SDK reloads the whole
transcript on every --resume, and past a threshold the first turn alone
exceeds the host's 30-min idle ceiling: the container is SIGKILLed before
it can reply, then the next message repeats the cycle forever. Symptom:
a hub that was responsive for days suddenly goes silent on a heavy turn.

Before resuming, the Claude provider now checks the transcript backing the
stored continuation; if it exceeds a size cap (default 12MB) or age cap
(default 14 days, from the first entry's timestamp) it archives a markdown
summary to conversations/ and starts a fresh session. Both caps are
operator-overridable via CLAUDE_TRANSCRIPT_ROTATE_BYTES /
CLAUDE_TRANSCRIPT_ROTATE_AGE_DAYS. The PreCompact archiver is refactored
into a shared archiveTranscriptFile() reused by the rotation path.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6686315a10

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +260 to +261
const days = Number(process.env.CLAUDE_TRANSCRIPT_ROTATE_AGE_DAYS);
return Number.isFinite(days) && days > 0 ? days * 86_400_000 : 14 * 86_400_000;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Respect zero age override when computing rotation threshold

When CLAUDE_TRANSCRIPT_ROTATE_AGE_DAYS is set to 0 (or a negative value), the comment says age-based rotation should be disabled, but this implementation falls back to the 14-day default instead. In deployments that intentionally set 0 to keep long-lived sessions resumable, continuations will still be rotated once they pass 14 days, causing unexpected session resets and context loss.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in #2595. transcriptRotateAgeMs() now distinguishes an unset/blank/non-numeric var (14-day default, unchanged) from an explicit 0/negative override, which returns Infinity so the age check is disabled and the byte-size cap alone governs — matching the documented behavior.

@gavrielc gavrielc merged commit 13eb53f into nanocoai:main May 22, 2026
2 checks passed
@gavrielc

Copy link
Copy Markdown
Collaborator

@IamAdamJowett Thanks for the contribution!

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.

2 participants