fix(web): restore swarm member list after page refresh#1589
Conversation
🦋 Changeset detectedLatest commit: be0ac31 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f87202de26
ℹ️ 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".
| case 'turn.ended': { | ||
| // After turn end the swarm's `<agent_swarm_result>` tool output is in | ||
| // the wire transcript and becomes the restore source; dropping the | ||
| // roster here also bounds its lifetime. | ||
| this.bySession.delete(sessionId); |
There was a problem hiding this comment.
Restrict roster clearing to the main turn
When any subagent finishes, its own turn.ended frame is published on the same session with agentId set to the subagent, and existing code already treats non-main turn frames specially (the web projector routes them as subagent progress and InFlightTurnTracker filters them out). This unconditional delete clears the whole roster as soon as the first child turn ends, so a refresh while the parent AgentSwarm call is still waiting on other subagents again loses the member list before the final swarm result is in the transcript; only the main agent's turn.ended should drop the roster.
Useful? React with 👍 / 👎.
f87202d to
be0ac31
Compare
Related Issue
No linked issue — the problem is explained below.
Problem
In the web UI, an AgentSwarm tool card loses its whole member list when the page is refreshed while subagents are still running: the card falls back to the "waiting for subtasks to join…" placeholder until the swarm's final tool result lands. Completed subagents never reappear at all.
Root cause: the card's live member rows only exist in the client's in-memory task store, populated by WS events. Only
subagent.spawnedcarries the swarm identity metadata (parentToolCallId/swarmIndex/description), and on refresh that event is never replayed — the snapshot has no subagent roster, REST/tasksonly serves the main agent's background tasks, and latersubagent.*events carry just thesubagentId, so the identity metadata is unrecoverable client-side.What changed
SubagentRosterTracker(same volatile-state pattern as the existingInFlightTurnTracker, owned byWSBroadcastServiceand updated inside its per-session dispatch queue) accumulates the live subagent roster per session and drops it onturn.ended, after which the swarm's<agent_swarm_result>tool output in the wire transcript is the restore source. The roster rides the session snapshot as an optionalsubagentsfield (protocol schema extended; optional for cross-version tolerance).tasksBySessionfrom the roster (preserving any accumulated streamed output), and thetaskCreatedmerge no longer lets a post-refresh skeleton re-projection clobber the roster-seeded description with the'Sub Agent'placeholder. The explicitrun_in_backgroundflag now round-trips so foreground swarm members aren't misclassified as background agents.This fits the existing refresh architecture: the snapshot is already the designated rebuild channel for volatile stream state (
in_flight_turn, pending approvals/questions), and the roster is the same class of data. Older servers/clients interoperate both ways since the field is additive and optional.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.