[Feature]: Pre-flight context-window guard for Pro agent sessions (fail fast, suggest a new session)
Problem or Motivation
Pro workbench agent sessions replay the full entry-tree history on every run with no context transformation:
lib/server/agent-runtime/runner.tsloads the whole branch vialoadSessionEntryHistoryand passes it ashistorytobuildAgentunchanged;transformContext(pi's only compaction hook) is never wired up..env.exampledocuments theOPENMAIC_AGENT_COMPACTION_*knobs as inert placeholders ("The reusable compaction runtime is not implemented yet — it lands in a later slice of work").
When a long session's context exceeds the driver model's window, the oversized prompt is still sent to the gateway and fails — and because a failed settle keeps the attempt count (resetAttempt: status !== 'failed') and undelivered user messages are requeued, the runner can retry the same oversized prompt up to maxAttempts (5) consecutive unattended times. The user sees repeated generic failures with no hint that the session is unrecoverable and a new session is the way out.
Proposed Solution
A pre-flight context guard at run start, before any model call:
- Check location: in the runner after
modelMessagesis computed (postplanResume/repair, prebuildAgent) — one check per run, not per LLM call, so we never strand in-flight tool calls. Mid-turn growth from large tool results stays out of scope for v1 and fails at the gateway as today. - Estimation: pi's
estimateContextTokenswith the all-zero-usage fallback (same pattern aslib/chat/pi/director-compaction.ts), covering system prompt + full history + this run's pending user messages + a reserved output allowance, compared againstdriver.piModel.contextWindow(route pin > catalog > 128k fallback, already resolved in the runner). - On breach — single, non-retryable terminal settle:
- a dedicated terminal status (e.g.
context_limit) rather than genericfailed; requeueIfUndelivered/planUndeliveredRequeuemust not requeue for unattended retry on this error class;- the pending user message is consumed with a receipt so it does not sit permanently undelivered;
- the workbench UI renders "context limit reached for this session — please start a new session" instead of a generic error.
- a dedicated terminal status (e.g.
- Gate condition = "compaction not active for this run", not "compaction unimplemented": the guard runs when
OPENMAIC_AGENT_COMPACTION_ENABLEDis unset/false, and later — once the compaction runtime lands — also when compaction is enabled but fails to reduce the context, so this remains the last-resort backstop instead of being retired.
Known trade-offs (fine for a guard): the estimate is approximate. A false positive ends the session slightly early; a false negative keeps today's behavior (gateway 400).
Acceptance Criteria
- A session whose estimated context exceeds the window settles exactly once in a dedicated terminal state — no auto-retry, no repeated gateway 400s for the same oversized prompt
- The pending user message is consumed with a receipt (never left permanently undelivered)
- The workbench UI shows a clear "context limit reached, start a new session" notice for this terminal state
- The guard is skipped when conversation compaction is enabled and successfully reduces the context (composes with the compaction issue, which links back here)
- Unit tests cover the estimator's all-zero-usage fallback and the inclusion of system prompt / pending messages / output reserve
Alternatives Considered
- Do nothing until durable compaction lands (the tracked "later slice") — long sessions keep burning 5 gateway failures each with an opaque UX.
- Reactive detection: classify the gateway's context-length 400 and settle from that — still wastes one full request per turn, and still needs the same non-retryable terminal-state work, so pre-flight is strictly cheaper once estimation exists.
Area
Model / provider integration
Additional Context
- Failure/retry semantics that make the non-retryable classification necessary:
runner.tssettle path (resetAttempt: status !== 'failed',requeueIfUndelivered), attempt cap message ("session failed N consecutive unattended attempts"). - The driver context-window value chain and its "must stay below the gateway's real request limit" rationale:
lib/server/agent-runtime/agent-driver-model.ts. - Estimation precedent:
lib/chat/pi/director-compaction.ts(estimateDirectorContextTokenshandles providers that omit streamed usage). - Companions: the durable-compaction issue for the Pro runtime (supersedes this guard when enabled) — cross-linked once filed.
Source: THU-MAIC/OpenMAIC