Skip to content

fix(web): dedupe background subagent rows in the agents dock - #1754

Merged
wbxl2000 merged 4 commits into
mainfrom
fix/web-dup-background-subagent
Jul 15, 2026
Merged

fix(web): dedupe background subagent rows in the agents dock#1754
wbxl2000 merged 4 commits into
mainfrom
fix/web-dup-background-subagent

Conversation

@wbxl2000

Copy link
Copy Markdown
Collaborator

Related Issue

No linked issue — the problem is explained below.

Problem

During streaming, while a background subagent (Agent tool with run_in_background: true) is running, opening the agents panel in the bottom dock shows two identical rows for the same agent.

Root cause: the server gives a background subagent two different ids — the agent id (delivered by the WS subagent.spawned event) and a fresh task id in the main agent's background-task store (served by REST /tasks, and broadcast in the task.started registration event). The web client kept one row per id: the WS projector added a row keyed by agent id, then the 1-second REST poll (active while tasks run, i.e. during streaming) returned the same agent keyed by task id. The merge only dedupes by exact id, so both rows survived — and both pass the dock's kind === 'subagent' && runInBackground filter. The snapshot-roster path already avoids this duplication (it deliberately excludes background subagents); the live WS + poll path had no equivalent reconciliation.

What changed

  • The task.started event for kind: 'agent' carries both ids. The projector now records the task id on the existing WS-owned row as backgroundTaskId instead of projecting a second row (which was also mis-typed as kind: 'bash').
  • The REST/WS task merge (keepLiveSubagents) folds a REST row whose id matches a live row's backgroundTaskId into that row. REST can still correct a terminal status the WS row missed, but a lagging poll never flips a finished row back to running.
  • Cancel resolves backgroundTaskId first, so the stop button keeps targeting the id REST actually knows.
  • Side effects of the same fix: background subagents no longer flash in the bash panel before the first poll, and a detached foreground subagent (Ctrl+B) now appears in the dock as a single row with live progress.

Tests: projector fold + late-subscribe fallback + unchanged process-task behavior; merge fold + terminal-stickiness cases. Full kimi-web suite (534 tests) and vue-tsc pass.

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 Jul 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6efe145

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

@pkg-pr-new

pkg-pr-new Bot commented Jul 15, 2026

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

commit: 6efe145

@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: 8a0986f9c2

ℹ️ 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 apps/kimi-web/src/lib/taskMerge.ts Outdated
Comment on lines +42 to +43
outputPreview: live.outputPreview ?? rest.outputPreview,
outputBytes: live.outputBytes ?? rest.outputBytes,

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 REST output updates when folding subagents

When a background subagent has been folded once, the live row can already have outputPreview/outputBytes from the first REST poll. Later polls build a fresh REST row with newer tail or final output, but this merge keeps the old live values whenever they are set, so the agent detail Result stops updating after the first folded poll and may never show the final output. Prefer the folded REST output when it is present (or compare bytes/status) while still preserving WS-only fields.

Useful? React with 👍 / 👎.

Comment on lines +1147 to +1150
id: taskId,
sessionId,
kind: 'subagent',
description,

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 Link late task registrations to the agent id

In the missed-subagent.spawned path, when task.started includes an agentId, this creates only a row keyed by the background task id and does not seed subagentMeta/backgroundTaskId for the agent id. The next agent-scoped progress frame is routed by agentId into patchSubagent, creating a second subagent row keyed by the agent id, so late subscribers can still see duplicate dock rows with split progress. Key this fallback by agentId when available and set backgroundTaskId, or otherwise seed both identities.

Useful? React with 👍 / 👎.

@wbxl2000

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: 4ed2a28f43

ℹ️ 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 +49 to +50
const rest = restBased.filter((t) => !foldedRestIds.has(t.id));
return [...rest, ...merged];

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 terminal output after folding REST rows

When loadTasksForSession folds a REST background-subagent row into the live agent-id row, the one-off terminal-output backfill still records fetched output by the REST task id and then updates existing rows by t.id. Because this code removes the REST-id row here, a completed background subagent that is loaded/refreshed with an existing live row can end up with status folded correctly but no final Result/output attached, and the fetched id is marked as already fetched so later backfills skip it.

Useful? React with 👍 / 👎.

@wbxl2000

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: 8828d8a662

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

...live,
// Terminal-stickiness: never let a lagging poll flip a finished row back
// to running, but let REST complete a row whose finish event was missed.
status: live.status === 'running' ? rest.status : live.status,

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 Update the phase when REST folds a terminal subagent

When a background subagent misses its subagent.completed/subagent.failed event but the REST poll returns a terminal status, this fold updates status while leaving the live row's stale subagentPhase (usually working or queued). The detail panel path (toAgentMember) prefers task.subagentPhase over terminal status, so clicking a completed dock row can still show it as Working/Suspended; set or clear the phase when the fold makes the row terminal.

Useful? React with 👍 / 👎.

@wbxl2000

Copy link
Copy Markdown
Collaborator Author

@codex

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 6efe145dbd

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

@wbxl2000
wbxl2000 merged commit 1186686 into main Jul 15, 2026
14 checks passed
@wbxl2000
wbxl2000 deleted the fix/web-dup-background-subagent branch July 15, 2026 15:07
@github-actions github-actions Bot mentioned this pull request Jul 15, 2026
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