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>
This commit is contained in:
Mohamed Boudra
2026-07-13 03:13:00 +08:00
committed by GitHub
co-authored by Omer
parent 41d882859c
commit 66445adc07
46 changed files with 5056 additions and 337 deletions
@@ -0,0 +1,45 @@
import { describe, expect, test } from "vitest";
import {
buildDeterministicWorkspaceTabId,
normalizeWorkspaceTabTarget,
workspaceTabTargetsEqual,
} from "./identity";
describe("provider subagent tab identity", () => {
test("normalizes and compares the parent and provider child as one tab identity", () => {
const target = normalizeWorkspaceTabTarget({
kind: "provider_subagent",
parentAgentId: " parent-a ",
subagentId: " child-a ",
});
expect(target).toEqual({
kind: "provider_subagent",
parentAgentId: "parent-a",
subagentId: "child-a",
});
expect(
target &&
workspaceTabTargetsEqual(target, {
kind: "provider_subagent",
parentAgentId: "parent-a",
subagentId: "child-a",
}),
).toBe(true);
});
test("does not collide when parent and child ids contain separators", () => {
const first = buildDeterministicWorkspaceTabId({
kind: "provider_subagent",
parentAgentId: "a_b",
subagentId: "c",
});
const second = buildDeterministicWorkspaceTabId({
kind: "provider_subagent",
parentAgentId: "a",
subagentId: "b_c",
});
expect(first).not.toBe(second);
});
});
@@ -21,6 +21,13 @@ export function normalizeWorkspaceTabTarget(
const agentId = trimNonEmpty(value.agentId);
return agentId ? { kind: "agent", agentId } : null;
}
if (value.kind === "provider_subagent") {
const parentAgentId = trimNonEmpty(value.parentAgentId);
const subagentId = trimNonEmpty(value.subagentId);
return parentAgentId && subagentId
? { kind: "provider_subagent", parentAgentId, subagentId }
: null;
}
if (value.kind === "terminal") {
const terminalId = trimNonEmpty(value.terminalId);
return terminalId ? { kind: "terminal", terminalId } : null;
@@ -76,6 +83,9 @@ export function workspaceTabTargetsEqual(
if (left.kind === "agent" && right.kind === "agent") {
return left.agentId === right.agentId;
}
if (left.kind === "provider_subagent" && right.kind === "provider_subagent") {
return left.parentAgentId === right.parentAgentId && left.subagentId === right.subagentId;
}
if (left.kind === "terminal" && right.kind === "terminal") {
return left.terminalId === right.terminalId;
}
@@ -131,6 +141,9 @@ export function buildDeterministicWorkspaceTabId(target: WorkspaceTabTarget): st
if (target.kind === "agent") {
return `agent_${target.agentId}`;
}
if (target.kind === "provider_subagent") {
return `provider_subagent_${target.parentAgentId.length}_${target.parentAgentId}_${target.subagentId.length}_${target.subagentId}`;
}
if (target.kind === "terminal") {
return `terminal_${target.terminalId}`;
}