- Replaced time-based sleeps and polling loops with event-driven promise resolvers and fake timers across agent and tool tests.
- Migrated test suites to share in-memory auth storage and fixtures using lifecycle hooks.
- Updated catalog model definitions, metadata, and configurations.
Scheduling agent.continue() inside navigateTree started a post-prompt
task before the interactive /tree handler rebuilt its transcript, so a
fast provider's agent_start/turn_start could render against the stale
pre-rebuild UI and then be clobbered by renderInitialMessages.
navigateTree now reports the commit via askReanswerCommitted instead of
resuming, and SelectorController.showTreeSelector calls the new
AgentSession.resumeAfterAskReanswer() only after renderInitialMessages +
reloadTodos, so the resumed turn always renders against the rebuilt
transcript.
Addresses chatgpt-codex-connector review on #6484.
Fixes#6483
Committing a new answer to a past `ask` via `/tree` branched a fresh
sibling toolResult and rebuilt context, but `navigateTree` never
scheduled `agent.continue()`. Unlike a live `ask` — whose continuation
is intrinsic to the streaming run loop — the /tree re-answer mutates the
tree outside any running turn, so the model never consumed the new
answer and the session sat idle until a manual prompt.
Schedule an agent continue at the end of the reanswerAskResult
completion, gated to the ask re-answer branch only so plain leaf moves
and the read-only reopenAsk probe stay idle.
Fixes#6483
navigateTree()'s targetId === oldLeafId no-op short-circuit ran before
the ask re-answer probe/completion block, so selecting an ask
toolResult that is already the current leaf silently reported success
without returning reopenAsk or branching a new answer. This happens
when the user interrupts right after answering ask (before a follow-up
assistant message lands) or another caller navigates straight onto the
ask result. Exempt allowAskReopen probes/completions targeting an ask
toolResult from the short-circuit so the two-phase re-answer protocol
still runs.
The `/tree` ask re-answer recovery path (#recoverAskReanswerQuestions)
read the persisted ask toolCall's raw arguments directly, so when secret
obfuscation is active the recovered question text could still contain
`#HASH#` placeholders instead of the original secret. The live tool
path already deobfuscates via transformToolCallArguments before use;
apply the same deobfuscateToolArguments call to the recovery path.
Wires an optional obfuscator through TestSessionOptions/createTestSession
so tests can exercise sessions with secret obfuscation active, and adds
a regression test confirming the reopened ask picker shows deobfuscated
plaintext rather than the raw placeholder.
#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.
- 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.
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).