Skip to content

fix(coding-agent): preserve late advisor notes after terminal answers#5186

Merged
can1357 merged 1 commit into
mainfrom
farm/afa5b066/duplicate-final-answers-after-agent-auth
Jul 14, 2026
Merged

fix(coding-agent): preserve late advisor notes after terminal answers#5186
can1357 merged 1 commit into
mainfrom
farm/afa5b066/duplicate-final-answers-after-agent-auth

Conversation

@roboomp

@roboomp roboomp commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

Repro

A focused regression in packages/coding-agent/test/agent-session-advisor-suppression.test.ts drives a primary turn to a terminal one-line answer, then runs an advisor advise({ severity: "concern" }) after the primary is idle. Before the fix, bun run gen:tool-views && bun test packages/coding-agent/test/agent-session-advisor-suppression.test.ts failed because the late advisor finding scheduled another primary/advisor cycle instead of remaining a card.

Cause

AgentSession#routeAdvice in packages/coding-agent/src/session/agent-session.ts routed idle non-suppressed advisor concern/blocker notes through sendCustomMessage(..., { deliverAs: "steer", triggerTurn: true }). resolveAdvisorDeliveryChannel in packages/coding-agent/src/advisor/advise-tool.ts had no terminal-tail predicate, so a late advisor finding after an assistant stop answer looked like actionable idle work and woke the primary even when no queued messages existed.

Fix

  • Added a terminal text-answer/no-queued-work predicate in AgentSession and passed it into advisor delivery routing.
  • Updated resolveAdvisorDeliveryChannel to preserve late interrupting advisor notes as visible advisor cards when the primary already ended cleanly and no queued work remains.
  • Added regression coverage for the advisor-confirmation path and a resolver unit case.
  • Added an Unreleased changelog entry for packages/coding-agent.

Verification

bun test packages/coding-agent/src/advisor/__tests__/advisor.test.ts packages/coding-agent/test/agent-session-advisor-suppression.test.ts → 79 pass, 0 fail. bun check → passed. Pre-publish gate in gh_push_branch passed and pushed 708eafaf8d31. Fixes #4840.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 708eafaf8d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

*/
#hasTerminalTextAnswerWithoutQueuedWork(): boolean {
if (this.agent.hasQueuedMessages() || this.#pendingNextTurnMessages.length > 0) return false;
return isTerminalTextAssistantAnswer(this.agent.state.messages.at(-1));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve all late advisor notes after the same answer

When two late interrupting notes arrive after one completed primary answer (for example, multiple configured advisors each report a concern), the first preserve path appends an advisor custom message to agent.state.messages. This check then looks only at the new tail, no longer sees the terminal assistant answer, and routes the next note through steer with triggerTurn: true, recreating the duplicate primary turn this change is intended to avoid. Consider looking back past preserved advisor cards or caching the terminal-answer state until a real primary/user turn changes it.

Useful? React with 👍 / 👎.

Comment on lines +1394 to +1395
if (part.type === "thinking") continue;
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat terminal metadata blocks as ignorable

Terminal Anthropic answers can include non-actionable metadata blocks such as redactedThinking (extended thinking) or fallback alongside final text; the provider code persists those content types. This predicate currently ignores only plain thinking, so such an otherwise-terminal text answer returns false and a late concern/blocker still wakes a duplicate primary turn. These metadata-only blocks should be handled like thinking rather than making the answer non-terminal.

Useful? React with 👍 / 👎.

@can1357 can1357 merged commit b52a4cd into main Jul 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

vouched Passed the vouch gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Duplicate final answers after agent-authored continuation turns

2 participants