Skip to content

fix: preserve interrupted assistant text#817

Open
kermanx wants to merge 6 commits into
mainfrom
xtr/persist-interrupted-assistant
Open

fix: preserve interrupted assistant text#817
kermanx wants to merge 6 commits into
mainfrom
xtr/persist-interrupted-assistant

Conversation

@kermanx

@kermanx kermanx commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue; this addresses interrupted assistant output being lost across session resume.

Problem

When a turn is cancelled or a session closes during streaming, visible assistant text that has already been shown can be lost if the provider has not emitted a completed content part yet. Resuming that session then continues without the partially emitted assistant message.

Thinking-only interruptions still need to avoid persisting orphaned thinking context. However, once visible assistant text has started, the preceding thinking is part of the same assistant response and should be preserved with that response.

What changed

  • Buffer streamed assistant text deltas and persist them as assistant content when the provider call aborts due to cancellation or session shutdown.
  • Preserve buffered thinking before interrupted text once visible assistant text has started, while still dropping thinking-only interrupted output.
  • Keep non-abort provider failures from persisting partial assistant output.
  • Add agent-core streaming coverage and SDK resume e2e coverage for text-only, thinking-only, and mixed thinking-plus-text interruption cases.
  • Document the interrupted streaming behavior in the English and Chinese interaction guides.

Verification

  • pnpm vitest run packages/node-sdk/test/session-prompt-events.test.ts packages/agent-core/test/loop/streaming.e2e.test.ts
  • pnpm --filter @moonshot-ai/agent-core typecheck
  • pnpm --filter @moonshot-ai/kimi-code-sdk typecheck
  • pnpm run build from docs/
  • git diff --check
  • Read-only diff audit for sensitive/internal identifiers: no issues found

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.

@changeset-bot

changeset-bot Bot commented Jun 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 400df7e

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

This PR includes changesets to release 2 packages
Name Type
@moonshot-ai/agent-core Patch
@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 Jun 16, 2026

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

commit: 400df7e

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

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

Comment on lines +118 to +120
response = await chatWithRetry({
llm,
params: chatParams,

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 Reset buffered deltas between retry attempts

In the retry path, chatWithRetry reuses these same streaming callbacks for every attempt. If an earlier attempt emits text or thinking deltas and then fails with a retryable error, the new buffers keep those discarded deltas; if a later attempt is cancelled, flushInterruptedAssistantParts() persists the stale output into content.part, so resumed history can include assistant content from a request the retry layer threw away. Scope the buffers to a single attempt or clear them when step.retrying is emitted.

Useful? React with 👍 / 👎.

@kermanx kermanx force-pushed the xtr/persist-interrupted-assistant branch from bd44ed8 to 93be955 Compare June 16, 2026 12:03
@kermanx

kermanx commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author

@codex

@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: 3dbe1f0f8c

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

step: currentStep,
stepUuid,
part,
part: { type: 'think', think: bufferedThink },

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 thinking signatures on abort flush

With signed-thinking providers, this synthesizes an aborted think part from only bufferedThink. KosongLLM forwards streamed thinking through onThinkDelta(part.think) only, so any encrypted signature on the streamed ThinkPart is not available here; after cancelling once text has started, the transcript can contain unsigned thinking, and the Anthropic conversion path skips unsigned Claude thinking on the next request. That makes resume/follow-up lose the reasoning block this change is trying to preserve, so the abort buffer needs to carry the completed/signature data or avoid writing unsigned signed-provider thinking.

Useful? React with 👍 / 👎.

@kermanx

kermanx commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author

@codex

@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: 400df7ea63

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

Comment thread packages/agent-core/src/loop/turn-step.ts
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