#1572·OpenMAIC

[Feature]: Pre-flight context-window guard for Pro agent sessions (fail fast, suggest a new session)

Author: cosarahCreated Sep 17, 2026Updated Sep 17, 2026
Labelsenhancement

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.ts loads the whole branch via loadSessionEntryHistory and passes it as history to buildAgent unchanged; transformContext (pi's only compaction hook) is never wired up.
  • .env.example documents the OPENMAIC_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:

  1. Check location: in the runner after modelMessages is computed (post planResume/repair, pre buildAgent) — 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.
  2. Estimation: pi's estimateContextTokens with the all-zero-usage fallback (same pattern as lib/chat/pi/director-compaction.ts), covering system prompt + full history + this run's pending user messages + a reserved output allowance, compared against driver.piModel.contextWindow (route pin > catalog > 128k fallback, already resolved in the runner).
  3. On breach — single, non-retryable terminal settle:
    • a dedicated terminal status (e.g. context_limit) rather than generic failed;
    • requeueIfUndelivered / planUndeliveredRequeue must 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.
  4. Gate condition = "compaction not active for this run", not "compaction unimplemented": the guard runs when OPENMAIC_AGENT_COMPACTION_ENABLED is 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.ts settle 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 (estimateDirectorContextTokens handles providers that omit streamed usage).
  • Companions: the durable-compaction issue for the Pro runtime (supersedes this guard when enabled) — cross-linked once filed.