Repository navigation
Show a session's subagents in the desktop chat - #1002
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
PR Summary by QodoShow Claude subagents in desktop chat and work context
AI Description
Diagram
High-Level Assessment
Files changed (38)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9dc0309031
ℹ️ 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".
| if (SchemaExtensions.Text(result, "status") == "async_launched" && SchemaExtensions.Text(result, "agentId") is { Length: > 0 } agentId) | ||
| return [new SubagentSignal.Detached(callId, agentId)]; |
There was a problem hiding this comment.
Accept snake_case agent IDs in launch results
When a valid async launch uses agent_id instead of agentId, this condition emits no Detached signal. SessionSubagents then treats the launch acknowledgement's ordinary ToolResult envelope as the terminal result, so the new row immediately appears done and the running banner never appears. The repository already accepts both spellings in SessionImporter.TryExtractFromAsyncLaunched and TryExtractFromToolUseResult, so this path should use the same fallback.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in 9baa528: the rules now read the same shapes the server's parser accepts.
| // The root object describes one result and names none: on a line with several it is | ||
| // attributed to none of them. | ||
| var toolUseResult = CountResults(blocks) == 1 ? root.Obj("toolUseResult") : null; |
There was a problem hiding this comment.
Project standalone async launch result records
This only propagates launch metadata from a user record's root toolUseResult, while supported Claude transcripts can represent the same launch as {"type":"result","tool_use_id":...,"tool_result":{"status":"async_launched",...}} (the existing SessionImporterScanTests and TryExtractFromAsyncLaunched explicitly handle that shape). Because ClaudeTranscriptEvents.Project drops result records, these launches never produce Detached: their rows lack the background tag and agent-id binding, so a later TaskStop completion keyed only by agent ID is ignored and the row remains running until session end.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
The projection mirrors kcap-server's ClaudeCodeNormalizer, which reads only user and assistant records, so a standalone type:"result" record produces no canonical event on either lane today — the live transcript shape carries the launch on the user line's root toolUseResult. Reading result records is a leaf-parity question (#775) rather than something this PR decides; a launch stored that way reads as it does today.
Code Review by Qodo
1.
|
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…#966) Only a line with one tool_result block gets it: the root object names no result, so on a line with several it belongs to none. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The signal is read off the raw envelope for every event, so a row the chat hides still yields it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A task-notification is known by origin_kind or by its opening tag: the server's events carry no origin_kind, and the remote lane otherwise shows the notification as the user's own turn. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Session end is a view over the rows rather than a transition: a late first read, a final-drain finish and a remote row that comes back all read right without a reopen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The remote feed keeps a result that carries only signals: a notification marked meta yields no row, and dropping it would leave its subagent running forever. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The section sits with the session-local facts rather than inside the server-read card, so it renders while that read is loading or has failed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The section lives in the work-context pane, which the remote view lacks; the same commit drops a file-path banner comment and rewords a CHANGES.md line as a compatibility fact. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
agent_id beside agentId, case-insensitive status and stop messages, and tags a stored notification may cut off before they close: every shape kcap-server already accepts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
80438ba to
dc45a7d
Compare
The not-before-start guard compares a completion's time with the launch it would end, and a spooled batch stores an old completion after a newer launch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A horizontal StackPanel measures its children unbounded, so the name's ellipsis never engaged and a long subagent_type ran over the tag and the state. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Part of #966 — AI-2840
What & why
A Claude session's subagents had no presence in the desktop app beyond a tool-call row reading "Ran an agent". The chat tab now shows a one-line "N subagents running" strip above the composer, and the work-context pane lists the session's subagents by type and description with their state and elapsed time. Both derive from the transcript alone: the Claude projection carries a tool-result line's
toolUseResultin theclaude_codeextension (the shape kcap-server already persists),ClaudeChatRulesturnsAgent/Taskcalls,async_launchedresults, task-notifications andTaskStopresults into vendor-neutral signals beside the chat rows, and one tracker per workspace folds them. Local and remote sessions alike; a remote session gets the strip alone, since its view has no pane. The daemon-side live count that keeps the rail honest while background subagents run is the second PR; this one carries the spec.Where to look
SessionSubagents.Finish: a completion naming a call the tracker knows settles only that call and never falls back to the agent id, so a repeated notification from a first run cannot end a resumed second run. Session end is a view over the rows, not a transition, so a status landing before the first transcript read, and a remote row that comes back, both read right.Verification
dotnet run --project test/Capacitor.App.Tests.Unit/Capacitor.App.Tests.Unit.csproj— 2485 total, 0 faileddotnet run --project test/Capacitor.Cli.Core.Tests.Unit/Capacitor.Cli.Core.Tests.Unit.csproj— 3423 total, 9 skipped, 0 failed after one rerun ofAgentAttachClientTests(a dispose-race test untouched here, 33/33 alone)dotnet run --project test/Capacitor.Models.Transcripts.Tests.Unit/Capacitor.Models.Transcripts.Tests.Unit.csproj— 148 total, 0 faileddotnet publish src/Capacitor.Cli/Capacitor.Cli.csproj -c Release 2>&1 | grep -E 'IL[23][01][0-9]{2}'— no output🤖 Generated with Claude Code