fix(cli): support mode when importing agents

This commit is contained in:
Keith Kowal
2026-09-11 14:57:01 -07:00
parent fa93c4290e
commit 89cb1bf34d
12 changed files with 82 additions and 6 deletions
@@ -4071,6 +4071,7 @@ test("importProviderSession imports the selected session without listing and pub
class ImportClient extends TestAgentClient {
listCalls = 0;
importInput: unknown = null;
importConfig: AgentSessionConfig | null = null;
importLaunchContext: AgentLaunchContext | undefined;
async listImportableSessions() {
@@ -4080,10 +4081,11 @@ test("importProviderSession imports the selected session without listing and pub
async importSession(input: ImportProviderSessionInput, context: ImportProviderSessionContext) {
this.importInput = input;
this.importConfig = context.storedConfig;
this.importLaunchContext = context.launchContext;
return {
session,
config: { provider: "codex" as const, cwd: workdir },
config: context.storedConfig,
persistence: {
provider: "codex" as const,
sessionId: input.providerHandleId,
@@ -4156,10 +4158,12 @@ test("importProviderSession imports the selected session without listing and pub
providerHandleId: "thread-selected",
cwd: workdir,
workspaceId: "ws-imported",
modeId: "full-access",
});
expect(client.listCalls).toBe(0);
expect(client.importInput).toEqual({ providerHandleId: "thread-selected", cwd: workdir });
expect(client.importConfig?.modeId).toBe("full-access");
expect(client.importLaunchContext).toEqual({
agentId: imported.id,
env: {
@@ -4168,6 +4172,7 @@ test("importProviderSession imports the selected session without listing and pub
},
});
expect(imported.lifecycle).toBe("idle");
expect(imported.config.modeId).toBe("full-access");
expect(imported.historyPrimed).toBe(true);
expect(manager.getTimeline(imported.id)).toEqual([
{ type: "user_message", text: "Trace provider imports" },
@@ -8146,6 +8151,7 @@ test("unarchiveSnapshot unarchives native provider storage before clearing archi
const unarchived = await manager.unarchiveSnapshot(agent.id, {
workspaceId: "ws-restored",
labels: { [PARENT_AGENT_ID_LABEL]: null, source: "reimport" },
modeId: "full-access",
});
const stored = await storage.get(agent.id);
@@ -8156,6 +8162,7 @@ test("unarchiveSnapshot unarchives native provider storage before clearing archi
expect(stored?.archivedAt).toBeNull();
expect(stored?.workspaceId).toBe("ws-restored");
expect(stored?.labels).toEqual({ retained: "yes", source: "reimport" });
expect(stored?.config?.modeId).toBe("full-access");
});
test("unarchiveSnapshotByHandle unarchives native provider storage for the matched snapshot", async () => {
@@ -1357,6 +1357,7 @@ export class AgentManager {
cwd: string;
workspaceId: string;
labels?: Record<string, string>;
modeId?: string;
}): Promise<ManagedAgent> {
return this.trackAgentRegistrationOperation(this.importProviderSessionInternal(input));
}
@@ -1367,6 +1368,7 @@ export class AgentManager {
cwd: string;
workspaceId: string;
labels?: Record<string, string>;
modeId?: string;
}): Promise<ManagedAgent> {
this.assertAcceptingAgentRegistrations();
const resolvedAgentId = validateAgentId(this.idFactory(), "importProviderSession");
@@ -1381,6 +1383,7 @@ export class AgentManager {
{
provider: input.provider,
cwd: input.cwd,
modeId: input.modeId,
},
resolvedAgentId,
);
@@ -2143,7 +2146,7 @@ export class AgentManager {
async unarchiveSnapshot(
agentId: string,
updates?: { workspaceId?: string; labels?: AgentLabelPatch },
updates?: { workspaceId?: string; labels?: AgentLabelPatch; modeId?: string },
): Promise<boolean> {
const registry = this.requireRegistry();
const record = await registry.get(agentId);
@@ -2159,6 +2162,7 @@ export class AgentManager {
...record,
...(updates?.workspaceId ? { workspaceId: updates.workspaceId } : {}),
...(updates?.labels ? { labels: applyLabelPatch(record.labels, updates.labels) } : {}),
...(updates?.modeId ? { config: { ...record.config, modeId: updates.modeId } } : {}),
archivedAt: null,
updatedAt: new Date().toISOString(),
});
@@ -198,7 +198,7 @@ export async function unarchiveAgentState(
_agentStorage: AgentStorage,
agentManager: AgentUnarchiveController,
agentId: string,
updates?: { workspaceId?: string; labels?: Record<string, string | null> },
updates?: { workspaceId?: string; labels?: Record<string, string | null>; modeId?: string },
): Promise<boolean> {
const unarchived = await agentManager.unarchiveSnapshot(agentId, updates);
if (!unarchived) return false;
@@ -558,11 +558,13 @@ test("normalizeImportAgentRequest accepts new and legacy import handle shapes",
requestId: "new-shape",
providerId: "custom-codex",
providerHandleId: "thread-1",
modeId: "full-access",
}),
).toEqual({
requestId: "new-shape",
provider: "custom-codex",
providerHandleId: "thread-1",
modeId: "full-access",
});
expect(
@@ -633,7 +635,11 @@ class ProviderImportHarness {
},
unarchiveSnapshot: async (
agentId: string,
updates?: { workspaceId?: string; labels?: Record<string, string | null> },
updates?: {
workspaceId?: string;
labels?: Record<string, string | null>;
modeId?: string;
},
) => {
if (this.unarchiveWait) {
await this.unarchiveWait;
@@ -654,6 +660,7 @@ class ProviderImportHarness {
...record,
workspaceId: updates?.workspaceId ?? record.workspaceId,
labels,
config: updates?.modeId ? { ...record.config, modeId: updates.modeId } : record.config,
archivedAt: null,
});
return true;
@@ -735,7 +742,12 @@ class ProviderImportHarness {
};
}
import(input: { providerHandleId: string; cwd?: string; labels?: Record<string, string> }) {
import(input: {
providerHandleId: string;
cwd?: string;
labels?: Record<string, string>;
modeId?: string;
}) {
return importProviderSession({
request: {
requestId: "import-thread",
@@ -743,6 +755,7 @@ class ProviderImportHarness {
providerHandleId: input.providerHandleId,
cwd: input.cwd,
labels: input.labels,
modeId: input.modeId,
},
workspaceProvisioning: createImportWorkspace("ws-restored"),
agentManager: this.manager,
@@ -752,7 +765,7 @@ class ProviderImportHarness {
}
}
test("importProviderSession uses the provider import path with the requested labels", async () => {
test("importProviderSession uses the provider import path with the requested labels and mode", async () => {
const harness = await ProviderImportHarness.create();
harness.timeline = [
{ type: "user_message", text: "Trace recent provider sessions" },
@@ -763,6 +776,7 @@ test("importProviderSession uses the provider import path with the requested lab
providerHandleId: "thread-imported",
cwd: "/tmp/imported-agent",
labels: { source: "import" },
modeId: "full-access",
});
expect(harness.freshImports).toEqual([
@@ -772,6 +786,7 @@ test("importProviderSession uses the provider import path with the requested lab
cwd: "/tmp/imported-agent",
workspaceId: "ws-restored",
labels: { source: "import" },
modeId: "full-access",
},
]);
expect(result).toEqual({
@@ -813,6 +828,7 @@ test("importProviderSession restores an archived session as the same standalone
providerHandleId: "thread-archived",
cwd: harness.snapshot.cwd,
labels: { source: "reimport" },
modeId: "full-access",
});
expect(result).toEqual({
@@ -824,6 +840,7 @@ test("importProviderSession restores an archived session as the same standalone
id: harness.snapshot.id,
workspaceId: "ws-restored",
labels: { existing: "label", source: "reimport" },
config: { modeId: "full-access" },
archivedAt: null,
});
expect((await harness.storage.get(harness.snapshot.id))?.labels).not.toHaveProperty(
@@ -48,6 +48,7 @@ export interface NormalizedImportAgentRequest {
cwd?: string;
workspaceId?: string;
labels?: Record<string, string>;
modeId?: string;
requestId: string;
}
@@ -112,6 +113,7 @@ export function normalizeImportAgentRequest(
cwd: msg.cwd,
workspaceId: msg.workspaceId,
labels: msg.labels,
...(msg.modeId ? { modeId: msg.modeId } : {}),
requestId: msg.requestId,
};
}
@@ -230,6 +232,7 @@ async function importProviderSessionNow(
await unarchiveAgentState(input.agentStorage, input.agentManager, archivedRecord.id, {
workspaceId,
labels: Object.keys(labelPatch).length > 0 ? labelPatch : undefined,
modeId: input.request.modeId,
});
try {
const snapshot = await ensureAgentLoaded(archivedRecord.id, {
@@ -253,6 +256,7 @@ async function importProviderSessionNow(
cwd,
workspaceId,
labels,
...(input.request.modeId ? { modeId: input.request.modeId } : {}),
});
await unarchiveAgentState(input.agentStorage, input.agentManager, snapshot.id);