feat(qa-lab): add control ui qa-channel roundtrip scenario

This commit is contained in:
Peter Steinberger
2026-04-12 19:40:48 -07:00
parent f682413f57
commit 20266c14cb
12 changed files with 472 additions and 5 deletions

View File

@@ -1,6 +1,7 @@
import { describe, expect, it, vi } from "vitest";
const loadSessionsMock = vi.fn();
const loadChatHistoryMock = vi.fn();
vi.mock("./app-chat.ts", () => ({
CHAT_SESSIONS_ACTIVE_MINUTES: 10,
@@ -24,7 +25,7 @@ vi.mock("./controllers/assistant-identity.ts", () => ({
loadAssistantIdentity: vi.fn(),
}));
vi.mock("./controllers/chat.ts", () => ({
loadChatHistory: vi.fn(),
loadChatHistory: loadChatHistoryMock,
handleChatEvent: vi.fn(() => "idle"),
}));
vi.mock("./controllers/devices.ts", () => ({
@@ -124,6 +125,39 @@ describe("handleGatewayEvent sessions.changed", () => {
});
});
describe("handleGatewayEvent session.message", () => {
it("reloads chat history for the active session", () => {
loadChatHistoryMock.mockReset();
const host = createHost();
host.sessionKey = "agent:qa:main";
handleGatewayEvent(host, {
type: "event",
event: "session.message",
payload: { sessionKey: "agent:qa:main" },
seq: 1,
});
expect(loadChatHistoryMock).toHaveBeenCalledTimes(1);
expect(loadChatHistoryMock).toHaveBeenCalledWith(host);
});
it("ignores transcript updates for other sessions", () => {
loadChatHistoryMock.mockReset();
const host = createHost();
host.sessionKey = "agent:qa:main";
handleGatewayEvent(host, {
type: "event",
event: "session.message",
payload: { sessionKey: "agent:qa:other" },
seq: 1,
});
expect(loadChatHistoryMock).not.toHaveBeenCalled();
});
});
describe("addExecApproval", () => {
it("keeps the newest approval at the front of the queue", () => {
const queue = addExecApproval(

View File

@@ -393,6 +393,17 @@ function handleChatGatewayEvent(host: GatewayHost, payload: ChatEventPayload | u
}
}
function handleSessionMessageGatewayEvent(
host: GatewayHost,
payload: { sessionKey?: string } | undefined,
) {
const sessionKey = payload?.sessionKey?.trim();
if (!sessionKey || sessionKey !== host.sessionKey) {
return;
}
void loadChatHistory(host as unknown as ChatState);
}
function handleGatewayEventUnsafe(host: GatewayHost, evt: GatewayEventFrame) {
host.eventLogBuffer = [
{ ts: Date.now(), event: evt.event, payload: evt.payload },
@@ -429,6 +440,11 @@ function handleGatewayEventUnsafe(host: GatewayHost, evt: GatewayEventFrame) {
return;
}
if (evt.event === "session.message") {
handleSessionMessageGatewayEvent(host, evt.payload as { sessionKey?: string } | undefined);
return;
}
if (evt.event === "presence") {
const payload = evt.payload as { presence?: PresenceEntry[] } | undefined;
if (payload?.presence && Array.isArray(payload.presence)) {