Skip to content

fix: restore swarm members from persisted lifecycle events - #3970

Open
chengluyu wants to merge 1 commit into
mainfrom
fix/restored-swarm-members
Open

chengluyu wants to merge 1 commit into
mainfrom
fix/restored-swarm-members

Conversation

@chengluyu

Copy link
Copy Markdown
Collaborator

Related Issue

Reported during testing of the Swarm member list in the desktop client; no separate issue.

Problem

After restarting the server with a foreground Swarm still in progress, reopening the conversation shows no members. The child conversations remain on disk, but their spawn and completion events were transient, so the cold transcript cannot reconstruct the member tasks or their parent tool calls.

What changed

  • Persist subagent spawn, start, completion, failure, and cancellation events in the requesting conversation's wire journal.
  • Fold those facts into transcript tasks and tool-frame member references, ordered by the recorded Swarm index. Keep parent-conversation ownership explicit so inherited fork history does not create unrelated members.
  • Show unfinished foreground members as lost when neither the requesting turn nor the child is running. Reading history does not start a session or restart work.

Existing conversations recorded before this fix do not contain the required association facts and cannot be reconstructed reliably. This change preserves new runs; it does not infer associations from child names or creation order.

Validation: 274 targeted tests across transcript folding, cold transcript reads, event persistence/replay, and subagent scope lifecycle. Regression cases cover interrupted members, completed/failed/cancelled members, multiple Swarm calls, member ordering, background task lifecycle, reused members, parent filtering, and historical child conversation reads. Type checks pass for agent-core-v2, kap-server, and transcript. Targeted type-aware lint passes with pre-existing warnings only; the no-comments check passes.

Checklist

  • I have read the CONTRIBUTING document.
  • I have described the reported problem above (no separate issue).
  • I have added tests that prove the fix works.
  • Ran gen-changesets skill.
  • This PR needs no doc update.

Copilot AI balanced review requested due to automatic review settings September 21, 2026 13:46
@changeset-bot

changeset-bot Bot commented Sep 21, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7d306f9

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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 21, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-21T13:52:50.533003Z 7d306f9 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pkg-pr-new

pkg-pr-new Bot commented Sep 21, 2026

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

commit: 7d306f9

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

Cold reconstruction can produce stale or duplicate task state and does not reliably identify members lost from earlier turns.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 3 Medium severity

Open (3)
What changed in this PR

Persists subagent lifecycle events so cold transcript reads can reconstruct Swarm members after restarts.

Changes:

  • Makes subagent lifecycle events durable and replayable.
  • Rebuilds member tasks/references and marks interrupted foreground members lost.
  • Adds persistence and cold-reconstruction coverage.
File Description
.changeset/​restore-swarm-members.md Records the user-visible fix.
packages/​agent-core-v2/​src/​session/​subagent/​mirrorAgentRun.ts Makes lifecycle events durable.
packages/​agent-core-v2/​test/​state/​eventDispatcher.test.ts Tests persistence and replay.
packages/​transcript/​src/​history/​foldFacts.ts Reconstructs tasks and member references.
packages/​transcript/​test/​layers.test.ts Tests lifecycle folding.
packages/​kap-server/​src/​services/​transcript/​transcriptService.ts Reconciles restored task liveness.
packages/​kap-server/​test/​services/​transcript.test.ts Tests cold Swarm restoration.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +672 to +674
activity === 'idle' && task.kind === 'subagent' && !task.detached && task.state === 'running' &&
(task.agentId === undefined ||
liveAgents?.handleOf(task.agentId)?.accessor.get(IAgentLoopService).snapshot().state !== 'running')
Comment on lines +280 to +281
const subagentTasks = new Map<string, string>();
const subagentRefs = new Map<string, Map<string, { ref: AgentRef; index?: number }>>();
Comment on lines +362 to +367
tasks.set(taskId, {
...task,
state,
endedAt: state === 'running' ? undefined : recordTimeIso(record),
resultSummary: typeof record['resultSummary'] === 'string' ? record['resultSummary'] : task.resultSummary,
error: typeof record['error'] === 'string' ? record['error'] : task.error,

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

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

Comment on lines +362 to +367
tasks.set(taskId, {
...task,
state,
endedAt: state === 'running' ? undefined : recordTimeIso(record),
resultSummary: typeof record['resultSummary'] === 'string' ? record['resultSummary'] : task.resultSummary,
error: typeof record['error'] === 'string' ? record['error'] : task.error,

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 Restore usage from completed subagent records

When a completed Swarm or Agent task is reopened, its durable subagent.completed record contains usage, but this fold only copies the summary and error into the task. The live projector's onSubagentRun includes that usage, so the cold snapshot silently loses token-usage details and differs from what the client saw before restart; copy validated usage into the restored task as well.

Useful? React with 👍 / 👎.

Comment on lines +672 to +674
activity === 'idle' && task.kind === 'subagent' && !task.detached && task.state === 'running' &&
(task.agentId === undefined ||
liveAgents?.handleOf(task.agentId)?.accessor.get(IAgentLoopService).snapshot().state !== 'running')

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 Mark stale members lost during a later parent turn

If a restart leaves a foreground member unfinished and the user starts a new parent-agent turn before transcript backfill, activity is turn, so this outer condition prevents the idle or absent child from becoming lost. Nothing subsequently terminalizes that old task, leaving the previous Swarm member displayed as running indefinitely; the decision needs to distinguish the member's originating turn from an unrelated current turn.

Useful? React with 👍 / 👎.

This branch has not been deployed

No deployments
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.

2 participants