Cursor selects server-native tools (bash, grep, ...) outside the advisor's grant. Those exec-channel blocks are stamped kCursorExecResolved: they already ran server-side through the advisor-scoped CursorExecHandlers bridge, which rejects ungranted tools in-band. quarantineAdvisorUnsafeOutput was flagging them as pre-dispatch hazards and discarding the entire turn, dropping the legitimate advise emitted alongside them.
Skip exec-resolved native blocks in the unavailable-tool check so the scoped bridge stays the grant gate and the advisor can still deliver advice.
Fixes#5900
Brings the per-advisor toggle, status-line glyphs, quota display, and the
failing-advisor stall/abort fix (f4c8143) onto main's rewritten advisor
runtime. Conflict reconciliation kept main's architecture (fingerprint
prefix reconciliation, host-level onTurnError recovery + fallback chains,
terminal-failure classification) and ported the branch semantics onto it:
- #failing latch: waitForCatchup resolves immediately while an advisor is
mid-failure; parked waiters wake the moment a turn fails, before any
async hook or retry sleep.
- Turn-end render containment: a formatter bug restores the cursor/prefix/
dedup snapshot and never propagates into the primary's turn-end callback
(per-advisor try/catch boundary in AgentSession).
- Quota pause: when host recovery declines a usage-limit failure, the
runtime latches quotaExhausted, requeues the batch, and notifies —
cleared only by an explicit reset.
- Hard halt after a permanent rejection or three backlog-drop cycles.
- #recoverAdvisorTurn also marks usage limits for structural errors thrown
before any assistant turn is recorded.
A broken advisor could hold the primary agent on the per-turn catch-up
gate for its full 30s budget while retrying, and an exception thrown from
onTurnEnd propagated into the primary's turn-end callback.
- waitForCatchup resolves immediately while the advisor is mid-failure
(new #failing latch, set at the failure catch BEFORE any async hook,
cleared on the next successful turn or reset/seed).
- Every parked waiter is woken the moment an advisor turn fails.
- The turn-end boundary isolates advisor exceptions per advisor: a
throwing advisor loses its delta, the primary and sibling advisors
continue untouched.
- A failed render (poisoned message, formatter bug) restores the delta
cursor and dedup state, so the delta is re-rendered next turn instead
of silently lost; the size probe itself is guarded and falls back to
the deferred renderer.
Grafted the evaluator's port (ec2c1e632) onto the merged advisor
runtime: terminal provider failures classified non-retriable (and not
context overflow) drop the bounded batch after one attempt with a
single notification; fallback-chain recovery and overflow recovery
retain precedence. Includes the one-prompt regression test and tags the
rollback-retry fixture's synthetic failure as transient.
Semantic merge with #5734 (delivered-prefix reconciliation) and #5748
(fallback chains): kept the coalescing round cap and wip threading,
adopted bounded cursor-preserving maintenance resets and overflow
recovery, and gated late-arrival consumption on coalescing rounds so
both suites' backlog and preserved-updates contracts hold.
Two shared failure modes with a single misbehaving advisor:
- A permanently rejected request (invalid_request_error, e.g. a model the
account no longer supports) retried forever: one notice, then silent
re-attempts on every turn, rebuilding heavy context each cycle. Quota
exhaustion already paused with a notice; this class now hard-stops the
runtime after a permanent rejection or three consecutive backlog-drop
cycles, with a visible notice. An explicit reset (/new, config rebuild,
restart) re-enables it, and waitForCatchup resolves while halted so the
primary agent never parks on a runtime that cannot drain.
- The delta render ran synchronously on the event loop; replaying a
multi-MB transcript after a reset blocked it for 600ms+ per render
(measured 675ms at ~54MB). Large deltas now render in size- and
count-bounded chunks that yield between slices (675ms -> single-digit
ms stalls). Tool call/result pairing survives chunk boundaries via a
shared whole-delta result index in formatSessionHistoryMarkdown; small
per-turn deltas keep the synchronous fast path.
- Switched advisor turns to the next configured model after provider quota or rate-limit failures.
- Emitted fallback applied and succeeded lifecycle events without reporting advisor unavailability after recovery.
- Added an end-to-end advisor quota fallback regression test.
Fixes#5740
- Tracked delivered message identities alongside the numeric cursor.
- Re-primed advisor context when a live transcript prefix diverged.
- Covered accepted empty-stop pruning before the next real user turn.
Fixes#5731
Cursor synthesizes an executed native delete as a tool call named delete. When write or edit enabled native deletion, the advisor quarantine allowlist still omitted that synthetic name and rolled back the completed turn.
Add delete to the allowlist from the same mutation-capability boolean that enables native deletion, with regression coverage for the quarantine path.
Fixes#5680
A late interrupting advisor note delivered after the primary ended with a
terminal text answer (no queued work) was routed to `preserve` for every
severity, so a `blocker` flagging a mistake in the final output became a
passive card that the model ignored until the next user turn.
Scope the terminal-answer preserve rule to non-blocker severities: a
`blocker` now steers a triggered turn so the primary acknowledges and
continues before the turn is considered done, while a late `concern` still
preserves as a visible card (keeps the #4840 no-duplicate-completion path).
Fixes#5628
- Replaced the advisor turn validation to raise errors only when a turn has no assistant response at all, instead of treating a content-less `stop` as a failure.
- Kept zero-content silent reviews from entering the retry/rollback/warn path by no longer raising "Advisor unavailable" for completed empty-stop turns.
- Updated advisor runtime tests and changelog language to assert and document that consecutive zero-usage silent turns now succeed without notifications.
The advisor runtime rejected every content-less stop completion as a
failed turn, so a deliberate silent review (the documented verifier
behavior) triggered retries and a spurious "unavailable" warning. Only
treat a content-less stop as a failure when it produced no output signal
(zero output and reasoning tokens), so a token-bearing silent stop counts
as a successful silent review.
Fixes#5493
- Anchored advisor compaction on provider-reported context usage (cached
input + generated output) floored by a full local estimate including the
advisor system prompt and tool schemas, so a near-full cached context is no
longer undercounted by the per-message estimate.
- Rejected stale provider usage retained across advisor compaction via a
runtime-only usage-anchor boundary recorded on the summary message.
- Recovered provider overflow by clearing only the advisor's own context at
the current primary cursor, retrying the bounded failing batch once against
a fresh context without replaying old primary history, and keeping later
updates eligible.
- Threaded the selected dashboard range through the stats Recent Errors UI,
API, and database timestamp filter before ordering and the 50-row limit.
Fixes#5282
When onTurnError signals a switched sibling credential (switched=true),
the retry error is now classified via isQuotaError:
- Second quota: marks the sibling via onTurnError, then enters quota pause
- Non-quota transient: routes through onTurnError and the generic
failure/requeue/notify path instead of unconditionally pausing
Treat content-less advisor stop completions as failed turns so the advisor retry/drop path handles silent provider responses instead of accepting them as successful reviews.
Fixes#5212
Blockers from final review:
- runtime.ts:457 TS2741: #collectAndMaintainBatch now returns wip in its
result type; #drain destructures it and passes it to the retry-requeue
unshift — a WIP batch that fails retry no longer silently loses its
[in progress] heading.
- agent-session.ts import order: annotateForStaleness moved before
formatAdvisorBatchContent (biome enforces case-insensitive alpha order).
Cap edge case (advisor nit + review CONCERN): final round of the
coalescing for-loop now breaks BEFORE the late-item splice so any
deltas that arrived during round MAX_COALESCE_ROUNDS-1's
maintainContext call stay in #pending for the next drain iteration
instead of being merged into an unbudgeted batch. Doc comment updated
to match ('left for the next drain iteration' is now accurate).
Cap test: bounded version that only pushes new turns for the first 3
maintainContext calls so the drain while-loop terminates cleanly after
a second iteration. The previous unbounded version created an infinite
drain loop (each maintainContext call unconditionally pushed another
turn) and timed out.
Blocker: wip field added to PendingDelta and threaded into the reprime
path. #collectAndMaintainBatch now captures the most-recent WIP state
from each delta batch and forwards it to #renderDelta in both the
normal and reprime branches, so a willContinue:true turn never loses
its [in progress] heading through a reprime.
Safety cap: MAX_COALESCE_ROUNDS=3 constant defined and used in the
coalescing for-loop, preventing indefinite dispatch stall under
pathological fast-primary + slow-maintainContext conditions.
Testability: annotateForStaleness extracted as an exported pure function
in advise-tool.ts and used in AgentSession#routeAdvice. Three unit
tests added in advisor.test.ts covering the no-staleness, staleness,
and note-preservation contracts. This addresses the ReviewSession
regression-test concern without requiring a full AgentSession harness.
Reprime turn-tally coverage: new test 'backlog stays accurate when a
delta arrives during the reprime-triggering maintainContext' asserts
runtime.backlog === 0 after all three turns, catching a deleted
turns += reduce(...) line.
Fragile double-await tests converted: sends-batch-when-maintenance-fails
and expands-plan-mode-context now use Promise.withResolvers signals
instead of counted await Promise.resolve() hops.
Docs: onTurnEnd JSDoc added; hasFreshBacklog comment broadened to cover
all drain-busy phases (not just agent.prompt).
Three related changes that address the pattern of the advisor flagging things
the primary already fixed:
Fix 1 — coalesce late-arriving deltas before agent.prompt (runtime.ts)
Refactored #drain into a reusable #collectAndMaintainBatch helper that loops
until the pending queue is stable (no new deltas arrive during a maintenance
check) before calling agent.prompt. Previously, any turn queued during the
maintainContext await was deferred a full extra model-call cycle; now it is
merged into the current batch after re-checking the token budget for the
expanded payload. Every await in the loop has an epoch guard so a
reset/dispose mid-await cannot leak a stale batch. finalTurns always counts
all merged turns so #backlog decrements correctly.
Fix 2 — hasFreshBacklog + delivery-time staleness annotation (runtime.ts, agent-session.ts)
Added AdvisorRuntime.hasFreshBacklog getter (true when #pending.length > 0
while agent.prompt is running — i.e., newer primary turns arrived after the
reviewed window). #routeAdvice checks it at delivery time and appends a
lightweight caveat to the note so the primary agent knows to verify before
acting. Uses #pending.length not #backlog, which is always > 0 mid-call.
Fix 3 — willContinue WIP marker in rendered delta + system prompt (runtime.ts, agent-session.ts, system.md)
onTurnEnd now accepts { willContinue } and passes it through to #renderDelta,
which tags the heading '[in progress — more steps follow]' for intermediate
turns. The agent-session.ts call site passes context.willContinue. The advisor
system prompt instructs the model to withhold critique on WIP updates.
Also fixed pre-existing inline casts in #renderDelta and #dedupContextMessage
that suppressed the type checker instead of using the narrowing already provided
by the role discriminant.
All 75 advisor tests pass; pre-existing type errors in cursor.ts are unrelated.
Included advisor tool-result text in the quarantine source check so legitimate findings from granted read/grep tools are not treated as model-generated contamination.
Kept assistant text out of the source set to avoid laundering prior advisor hallucinations.
Fixes#5181
Scanned allowed advise tool notes for output-only destructive directives before the tool can route them to the primary agent.
Kept provenance checks against the watched session update so legitimate warnings about user-provided dangerous text still pass.
Fixes#5181
Prevented late interrupting advisor findings from waking the primary after a terminal text answer when no queued work remains.
Added regression coverage for the advisor-confirmation path so duplicate primary turns are caught.
Fixes#4840
Cleared provider-native replay payloads and stop details when Advisor output is quarantined so persisted transcripts only contain the sanitized error.
Added regression coverage for OpenAI Responses-style providerPayload leakage.
Fixes#5181
Quarantined Advisor assistant turns that request tools outside the granted tool pool before they can enter the Advisor context.
Reset the Advisor runtime after quarantine so the next update re-primes from the primary transcript instead of replaying contaminated private context.
Fixes#5181
Separated advisor provider session identity from local advisor labels so Codex requests carry stable UUIDv7 values while transcripts keep their advisor-specific names.
Fixes#5040
- Quota-paused advisor retains the failed batch in pending queue (not dropped),
releases catchup waiters so the primary agent isn't blocked, and replays the
turn after the quota cooldown resets
- Legacy no-model advisor status now tracked via slug 'default' instead of ''
so #advisorStatuses covers all configurations
- discoverAdvisorConfigs() now preserves the enabled field from WATCHDOG.yml
(was silently dropped during YAML discovery)
- Test error message fixed to match isQuotaError regex (rate limit with space)
- Add 'Enabled' toggle field to detail editor (● on / ○ off)
- Show ●/○ markers in roster list for enabled/disabled advisors
- Show enabled status in advisor preview panel
- Add overlay test verifying disabled advisors render with ○ marker