[AI-1218] Antigravity live subagent nesting — CLI (INVOKE_SUBAGENT detection)#285
Conversation
Antigravity fires no subagent hooks, so a live subagent is only discoverable once the child reports back by writing a message file into the parent's brain/<parent>/.system_generated/messages/ dir. Add AntigravitySubagents.ChildrenOf, a parent-scoped variant of the existing BuildParentMap (import-time full scan) that reads only one parent's messages dir and returns the distinct child ids that have recipient == parent. Wire it into the antigravity watcher: scanned once per drain tick (mirrors the Gemini/OpenCode live subagent discovery) and once more on final drain before exit, so a late-reporting child isn't missed. Newly seen children are POSTed to /hooks/antigravity/subagent-link (the server normalizes dashed/dashless ids via CanonicalSessionId.Normalize, so no id-form translation is needed here); a child is only recorded in the in-memory WatchState.PostedSubagentLinks set once its POST succeeds, so a failed POST retries fail-open on the next scan.
…d suppression A parent that invokes subagents never idle-ended (only ended on Antigravity quit), so its adopted children never consolidated onto the parent card until the app was quit. Root cause: UpdateAntigravityPendingToolCalls counted invoke_subagent/define_subagent as tool calls in flight, but those resolve ASYNCHRONOUSLY via a separate conversation (child reports back through brain/<parent>/messages) and never produce a RUN_COMMAND/VIEW_FILE/LIST_DIRECTORY/CODE_ACTION result step to decrement the counter — so PendingAntigravityToolCalls stayed > 0 forever and ShouldEndOnIdle was permanently suppressed. Exclude the async subagent-orchestration tools from the in-flight count; real command tools still suppress idle-end while genuinely running. Test: PendingToolCalls_excludes_async_subagent_orchestration_so_a_parent_can_idle_end (9/9).
Shared static parser (ChildConversationIdsFromLine / IsInvokeSubagentLine) that reads the authoritative spawn-time parent-to-child signal from a parent's INVOKE_SUBAGENT transcript step. Foundation for the live watcher and import parent-map redesign (Tasks 2/3).
BuildParentMap now derives child->parent linkage from each conversation's transcript_full.jsonl INVOKE_SUBAGENT steps (the spawn-time signal) instead of scanning brain/<parent>/.system_generated/messages/*.json, which missed children that never reported back and had an inverted sender/recipient direction in some captures. DiscoverAsync logs invoke-edge/dangling-child/ messages-without-invoke drift counters now that messages/ is no longer the detection source.
…ENT linkage Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR Summary by QodoAntigravity: link subagents via INVOKE_SUBAGENT transcript steps (live + import)
AI Description
Diagram
High-Level Assessment
Files changed (9)
|
Code Review by Qodo
1.
|
…counters - README: Antigravity subagents now nest in BOTH live capture and historical import (derived from INVOKE_SUBAGENT), correcting the stale "live nesting is a follow-up". - AntigravityImportSource drift counters: HashSet membership (Ordinal) so they stay O(n), not O(n²) List.Contains / parentMap.Values.Contains on large brain trees. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolve conflicts between AI-1218 (live INVOKE_SUBAGENT subagent nesting) and main's #287 (dashless canonical Antigravity session/agent ids + AI-1238 format-insensitive --session filter): - AntigravityImportSource: keep dashless session id + sessionFilter normalization (main) alongside INVOKE_SUBAGENT parent map + AI-1218 drift counters (PR); doc comment describes both. - WatchCommand final drain: capture DrainNewLines result for the exit-time subagent-link scan (PR) while passing isFinalDrain: true (main). - Integration test: assert subagent-start carries dashless agent_id (main) and match the child transcript by the precise "agent_id":"<dashless>" field (PR concern — the root batch now embeds the child id in its INVOKE_SUBAGENT step). - Unit tests: session id assertions now expect the dashless canonical form; Children stay dashed (filesystem paths). - README: live-nesting-via-INVOKE_SUBAGENT wording (PR) plus main's --session dashed/dashless note. Unit 2588/2588, Antigravity integration 5/5; NativeAOT publish clean (0 IL2026/IL3050). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Live subagent nesting for Google Antigravity — CLI half. Server half: a paired PR in
kurrent-io/kcap-server(must merge first here, then that PR bumps thesrc/clisubmodule).Detects parent→child subagent links from the authoritative
INVOKE_SUBAGENTstep in the parent's transcript (spawn-time, complete, direction-unambiguous), replacing the old post-hocmessages/*.jsonscan (bidirectional / incomplete / late).Changes
AntigravitySubagents.ChildConversationIdsFromLine/IsInvokeSubagentLine: extract child conversationIds from anINVOKE_SUBAGENTstep's embedded JSON.WatchCommand) — parseINVOKE_SUBAGENTfrom drained transcript lines and POST the subagent-link at spawn time; the oldmessages/scan (ChildrenOf) is deleted.AntigravitySubagents.BuildParentMap,AntigravityImportSource) — build the parent map fromINVOKE_SUBAGENT+ drift counters.invoke_subagent/define_subagentasync tool calls from idle-end suppression so a subagent-spawning parent still times out.INVOKE_SUBAGENTlinkage.Tests
AI-1218 CLI unit tests 71/71; NativeAOT publish clean (0 IL2026/IL3050).
Linear: AI-1218
🤖 Generated with Claude Code