Live end-to-end against real pi --mode rpc revealed Pi streams assistant text as top-level message_update events whose nested assistantMessageEvent carries the incremental delta — not the top-level text_delta the fake-pi-rpc emits. The bridge now maps message_update(assistantMessageEvent.text_delta).delta -> FE text_delta, so the chat shows the model's output during a real session. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
56 lines
2.3 KiB
TypeScript
56 lines
2.3 KiB
TypeScript
import { test, expect, vi } from "vitest";
|
|
import { SessionBridge } from "../src/bridge/session-bridge.js";
|
|
|
|
function fakeRpc() {
|
|
const sent: any[] = [];
|
|
let evcb: any;
|
|
return {
|
|
rpc: {
|
|
send: (c: any) => sent.push(c),
|
|
on: (_: any, cb: any) => { evcb = cb; },
|
|
request: vi.fn(),
|
|
} as any,
|
|
sent,
|
|
fire: (m: any) => evcb(m),
|
|
};
|
|
}
|
|
|
|
test("real Pi message_update (assistantMessageEvent text_delta) becomes a text_delta to the FE", () => {
|
|
const { rpc, fire } = fakeRpc();
|
|
const b = new SessionBridge(rpc);
|
|
const seen: any[] = [];
|
|
b.onClientEvent((e) => seen.push(e));
|
|
// Shape emitted by real `pi --mode rpc`: text streams inside message_update.assistantMessageEvent.delta
|
|
fire({ type: "message_update", assistantMessageEvent: { type: "text_delta", contentIndex: 0, delta: "Avvio" } });
|
|
expect(seen[0]).toEqual({ type: "text_delta", text: "Avvio" });
|
|
// Non-text message_update events (e.g. text_start) are ignored.
|
|
fire({ type: "message_update", assistantMessageEvent: { type: "text_start", contentIndex: 0 } });
|
|
expect(seen).toHaveLength(1);
|
|
});
|
|
|
|
test("extension_ui_request nativo (method:input, title=json) diventa ui_request ed è il pendente", () => {
|
|
const { rpc, fire } = fakeRpc();
|
|
const b = new SessionBridge(rpc);
|
|
const seen: any[] = [];
|
|
b.onClientEvent((e) => seen.push(e));
|
|
const descriptor = { id: "u1", widget: "select" };
|
|
fire({ type: "extension_ui_request", id: "u1", method: "input", title: JSON.stringify(descriptor) });
|
|
expect(seen[0]).toEqual({ type: "ui_request", ui_request: descriptor });
|
|
expect(b.pendingWidget()).toEqual(descriptor);
|
|
});
|
|
|
|
test("respond invia extension_ui_response con payload in value e azzera il pendente", () => {
|
|
const { rpc, sent, fire } = fakeRpc();
|
|
const b = new SessionBridge(rpc);
|
|
fire({ type: "extension_ui_request", id: "u1", method: "input", title: JSON.stringify({ id: "u1", widget: "select" }) });
|
|
b.respond({ id: "u1", choices: ["a"] });
|
|
expect(sent.at(-1)).toEqual({ type: "extension_ui_response", id: "u1", value: JSON.stringify({ id: "u1", choices: ["a"] }) });
|
|
expect(b.pendingWidget()).toBeNull();
|
|
});
|
|
|
|
test("steer invia un comando steer", () => {
|
|
const { rpc, sent } = fakeRpc();
|
|
new SessionBridge(rpc).steer("considera solo il 2024");
|
|
expect(sent.at(-1)).toEqual({ type: "steer", message: "considera solo il 2024" });
|
|
});
|