Files
paseo/packages/protocol/src/messages.provider-subagents.test.ts
T
66445adc07 Show provider subagents in the subagents track (#2013)
* Add provider-owned subagent stream contract

* OpenCode provider: detect child sessions, attach adopted children to the hosting server, synthesize external turns

- Emit internal provider_child_session_detected / provider_session_deleted stream events from the global event stream (before the no-active-turn gate), including BFS hydration of existing descendants on subscribe
- Track child sessions in a registry mapping child session id -> hosting server url; resumeSession attaches via acquireExisting() so adopted children share the parent's server generation and keep receiving live events
- Synthesize external turns for adopted (externally driven) sessions so timeline, tool calls, and permission requests stream without a Paseo-initiated prompt
- Honor launchContext.env in resumeSession (acquireDedicated parity with createSession)
- Stop folding child tool parts into the parent's sub_agent action log; the parent row stays as a pointer

* Render provider subagents in workspace timelines

* fix provider subagent timeline reliability

* fix provider subagent lifecycle edge cases

* fix provider subagent event fidelity

* Fix provider subagent history fidelity

* Fix restored provider subagent state

* Fix provider subagent visibility edges

* Restore provider subagent timelines

* Page provider subagent history

* Forward subagent questions reliably

* fix(subagents): preserve OpenCode child context

* fix(subagents): harden provider timeline routing

* fix(subagents): preserve provider page continuity

* fix(subagents): retain live child prompts

---------

Co-authored-by: Omer <639682+omercnet@users.noreply.github.com>
2026-07-13 03:13:00 +08:00

77 lines
2.3 KiB
TypeScript

import { describe, expect, test } from "vitest";
import { SessionInboundMessageSchema, SessionOutboundMessageSchema } from "./messages.js";
describe("provider subagent protocol", () => {
test("accepts a scoped timeline request and structured live update", () => {
expect(
SessionInboundMessageSchema.parse({
type: "agent.provider_subagents.timeline.get.request",
parentAgentId: "parent-1",
subagentId: "child-1",
requestId: "request-1",
}),
).toMatchObject({ parentAgentId: "parent-1", subagentId: "child-1" });
expect(
SessionOutboundMessageSchema.parse({
type: "agent.provider_subagents.update",
payload: {
kind: "timeline",
parentAgentId: "parent-1",
subagentId: "child-1",
provider: "claude",
epoch: "epoch-1",
seq: 4,
timestamp: "2026-07-12T10:00:00.000Z",
item: { type: "assistant_message", text: "Found it." },
},
}),
).toMatchObject({
payload: {
kind: "timeline",
parentAgentId: "parent-1",
subagentId: "child-1",
seq: 4,
},
});
});
test("accepts a provider child working directory while remaining compatible when absent", () => {
const descriptor = {
id: "child-1",
parentAgentId: "parent-1",
provider: "opencode",
title: "Explore",
description: null,
status: "running",
createdAt: "2026-07-12T10:00:00.000Z",
updatedAt: "2026-07-12T10:00:00.000Z",
toolCallId: null,
};
expect(
SessionOutboundMessageSchema.parse({
type: "agent.provider_subagents.list.response",
payload: {
requestId: "request-1",
parentAgentId: "parent-1",
subagents: [{ ...descriptor, cwd: "/workspace/child" }],
error: null,
},
}),
).toMatchObject({ payload: { subagents: [{ cwd: "/workspace/child" }] } });
expect(
SessionOutboundMessageSchema.parse({
type: "agent.provider_subagents.list.response",
payload: {
requestId: "request-2",
parentAgentId: "parent-1",
subagents: [descriptor],
error: null,
},
}),
).toMatchObject({ payload: { subagents: [{ id: "child-1" }] } });
});
});