fix: wait for Workbench event semantics

Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
root
2026-07-18 09:54:06 +02:00
parent faef792fab
commit 28c6a21d27
3 changed files with 166 additions and 21 deletions
+88
View File
@@ -125,6 +125,94 @@ describe("Workbench native HTTP adapter", () => {
}
});
test("L0 CLI waits for explicit Kafka event semantics in one SSE window", async () => {
const subscribers = new Set<(envelope: any) => void>();
const bridge = {
capabilities: { liveKafkaSse: true },
ready: Promise.resolve(),
subscribeLiveHwlabEvents(listener: (envelope: any) => void) { subscribers.add(listener); return () => subscribers.delete(listener); }
};
const app = createWorkbenchHttpApp({
mode: "agentrun-native",
kafkaEventBridge: bridge,
async dispatch() { return { ok: true }; },
async snapshot() { return { sessions: {}, turns: {} }; }
});
const server = Bun.serve({ port: 0, fetch: (request) => app.fetch(request) });
try {
const pending = runWorkbenchCli([
"events", "inspect",
"--session-id", "ses_l0_semantics",
"--trace-id", "trc_l0_semantics",
"--over-api",
"--api-url", `http://127.0.0.1:${server.port}`,
"--timeout-ms", "1000",
"--wait-for", "user,backend,assistant,terminal,final"
], {});
for (let index = 0; index < 20 && subscribers.size === 0; index += 1) await Bun.sleep(5);
const events = [
{ type: "user", eventType: "user" },
{ type: "backend", eventType: "backend" },
{ type: "assistant", eventType: "assistant", final: true, replyAuthority: true, finalResponse: { text: "done" } },
{ type: "result", eventType: "terminal", terminal: true, terminalStatus: "completed", status: "completed" }
];
for (const event of events) {
for (const listener of subscribers) listener({ schema: "hwlab.event.v1", sessionId: "ses_l0_semantics", traceId: "trc_l0_semantics", event });
}
expect(await pending).toMatchObject({
ok: true,
eventCount: 4,
eventTypes: ["user", "backend", "assistant", "terminal"],
waitFor: ["user", "backend", "assistant", "terminal", "final"],
observedSemantics: ["user", "backend", "assistant", "terminal", "final"],
missingSemantics: [],
semanticCounts: { user: 1, backend: 1, assistant: 1, terminal: 1, final: 1 },
terminalStatuses: ["completed"]
});
} finally {
server.stop(true);
await app.close();
}
});
test("L0 CLI reports missing Kafka event semantics on timeout", async () => {
const subscribers = new Set<(envelope: any) => void>();
const app = createWorkbenchHttpApp({
mode: "agentrun-native",
kafkaEventBridge: {
capabilities: { liveKafkaSse: true },
ready: Promise.resolve(),
subscribeLiveHwlabEvents(listener: (envelope: any) => void) { subscribers.add(listener); return () => subscribers.delete(listener); }
},
async dispatch() { return { ok: true }; },
async snapshot() { return { sessions: {}, turns: {} }; }
});
const server = Bun.serve({ port: 0, fetch: (request) => app.fetch(request) });
try {
const pending = runWorkbenchCli([
"events", "inspect",
"--trace-id", "trc_l0_missing_terminal",
"--over-api",
"--api-url", `http://127.0.0.1:${server.port}`,
"--timeout-ms", "50",
"--wait-for", "assistant,terminal"
], {});
for (let index = 0; index < 20 && subscribers.size === 0; index += 1) await Bun.sleep(2);
for (const listener of subscribers) listener({ schema: "hwlab.event.v1", traceId: "trc_l0_missing_terminal", event: { type: "assistant", eventType: "assistant" } });
expect(await pending).toMatchObject({
ok: false,
status: "event-timeout",
observedSemantics: ["assistant"],
missingSemantics: ["terminal"],
terminalStatuses: [],
error: { code: "workbench_kafka_sse_event_missing" }
});
} finally {
server.stop(true);
await app.close();
}
});
test("L0 SSE exposes Kafka startup failure without waiting for a read model", async () => {
const startupError = Object.assign(new Error("Kafka broker unavailable"), { code: "kafka_broker_unavailable" });
const response = await createWorkbenchHttpApp({