feat(opencode): add subagent timeline support (#658)

Map OpenCode `task` tool events into the existing sub_agent timeline shape,
enabling live activity tracking for OpenCode subagent sessions.

- Add `childSessionId` (optional) to sub_agent schema
- Make `actions` optional with default [] for backward compatibility
- Parse task tool input/output into sub_agent detail (tool-call-detail-parser)
- Track child session linkage and buffer/flush child tool parts
- Render sub_agent actions and session IDs in the app component
- Add glob tool detail mapping
- Include backward-compat schema test

Co-authored-by: Ryan Swift <ryan@mlh.io>
This commit is contained in:
Mohamed Boudra
2026-05-02 13:19:42 +08:00
committed by GitHub
co-authored by Ryan Swift
parent a64be66353
commit fdc510d3a0
8 changed files with 718 additions and 16 deletions
@@ -147,6 +147,30 @@ describe("shared messages tool_call schema", () => {
}
});
it("defaults missing sub_agent actions for older payloads", () => {
const parsed = AgentTimelineItemPayloadSchema.parse({
type: "tool_call",
callId: "call_sub_agent_legacy",
name: "Task",
status: "running",
error: null,
detail: {
type: "sub_agent",
subAgentType: "Explore",
description: "Inspect repository structure",
log: "[Read] README.md",
},
});
expect(parsed.type).toBe("tool_call");
if (parsed.type === "tool_call") {
expect(parsed.detail.type).toBe("sub_agent");
if (parsed.detail.type === "sub_agent") {
expect(parsed.detail.actions).toEqual([]);
}
}
});
it("parses plain_text detail with icon and rejects unknown icon names", () => {
const parsed = AgentTimelineItemPayloadSchema.parse({
type: "tool_call",
+11 -7
View File
@@ -412,14 +412,18 @@ const ToolCallDetailPayloadSchema: z.ZodType<ToolCallDetail, z.ZodTypeDef, unkno
type: z.literal("sub_agent"),
subAgentType: z.string().optional(),
description: z.string().optional(),
childSessionId: z.string().optional(),
log: z.string(),
actions: z.array(
z.object({
index: z.number().int().positive(),
toolName: z.string(),
summary: z.string().optional(),
}),
),
actions: z
.array(
z.object({
index: z.number().int().positive(),
toolName: z.string(),
summary: z.string().optional(),
}),
)
.optional()
.default([]),
}),
z.object({
type: z.literal("plain_text"),