mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-23 09:42:53 +00:00
fix(client): await handshake before checking import mode support
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -3212,8 +3212,11 @@ export class DaemonClient {
|
||||
|
||||
async importAgent(input: ImportAgentInput): Promise<AgentSnapshotPayload> {
|
||||
// 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({
|
||||
|
||||
Reference in New Issue
Block a user