Skip to content

fix(agent-core): record and close tool calls interrupted mid-stream - #1790

Merged
RealKai42 merged 1 commit into
mainfrom
kaiyi/port-of-spain
Jul 16, 2026
Merged

fix(agent-core): record and close tool calls interrupted mid-stream#1790
RealKai42 merged 1 commit into
mainfrom
kaiyi/port-of-spain

Conversation

@RealKai42

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — the problem is explained below (from a real user session export).

Problem

A user session got wedged: every turn failed with HTTP 400 the message at position 35 with role 'assistant' must not be empty, and the compaction request failed the same way, so the session could not recover on its own.

Root cause: when the provider stream breaks off mid-tool-call (Anthropic pause_turn, engine overload, token limit), the loop dropped the partial tool call silently — never executed (correct), but also never recorded. The step persisted as an assistant message whose only content was an empty thinking block, which strict endpoints reject as an empty message on every subsequent request.

What changed

  • When a step ends abnormally (paused / unknown / max_tokens) while the response still carries tool calls, each call is now recorded — id and name as received, arguments sanitized to {} when the truncated JSON is unparseable — and immediately closed with a synthetic interrupted error result. The calls are never executed; the exchange stays wire-valid; the model learns the calls never ran so it can re-issue them cleanly.
  • Filtered responses keep their existing behavior (calls vanish, so flagged content is not persisted and re-sent), and a well-formed tool_use response whose turn is stopped by usage recording keeps its existing skip behavior.
  • Verified against the real session export (replaying the wire log with the fix applied clears the rejection) and with a full-pipeline smoke matrix (loop recording → context replay → projection → provider request body) covering paused/overloaded/max_tokens interruptions, argument sanitization, parallel calls, resume, compaction, and the strict-resend projection.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • 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.

When the provider stream breaks off mid-tool-call (pause_turn, engine
overload, token limit), the step ended with the partial tool call dropped
silently: never executed, never recorded. If the response carried no other
usable content, the persisted assistant message was effectively empty and
every subsequent request — including compaction — was rejected with
"assistant must not be empty" (HTTP 400), wedging the session.

Record each unexecuted call instead (arguments sanitized to {} when the
truncated JSON is unparseable) and immediately close it with a synthetic
interrupted error result. The exchange stays wire-valid, the history stays
truthful, and the model learns the calls never ran so it can re-issue them.
@changeset-bot

changeset-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d2c589f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

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

@pkg-pr-new

pkg-pr-new Bot commented Jul 16, 2026

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

commit: d2c589f

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

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

type: 'tool.result',
parentUuid: toolCall.id,
toolCallId: toolCall.id,
result: { output: UNEXECUTED_TOOL_CALL_OUTPUT, isError: true },

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 Run finalize hooks for interrupted tool results

In contexts with a finalizeToolResult hook, a provider response that ends as paused, unknown, or max_tokens with tool calls now persists this synthetic error result directly, so the hook never sees it. Other error result paths, including preflight failures, are finalized before persistence; bypassing that here skips host redaction/budgeting and the Agent's PostToolUseFailure hook path for exactly these recorded tool.results. Please route the synthetic interrupted result through the same finalization path before dispatching it.

Useful? React with 👍 / 👎.

@RealKai42
RealKai42 merged commit 373abb0 into main Jul 16, 2026
18 checks passed
@RealKai42
RealKai42 deleted the kaiyi/port-of-spain branch July 16, 2026 13:12
@github-actions github-actions Bot mentioned this pull request Jul 16, 2026
@wbxl2000 wbxl2000 mentioned this pull request Jul 17, 2026
5 tasks
7723qqq pushed a commit to 7723qqq/kimi-code that referenced this pull request Jul 17, 2026
…oonshotAI#1790)

When the provider stream breaks off mid-tool-call (pause_turn, engine
overload, token limit), the step ended with the partial tool call dropped
silently: never executed, never recorded. If the response carried no other
usable content, the persisted assistant message was effectively empty and
every subsequent request — including compaction — was rejected with
"assistant must not be empty" (HTTP 400), wedging the session.

Record each unexecuted call instead (arguments sanitized to {} when the
truncated JSON is unparseable) and immediately close it with a synthetic
interrupted error result. The exchange stays wire-valid, the history stays
truthful, and the model learns the calls never ran so it can re-issue them.
7723qqq pushed a commit to 7723qqq/kimi-code that referenced this pull request Jul 17, 2026
…oonshotAI#1790)

When the provider stream breaks off mid-tool-call (pause_turn, engine
overload, token limit), the step ended with the partial tool call dropped
silently: never executed, never recorded. If the response carried no other
usable content, the persisted assistant message was effectively empty and
every subsequent request — including compaction — was rejected with
"assistant must not be empty" (HTTP 400), wedging the session.

Record each unexecuted call instead (arguments sanitized to {} when the
truncated JSON is unparseable) and immediately close it with a synthetic
interrupted error result. The exchange stays wire-valid, the history stays
truthful, and the model learns the calls never ran so it can re-issue them.
ywh114 pushed a commit to ywh114/kimi-code that referenced this pull request Jul 19, 2026
…oonshotAI#1790)

When the provider stream breaks off mid-tool-call (pause_turn, engine
overload, token limit), the step ended with the partial tool call dropped
silently: never executed, never recorded. If the response carried no other
usable content, the persisted assistant message was effectively empty and
every subsequent request — including compaction — was rejected with
"assistant must not be empty" (HTTP 400), wedging the session.

Record each unexecuted call instead (arguments sanitized to {} when the
truncated JSON is unparseable) and immediately close it with a synthetic
interrupted error result. The exchange stays wire-valid, the history stays
truthful, and the model learns the calls never ran so it can re-issue them.
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