mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-07 00:44:49 +00:00
feat(subagents): archive finished children and show native names
Keep native provider sessions intact while letting users clear completed children from the track. Persist dismissed provider child IDs so history replay does not restore them.
This commit is contained in:
@@ -74,6 +74,7 @@ export async function ensureAgentLoaded(
|
||||
}
|
||||
snapshot = await deps.agentManager.createAgent(config, agentId, {
|
||||
labels: record.labels,
|
||||
dismissedProviderSubagentIds: record.dismissedProviderSubagentIds,
|
||||
workspaceId: record.workspaceId,
|
||||
});
|
||||
deps.logger.info({ agentId, provider: record.provider }, "Agent created from stored config");
|
||||
|
||||
@@ -2826,6 +2826,73 @@ test("hydrateTimelineFromProvider restores and broadcasts provider children from
|
||||
});
|
||||
});
|
||||
|
||||
test("archiveFinishedSubagents archives managed children and keeps dismissed provider history hidden", async () => {
|
||||
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-archive-finished-subagents-"));
|
||||
const storage = new AgentStorage(join(workdir, "agents"), logger);
|
||||
class ParentSession extends TestAgentSession {
|
||||
override async *streamHistory(): AsyncGenerator<AgentStreamEvent> {
|
||||
yield {
|
||||
type: "provider_subagent",
|
||||
provider: "codex",
|
||||
event: { type: "upsert", id: "native-finished", status: "completed" },
|
||||
};
|
||||
yield {
|
||||
type: "provider_subagent",
|
||||
provider: "codex",
|
||||
event: { type: "upsert", id: "native-running", status: "running" },
|
||||
};
|
||||
yield {
|
||||
type: "provider_subagent",
|
||||
provider: "codex",
|
||||
event: { type: "upsert", id: "native-failed", status: "failed" },
|
||||
};
|
||||
}
|
||||
}
|
||||
class SubagentsClient extends TestAgentClient {
|
||||
private sessionsCreated = 0;
|
||||
|
||||
override async createSession(config: AgentSessionConfig): Promise<AgentSession> {
|
||||
this.sessionsCreated += 1;
|
||||
return this.sessionsCreated === 1 ? new ParentSession(config) : new TestAgentSession(config);
|
||||
}
|
||||
}
|
||||
const manager = new AgentManager({
|
||||
clients: { codex: new SubagentsClient() },
|
||||
registry: storage,
|
||||
logger,
|
||||
});
|
||||
const parent = await manager.createAgent({ provider: "codex", cwd: workdir }, undefined, {
|
||||
workspaceId: undefined,
|
||||
});
|
||||
const managedFinished = await manager.createAgent(
|
||||
{ provider: "codex", cwd: workdir },
|
||||
undefined,
|
||||
{ labels: { [PARENT_AGENT_ID_LABEL]: parent.id }, workspaceId: undefined },
|
||||
);
|
||||
await manager.hydrateTimelineFromProvider(parent.id);
|
||||
|
||||
const result = await manager.archiveFinishedSubagents(parent.id);
|
||||
|
||||
expect(result).toEqual({
|
||||
archivedAgentIds: [managedFinished.id],
|
||||
dismissedProviderSubagentIds: ["native-finished", "native-failed"],
|
||||
});
|
||||
expectArchivedAgentRecord(await storage.get(managedFinished.id), "closed");
|
||||
expect(manager.listProviderSubagents(parent.id).map((subagent) => subagent.id)).toEqual([
|
||||
"native-running",
|
||||
]);
|
||||
expect((await storage.get(parent.id))?.dismissedProviderSubagentIds).toEqual([
|
||||
"native-finished",
|
||||
"native-failed",
|
||||
]);
|
||||
|
||||
await manager.hydrateTimelineFromProvider(parent.id, { force: true });
|
||||
|
||||
expect(manager.listProviderSubagents(parent.id).map((subagent) => subagent.id)).toEqual([
|
||||
"native-running",
|
||||
]);
|
||||
});
|
||||
|
||||
test("force provider hydration removes children absent from current history", async () => {
|
||||
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-provider-child-force-"));
|
||||
const storage = new AgentStorage(join(workdir, "agents"), logger);
|
||||
|
||||
@@ -226,6 +226,7 @@ type ProviderClientMap = Partial<Record<AgentProvider, AgentClient>>;
|
||||
|
||||
export interface CreateAgentOptions {
|
||||
labels?: Record<string, string>;
|
||||
dismissedProviderSubagentIds?: string[];
|
||||
initialPrompt?: string;
|
||||
env?: Record<string, string>;
|
||||
persistSession?: boolean;
|
||||
@@ -338,6 +339,7 @@ interface ManagedAgentBase {
|
||||
* User-defined labels for categorizing agents (e.g., { surface: "workspace" }).
|
||||
*/
|
||||
labels: Record<string, string>;
|
||||
dismissedProviderSubagentIds: Set<string>;
|
||||
}
|
||||
|
||||
type ManagedAgentWithSession = ManagedAgentBase & {
|
||||
@@ -962,6 +964,51 @@ export class AgentManager {
|
||||
return this.providerSubagents.list(parentAgentId);
|
||||
}
|
||||
|
||||
async archiveFinishedSubagents(parentAgentId: string): Promise<{
|
||||
archivedAgentIds: string[];
|
||||
dismissedProviderSubagentIds: string[];
|
||||
}> {
|
||||
const parent = this.requirePublicAgent(parentAgentId);
|
||||
const registry = this.requireRegistry();
|
||||
const archivedAgentIds: string[] = [];
|
||||
|
||||
for (const record of await registry.list()) {
|
||||
if (record.archivedAt || record.labels[PARENT_AGENT_ID_LABEL] !== parentAgentId) {
|
||||
continue;
|
||||
}
|
||||
const live = this.agents.get(record.id);
|
||||
const status = live?.lifecycle ?? record.lastStatus;
|
||||
if (isAgentBusy(status)) {
|
||||
continue;
|
||||
}
|
||||
if (live) {
|
||||
await this.archiveAgent(record.id);
|
||||
} else {
|
||||
await this.markRecordArchived(record);
|
||||
await this.cascadeArchiveChildren(record.id);
|
||||
}
|
||||
archivedAgentIds.push(record.id);
|
||||
}
|
||||
|
||||
const dismissedProviderSubagentIds = this.providerSubagents
|
||||
.list(parentAgentId)
|
||||
.filter((subagent) => subagent.status !== "running")
|
||||
.map((subagent) => subagent.id);
|
||||
for (const subagentId of dismissedProviderSubagentIds) {
|
||||
parent.dismissedProviderSubagentIds.add(subagentId);
|
||||
const event = this.providerSubagents.apply(parentAgentId, parent.provider, {
|
||||
type: "remove",
|
||||
id: subagentId,
|
||||
});
|
||||
this.dispatch({ type: "provider_subagent", event });
|
||||
}
|
||||
if (dismissedProviderSubagentIds.length > 0) {
|
||||
await this.persistSnapshot(parent);
|
||||
}
|
||||
|
||||
return { archivedAgentIds, dismissedProviderSubagentIds };
|
||||
}
|
||||
|
||||
getProviderSubagent(
|
||||
parentAgentId: string,
|
||||
subagentId: string,
|
||||
@@ -1005,6 +1052,7 @@ export class AgentManager {
|
||||
const session = await client.createSession(providerLaunchConfig, launchContext, createOptions);
|
||||
return this.registerSession(session, storedConfig, resolvedAgentId, {
|
||||
labels: options.labels,
|
||||
dismissedProviderSubagentIds: options.dismissedProviderSubagentIds,
|
||||
initialTitle: options.initialTitle,
|
||||
workspaceId: options.workspaceId,
|
||||
});
|
||||
@@ -1030,6 +1078,7 @@ export class AgentManager {
|
||||
lastUserMessageAt?: Date | null;
|
||||
labels?: Record<string, string>;
|
||||
workspaceId?: string;
|
||||
dismissedProviderSubagentIds?: string[];
|
||||
},
|
||||
): Promise<ManagedAgent> {
|
||||
return this.trackAgentRegistrationOperation(
|
||||
@@ -1047,6 +1096,7 @@ export class AgentManager {
|
||||
lastUserMessageAt?: Date | null;
|
||||
labels?: Record<string, string>;
|
||||
workspaceId?: string;
|
||||
dismissedProviderSubagentIds?: string[];
|
||||
},
|
||||
): Promise<ManagedAgent> {
|
||||
this.assertAcceptingAgentRegistrations();
|
||||
@@ -1233,6 +1283,7 @@ export class AgentManager {
|
||||
lastUsage: preservedLastUsage,
|
||||
lastError: preservedLastError,
|
||||
attention: preservedAttention,
|
||||
dismissedProviderSubagentIds: [...existing.dismissedProviderSubagentIds],
|
||||
});
|
||||
} finally {
|
||||
if (!handedToRegistration) {
|
||||
@@ -1443,6 +1494,7 @@ export class AgentManager {
|
||||
attention: { requiresAttention: false },
|
||||
internal: record.internal,
|
||||
labels: record.labels,
|
||||
dismissedProviderSubagentIds: new Set(record.dismissedProviderSubagentIds),
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -2566,6 +2618,7 @@ export class AgentManager {
|
||||
initialTitle?: string | null;
|
||||
publishWhenReady?: boolean;
|
||||
workspaceId?: string;
|
||||
dismissedProviderSubagentIds?: string[];
|
||||
},
|
||||
): Promise<ManagedAgent> {
|
||||
let registered = false;
|
||||
@@ -2701,6 +2754,7 @@ export class AgentManager {
|
||||
attention?: AttentionState;
|
||||
persistence?: AgentPersistenceHandle;
|
||||
workspaceId?: string;
|
||||
dismissedProviderSubagentIds?: string[];
|
||||
}
|
||||
| undefined;
|
||||
}): ActiveManagedAgent {
|
||||
@@ -2738,6 +2792,7 @@ export class AgentManager {
|
||||
attention: resolveInitialAttention(options?.attention),
|
||||
internal: config.internal ?? false,
|
||||
labels: options?.labels ?? {},
|
||||
dismissedProviderSubagentIds: new Set(options?.dismissedProviderSubagentIds ?? []),
|
||||
} as ActiveManagedAgent;
|
||||
}
|
||||
|
||||
@@ -2851,8 +2906,10 @@ export class AgentManager {
|
||||
event: AgentStreamEvent,
|
||||
): Promise<void> {
|
||||
if (event.type === "provider_subagent") {
|
||||
const update = this.providerSubagents.apply(agent.id, event.provider, event.event);
|
||||
this.dispatch({ type: "provider_subagent", event: update });
|
||||
const update = this.applyProviderSubagentEvent(agent, event);
|
||||
if (update) {
|
||||
this.dispatch({ type: "provider_subagent", event: update });
|
||||
}
|
||||
return;
|
||||
}
|
||||
const turnId = getAgentStreamEventTurnId(event);
|
||||
@@ -2892,6 +2949,16 @@ export class AgentManager {
|
||||
);
|
||||
}
|
||||
|
||||
private applyProviderSubagentEvent(
|
||||
agent: ManagedAgent,
|
||||
event: Extract<AgentStreamEvent, { type: "provider_subagent" }>,
|
||||
): ProviderSubagentStoreEvent | null {
|
||||
if (agent.dismissedProviderSubagentIds.has(event.event.id)) {
|
||||
return null;
|
||||
}
|
||||
return this.providerSubagents.apply(agent.id, event.provider, event.event);
|
||||
}
|
||||
|
||||
private async resolveInitialPersistedTitle(
|
||||
agentId: string,
|
||||
config: AgentSessionConfig,
|
||||
@@ -3029,8 +3096,8 @@ export class AgentManager {
|
||||
}
|
||||
}
|
||||
for (const event of providerSubagentEvents) {
|
||||
const update = this.providerSubagents.apply(agent.id, event.provider, event.event);
|
||||
if (broadcast) {
|
||||
const update = this.applyProviderSubagentEvent(agent, event);
|
||||
if (broadcast && update) {
|
||||
this.dispatch({ type: "provider_subagent", event: update });
|
||||
}
|
||||
}
|
||||
@@ -3060,8 +3127,8 @@ export class AgentManager {
|
||||
try {
|
||||
for await (const event of agent.session.streamHistory()) {
|
||||
if (event.type === "provider_subagent") {
|
||||
const update = this.providerSubagents.apply(agent.id, event.provider, event.event);
|
||||
if (broadcast) {
|
||||
const update = this.applyProviderSubagentEvent(agent, event);
|
||||
if (broadcast && update) {
|
||||
this.dispatch({ type: "provider_subagent", event: update });
|
||||
}
|
||||
continue;
|
||||
|
||||
@@ -93,6 +93,7 @@ export function toStoredAgentRecord(
|
||||
? agent.attention.attentionTimestamp.toISOString()
|
||||
: null,
|
||||
internal: options?.internal,
|
||||
dismissedProviderSubagentIds: [...agent.dismissedProviderSubagentIds],
|
||||
} satisfies StoredAgentRecord;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ const STORED_AGENT_SCHEMA = z.object({
|
||||
attentionReason: z.enum(["finished", "error", "permission"]).nullable().optional(),
|
||||
attentionTimestamp: z.string().nullable().optional(),
|
||||
internal: z.boolean().optional(),
|
||||
dismissedProviderSubagentIds: z.array(z.string()).default([]),
|
||||
archivedAt: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
|
||||
@@ -5036,6 +5036,7 @@ function readClaudeSidechainHistory(historyPath: string): string[] {
|
||||
}
|
||||
|
||||
interface ClaudeHistoricalSubagentToolCall {
|
||||
name?: string;
|
||||
subagentType?: string;
|
||||
description?: string;
|
||||
}
|
||||
@@ -5057,9 +5058,11 @@ function readClaudeHistoricalSubagentToolCalls(
|
||||
continue;
|
||||
}
|
||||
const input = toObjectRecord(block.input);
|
||||
const name = readNonEmptyString(input?.name);
|
||||
const subagentType = readNonEmptyString(input?.subagent_type);
|
||||
const description = readNonEmptyString(input?.description);
|
||||
toolCalls.set(block.id, {
|
||||
...(name ? { name } : {}),
|
||||
...(subagentType ? { subagentType } : {}),
|
||||
...(description ? { description } : {}),
|
||||
});
|
||||
@@ -5121,6 +5124,12 @@ function buildClaudePersistedSidechainEvents(
|
||||
return events;
|
||||
}
|
||||
|
||||
function resolveClaudeHistoricalSubagentTitle(
|
||||
toolCall: ClaudeHistoricalSubagentToolCall | undefined,
|
||||
): string {
|
||||
return toolCall?.name ?? toolCall?.subagentType ?? "Claude subagent";
|
||||
}
|
||||
|
||||
function buildClaudePersistedSidechainAgentEvents(
|
||||
agentId: string,
|
||||
entries: ClaudeHistoryEntry[],
|
||||
@@ -5139,7 +5148,7 @@ function buildClaudePersistedSidechainAgentEvents(
|
||||
event: {
|
||||
type: "upsert",
|
||||
id,
|
||||
title: toolCall?.subagentType ?? "Claude subagent",
|
||||
title: resolveClaudeHistoricalSubagentTitle(toolCall),
|
||||
description: toolCall?.description ?? null,
|
||||
status: "running",
|
||||
toolCallId: result?.toolCallId ?? null,
|
||||
|
||||
+15
-1
@@ -139,7 +139,11 @@ describe("ClaudeAgentSession history replay regression", () => {
|
||||
type: "tool_use",
|
||||
id: "history-task-call",
|
||||
name: "Agent",
|
||||
input: { description: "Inspect persisted history" },
|
||||
input: {
|
||||
name: "history_researcher",
|
||||
subagent_type: "Explore",
|
||||
description: "Inspect persisted history",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -303,6 +307,16 @@ describe("ClaudeAgentSession history replay regression", () => {
|
||||
timestamp: "2026-07-12T10:00:01.000Z",
|
||||
},
|
||||
});
|
||||
expect(historyEvents).toContainEqual({
|
||||
type: "provider_subagent",
|
||||
provider: "claude",
|
||||
event: expect.objectContaining({
|
||||
type: "upsert",
|
||||
id: "history-task-call",
|
||||
title: "history_researcher",
|
||||
status: "running",
|
||||
}),
|
||||
});
|
||||
expect(historyEvents).toContainEqual({
|
||||
type: "provider_subagent",
|
||||
provider: "claude",
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { SDKMessage } from "@anthropic-ai/claude-agent-sdk";
|
||||
|
||||
import { ClaudeSidechainTracker } from "./sidechain-tracker.js";
|
||||
|
||||
describe("ClaudeSidechainTracker", () => {
|
||||
it("uses Claude's native agent name for the provider subagent title", () => {
|
||||
const tracker = new ClaudeSidechainTracker({
|
||||
getToolInput: () => ({
|
||||
name: "repo_researcher",
|
||||
subagent_type: "Explore",
|
||||
description: "Inspect the repository",
|
||||
}),
|
||||
});
|
||||
|
||||
const events = tracker.handleMessage(
|
||||
{
|
||||
type: "assistant",
|
||||
parent_tool_use_id: "task-1",
|
||||
message: { content: [] },
|
||||
} as unknown as SDKMessage,
|
||||
"task-1",
|
||||
);
|
||||
|
||||
expect(events[0]).toEqual({
|
||||
type: "provider_subagent",
|
||||
provider: "claude",
|
||||
event: {
|
||||
type: "upsert",
|
||||
id: "task-1",
|
||||
title: "repo_researcher",
|
||||
description: "Inspect the repository",
|
||||
status: "running",
|
||||
toolCallId: "task-1",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -22,6 +22,7 @@ interface SubAgentActionEntry {
|
||||
}
|
||||
|
||||
interface SubAgentActivityState {
|
||||
name?: string;
|
||||
subAgentType?: string;
|
||||
description?: string;
|
||||
actions: SubAgentActionEntry[];
|
||||
@@ -130,7 +131,7 @@ export class ClaudeSidechainTracker {
|
||||
event: {
|
||||
type: "upsert",
|
||||
id: parentToolUseId,
|
||||
title: state.subAgentType ?? "Claude subagent",
|
||||
title: state.name ?? state.subAgentType ?? "Claude subagent",
|
||||
description: state.description ?? null,
|
||||
status: "running",
|
||||
toolCallId: parentToolUseId,
|
||||
@@ -163,7 +164,7 @@ export class ClaudeSidechainTracker {
|
||||
event: {
|
||||
type: "upsert",
|
||||
id,
|
||||
title: state.subAgentType ?? "Claude subagent",
|
||||
title: state.name ?? state.subAgentType ?? "Claude subagent",
|
||||
description: state.description ?? null,
|
||||
status,
|
||||
toolCallId: id,
|
||||
@@ -185,7 +186,7 @@ export class ClaudeSidechainTracker {
|
||||
event: {
|
||||
type: "upsert",
|
||||
id,
|
||||
title: state.subAgentType ?? "Claude subagent",
|
||||
title: state.name ?? state.subAgentType ?? "Claude subagent",
|
||||
description: state.description ?? null,
|
||||
status,
|
||||
toolCallId: id,
|
||||
@@ -267,10 +268,15 @@ export class ClaudeSidechainTracker {
|
||||
parentToolUseId: string,
|
||||
): boolean {
|
||||
const taskInput = this.getToolInput(parentToolUseId);
|
||||
const nextName = this.normalizeSubAgentText(taskInput?.name);
|
||||
const nextSubAgentType = this.normalizeSubAgentText(taskInput?.subagent_type);
|
||||
const nextDescription = this.normalizeSubAgentText(taskInput?.description);
|
||||
|
||||
let changed = false;
|
||||
if (nextName && nextName !== state.name) {
|
||||
state.name = nextName;
|
||||
changed = true;
|
||||
}
|
||||
if (nextSubAgentType && nextSubAgentType !== state.subAgentType) {
|
||||
state.subAgentType = nextSubAgentType;
|
||||
changed = true;
|
||||
|
||||
@@ -234,7 +234,7 @@ describe("codex tool-call mapper", () => {
|
||||
id: `activity-${kind}`,
|
||||
kind,
|
||||
agentThreadId: "child-thread-1",
|
||||
agentPath: "/root/investigator",
|
||||
agentPath: "/root/research/investigator",
|
||||
});
|
||||
|
||||
expect(item).toEqual({
|
||||
@@ -245,8 +245,8 @@ describe("codex tool-call mapper", () => {
|
||||
error: null,
|
||||
detail: {
|
||||
type: "sub_agent",
|
||||
subAgentType: "Sub-agent",
|
||||
description: "/root/investigator",
|
||||
subAgentType: "research/investigator",
|
||||
description: "/root/research/investigator",
|
||||
log: "",
|
||||
actions: [],
|
||||
},
|
||||
|
||||
@@ -993,6 +993,7 @@ function mapCollabAgentToolCallItem(
|
||||
function mapSubAgentActivityItem(
|
||||
item: z.infer<typeof CodexSubAgentActivityItemSchema>,
|
||||
): ToolCallTimelineItem {
|
||||
const nativeName = item.agentPath.replace(/^\/root(?:\/|$)/, "") || "Sub-agent";
|
||||
return {
|
||||
type: "tool_call",
|
||||
callId: item.id,
|
||||
@@ -1001,7 +1002,7 @@ function mapSubAgentActivityItem(
|
||||
error: null,
|
||||
detail: {
|
||||
type: "sub_agent",
|
||||
subAgentType: "Sub-agent",
|
||||
subAgentType: nativeName,
|
||||
description: item.agentPath,
|
||||
log: "",
|
||||
actions: [],
|
||||
|
||||
@@ -110,6 +110,7 @@ export function extractTimestamps(record: StoredAgentRecord): {
|
||||
lastUserMessageAt: Date | null;
|
||||
labels?: Record<string, string>;
|
||||
workspaceId?: string;
|
||||
dismissedProviderSubagentIds?: string[];
|
||||
} {
|
||||
return {
|
||||
createdAt: new Date(record.createdAt),
|
||||
@@ -117,6 +118,7 @@ export function extractTimestamps(record: StoredAgentRecord): {
|
||||
lastUserMessageAt: record.lastUserMessageAt ? new Date(record.lastUserMessageAt) : null,
|
||||
labels: record.labels,
|
||||
workspaceId: record.workspaceId,
|
||||
dismissedProviderSubagentIds: record.dismissedProviderSubagentIds,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1486,6 +1486,8 @@ export class Session {
|
||||
return this.handleFetchAgentTimelineRequest(msg);
|
||||
case "agent.provider_subagents.list.request":
|
||||
return this.handleProviderSubagentListRequest(msg);
|
||||
case "agent.subagents.archive_finished.request":
|
||||
return this.handleArchiveFinishedSubagentsRequest(msg);
|
||||
case "agent.provider_subagents.timeline.get.request":
|
||||
return this.handleProviderSubagentTimelineRequest(msg);
|
||||
case "agent.fork_context.request":
|
||||
@@ -5448,6 +5450,34 @@ export class Session {
|
||||
}
|
||||
}
|
||||
|
||||
private async handleArchiveFinishedSubagentsRequest(
|
||||
msg: Extract<SessionInboundMessage, { type: "agent.subagents.archive_finished.request" }>,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const result = await this.agentManager.archiveFinishedSubagents(msg.parentAgentId);
|
||||
this.emit({
|
||||
type: "agent.subagents.archive_finished.response",
|
||||
payload: {
|
||||
requestId: msg.requestId,
|
||||
parentAgentId: msg.parentAgentId,
|
||||
...result,
|
||||
error: null,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
this.emit({
|
||||
type: "agent.subagents.archive_finished.response",
|
||||
payload: {
|
||||
requestId: msg.requestId,
|
||||
parentAgentId: msg.parentAgentId,
|
||||
archivedAgentIds: [],
|
||||
dismissedProviderSubagentIds: [],
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async handleProviderSubagentTimelineRequest(
|
||||
msg: Extract<SessionInboundMessage, { type: "agent.provider_subagents.timeline.get.request" }>,
|
||||
): Promise<void> {
|
||||
|
||||
@@ -1262,6 +1262,8 @@ export class VoiceAssistantWebSocketServer {
|
||||
agentForkContext: true,
|
||||
// COMPAT(providerSubagents): added in v0.1.107, remove gate after 2027-01-12.
|
||||
providerSubagents: true,
|
||||
// COMPAT(archiveFinishedSubagents): added in v0.1.108, remove gate after 2027-01-14.
|
||||
archiveFinishedSubagents: true,
|
||||
// COMPAT(workspacePinning): added in v0.1.107, remove gate after 2027-01-12.
|
||||
workspacePinning: true,
|
||||
// COMPAT(workspaceGithubClone): added in v0.1.108, remove gate after 2027-01-13.
|
||||
|
||||
Reference in New Issue
Block a user