diff --git a/docs/features/custom-agents.md b/docs/features/custom-agents.md index fb2f81fd1..1bb09c830 100644 --- a/docs/features/custom-agents.md +++ b/docs/features/custom-agents.md @@ -434,6 +434,8 @@ By default, all custom agents are available for automatic selection (`infer: tru When a sub-agent runs, the parent session emits lifecycle events. Subscribe to these events to build UIs that visualize agent activity. +Sub-agent-originated session events share the parent session stream and include envelope-level `agentId`. Root/main agent events and session-level events omit `agentId`, so renderers can keep the parent response separate from sub-agent traces by checking the event envelope. + ### Event types | Event | Emitted when | Data | diff --git a/docs/features/streaming-events.md b/docs/features/streaming-events.md index 3703f871d..322a3dc1a 100644 --- a/docs/features/streaming-events.md +++ b/docs/features/streaming-events.md @@ -56,6 +56,7 @@ Every session event, regardless of type, includes these fields: | `id` | `string` (UUID v4) | Unique event identifier | | `timestamp` | `string` (ISO 8601) | When the event was created | | `parentId` | `string \| null` | ID of the previous event in the chain; `null` for the first event | +| `agentId` | `string?` | Sub-agent instance ID for sub-agent-originated events; absent for root/main agent and session-level events | | `ephemeral` | `boolean?` | `true` for transient events; absent or `false` for persisted events | | `type` | `string` | Event type discriminator (see tables below) | | `data` | `object` | Event-specific payload | @@ -217,6 +218,37 @@ session.on(AssistantMessageDeltaEvent.class, event -> > [!TIP] > **(TypeScript)** The TypeScript SDK uses a discriminated union—when you match on `event.type`, the `data` payload is automatically narrowed to the correct shape. +## Render only the parent agent response + +Sub-agent events share the parent session stream and include envelope-level `agentId`. Root/main agent events and session-level events omit `agentId`, so main-chat renderers can ignore assistant events where `agentId` is set and route those events to traces or progress UI instead. + +
+TypeScript + +```typescript +session.on("assistant.message_delta", (event) => { + if (!event.agentId) process.stdout.write(event.data.deltaContent); +}); +``` + +
+
+Python + +```python +from copilot import CopilotSession +from copilot.session_events import SessionEventType + +def subscribe_parent_response(session: CopilotSession): + def handle(event): + if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA and event.agent_id is None: + print(event.data.delta_content, end="", flush=True) + + session.on(handle) +``` + +
+ ## Assistant events These events track the agent's response lifecycle—from turn start through streaming chunks to the final message. @@ -271,7 +303,7 @@ The assistant's complete response for this LLM call. May include tool invocation | `phase` | `string` | | Generation phase (e.g., `"thinking"` vs `"response"`) | | `outputTokens` | `number` | | Actual output token count from the API response | | `interactionId` | `string` | | CAPI interaction ID for telemetry | -| `parentToolCallId` | `string` | | Set when this message originates from a sub-agent | +| `parentToolCallId` | `string` | | Deprecated. Use envelope-level `agentId` for sub-agent attribution | **`ToolRequest` fields:** @@ -290,7 +322,7 @@ Ephemeral. Incremental chunk of the assistant's text response, streamed in real |------------|------|----------|-------------| | `messageId` | `string` | ✅ | Matches the corresponding `assistant.message` event | | `deltaContent` | `string` | ✅ | Text chunk to append to the message | -| `parentToolCallId` | `string` | | Set when originating from a sub-agent | +| `parentToolCallId` | `string` | | Deprecated. Use envelope-level `agentId` for sub-agent attribution | ### `assistant.turn_end` @@ -317,7 +349,7 @@ Ephemeral. Token usage and cost information for an individual API call. | `apiCallId` | `string` | | Completion ID from the provider (e.g., `chatcmpl-abc123`) | | `apiEndpoint` | `"/chat/completions" \| "/v1/messages" \| "/responses" \| "ws:/responses"` | | API endpoint used for the model call; useful for observability and cost attribution. `ws:/responses` is the websocket variant of the responses API | | `providerCallId` | `string` | | GitHub request tracing ID (`x-github-request-id`) | -| `parentToolCallId` | `string` | | Set when usage originates from a sub-agent | +| `parentToolCallId` | `string` | | Deprecated. Use envelope-level `agentId` for sub-agent attribution | | `quotaSnapshots` | `Record` | | Per-quota resource usage, keyed by quota identifier | | `copilotUsage` | `CopilotUsage` | | Itemized token cost breakdown from the API | @@ -344,7 +376,7 @@ Emitted when a tool begins executing. | `arguments` | `object` | | Parsed arguments passed to the tool | | `mcpServerName` | `string` | | MCP server name, when the tool is provided by an MCP server | | `mcpToolName` | `string` | | Original tool name on the MCP server | -| `parentToolCallId` | `string` | | Set when invoked by a sub-agent | +| `parentToolCallId` | `string` | | Deprecated. Use envelope-level `agentId` for sub-agent attribution | ### `tool.execution_partial_result` @@ -378,7 +410,7 @@ Emitted when a tool finishes executing—successfully or with an error. | `result` | `Result` | | Present on success (see below) | | `error` | `{ message, code? }` | | Present on failure | | `toolTelemetry` | `object` | | Tool-specific telemetry (e.g., CodeQL check counts) | -| `parentToolCallId` | `string` | | Set when invoked by a sub-agent | +| `parentToolCallId` | `string` | | Deprecated. Use envelope-level `agentId` for sub-agent attribution | **`Result` fields:** @@ -789,6 +821,8 @@ session.idle → Ready for next message (ephemeral) ## All event types at a glance +This table lists key `data` payload fields. Common envelope fields are documented above. + | Event Type | Ephemeral | Category | Key Data Fields | |------------|-----------|----------|-----------------| | `assistant.turn_start` | | Assistant | `turnId`, `interactionId?` | @@ -797,7 +831,7 @@ session.idle → Ready for next message (ephemeral) | `assistant.reasoning_delta` | ✅ | Assistant | `reasoningId`, `deltaContent` | | `assistant.streaming_delta` | ✅ | Assistant | `totalResponseSizeBytes` | | `assistant.message` | | Assistant | `messageId`, `content`, `toolRequests?`, `outputTokens?`, `phase?` | -| `assistant.message_delta` | ✅ | Assistant | `messageId`, `deltaContent`, `parentToolCallId?` | +| `assistant.message_delta` | ✅ | Assistant | `messageId`, `deltaContent` | | `assistant.turn_end` | | Assistant | `turnId` | | `assistant.usage` | ✅ | Assistant | `model`, `apiEndpoint?`, `inputTokens?`, `outputTokens?`, `cost?`, `duration?` | | `tool.user_requested` | | Tool | `toolCallId`, `toolName`, `arguments?` |