#recoverAskReanswerQuestions previously bailed on any non-message-typed
ancestor, but #recordToolExecutionStart() appends a custom
tool_execution_start entry between the assistant message and every
toolResult in real persisted sessions. The walk now generically skips
any ancestor that isn't the target assistant message (or a turn-
boundary user message), so the normal single-tool-call ask case
recovers correctly instead of always falling back to a plain leaf
move.
Kept top-level custom tool descriptors when a retained xd device uses the same name. Added compact and inline inventory coverage for mounted-only and dual-presentation tools.
Subprocess.kill("SIGTERM") terminates a Windows child immediately
regardless of any handler, so the child in
"is idempotent even when close() had to escalate to SIGKILL" can never
trap SIGTERM to exercise the escalation path — the >=900ms grace-window
assertion would fail spuriously on win32 even though close() behaves
correctly there. Guards it the same way stdio.test.ts's
terminateStdioProcess describe block already skips its POSIX-only
real-signal tests.
- Gate navigateTree()'s ask toolResult reopenAsk protocol behind a new
allowAskReopen option, set only by the interactive /tree selector.
Every other navigateTree() caller (extensions, hooks, ACP,
session-extension actions) now falls through to the pre-#5642 plain
leaf move instead of reporting a successful no-op navigation.
- Anchor the branch-summary entry collection on targetEntry.parentId
for an ask re-answer completion so the replaced (abandoned) answer
is included in the summary instead of silently dropped.
- #recoverAskReanswerQuestions now walks the ancestor chain past
interleaved sibling toolResults to find the assistant entry that
actually emitted the ask toolCall, instead of assuming it's the
toolResult's direct parent.
- Replace the fabricated `as unknown as AgentToolContext` standalone
tool context in SelectorController#reanswerAsk with
AgentSession#buildAskReanswerContext(), a fully-typed context
backed by real session state.
- #reanswerAsk now rejects a chatRedirect ("Chat about this") result
instead of silently completing the navigation with it.
- Fix the CHANGELOG entry's external-contribution attribution format.
- Fixed xd:// mount notices forcing their own model turn by deferring them until the next user prompt instead.
- Added `#pendingXdevMountDelta` field and `#takePendingXdevMountNotice()` to coalesce mount/unmount events and ride along with prompts.
- Mount and unmount events that cancel each other out before the next prompt are now dropped from the coalesced delta.
- Notices remain buffered during quiet startup mode (`startup.quiet`) and are delivered on the subsequent user prompt.
terminateStdioProcess() treated a detached leader's cooperative SIGTERM
exit as proof the whole process group was gone, so close() skipped the
group SIGKILL and left a SIGTERM-trapping/ignoring grandchild running as
an orphan — exactly the process tree this change set out to reap. A
detached transport now always sweeps the group SIGKILL after SIGTERM,
even when the leader itself already exited.
waitForProcessExit() also left its losing Bun.sleep() timer running
after Promise.race settled from the other side, holding the event loop
open for up to the full grace window on every close(). It now uses a
cancellable setTimeout cleared in a finally block.
Adds a regression test spawning a non-trapping leader with a
SIGTERM-trapping grandchild to cover the gap the first fix closes.
- Parsed live named efforts, mandatory-thinking state, and model protocol metadata.
- Sent native Kimi named efforts and adaptive Anthropic override efforts without generic token budgets.
Fixes#5893
selectCollapsedTodos took the active-overflow branch at active.length >= cap, so exactly cap actives plus trailing pending returned the cap rows with an empty summary — the pending work vanished with no '… N more' indicator.
Use a strict '> cap' guard so equality falls through to the normal branch, which counts every hidden row.
Fixes#5873
The first pass anchored a slice on the active task, which still showed completed rows, kept the active item mid-window, and gave the two views divergent selection logic. Per reviewer, replace it with one shared policy both collapsed views run.
selectCollapsedTodos (todo.ts) omits completed/abandoned, pulls every active task (in_progress or subagent-matched pending) to the head in todo order, fills remaining rows with following pending tasks, and emits '… N more active todos' when active work alone exceeds the cap; it falls back to closed tasks for a settled phase so HUD persistence still renders. renderTreeList gains a trailingSummary primitive so item selection lives in the todo domain. The transient tool result reaches live subagent matches via setActiveTodoDescriptionsProvider, wired from interactive mode's observer registry, so both views share the active set.
Fixes#5873
Selecting an ask toolResult in /tree previously just repositioned the
leaf onto the stale answer without re-running the interactive picker
(issue #5642). navigateTree() now detects an ask toolResult target and
returns { reopenAsk: { toolCallId, questions } } recovered from the
original toolCall's persisted arguments, instead of mutating anything.
The TUI's tree selector re-opens the ask picker via a standalone
AskTool.execute() call (reusing the live tool-execution UI context),
then calls navigateTree() again with { reanswerAskResult } to branch a
*new* sibling toolResult off the same ask toolCall -- the original
answer's branch stays fully reachable. Non-ask toolResults, and ask
toolResults whose original arguments can't be recovered (legacy/
corrupted sessions), keep the existing plain leaf-move behavior.
This implements direction 2 from the issue's maintainer triage
(re-answer as a new sibling branch), not direction 1 (resuming the
agent turn) or direction 3 (docs-only).
Before: StdioTransport.close() did a bare `this.#process.kill()` — a single
direct SIGTERM to the immediate child, with no wait and no escalation. On
Linux (and other non-Windows/non-macOS POSIX hosts), the MCP server is
spawned detached (setsid, its own session leader) so terminal job-control
signals can't stop it. A detached server that traps/ignores SIGTERM — or a
grandchild it spawns inside that session — survived omp process exit and
was orphaned, re-parented to PID 1.
After: close() runs a bounded, idempotent teardown:
1. End stdin first (cooperative EOF) so a well-behaved server can exit on
its own before any signal is sent.
2. Send SIGTERM: to the whole process group (negative-pid `process.kill`)
when this transport actually spawned detached on a POSIX host, else to
the direct child only. A negative-pid signal is never attempted for a
non-detached transport, since it could hit an unrelated group. ESRCH
from the group signal means the group is already gone (treated as
success); any other group-signal failure falls back to a direct-child
signal.
3. Wait up to ~1s for the direct child to exit; if it hasn't, escalate to
SIGKILL (group-or-direct, same rule as step 2) and wait a further
bounded ~0.5s before returning. Total worst case (~1.5s) stays well
inside the ~3s MCP disconnect-all budget in agent-session.ts dispose().
`#process` is captured into a local and nulled before the first `await`, so
repeat/concurrent close() calls see it already cleared and skip re-signaling
— idempotent per the existing contract documented above close().
Extracted the signal/escalate logic into an exported `terminateStdioProcess`
(plus a `KillableSubprocess` structural type, decoupled from the stdio pipe
generics) so tests can drive group-signal escalation with an explicit
`detached` flag — `StdioTransport.connect()` ties `detached` to the host's
real `process.platform` via `resolveStdioSpawnCommand()`, so a POSIX
detached session can't be reproduced end-to-end through `connect()` on a
non-Linux dev/CI host, but a real detached process group can still be
spawned directly on any POSIX host to exercise it.
Tests added to stdio.test.ts: detached child trapping SIGTERM escalates to
SIGKILL; a detached parent's SIGTERM-trapping grandchild is only reached by
the group SIGKILL (proves group, not direct-child-only, signaling); a
well-behaved child closes promptly without escalating; a non-detached
transport never attempts a group signal. Extended
test/mcp-stdio-transport.test.ts's existing close() idempotency coverage
with a case where the first close() had to run the full escalation path.
Fixes#5578.
Create the resolved agent database parent before the synchronous compatibility store opens SQLite, and cover a fresh nested agent directory.
Fixes#5879
Both collapsed todo renderers took fixed edge slices: the tool result kept the tail eight (truncateFrom start) and the HUD kept the head five (base.slice), so a mid-phase in_progress task fell in the omitted middle of both.
Add an anchorIndex option to renderTreeList that slides the collapsed window over the anchored item with two-sided '… N more' summaries, and anchor both call sites on the in_progress task (HUD falls back to the first subagent-matched pending task).
Fixes#5873
Caller-provided output schemas are free-form JSON and cannot be represented by OpenAI strict tool schemas. Keep todo strict while explicitly sending task as non-strict.
- Preserved goal-mode tool injection for ordinary explicit tool lists.
- Kept plan-mode LSP and IRC unavailable under the host capability clamp.
- Added regressions for both capability boundaries.
- Added per-invocation task schemas with strict and permissive validation.
- Shared task and eval agent policy, artifacts, isolation, and lifecycle handling.
- Enabled host-restricted plan-mode eval agents and persisted their capability clamp.
Fixes#5279
Moved persisted roster discovery out of the Agent Hub UI so model-facing coordination can use the same disk-backed registration path.
Rehydrated parked peers before an empty hub list response and added a crash-resume regression test.
Fixes#5864
Only a 403 response that identifies unapproved_client now blocks generateAuthUrl before its clientless probe. Invalid metadata, invalid redirect, generic forbidden, retryable, and server failures preserve the probe path.
Consolidated fallback coverage across 400, 403, 429, and 503 responses.
Fixes#5852
Added #isDefinitiveRegistrationRejection so only non-retryable 4xx DCR errors block generateAuthUrl; 408/425/429 fall through to the clientless authorization probe alongside transport and 5xx failures.
Fixes#5852
Only a 4xx DCR client error blocks generateAuthUrl; transport (status 0) and 5xx failures fall through to the clientless authorization probe so providers accepting client-less authorization keep working.
Fixes#5852
Surfaced rejected dynamic client registration from generateAuthUrl before probing or returning an authorization URL without client_id.
Added coverage for a Cropwise-style 403 unapproved_client response.
Fixes#5852