Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/coding-agent/src/core/compaction/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,10 @@
- `stream-watchdog.ts`: `consumeStreamWithIdleTimeout()` accepts an optional `maxDurationMs` and throws the new
`StreamDurationBudgetError` when one stream outlives it. The budget is a single absolute deadline for the whole
stream, not a per-read timer, and it is cleared alongside the idle timer. Caller aborts still win over the budget.
- `DEFAULT_SUMMARIZATION_MAX_DURATION_MS` = 120s, applied by `compaction.ts` `completeSummarization()` and the
extension's `speculative.ts` request path. `retryAssistantCall` applies it per attempt.
- `DEFAULT_SUMMARIZATION_MAX_DURATION_MS` = 900s, applied by `compaction.ts` `completeSummarization()` and the
extension's `speculative.ts` request path. `retryAssistantCall` applies it per attempt. The 120s cap aborted
legitimate large-session summaries (observed: 257k tokens on Grok 4.6) while the stream was still live; idle
timeout (300s silence) still kills a hung connection.

### Why

Expand Down
11 changes: 6 additions & 5 deletions packages/coding-agent/src/core/compaction/stream-watchdog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ export class StreamDurationBudgetError extends Error {
export const DEFAULT_SUMMARIZATION_IDLE_TIMEOUT_MS = 300_000;

/**
* Total time one summarization attempt may hold the session. Well above healthy
* summarizations (tens of seconds) and below the idle budget, so a live-but-slow
* provider fails fast enough to keep the session interactive. Retries apply this
* budget per attempt.
* Total time one summarization attempt may hold the session. Large sessions
* (200k+ tokens) on slower models take minutes, not tens of seconds; 15 minutes
* still bounds a live-but-slow provider so the session is not stuck forever.
* Silence is a different class and stays on the 300s idle timeout. Retries apply
* this budget per attempt.
*/
export const DEFAULT_SUMMARIZATION_MAX_DURATION_MS = 120_000;
export const DEFAULT_SUMMARIZATION_MAX_DURATION_MS = 900_000;

export interface ConsumeStreamWithIdleTimeoutOptions<T> {
/** Silence budget per read; the timer resets on every event. */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { describe, expect, it } from "vitest";
import { DEFAULT_SUMMARIZATION_MAX_DURATION_MS } from "../../src/core/compaction/stream-watchdog.ts";

describe("DEFAULT_SUMMARIZATION_MAX_DURATION_MS", () => {
it("gives large-session summarization 15 minutes", () => {
expect(DEFAULT_SUMMARIZATION_MAX_DURATION_MS).toBe(900_000);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,6 @@ describe("consumeStreamWithIdleTimeout wall-clock budget", () => {
});

it("exposes a default wall-clock budget below the idle timeout", () => {
expect(DEFAULT_SUMMARIZATION_MAX_DURATION_MS).toBe(120_000);
expect(DEFAULT_SUMMARIZATION_MAX_DURATION_MS).toBe(900_000);
});
});
Loading