fix(subagents): preserve restored hidden tabs safely

This commit is contained in:
Mohamed Boudra
2026-07-15 00:10:31 +02:00
parent 6e50c22956
commit f6ec93bb68
10 changed files with 153 additions and 5 deletions
@@ -14,6 +14,10 @@ export const CLIENT_CAPS = {
// COMPAT(providerSubagents): added in v0.1.107. The daemon emits provider-owned
// child descriptors and timelines only to clients that understand the new messages.
providerSubagents: "provider_subagents",
// COMPAT(providerSubagentRetainTimeline): added in v0.1.108. Retained-remove
// updates are withheld from older clients because they interpret every remove
// as destructive and blank already-open provider child tabs.
providerSubagentRetainTimeline: "provider_subagent_retain_timeline",
browserHost: "browser_host",
} as const;
@@ -115,4 +115,40 @@ describe("provider subagent protocol", () => {
}),
).toMatchObject({ payload: { kind: "remove", retainTimeline: true } });
});
test("accepts a retained descriptor with provider timeline history", () => {
expect(
SessionOutboundMessageSchema.parse({
type: "agent.provider_subagents.timeline.get.response",
payload: {
requestId: "request-1",
parentAgentId: "parent-1",
subagentId: "child-1",
provider: "codex",
subagent: {
id: "child-1",
parentAgentId: "parent-1",
provider: "codex",
title: "Finished child",
description: null,
status: "completed",
createdAt: "2026-07-12T10:00:00.000Z",
updatedAt: "2026-07-12T10:01:00.000Z",
toolCallId: null,
},
hiddenFromTrack: true,
direction: "tail",
epoch: "epoch-1",
reset: false,
staleCursor: false,
gap: false,
window: { minSeq: 0, maxSeq: 0, nextSeq: 1 },
hasOlder: false,
hasNewer: false,
rows: [],
error: null,
},
}),
).toMatchObject({ payload: { hiddenFromTrack: true, subagent: { id: "child-1" } } });
});
});
+3
View File
@@ -3093,6 +3093,8 @@ export const ProviderSubagentTimelineResponseMessageSchema = z.object({
parentAgentId: z.string(),
subagentId: z.string(),
provider: AgentProviderSchema.nullable(),
subagent: ProviderSubagentDescriptorPayloadSchema.optional(),
hiddenFromTrack: z.boolean().optional(),
direction: z.enum(["tail", "before", "after"]),
epoch: z.string(),
reset: z.boolean(),
@@ -4877,6 +4879,7 @@ export const WSHelloMessageSchema = z.object({
[CLIENT_CAPS.customModeIcons]: z.boolean().optional(),
[CLIENT_CAPS.terminalReflowableSnapshot]: z.boolean().optional(),
[CLIENT_CAPS.providerSubagents]: z.boolean().optional(),
[CLIENT_CAPS.providerSubagentRetainTimeline]: z.boolean().optional(),
[CLIENT_CAPS.browserHost]: BrowserAutomationHostCapabilitySchema.optional(),
})
.passthrough()