diff --git a/packages/client/src/daemon-client.test.ts b/packages/client/src/daemon-client.test.ts index 24acc43bb2..384748953e 100644 --- a/packages/client/src/daemon-client.test.ts +++ b/packages/client/src/daemon-client.test.ts @@ -5411,104 +5411,121 @@ test.each([undefined, false])( ); test.each([ - ["full-access", true], - [undefined, undefined], -] as const)("imports an agent by provider handle id with mode %s", async (modeId, supported) => { - const logger = createMockLogger(); - const mock = createMockTransport(); + ["full-access", true, true], + [undefined, undefined, true], + ["full-access", true, false], +] as const)( + "imports an agent by provider handle id with mode %s", + async (modeId, supported, connectedBeforeImport) => { + const logger = createMockLogger(); + const mock = createMockTransport(); - const client = new DaemonClient({ - url: "ws://test", - clientId: "clsk_unit_test", - logger, - reconnect: { enabled: false }, - transportFactory: () => mock.transport, - }); - clients.push(client); + const client = new DaemonClient({ + url: "ws://test", + clientId: "clsk_unit_test", + logger, + reconnect: { enabled: false }, + transportFactory: () => mock.transport, + }); + clients.push(client); - const connectPromise = client.connect(); - mock.triggerOpen({ features: { importSessionMode: supported } }); - await connectPromise; + const connectPromise = client.connect(); + if (connectedBeforeImport) { + mock.triggerOpen({ features: { importSessionMode: supported } }); + await connectPromise; + } - const promise = client.importAgent({ - providerId: "custom-codex", - providerHandleId: "thread-1", - cwd: "/tmp/repo", - modeId, - }); + const promise = client.importAgent({ + providerId: "custom-codex", + providerHandleId: "thread-1", + cwd: "/tmp/repo", + modeId, + }); - expect(mock.sent).toHaveLength(1); - const request = JSON.parse(String(mock.sent[0])) as { - type: "session"; - message: { - type: "import_agent_request"; - requestId: string; - providerId?: string; - providerHandleId?: string; - sessionId?: string; - cwd?: string; - modeId?: string; + let importError: unknown; + void promise.catch((error) => { + importError = error; + }); + if (!connectedBeforeImport) { + await Promise.resolve(); + expect(importError).toBeUndefined(); + expect(mock.sent).toHaveLength(0); + mock.triggerOpen({ features: { importSessionMode: supported } }); + await connectPromise; + } + await vi.waitFor(() => expect(mock.sent).toHaveLength(1)); + const request = JSON.parse(String(mock.sent[0])) as { + type: "session"; + message: { + type: "import_agent_request"; + requestId: string; + providerId?: string; + providerHandleId?: string; + sessionId?: string; + cwd?: string; + modeId?: string; + }; }; - }; - expect(request.message).toMatchObject({ - type: "import_agent_request", - providerId: "custom-codex", - providerHandleId: "thread-1", - cwd: "/tmp/repo", - }); - expect(request.message.modeId).toBe(modeId); - expect(request.message).not.toHaveProperty("sessionId"); + expect(request.message).toMatchObject({ + type: "import_agent_request", + providerId: "custom-codex", + providerHandleId: "thread-1", + cwd: "/tmp/repo", + }); + expect(request.message.modeId).toBe(modeId); + expect(request.message).not.toHaveProperty("sessionId"); - mock.triggerMessage( - wrapSessionMessage({ - type: "status", - payload: { - status: "agent_resumed", - requestId: request.message.requestId, - agentId: "agent-1", - timelineSize: 0, - agent: { - id: "agent-1", - provider: "custom-codex", - cwd: "/tmp/repo", - model: null, - features: [], - thinkingOptionId: null, - effectiveThinkingOptionId: null, - createdAt: "2026-04-30T00:00:00.000Z", - updatedAt: "2026-04-30T00:00:00.000Z", - lastUserMessageAt: null, - status: "idle", - capabilities: { - supportsStreaming: false, - supportsSessionPersistence: false, - supportsDynamicModes: false, - supportsMcpServers: false, - supportsReasoningStream: false, - supportsToolInvocations: false, - }, - currentModeId: null, - availableModes: [], - pendingPermissions: [], - persistence: { + mock.triggerMessage( + wrapSessionMessage({ + type: "status", + payload: { + status: "agent_resumed", + requestId: request.message.requestId, + agentId: "agent-1", + timelineSize: 0, + agent: { + id: "agent-1", provider: "custom-codex", - sessionId: "thread-1", - nativeHandle: "thread-1", + cwd: "/tmp/repo", + model: null, + features: [], + thinkingOptionId: null, + effectiveThinkingOptionId: null, + createdAt: "2026-04-30T00:00:00.000Z", + updatedAt: "2026-04-30T00:00:00.000Z", + lastUserMessageAt: null, + status: "idle", + capabilities: { + supportsStreaming: false, + supportsSessionPersistence: false, + supportsDynamicModes: false, + supportsMcpServers: false, + supportsReasoningStream: false, + supportsToolInvocations: false, + }, + currentModeId: null, + availableModes: [], + pendingPermissions: [], + persistence: { + provider: "custom-codex", + sessionId: "thread-1", + nativeHandle: "thread-1", + }, + title: null, + labels: {}, + requiresAttention: false, + attentionReason: null, }, - title: null, - labels: {}, - requiresAttention: false, - attentionReason: null, }, - }, - }), - ); + }), + ); - await expect(promise).resolves.toMatchObject({ - id: "agent-1", - provider: "custom-codex", - }); -}); + await expect(promise).resolves.toMatchObject({ + id: "agent-1", + provider: "custom-codex", + }); + }, +); test("uses server-provided dictation finish timeout budget", async () => { useHeartbeatClock(); diff --git a/packages/client/src/daemon-client.ts b/packages/client/src/daemon-client.ts index b3d0e75a71..ae6a8f1430 100644 --- a/packages/client/src/daemon-client.ts +++ b/packages/client/src/daemon-client.ts @@ -3212,8 +3212,11 @@ export class DaemonClient { async importAgent(input: ImportAgentInput): Promise { // COMPAT(importSessionMode): added after v0.11.4, remove gate after 2027-04-16. - if (input.modeId && this.lastServerInfoMessage?.features?.importSessionMode !== true) { - throw new Error("Update the host to select a mode when importing an agent."); + if (input.modeId) { + await this.whenConnected(); + if (this.lastServerInfoMessage?.features?.importSessionMode !== true) { + throw new Error("Update the host to select a mode when importing an agent."); + } } const requestId = this.createRequestId(); const message = SessionInboundMessageSchema.parse({