Skip to content

fix(agent-core-v2): stop tool actor teardown from aborting sibling tools - #3626

Merged
sailist merged 4 commits into
MoonshotAI:mainfrom
sailist:bug-157-09-07-tool-batch-sibling-abort
Sep 8, 2026
Merged

fix(agent-core-v2): stop tool actor teardown from aborting sibling tools#3626
sailist merged 4 commits into
MoonshotAI:mainfrom
sailist:bug-157-09-07-tool-batch-sibling-abort

Conversation

@sailist

@sailist sailist commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — regression found by bisecting release e2e failures to #3580.

Problem

When the model emits a batch of parallel tool calls, the first tool to finish aborts its still-running siblings: users see TOOL ERROR / "Tool X was aborted" on calls that should have completed normally, and MCP tools emit spurious notifications/cancelled.

Mechanism: #3580 gave every tool actor its own AbortController and merged all of a batch's signals via AbortSignal.any for the executor service. When a tool completes, XState stops the invoked callback actor and its teardown hook unconditionally aborts the controller — firing the merged batch signal, so the executor service marks every still-running sibling as aborted. The pre-#3580 loop used a turn/step-level shared signal, so completing one tool never aborted anything.

What changed

  • Cancellation signals are owned at the spawn sites, not inside actors. The agent machine creates one AbortController per tool call (ToolInput.signal) and the turn machine creates one per LLM attempt (LlmInput.signal). Tool and request actors are now pure execute-and-report: no controller, no receive, no teardown hook. A finished tool's teardown can no longer fire the shared batch signal — the sibling abort is eliminated structurally, not guarded.
  • Abort is delivered synchronously at the decision points. input.abort / turn.abort abort the owned controllers directly (the tool.abort event remains for tools still waiting on permission); stopTurnTools aborts-then-stops. The llm.abort command event and the requester machine's streaming/aborting substates are removed.
  • Turn abort is immediate during generation and bounded-grace during tool execution. thinking/retrying/draining go straight to aborted, salvaging the partial assistant message. acting gains running/aborting substates: tool outcomes keep collecting real results during a grace window (abortGraceMs, default 2.5s, aligned with the executor's 2s abort grace), then missing outcomes are synthesized as aborted; a repeated abort completes immediately.
  • Regression test: a parallel batch where a fast tool completes while a slow tool is gated — asserts the slow tool's own signal is never aborted and both results land (replaces a zero-value DI smoke test to keep the test count flat).

Verified: focused agent-core-v2 suites (150 tests), tsc --noEmit, scoped oxlint.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue (none — internal discovery via e2e regression bisection).
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: c7d122c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Sep 7, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@c7d122c
npx https://pkg.pr.new/@moonshot-ai/kimi-code@c7d122c

commit: c7d122c

@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: f4e2342dc4

ℹ️ 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".

function createExecuteActor(executor: ToolExecutor) {
return fromCallback<ToolEvent, ToolInput>(({ input, sendBack, receive }) => {
const controller = new AbortController();
return fromCallback<ToolEvent, ToolInput>(({ input, sendBack }) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Abort detached tools when their parent actor stops

When a tool has called detach, the foreground turn can finish and AgentLoopService.dispose() sees no active turn, so it calls engine.stop() without invoking any of the new controller-abort actions. Stopping the spawned callback actor no longer aborts its signal here because its teardown was removed, allowing a signal-aware background MCP/process tool to keep executing after its agent or session has been disposed. Preserve an actor-stop cleanup that aborts the externally owned controller, or explicitly abort every background controller before stopping the engine.

Useful? React with 👍 / 👎.

const waitForTasks = createWaitForTasks(self);
const turnTools = { ...context.turnTools };
for (const toolCall of event.toolCalls) {
const controller = new AbortController();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add a CLI changeset for the user-visible fix

This commit fixes a user-visible CLI regression—parallel tool calls previously surfaced spurious abort errors—but it contains no .changeset entry. The repository's changeset policy requires user-perceivable internal-package bug fixes to record a patch for @moonshot-ai/kimi-code; otherwise this fix will be omitted from the generated release changelog.

AGENTS.md reference: AGENTS.md:L85-L87

Useful? React with 👍 / 👎.

@sailist

sailist commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator Author

@codex

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: c7d122cee2

ℹ️ 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".

@sailist
sailist merged commit 6e51631 into MoonshotAI:main Sep 8, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant