feat(web): parité layout AppImage desktop + switch TUI naïf/CLI custom — #208
This commit is contained in:
@ -211,4 +211,119 @@ describe("HttpAgentGateway WS round-trip (B6 frames)", () => {
|
||||
}));
|
||||
await expect(resPromise).rejects.toMatchObject({ code: "NOT_FOUND" });
|
||||
});
|
||||
|
||||
it("routes structured chat through chat.* frames and HTTP close/cancel commands", async () => {
|
||||
const calls: { url: string; init: unknown }[] = [];
|
||||
const socket = new FakeSocket();
|
||||
const ws = new WsLiveClient({
|
||||
wsUrl: "wss://host",
|
||||
socketFactory: () => {
|
||||
queueMicrotask(() => socket.onopen?.());
|
||||
return socket;
|
||||
},
|
||||
});
|
||||
const gw = new HttpAgentGateway(
|
||||
new HttpInvoker({
|
||||
baseUrl: "https://host",
|
||||
fetchImpl: async (url, init) => {
|
||||
calls.push({ url, init });
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => undefined,
|
||||
text: async () => "",
|
||||
};
|
||||
},
|
||||
}),
|
||||
ws,
|
||||
);
|
||||
|
||||
const launched = gw.launchAgentChat("proj-1", "agent-1", {
|
||||
cwd: "/srv/app",
|
||||
nodeId: "node-1",
|
||||
rows: 24,
|
||||
cols: 80,
|
||||
conversationId: "resume-9",
|
||||
});
|
||||
const launch = await replyToLast(socket, 0, (id) => ({
|
||||
kind: "chat.attached",
|
||||
replyTo: id,
|
||||
payload: {
|
||||
sessionId: "chat-1",
|
||||
cellKind: "chat",
|
||||
assignedConversationId: "conv-1",
|
||||
},
|
||||
}));
|
||||
await expect(launched).resolves.toEqual({
|
||||
sessionId: "chat-1",
|
||||
cellKind: "chat",
|
||||
assignedConversationId: "conv-1",
|
||||
});
|
||||
expect(launch.kind).toBe("agent.launch");
|
||||
expect(launch.payload).toEqual({
|
||||
projectId: "proj-1",
|
||||
agentId: "agent-1",
|
||||
nodeId: "node-1",
|
||||
rows: 24,
|
||||
cols: 80,
|
||||
cellKind: "chat",
|
||||
conversationId: "resume-9",
|
||||
freshStart: false,
|
||||
});
|
||||
|
||||
const reattached = gw.reattachAgentChat("chat-1", () => {});
|
||||
const attach = await replyToLast(socket, 1, (id) => ({
|
||||
kind: "chat.attached",
|
||||
replyTo: id,
|
||||
payload: {
|
||||
sessionId: "chat-1",
|
||||
scrollback: [{ kind: "final", content: "Recovered reply" }],
|
||||
},
|
||||
}));
|
||||
await expect(reattached).resolves.toEqual({
|
||||
sessionId: "chat-1",
|
||||
scrollback: [{ kind: "final", content: "Recovered reply" }],
|
||||
});
|
||||
expect(attach.kind).toBe("chat.attach");
|
||||
expect(attach.payload).toEqual({ sessionId: "chat-1" });
|
||||
|
||||
const chunks: unknown[] = [];
|
||||
const sent = gw.sendAgentChat("chat-1", "Hello", (chunk) => chunks.push(chunk), {
|
||||
attachments: [{ path: "/tmp/note.txt", filename: "note.txt" }],
|
||||
});
|
||||
const chatSend = await replyToLast(socket, 2, (id) => ({
|
||||
kind: "chat.output",
|
||||
replyTo: id,
|
||||
payload: {
|
||||
sessionId: "chat-1",
|
||||
chunk: { kind: "final", content: "Hello back" },
|
||||
},
|
||||
}));
|
||||
await sent;
|
||||
expect(chatSend.kind).toBe("chat.send");
|
||||
expect(chatSend.payload).toEqual({
|
||||
sessionId: "chat-1",
|
||||
prompt: "Hello",
|
||||
attachments: [{ path: "/tmp/note.txt", filename: "note.txt" }],
|
||||
});
|
||||
expect(chunks).toEqual([{ kind: "final", content: "Hello back" }]);
|
||||
|
||||
await gw.cancelAgentChat("chat-1");
|
||||
await gw.closeAgentChat("chat-1");
|
||||
await gw.detachAgentChat("chat-1");
|
||||
|
||||
expect(calls).toHaveLength(2);
|
||||
expect(JSON.parse((calls[0].init as { body: string }).body)).toEqual({
|
||||
command: "cancel_agent_chat",
|
||||
args: { sessionId: "chat-1" },
|
||||
});
|
||||
expect(JSON.parse((calls[1].init as { body: string }).body)).toEqual({
|
||||
command: "close_agent_session",
|
||||
args: { sessionId: "chat-1" },
|
||||
});
|
||||
expect(JSON.parse(socket.sent[3])).toMatchObject({
|
||||
kind: "chat.detach",
|
||||
payload: { sessionId: "chat-1" },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user