Skip to content

fix(kap-server): project swarm member tasks and agent refs in v3 protocol - #3716

Merged
sailist merged 1 commit into
MoonshotAI:devfrom
sailist:bug-189-09-10-swarm-tool-call-fields
Sep 10, 2026
Merged

fix(kap-server): project swarm member tasks and agent refs in v3 protocol#3716
sailist merged 1 commit into
MoonshotAI:devfrom
sailist:bug-189-09-10-swarm-tool-call-fields

Conversation

@sailist

@sailist sailist commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

Follow-up to #3532 (flat entity message protocol, v3 WS + history API): field-alignment fixes for AgentSwarm reported by the frontend against the design doc.

Problem

While an AgentSwarm tool call is running, v3 WS consumers only see the tool_call's own status — no member-level data at all:

  • member task entities are never produced (the engine swarm path registers no task records and SubagentSpawned carries no taskId, so the projection dropped everything; v1's transcript path had a taskId ?? subagentId fallback, making this a v3 regression);
  • member model / thinking_effort are lost with it (they already sit in the SubagentSpawned payload);
  • REST history cannot rebuild swarm agent_refs or member tasks either (spawn events are not durable; cold fold only synthesized Agent-tool tasks with role child);
  • members' agent.state (origin tool-swarm) is only seeded from the live spawn event, so a client that attaches after the spawn (page opened mid-swarm, server restart) never receives it.

Only after the tool finishes does output (<agent_swarm_result>) reveal member info at once, so the swarm card shows nothing live.

What changed

  • Live projection (agentProjector.ts): when a spawn has swarmIndex but no taskId, synthesize the member task with task_id = subagentId (v1 fallback precedent), carrying child_agent_id, model, thinking_effort from the event payload; settlement reuses the existing subagent.completed/failed path. Synthesis is scoped to swarm members — a plain foreground Agent call still produces no task entity (design §16.3 mode A), and the swarm tool_call's singular task_id stays unset.
  • REST cold fold (coldFold.ts): new synthesizeSwarmMemberTasks — resume members take their agent id from resume_agent_ids keys; item members are parsed from <agent_swarm_result> output (member index matches the engine's 1-based resume-first numbering), with task_id = agent_id so live and REST converge by replace-by-id; the tool_call gains agent_refs with role: 'member'.
  • agent.state late bind (sessionProjection.ts): after folding the main agent's wire at bind, unresolved AgentSwarm tool drafts are matched against pre-existing origin-less members (resume by id, item via the swarmItem label from ISessionMetadata) to seed the tool-swarm origin.
  • Tests kept net-zero per file (two near-duplicate full-cut cases merged to make room; existing cases extended for the swarm live path, the cold-fold path, and the late-bind path).

agent-core-v2 is untouched; every outbound message still passes the zod schema.

Checklist

@changeset-bot

changeset-bot Bot commented Sep 10, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 539ad97

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

@sailist
sailist merged commit be69ace into MoonshotAI:dev Sep 10, 2026
13 checks passed

@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: 539ad97bda

ℹ️ 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 +1474 to +1477
: outcome === undefined
? tool.status === 'done'
? 'completed'
: 'failed'

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 Keep in-flight resumed member tasks running

When /history is queried for a live session while an AgentSwarm resume is still executing, members is empty but resumeIds is populated and the tool remains running, so this branch emits every resumed task as failed with state_reason: "interrupted". The endpoint therefore reports failure during the exact mid-swarm scenario this projection is intended to support; use running when options.live and the tool are still running.

Useful? React with 👍 / 👎.

index: number,
member: SwarmMemberResult | undefined,
): void => {
if (tasks.has(agentId)) return;

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 Refresh synthesized tasks on repeated resumes

When the same agent is resumed by two sequential AgentSwarm calls, the first call inserts a task keyed by that agent ID and this early return prevents the second call from updating it. Cold history consequently retains the first run's description, status, result, and timestamps, whereas the live projector upserts that task ID with the latest run, so reconnecting can replace current results with stale ones.

Useful? React with 👍 / 👎.

Comment on lines +1459 to +1460
const model = typeof args['model'] === 'string' ? args['model'] : undefined;
const thinkingEffort = typeof args['thinking'] === 'string' ? args['thinking'] : undefined;

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 Use executed model metadata for swarm members

These values do not represent the models that actually executed: AgentSwarmToolInputSchema has no thinking property, and its model property applies only to item-spawned agents while resumed agents explicitly keep their existing model. Thus valid cold history always loses thinking_effort and can label resumed members with an ignored model override; the fold needs durable execution/profile metadata and must distinguish resumed members from new item members.

Useful? React with 👍 / 👎.

Comment on lines +625 to +629
const meta = agents?.[memberId];
const item = meta?.labels?.['swarmItem'] ?? meta?.swarmItem;
if (item !== undefined) {
const itemPosition = items.findIndex((candidate) => candidate.trim() === item);
if (itemPosition >= 0) swarmIndex = resumeIds.length + itemPosition + 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 Avoid matching swarm members by item alone

When projection first binds during a later swarm whose item text was used in an earlier swarm, historical swarm agents are still origin-less and carry the same persisted swarmItem, so this lookup assigns all of them the current tool call and swarm index. Recovery then exposes unrelated old agents as members of the active swarm; item-based matching must additionally correlate the agent to the current invocation rather than treating the label as unique across the session.

Useful? React with 👍 / 👎.

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