fix(kap-server): project swarm member tasks and agent refs in v3 protocol - #3716
Conversation
|
There was a problem hiding this comment.
💡 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".
| : outcome === undefined | ||
| ? tool.status === 'done' | ||
| ? 'completed' | ||
| : 'failed' |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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 👍 / 👎.
| const model = typeof args['model'] === 'string' ? args['model'] : undefined; | ||
| const thinkingEffort = typeof args['thinking'] === 'string' ? args['thinking'] : undefined; |
There was a problem hiding this comment.
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 👍 / 👎.
| 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; |
There was a problem hiding this comment.
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 👍 / 👎.
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
AgentSwarmtool call is running, v3 WS consumers only see the tool_call's ownstatus— no member-level data at all:SubagentSpawnedcarries notaskId, so the projection dropped everything; v1's transcript path had ataskId ?? subagentIdfallback, making this a v3 regression);model/thinking_effortare lost with it (they already sit in theSubagentSpawnedpayload);agent_refsor member tasks either (spawn events are not durable; cold fold only synthesizedAgent-tool tasks with rolechild);agent.state(origintool-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
agentProjector.ts): when a spawn hasswarmIndexbut notaskId, synthesize the member task withtask_id = subagentId(v1 fallback precedent), carryingchild_agent_id,model,thinking_effortfrom the event payload; settlement reuses the existingsubagent.completed/failedpath. Synthesis is scoped to swarm members — a plain foregroundAgentcall still produces no task entity (design §16.3 mode A), and the swarm tool_call's singulartask_idstays unset.coldFold.ts): newsynthesizeSwarmMemberTasks— resume members take their agent id fromresume_agent_idskeys; item members are parsed from<agent_swarm_result>output (member index matches the engine's 1-based resume-first numbering), withtask_id = agent_idso live and REST converge by replace-by-id; the tool_call gainsagent_refswithrole: 'member'.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 theswarmItemlabel fromISessionMetadata) to seed thetool-swarmorigin.agent-core-v2 is untouched; every outbound message still passes the zod schema.
Checklist
/approve).gen-changesetsskill, or this PR needs no changeset. (Server-internal protocol field alignment on an unreleased protocol surface; not perceivable by CLI users — same call as feat(kap-server): add flat entity message protocol (v3 WS + history API) #3532/feat(kap-server): add steer flag to submit a prompt directly into the running turn #3705.)gen-docsskill, or this PR needs no doc update.