Skip to content

[stack 8/8] fix(runtime): bound transcript and autonomous recovery - #1165

Open
sethkarten wants to merge 5 commits into
stack/external-07-providersfrom
stack/external-08-autonomy
Open

[stack 8/8] fix(runtime): bound transcript and autonomous recovery#1165
sethkarten wants to merge 5 commits into
stack/external-07-providersfrom
stack/external-08-autonomy

Conversation

@sethkarten

@sethkarten sethkarten commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Stack 8/8 — fix(runtime): bound transcript and autonomous recovery

Active review snapshot — do not merge yet. The complete stack is open for architecture/design review, while final cumulative audit, CI, Cursor Bug Bot, and Macroscope findings are being remediated. Branches will be force-updated after validation.

Base: stack/external-07-providers
Review order: merge only after the preceding stack layer is accepted. This PR is not intended to merge independently out of order.

Stack navigation

  1. #1158 — ci: harden verification and release compatibility
  2. #1159 — fix(security): harden session and autonomous execution boundaries
  3. #1160 — fix(coding-agent): make persisted state crash-safe
  4. #1161 — fix(daemon): fence worker and supervisor lifecycle state
  5. #1162 — fix(coding-agent): repair queued and archived session lifecycle
  6. #1163 — fix(coding-agent): complete Windows kernel and daemon startup
  7. #1164 — fix(providers): harden MCP OAuth and Codex transports
  8. #1165 — fix(runtime): bound transcript and autonomous recovery

Summary

  • Repair interrupted tool-result ordering without orphaning or duplicating results.
  • Bound degenerate reasoning generation with diagnostics and an opt-out.
  • Persist goal pause/resume semantics for externally blocked work.
  • Compact complete input with conservative budgets, bounded progress, and fail-closed summaries.

Validation

  • npm run check; focused compaction/autonomy suites 76 passed with 2 credential-gated skips; final adversarial review cleared.
  • Residual/non-blocking: guard limited to OpenAI reasoning; oversized compaction message truncates; no extra cooldown

Provenance

  • Authored independently from upstream/main using issue reports and PR descriptions/comments only.
  • No external contributor branch, diff, commit, implementation code, or test code was fetched, inspected, copied, or reused.
  • The implementation and regression tests in this stack are maintainer-owned.

Linked-item disposition

Fixed on merge

Independently superseded pull requests

Partial/distinct overlap — remains open

Reviewer notes

  • Please review this layer against its immediate stack base, not against main, to avoid cumulative duplicate diffs.
  • No merge is requested; the complete stack is being left for human review.

This was referenced Aug 10, 2026
@sethkarten
sethkarten force-pushed the stack/external-08-autonomy branch from e234355 to 23945e2 Compare August 10, 2026 19:23
): number | undefined {
const messages = this.agent.state.messages;
if (compactionTimestamp !== undefined && assistantMessage.timestamp <= compactionTimestamp) {
return messages.reduce((tokens, message) => tokens + estimateTokens(message), 0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High core/agent-session.ts:7962

The stale-usage fallback at line 7962 sums estimateTokens(message) across all messages, but estimateTokens counts only text blocks for user messages and assigns zero tokens to image attachments. After compaction, when the only post-compaction input is a user message with images, the fallback produces a near-zero token estimate, so threshold compaction is skipped even though the images occupy real context — causing repeated context-limit errors instead of compaction. The fallback should account for image blocks (as estimateTokens already does for toolResult and custom messages) or use a complete context estimator.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @packages/coding-agent/src/core/agent-session.ts around line 7962:

The stale-usage fallback at line 7962 sums `estimateTokens(message)` across all messages, but `estimateTokens` counts only text blocks for `user` messages and assigns zero tokens to image attachments. After compaction, when the only post-compaction input is a user message with images, the fallback produces a near-zero token estimate, so threshold compaction is skipped even though the images occupy real context — causing repeated context-limit errors instead of compaction. The fallback should account for image blocks (as `estimateTokens` already does for `toolResult` and `custom` messages) or use a complete context estimator.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 23945e2. Configure here.

content: [{ type: "text", text: "No result provided" }],
isError: true,
timestamp: Date.now(),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Tool results emitted out of order

High Severity

Hoisted tool results are appended in look-ahead discovery order, then missing synthetics follow in toolCalls order. When only some results exist after an interrupt, the emitted sequence can disagree with the assistant toolCall order. Providers that pack consecutive toolResult messages into one user turn (notably Anthropic) can reject that transcript with a 400.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 23945e2. Configure here.

if (foundReasoningField) {
const delta = deltaFields[foundReasoningField];
if (typeof delta === "string" && delta.length > 0) {
const repetition = repetitionGuard?.push(delta);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nice catch on the degenerate repetition issue. The repetitionGuard is a solid safety net — detecting and throwing StreamFailureError on degenerate output is the right defensive measure.

One suggestion: consider also adding anti-repetition guidance to the system prompt as a complementary layer. The guard catches repetition after it starts (wasting tokens in the process), while prompt-level
instructions can prevent it from starting.

Claude Code's system prompt includes explicit anti-repetition directives like:
Do NOT read this resource again unless you think it may have changed.
Do not re-select memories you already returned for an earlier query.
When you already know which part of the file you need, only read that part.

Something similar in buildRlmPrompt() (e.g. in rlm.ts) would reduce how often the guard triggers:
Do not re-run the same tool call with identical arguments unless the underlying data may have changed.
If you find yourself repeating the same action with the same result, stop and reconsider your approach.
Do not re-read files or re-execute commands whose results you already have in context.

This way the prompt prevents most repetition, and the repetitionGuard catches the rest. Neither alone is sufficient — prompts can't guarantee compliance, and code-level detection without prompt guidance means
the guard fires frequently and degrades UX.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants