- Added `onTurnError` hook to `AdvisorRuntime` to handle failed turns before retries.
- Integrated credential blocking in `AgentSession` to prevent retrying usage-limited accounts when advisor turns fail.
- Included account key in `codex-auto-reset` debug logs to improve skip reason visibility.
Drained pending IRC asides before parking irc wait so replies that arrive between wait calls are returned instead of being treated only as queued interrupts.
Added regression coverage for the already-aborted queued-IRC signal path and documented the fix in the coding-agent changelog.
Fixes#4657
Reset per-turn maintenance counters before IRC wake prompts so yielded subagents do not carry stale yield termination into later wake turns.
Add regression coverage for empty-stop retry after an IRC wake following a yielded run.
Fixes#4658
- Allowed implicit default fallback resolution when other role fallback chains are configured.
- Covered the mixed-role first-run fallback case.
Fixes#4533
Rebuilt active advisor runtimes when modelRoles.advisor changes so live sessions stop using stale advisor models.
Added regression coverage for advisor role updates reaching the live advisor without a manual /advisor restart.
Fixes#4612
`#resolveAdvisorRuntimeDescriptors` hardcoded `ThinkingLevel.Medium` when
no thinking suffix was configured. For reasoning models with no
controllable effort surface (`devin-agent`: `reasoning: true`,
`thinking: undefined` — Cascade selects effort by routing to sibling
model ids, not a wire param), that default tripped
`requireSupportedEffort` on the first advisor prompt with an empty
`Supported efforts:` list, disabling the advisor session-wide.
Route the default through `resolveThinkingLevelForModel(model, level)`
which preserves explicit `off`, clamps a concrete effort into the
model's supported range, and returns `undefined` for reasoning models
without controllable efforts — falling back to `Inherit` so no effort is
sent while reasoning stays enabled. Matches the `auto`-path fix
(`clampAutoThinkingEffort`) and the Autonomous Memory clamp
(`clampThinkingLevelForModel`).
Fixes#4579
TTSR stream-interrupt aborts now carry a per-tool reason so the placeholder
loop labels only the tool call whose stream matched the rule with the rule
name and gives sibling committed tool calls a neutral "TTSR interrupt on
another tool call" reason. Previously the single `message.errorMessage`
was stamped onto every retained tool-call block, so unrelated read/edit
calls read as violating a rule they never matched and misled the model's
own reasoning about which call fired.
Threads the matched `toolcall:<id>` extracted from the TTSR match context
through `agent.abort(...)` as a `ToolScopedAbortReason` object; the agent
loop unwraps it in `emitAbortedAssistantMessage` into a
`toolCallAbortMessages` map on the aborted `AssistantMessage`, and the
`stopReason === "aborted"` fanout in `runAgentLoop` prefers the per-tool
message when one exists.
Fixes#2783
- Treated the active model as the default retry primary when only retry.fallbackChains.default is configured.
- Covered the first-run case where modelRoles.default is unset but a default fallback chain exists.
Fixes#4533
- Exposed retry fallback chains in the model settings panel.
- Added a /model action that assigns the selected model as the default retry fallback.
- Cleared retry cooldown suppression when users manually switch models.
Fixes#4533
- Introduced an automated retry recovery system to track, manage, and persist recovered error states within agent sessions.
- Enabled compact transcript rendering for recovered auto-retry errors by removing heuristic commit machinery.
- Improved raw read tracking and provenance in the ReadTool to support refined file snapshot recording and hashline editing.
- Excluded recovered assistant messages from default model context and updated event controllers to handle retry recovery life cycles.
- Made resolveShapeForText choose silver16-bw for CJK-heavy auto transcripts while preserving explicit variants and unsafe glyph protection.
- Added silver16-bw to the snapcompact shape settings submenu and renamed unsupported-glyph warnings.
- Covered auto shape selection, explicit variant precedence, unsafe glyph scans, and settings option parity.
Fixes#4486
- Added a Usage.orchestration sidecar for provider-side service tokens so Responses/Codex totals and costs stay accurate without inflating visible prompt input/cache buckets.
- Updated Codex/WebSocket usage, session/status aggregates, and usage reporting to preserve orchestration-aware totals.
- Added regressions for OpenAI Responses accounting, Codex WebSocket terminal usage, cost calculation, and session aggregation.
Fixes#4469
A request the provider rejects (e.g. 413 oversized payload) yields a
synthesized assistant turn with empty content and stopReason 'error'.
That turn is written to session.jsonl, so on reload it replays as an
empty assistant turn and re-sends the same rejected context. Keep the
rejection UI-only (pinned error) and out of persisted history so a
reloaded session resumes from the last good turn.
- Classified OpenAI-compatible custom relays serving OpenAI model ids into the OpenAI service-tier family.
- Passed the model into OpenAI service-tier wire gating so custom relays emit service_tier when eligible.
- Reported /fast on as unavailable when the active model has no service-tier family.
Fixes#4386
Plan-approval's 'Approve and compact context' used to pass the rendered
plan-mode-compact-instructions prompt as the first positional argument
to handleCompactCommand -> session.compact(), which landed on the
session_before_compact extension hook as customInstructions. Extensions
treating that field as user focus (e.g. to bias a query-focused summary)
would then see plan-mode boilerplate instead of operator intent and
produce query-biased compactions.
Add CompactOptions.internalGuidance: a private summarizer-only channel.
session.compact() reads it into the fallback-model summarizer while the
session_before_compact hook payload still only carries the public
customInstructions arg (undefined for the plan-compact path). The
snapcompact-disable predicate and the /compact rejectsFocus guard cover
both fields so a directed summary is never silently downgraded.
Extend the interactive-mode handleCompactCommand facade + command
controller with a fourth internalGuidance parameter, and switch the
plan-approval callsite in interactive-mode.ts to route the plan prompt
through it.
Fixes#4359
Selecting "Approve and compact context" while a user turn was typed during
compaction surfaced `Failed to finalize approved plan: Agent is already
processing` and silently discarded the operator's queued turn.
`flushCompactionQueue` fires the queued user turn (fire-and-forget) before
`handleCompactCommand` returns, so by the time `#approvePlan` resumed, the
session was streaming. The previous shape aborted the queued turn and still
raced into `AgentBusyError` when `session.prompt()` ran before abort settled.
The finalize path now queues the plan-approved directive as a synthetic
follow-up when the session is streaming, and catches a racing `AgentBusyError`
from `prompt()` with the same fallback. `AgentSession.followUp()` gained a
`{ synthetic, expandPromptTemplates, attribution }` option so the hidden
execution directive lands as an agent-attributed developer message on the
follow-up queue, without flipping advisor auto-resume the user-follow-up path
does.
Fixes#4358
Idle recap crashed with `TypeError: undefined is not an object (evaluating
'H.content.filter')` when the reporter's session_stop block-decision extension
poisoned the transcript with eight persisted `session-stop-continuation`
custom entries. The primary block-reason continuation flow was intact — the
custom message correctly renders as a `role: "developer"` LLM message —
but the follow-on idle recap fired 4 minutes later, and the side-channel
provider stream handed back a `done` event whose `message.content` had been
dropped. `runEphemeralTurn`'s sanitize step at
`assistantMessage.content.filter(block => block.type !== "toolCall")` then
tripped the TypeError, propagated as "Idle recap turn failed" in the debug
log while silently muting the session.
- Normalized the provider "done" event's `message.content` to `[]` when
the shape is not an array, so a wrapper stream that drops content surfaces
as an empty recap reply instead of an unrecoverable side-channel crash.
- Guarded `#buildEphemeralSnapshot`'s in-flight-assistant preservation
branch with `Array.isArray(streaming.content)` for the same reason.
- Added a regression test that seeds the reporter's transcript shape (one
real turn, eight persisted `session-stop-continuation` customs carrying a
multi-line block reason with U+2717 glyphs) and drives a recap through a
side stream that returns `content: undefined` on `done`. The test
fails on the pre-fix tree with the exact reporter TypeError and passes
after the guard lands.
Fixes#4323
- Added `#hasPendingAsyncWake` to detect running or pending background jobs owned by the agent.
- Deferred todo reminders and `session_stop` hook passes until all agent-owned background async jobs complete.
- Ensured scheduling pauses caused by async jobs do not trigger terminal session stops or premature todo nags.
- Added `getModel` to `AgentLoopConfig` to allow runtime model resolution.
- Updated `streamAssistantResponse` to resolve the model dynamically per provider call instead of using the stale configuration snapshot.
- Enabled mid-run model switches to take effect immediately for context promotion and retry fallbacks.
Normalized extension custom-message payloads before session state or persistence, including bare string sendMessage shorthands. Skipped legacy bare custom_message entries during context rebuilds and dropped malformed custom/hook messages before LLM conversion. Added regression coverage for the poisoned-session resume crash.\n\nFixes #4345