Part of #1
Question
Add the subagent attribution edge to T3's model, so subagent work is distinguishable from main-agent work in the event store.
Decided in Close the agent-lenses data gap — see that ticket for the full reasoning. This ticket executes it (the map's Notes carry execution).
Resized by measurement
Measure the fidelity of forwarded subagent tool payloads proved subagent content arrives only as complete assistant/user messages — 0 of 32 stream_event messages carried a parent_tool_use_id. T3 emits tool items only from the stream_event path, and handleAssistantMessage drops every tool_use block but ExitPlanMode (ClaudeAdapter.ts:2522).
So this is not "stop discarding a field." T3 must start emitting tool items for subagent blocks, from the complete-message path, attributed. Payload fidelity is confirmed full (paths, commands, multi-KB results), so the data is all there.
The change:
- Add optional top-level
parentToolCallId?: string to ItemLifecyclePayload (packages/contracts/src/providerRuntime.ts:404) — the id of the launching tool call, never a thread id, never a T3-invented agent id.
- Emit tool lifecycle items for subagent
tool_use / tool_result blocks in handleAssistantMessage, populating parentToolCallId from the message's parent_tool_use_id. These blocks are on the wire today and dropped.
- Do not double-emit for the main agent. Main-agent tool blocks appear on both paths; T3 takes them from
stream_event and ignores the complete message. Only blocks with a non-null parent_tool_use_id may be emitted from the complete-message path.
- Subagent messages carry no streaming deltas, so these items are emitted complete rather than started-then-updated. Confirm that suits the item lifecycle.
- Leave
forwardSubagentText off. Mindwalk's Event is 100% tool-derived; no scene renders subagent prose.
- Verify it survives
projectActivityPayload — it should, untouched, since the projection spreads ...payload and rewrites only data (ActivityPayloadProjection.ts:195-201). No widening of the six-key whitelist.
Scope boundary — server-side only, and no suppression. Subagent items are allowed to appear in chat. They are ordinary tool-lifecycle items, so they join the per-turn work log group that already collapses by default (expandedWorkGroupIds, MessagesTimeline.tsx:224; auto-expanded only for the latest turn, line 1289), with run-length collapsing in session-logic.ts. The assistant message body is untouched, so nothing bloats. No ChatView or mobile change is needed — this ticket is entirely server-side.
Accepted consequence: a Task's interior appears as a flat run of entries inside the turn's work log, in arrival order, rather than nested under its launch row. Nesting it properly is a product feature and stays fog.
No backfill. Threads already in the event store keep their flattened, unattributed subagent calls. The id was never written down.
Out of scope here: the other four providers. Codex and OpenCode attribution is unmeasured, Cursor/Grok emit no collab_agent_tool_call at all. When a provider does gain an edge, its adapter resolves whatever it has (e.g. a Codex parent thread id) to the launching call id itself.
Part of #1
Question
Add the subagent attribution edge to T3's model, so subagent work is distinguishable from main-agent work in the event store.
Decided in Close the agent-lenses data gap — see that ticket for the full reasoning. This ticket executes it (the map's Notes carry execution).
Resized by measurement
Measure the fidelity of forwarded subagent tool payloads proved subagent content arrives only as complete
assistant/usermessages — 0 of 32stream_eventmessages carried aparent_tool_use_id. T3 emits tool items only from thestream_eventpath, andhandleAssistantMessagedrops everytool_useblock butExitPlanMode(ClaudeAdapter.ts:2522).So this is not "stop discarding a field." T3 must start emitting tool items for subagent blocks, from the complete-message path, attributed. Payload fidelity is confirmed full (paths, commands, multi-KB results), so the data is all there.
The change:
parentToolCallId?: stringtoItemLifecyclePayload(packages/contracts/src/providerRuntime.ts:404) — the id of the launching tool call, never a thread id, never a T3-invented agent id.tool_use/tool_resultblocks inhandleAssistantMessage, populatingparentToolCallIdfrom the message'sparent_tool_use_id. These blocks are on the wire today and dropped.stream_eventand ignores the complete message. Only blocks with a non-nullparent_tool_use_idmay be emitted from the complete-message path.forwardSubagentTextoff. Mindwalk'sEventis 100% tool-derived; no scene renders subagent prose.projectActivityPayload— it should, untouched, since the projection spreads...payloadand rewrites onlydata(ActivityPayloadProjection.ts:195-201). No widening of the six-key whitelist.Scope boundary — server-side only, and no suppression. Subagent items are allowed to appear in chat. They are ordinary tool-lifecycle items, so they join the per-turn work log group that already collapses by default (
expandedWorkGroupIds,MessagesTimeline.tsx:224; auto-expanded only for the latest turn, line 1289), with run-length collapsing insession-logic.ts. The assistant message body is untouched, so nothing bloats. No ChatView or mobile change is needed — this ticket is entirely server-side.Accepted consequence: a
Task's interior appears as a flat run of entries inside the turn's work log, in arrival order, rather than nested under its launch row. Nesting it properly is a product feature and stays fog.No backfill. Threads already in the event store keep their flattened, unattributed subagent calls. The id was never written down.
Out of scope here: the other four providers. Codex and OpenCode attribution is unmeasured, Cursor/Grok emit no
collab_agent_tool_callat all. When a provider does gain an edge, its adapter resolves whatever it has (e.g. a Codex parent thread id) to the launching call id itself.