Auto-retry empty/reasonless provider aborts without model fallback (issue #2685). Includes review fix b7a3d01439: skip the retry while the session is disposing to avoid a shutdown hang.
A dispose-driven bare abort() yields the same empty/reason-less aborted turn as a transient provider abort, but with #isDisposed set and #abortInProgress unset. #isRetryableReasonlessAbort matched it and routed it through #handleRetryableError, which created #retryPromise and scheduled a continuation the disposed guard then skipped without resolving the promise — hanging the in-flight prompt() in #waitForPostPromptRecovery during shutdown.
Guard the predicate on !#isDisposed so lifecycle aborts settle the turn, and add a regression test. Addresses review feedback on #2689.
Empty provider-side aborted turns now enter the existing auto-retry path without switching retry model fallback, while aborted turns with partial content still settle normally.\n\nFixes #2685
The first-message eager-task / eager-todo preludes are the oldest messages in a
session, so auto-compaction summarizes them away and the agent silently loses the
delegate-via-tasks / phased-todo guidance mid-work. Re-assert those reminders on
the auto-continuation turn that follows a compaction.
- Widen #createEagerTaskPrelude / #createEagerTodoPrelude to accept
`string | undefined`; `undefined` (post-compaction) skips only the
first-message and prompt-suffix gates, keeping the mode / agent-kind /
plan-mode / surviving-todo / active-tool gates intact.
- Reminder-only post-compaction: the todo nudge never attaches a forced `todo`
tool_choice on the resumed turn (forcing a tool after a mid-turn compaction
would override the agent's in-flight action).
- Add #buildPostCompactionEagerNudges() and prepend its output on the single
#scheduleAutoContinuePrompt continuation hook. All three call sites are
willRetry-safe, so overflow/incomplete retry recoveries never carry the nudge.
Op: extend
- SetAdvisorEnabled now used settings.override for advisor.enabled when enabling or disabling, keeping advisor toggles session-local.
- /advisor on|off handlers now called refreshStatusLine after each toggle, and the status line updates immediately in the UI.
- A regression test was added to assert setAdvisorEnabled invokes override with both values and does not call set.
- Passed USER_INTERRUPT_LABEL through abort paths in collab, ACP, RPC, runtime, and SDK flows.
- Added userInitiated to synthetic continue inputs and session prompt calls.
- Suppressed advisor auto-resume during user aborts and preserved queued concerns.
- Cleared suppression on user prompts and reclaimed parked advisor cards on abort settle.
Checked structured classifier refusals before the interrupted-output retry guard so provider refusals with explanatory content still use the configured fallback path.
Fixes#2683
- Added an includeToolIntent option to session history formatting to control intent comments.
- Updated tool call rendering to prefix call lines with a leading comment when the tool argument contains a non-empty intent field.
- Enabled intent rendering in advisor history updates and added tests for intent-on and intent-off output paths.
- Added awaited `onTurnEnd` and `setOnTurnEnd` wiring for turn-end callbacks.
- Added `advisor.syncBacklog` settings (off/1/3/5) and documented 30-second catch-up caps.
- Fixed advisor runtime backlog handling with failure counters, waiters, and retry requeue.
- Updated agent sessions to enqueue advisor updates on turn end and removed direct `turn_end` branch logic.
Prevented auto-retry from regenerating write calls after a provider stream timeout has already exposed assistant content or tool-call arguments.
Fixes#2683
- Added discovery of local, user, and ancestor `WATCHDOG.md` files via `discoverWatchdogFiles`.
- Appended discovered watchdog prompts to advisor system prompts during session setup.
- Added protocol startup defaults that force `advisor.enabled` and `advisor.subagents` false.
- Handled `maintainContext` failures and drained pending updates before token estimation.
- Added advisor context maintenance hook and token estimation before prompting for auto-upkeep.
- Added re-prime replay handling to reset advisor context and recover deferred prompts.
- Implemented session-level context compaction with model promotion and snapcompact-first fallback summarization.
- Surfaced advisor settings in the model tab and updated advisor system guidance text.
- Created AdvisorRuntime and AdviseTool to drive a read-only advisor agent that delivers severity-tagged advice (nit, concern, blocker) with interruption policy and transcript delta rendering.
- Added /advisor slash command with on/off/status/dump subcommands to control advisor lifecycle and inspect advisor metrics (model, messages, tokens, cost).
- Added advisor.enabled and advisor.subagents settings to enable passive advisor review on main agent and spawned task/eval subagents.
- Implemented advisor message rendering with severity-color badges (blocker=error, concern=warning, nit=muted) in chat log and status line indicator (++ badge).
- Extended yield-queue and session-history-format to support advisor batching and optional thinking block inclusion.
Keep provider-anchored pre-prompt checks based on the prior provider prompt usage plus only positive current system/tool token growth, avoiding a second full charge for unchanged non-message context.
Fixes#2628
- Renamed ToolCallSyntax type to Dialect and Grammar interface to DialectDefinition across all packages.
- Moved grammar directory to dialect and updated all import paths in agent, ai, catalog, and coding-agent packages.
- Added renderTranscript and renderThinking methods to DialectDefinition, enabling native dialect-aware conversation serialization.
- Consolidated rendering utilities into new dialect/rendering.ts with shared helpers for ChatML, legacy text, and dialect-specific formatting.
- Updated conversation serialization in agent and coding-agent to use dialect.renderTranscript() for native turn envelope rendering.
- Canonicalized assistant and thinking messages by trimming and collapsing dot text.
- Skipped rendering assistant and thinking blocks when canonicalized content was empty.
- Filtered ACP thinking notifications and session outputs to ignore placeholder content.
- Added canonicalizeMessage tests for undefined, blank, whitespace, and dot-only inputs.
- Updated compaction, branch summarization, and session dump formatting to pass preferred model tool syntax into conversation serialization.
- Enhanced shared serializers to render assistant tool calls and tool results through grammar envelopes when syntax is available, with the prior compact format as fallback.
- Aligned prompt, preview, and test fixtures to the new transcript tags: `[Think]`, `[Tool Call]`, and `[Tool Result]`.
- #handleAgentEvent now delegates non-`agent_end` events to a new `#processAgentEvent` routine.
- For `agent_end`, it tracked a resolver promise via `#trackPostPromptTask` before awaiting event handling and resolved it in `finally`.
- This kept `#waitForPostPromptRecovery()` from returning before deferred compaction or handoff work was registered.
Track the non-message token estimate used for provider-anchored assistant usage, then add only positive current system/tool growth during pre-prompt threshold checks. Restore released collab changelog entries to the immutable 15.13.1 section.
Fixes#2628
Include the current system prompt and active tool schemas when provider-anchored pre-prompt checks estimate threshold pressure, and cover the case with a regression test.
Fixes#2628
Use provider-anchored context usage for pre-prompt context-full threshold checks when available, then add only pending prompt tokens. This keeps OpenAI Responses encrypted reasoning payloads from overcounting local prompt pressure while the visible context usage remains below threshold.
Fixes#2628
- Added `features.unexpectedStopDetection` and `unexpectedStopModel` settings for opt-in behavior.
- Added assistant-stop handling to classify stop reasons and resume generation with retry prompts.
- Added unexpected-stop classifier logic with candidate checks, model fallback, and YES/NO parsing.
- Added retry tracking that caps auto-continues at three attempts and logs a warning when exceeded.
- Added `jsonSchemaToTypeScript` and `renderToolInventory` to generate tool blocks with TypeScript signatures.
- Added `examples` and `TSchema` fields to dump-tool metadata and passed them through prompt rendering.
- Changed Harmony invocation rendering to omit `<|constrain|>json` markers in tool call payloads.
- Added compact native tool list-mode inventory rendering with full `# Tool:` output elsewhere.
Persisted isolated subagents created their fresh JSONL session through
SessionManager.open(), which fell back to getProjectDir() when the file had no
header. createAgentSession still received the isolated worktree cwd, but built-in
tools resolve paths through sessionManager.getCwd(), so file tools could target
the parent repository while patch capture saw no isolated delta.
Allow SessionManager.open() to take an initial cwd for empty/missing session
files and pass the isolated worktree cwd from task execution. Non-empty resumes
still use the persisted header cwd. Add regression coverage asserting persisted
isolated subagent sessions expose the worktree cwd through their session
manager.
The pre-prompt context check ran compaction directly, so snapcompact (or
any strategy) fired before auto-promote ever got a chance — defeating
Auto-Promote Context. It now tries promotion to a larger-context model
first (mirroring the post-turn threshold path) and only compacts when no
target is available.
Auto and manual compaction now project a snapcompact result's
post-compaction size (kept history + frames at the image budget + summary
+ non-message overhead); when it still exceeds the model's usable window,
they downgrade to a context-full LLM summary instead of leaving the
session overflowing.
- Removed the streaming guard that previously rejected /tan while the parent response was still generating.
- Passed "deliverAs: \"nextTurn\"" when sending the background dispatch breadcrumb and kept "triggerTurn: false" so an in-flight turn is not steered.
- Skipped rebuilding chat messages during streaming sessions and updated tests to cover the non-blocking dispatch path.
`#checkTodoCompletion` used to append a `<system-reminder>` and then call
`#scheduleAgentContinue`, so a text-only acknowledgement ("paused at your
instruction") triggered another `agent_end` that re-ran the same check and
fired the next reminder — counter ticked 1/3 → 2/3 → 3/3 inside a single user
pause without any user input. The user perceived three back-to-back reminders
appear from nowhere; the agent felt implicit pressure to invent busy-work or
take destructive ops to silence the loop.
Added `#todoReminderAwaitingProgress`: a reminder sets it, any `toolResult`
(real tool-level progress) or a new user prompt clears it, and
`#checkTodoCompletion` stays silent while it is set. Reset alongside
`#todoReminderCount` on user prompts, session reset, handoff, and the no-op
short-circuits in `#checkTodoCompletion` so the field never gets stuck.
Escalation through `todo.reminders.max` still works when the agent makes
tool-level progress between stops — that is the case the cap was designed for.
Regression coverage in `agent-session-todo-reminder-loop.test.ts` drives a
mocked `agent.continue` to mirror the bug-reported model behaviour and pins
the contract: exactly one reminder per user pause when the agent only
acknowledges; re-escalation when the agent actually calls a tool between stops.
Fixes#2590
- agent-loop: raise repetition-detection floor to 180 chars and clear thinking
replay anchors when collapsing a detected loop.
- providers/google: ignore empty text parts, retain terminal thoughtSignatures,
and stop function-call signatures clobbering the prior block.
- autolearn: capture goal-mode at the turn boundary; harden managed-skill writes
against hard-links/symlinks (O_NOFOLLOW + nlink); refuse minting managed skills
whose name an authored skill already claims.
- eager tasks: thread agentKind through the session so a custom top-level agentId
still gets always-mode delegation; split Eager Tasks prompt into hard vs soft.
- title-generator: race the online title model against a local tiny-model fallback.
- eager-todo: keep the soft reminder aligned with the todo init schema.
- mcp/stdio: keep close() detaching the read loop instead of awaiting it.
- stream loop: fix collapsing and tool-call thought-signature handling.
- Kept queued resolve handlers pending when a downgraded forced tool choice completes without invoking the requested tool.
- Covered the skipped-tool and invoked-tool queue paths in tool-choice queue tests.
Fixes#2546
The sqlite_master probe runs before any other statement and can take a
read lock or trigger WAL recovery, so under the same concurrent-startup
race fixed in #2421 it could throw SQLITE_BUSY before PRAGMA
busy_timeout ran. Install the busy handler immediately after opening
the Database, matching every other init path.
Fixes#2421
- Added a generation-aware early-exit check in `#waitForPostPromptRecovery` to return when the prompt turn is superseded.
- Passed the current prompt generation into the post-prompt recovery wait after prompting.
- Ensured aborted prompts no longer block while later queued retry/follow-up turns drain.
Two controller bugs from review:
- The post-stop nudge still queued a passive `nextTurn` message during goal
mode (goal mode only disabled `autoContinue`). That message rides the goal
continuation and can divert the goal loop into capture. Return early when
goal mode is active.
- `#suppressNext` was latched before the fire-and-forget `sendCustomMessage`.
It must arm synchronously (the synthetic turn's `agent_end` fires inside
`sendCustomMessage` before it resolves), but a rejected or *deferred* dispatch
(ACP clients downgrade `triggerTurn` to a queue) then produces no `agent_end`,
so the latch swallowed the next real stop. `sendCustomMessage` now returns
whether it actually started a turn; the controller disarms the latch when no
turn ran (rejection or deferral).
`task/executor.ts` widens its pending-message array to `Promise<unknown>[]` to
absorb the new return type (the resolved values are discarded).
Addresses review threads on PR #2542 (threads 5, 9, 12).
- Routed `customType: "handoff"` messages to the compact divider path in Agent Hub and UI helpers.
- Added handoff summary expansion that extracts context text and strips `<handoff-context>` wrappers.
- Refactored shared divider rendering into `SummaryDividerComponent` used by compaction and handoff messages.
- Added a `snapcompact-savings.jsonl` append-only journal for per-tool savings records.
- Extended `SnapcompactInlineTransformer` to compute `savedTokens` and emit savings to an optional sink.
- Integrated `createSnapcompactSavingsRecorder` into `createAgentSession` for session-aware tool-result metrics.
- Skipped journaling entries without a session and deduped duplicate `toolCallId`s per recorder lifecycle.
- Added tests for savings-sink emissions and savings-journal read/write behavior.
- Added session-domain modules and exports for session-entries, context, listing, loader, and migrations.
- Changed persistence to async append writes plus writeTextAtomic, removing sync line APIs.
- Added compaction-aware session context rebuild with dangling tool-call cleanup.
- Added resumable session resolution with status inference, id/stem/suffix matching, and backup recovery.
- Validated queued toolChoice against active tools in agent and coding-agent sessions.
- Rejected queued forced choices with reason "unavailable" when selected tools were inactive.
- Dropped provider toolChoice payloads when requested function tools were not offered.
- Probed Tokio worker-thread support and fell back to current-thread runtime creation.
Adds a fastModeScope setting (both|openai|claude, default both). setFastMode(true)
derives the service tier from it (both->priority, openai->openai-only,
claude->claude-only) instead of hardcoding unscoped priority; the off path and
the already-on no-op are unchanged. /fast status now reports the active scope.
Default both preserves existing behavior.
- Fixed initial assistant persistence by synchronously materializing in-memory entries and keeping a writer open.
- Fixed mid-close append handling to write entries through a one-shot sync writer instead of queuing a rewrite.
- Fixed persist-task gating by tracking pending writes before starting immediate persistence.
- Queued steering now drains after session settlement, so aborted auto-continued turns no longer leave queued messages stranded.
- Resumable-state detection now treats tool-result messages as resumable so continue can process queued steering after an interrupted tool execution.
- Regression tests were added for queued steer draining after abort and after an interrupted tool result.