1909 lines
84 KiB
TypeScript
1909 lines
84 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
|
|
import { test } from "bun:test";
|
|
|
|
import { projectAgentRunKafkaEventToHwlabEvent, queryKafkaEventStream } from "../../internal/cloud/kafka-event-bridge.ts";
|
|
import { mapAgentRunRecordsToHwlabDebugEvents, projectHwlabRecordsThroughWorkbench, renderKafkaCliText, runKafkaCli } from "../src/hwlab-cli/kafka-regenerate.ts";
|
|
import { renderTraceRowsMarkdown, traceDisplayRows, traceToolSummary } from "../src/hwlab-cli/trace-renderer.ts";
|
|
|
|
const SESSION_ID = "ses_agentrun_5ec4e141_6abc_466d_9afe_049f7c0ac105";
|
|
const HWLAB_SESSION_ID = "ses_5ec4e141-6abc-466d-9afe-049f7c0ac105";
|
|
const TRACE_ID = "trc_mretx18t3jl4tg";
|
|
const RUN_ID = "run_a78cbcb05f2c4afaa748a3158db44998";
|
|
|
|
test("Kafka help shows canonical default input and explicit reconstruction debug input", async () => {
|
|
const result = await runKafkaCli(["help", "--json"], { env: {}, now: () => "2026-07-10T12:00:00.000Z" });
|
|
assert.equal(result.exitCode, 0);
|
|
assert.ok(result.payload.commands.some((command: string) => /regenerate hwlab.*--input-topic agentrun\.event\.v1/u.test(command)));
|
|
assert.ok(result.payload.commands.some((command: string) => /regenerate hwlab.*--input-topic agentrun\.event\.debug\.v1/u.test(command)));
|
|
assert.ok(result.payload.commands.some((command: string) => /render trace.*--input-topic hwlab\.event\.v1/u.test(command)));
|
|
assert.ok(result.payload.commands.some((command: string) => /inspect order.*--session-id ses_/u.test(command)));
|
|
});
|
|
|
|
test("Kafka session order inspection discovers a retained session and proves source through DOM order", async () => {
|
|
const firstTrace = "trc_order_first";
|
|
const secondTrace = "trc_order_second";
|
|
const agentrun = [
|
|
agentrunOrderRecord(1, firstTrace, "user_message", { userMessageId: "msg_order_first_user", text: "first input" }),
|
|
agentrunOrderRecord(2, firstTrace, "backend_status", { phase: "running", message: "first running" }),
|
|
agentrunOrderRecord(3, firstTrace, "assistant_message", { text: "first reply", replyAuthority: true, final: true }),
|
|
agentrunOrderRecord(4, firstTrace, "terminal_status", { terminalStatus: "completed", message: "completed" }),
|
|
agentrunOrderRecord(5, secondTrace, "user_message", { userMessageId: "msg_order_second_user", text: "second input" }),
|
|
agentrunOrderRecord(6, secondTrace, "backend_status", { phase: "running", message: "second running" })
|
|
];
|
|
const hwlab = agentrun.map((record, index) => hwlabOrderRecord(record, index));
|
|
const reads: any[] = [];
|
|
const result = await runKafkaCli([
|
|
"inspect", "order",
|
|
"--contains-text", "second input",
|
|
"--group-prefix", "hwlab-v03-workbench-isolated-debug-order-test",
|
|
"--json"
|
|
], {
|
|
env: {},
|
|
now: () => "2026-07-11T12:00:00.000Z",
|
|
async readKafka(input) {
|
|
reads.push(input);
|
|
return completeKafkaRead(input.stream === "agentrun" ? agentrun : hwlab);
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.scope.resolvedSessionId, HWLAB_SESSION_ID);
|
|
assert.equal(result.payload.scope.discovery.containsTextChars, 12);
|
|
assert.equal(result.payload.scope.discovery.matchedEventCount, 1);
|
|
assert.equal(result.payload.scope.discovery.candidates[0].offset, hwlab[4].offset);
|
|
assert.equal(reads.some((input) => input.stream === "agentrun" && input.sessionId === HWLAB_SESSION_ID), true);
|
|
assert.equal(result.payload.input.refreshHandoff.counts.replayed, 6);
|
|
assert.equal(result.payload.order.eventCount, 6);
|
|
assert.equal(result.payload.order.firstDivergence, null);
|
|
assert.deepEqual(result.payload.order.turnPairs.map((pair: any) => [pair.traceId, pair.dom.user, pair.dom.agent]), [
|
|
[firstTrace, 0, 1],
|
|
[secondTrace, 2, 3]
|
|
]);
|
|
assert.deepEqual(result.payload.projection.finalMessageOrder.map((row: any) => [row.role, row.traceId]), [
|
|
["user", firstTrace],
|
|
["agent", firstTrace],
|
|
["user", secondTrace],
|
|
["agent", secondTrace]
|
|
]);
|
|
assert.equal(result.payload.validation.mapperOneToOne, true);
|
|
assert.equal(result.payload.validation.sourceOrderPreserved, true);
|
|
assert.equal(result.payload.validation.sseOrderPreserved, true);
|
|
assert.equal(result.payload.validation.reducerOrderPreserved, true);
|
|
assert.equal(result.payload.validation.domTurnOrderPreserved, true);
|
|
assert.equal(result.payload.validation.noFirstDivergence, true);
|
|
});
|
|
|
|
test("Kafka session order inspection identifies AgentRun as the first layer when user_message is committed late", async () => {
|
|
const traceId = "trc_order_late_user";
|
|
const agentrun = [
|
|
agentrunOrderRecord(1, traceId, "backend_status", { phase: "running", message: "running before input" }),
|
|
agentrunOrderRecord(2, traceId, "user_message", { userMessageId: "msg_order_late_user", text: "late input" }),
|
|
agentrunOrderRecord(3, traceId, "assistant_message", { text: "reply", replyAuthority: true, final: true }),
|
|
agentrunOrderRecord(4, traceId, "terminal_status", { terminalStatus: "completed", message: "completed" })
|
|
];
|
|
const hwlab = agentrun.map((record, index) => hwlabOrderRecord(record, index));
|
|
const result = await runKafkaCli([
|
|
"inspect", "order",
|
|
"--session-id", HWLAB_SESSION_ID,
|
|
"--group-prefix", "hwlab-v03-workbench-isolated-debug-order-test",
|
|
"--json"
|
|
], {
|
|
env: {},
|
|
now: () => "2026-07-11T12:00:00.000Z",
|
|
async readKafka(input) {
|
|
return completeKafkaRead(input.stream === "agentrun" ? agentrun : hwlab);
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.order.firstDivergence.layer, "agentrun.event.v1");
|
|
assert.equal(result.payload.order.firstDivergence.code, "user-after-agent-source-event");
|
|
assert.deepEqual(result.payload.order.firstDivergence.evidence.agentrun, { user: 1, agent: 0 });
|
|
assert.deepEqual(result.payload.order.firstDivergence.evidence.dom, { user: 1, agent: 0 });
|
|
assert.equal(result.payload.validation.domTurnOrderPreserved, false);
|
|
assert.equal(result.payload.validation.noFirstDivergence, false);
|
|
});
|
|
|
|
test("offline HWLAB JSONL uses the shared Web pipeline and renders final response only outside Trace", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-trace-render-"));
|
|
const inputFile = path.join(cwd, "hwlab-events.jsonl");
|
|
const outputFile = path.join(cwd, "trace.md");
|
|
const finalText = "Hi. What do you want to work on?";
|
|
const records = [
|
|
hwlabRecord(1, { type: "user", eventType: "user", status: "sent", label: "agentrun:user:message", userMessageId: "msg_render_trace_user", messageId: "msg_render_trace_user", targetTraceId: "trc_render_trace_target", text: "hi" }),
|
|
hwlabRecord(2, { type: "tool", eventType: "tool", status: "started", label: "agentrun:tool:commandExecution", toolName: "commandExecution", itemId: "tool_rg", command: "/bin/bash -lc 'cd /workspace && rg -n target src'" }),
|
|
hwlabRecord(3, { type: "tool", eventType: "tool", status: "completed", label: "agentrun:tool:commandExecution", toolName: "commandExecution", itemId: "tool_rg", command: "/bin/bash -lc 'cd /workspace && rg -n target src'", durationMs: 1234, exitCode: 0, stdoutSummary: "src/main.ts:42:target" }),
|
|
hwlabRecord(4, { type: "assistant", eventType: "assistant", status: "running", label: "agentrun:assistant:message", itemId: "assistant_progress", message: "I am checking the workspace.", assistantText: "I am checking the workspace.", assistantSource: "agent-message" }),
|
|
hwlabRecord(5, { type: "assistant", eventType: "assistant", status: "running", label: "agentrun:assistant:message", itemId: "assistant_final", message: finalText, assistantText: finalText, assistantSource: "completed-agent-message" }),
|
|
hwlabRecord(6, { type: "assistant", eventType: "assistant", status: "running", label: "agentrun:assistant:message", itemId: "assistant_final", message: finalText, assistantText: finalText, assistantSource: "completed-agent-message-final", replyAuthority: true, final: true, finalResponse: { text: finalText } }),
|
|
hwlabRecord(7, { type: "result", eventType: "terminal", status: "completed", label: "agentrun:terminal:completed", terminal: true, message: "AgentRun completed" })
|
|
];
|
|
await writeFile(inputFile, records.map((record) => JSON.stringify(record)).join("\n") + "\n", "utf8");
|
|
|
|
const result = await runKafkaCli([
|
|
"render", "trace",
|
|
"--from", "jsonl",
|
|
"--trace-id", TRACE_ID,
|
|
"--session-id", HWLAB_SESSION_ID,
|
|
"--run-id", RUN_ID,
|
|
"--command-id", "cmd_render_trace",
|
|
"--jsonl-file", inputFile,
|
|
"--output-markdown", outputFile,
|
|
"--format", "markdown"
|
|
], { cwd, env: {}, now: () => "2026-07-10T12:00:00.000Z" });
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.input.scannedCount, 7);
|
|
assert.equal(result.payload.input.matchedCount, 7);
|
|
assert.equal(result.payload.projection.decodedCount, 7);
|
|
assert.equal(result.payload.projection.appliedCount, 7);
|
|
assert.equal(result.payload.projection.eventCount, 6, "formal user input must not create an agent trace event");
|
|
assert.equal(result.payload.projection.userMessageCount, 1);
|
|
assert.deepEqual(result.payload.projection.userMessageIds, ["msg_render_trace_user"]);
|
|
assert.deepEqual(result.payload.projection.userTargetTraceIds, ["trc_render_trace_target"]);
|
|
assert.equal(result.payload.projection.terminalObserved, true);
|
|
assert.equal(result.payload.projection.lastEventAt, "2026-07-10T12:00:00.006Z");
|
|
assert.deepEqual(result.payload.identity.observedRunIds, [RUN_ID]);
|
|
assert.deepEqual(result.payload.identity.observedCommandIds, ["cmd_render_trace"]);
|
|
assert.equal(result.payload.identity.sourceEventIdentityDuplicates, 0);
|
|
assert.equal(result.payload.render.toolRowCount, 1);
|
|
assert.equal(result.payload.render.assistantRowCount, 1);
|
|
assert.equal(result.payload.render.finalResponsePresent, true);
|
|
assert.equal(result.payload.render.finalResponseInTrace, false);
|
|
assert.deepEqual(result.payload.replayLineage.counts, { source: 7, applied: 7, assistant: 1, tool: 1, terminal: 1, final: 1 });
|
|
assert.deepEqual(result.payload.replayLineage.layers.source, {
|
|
total: 7,
|
|
user: 1,
|
|
assistant: 3,
|
|
tool: 2,
|
|
terminal: 1,
|
|
final: 1,
|
|
other: 1,
|
|
assistantDuplicate: { fingerprintCount: 0, occurrenceCount: 0, first: null, valuesPrinted: false }
|
|
});
|
|
assert.equal(result.payload.replayLineage.layers.final.duplicatedInRender, false);
|
|
assert.equal(result.payload.replayLineage.firstVisibleDuplicateLayer, null);
|
|
assert.equal(result.payload.render.rowsReturned, result.payload.render.rowCount);
|
|
assert.equal(result.payload.render.rowsOmitted, 0);
|
|
assert.equal(result.payload.validation.allMatchedEventsApplied, true);
|
|
assert.equal(result.payload.validation.finalResponseOutsideTrace, true);
|
|
const toolRow = result.payload.render.rows.find((row: any) => row.rowId.startsWith("tool:"));
|
|
assert.match(toolRow.header, /^\d{2}:\d{2}:\d{2}$/u);
|
|
assert.doesNotMatch(toolRow.header, /\b(?:ok|fail|run|running)\b|total=|duration=|exit=/u);
|
|
assert.equal(toolRow.toolState, "success");
|
|
assert.equal(toolRow.statusLabel, "工具调用成功");
|
|
assert.equal(toolRow.preview, "cd /workspace && rg -n target src");
|
|
const markdown = await readFile(outputFile, "utf8");
|
|
assert.equal((markdown.match(new RegExp(finalText.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"), "gu")) ?? []).length, 1);
|
|
assert.equal((markdown.match(/^hi$/gmu) ?? []).length, 1);
|
|
assert.match(markdown, /## 用户输入/u);
|
|
assert.match(markdown, /target trace: trc_render_trace_target/u);
|
|
assert.match(markdown, /## 运行记录/u);
|
|
assert.match(markdown, /## 最终回复/u);
|
|
assert.match(markdown, /<summary aria-label="工具调用成功:[^"]+" title="工具调用成功:[^"]+"><code>\d{2}:\d{2}:\d{2}<\/code> cd \/workspace && rg -n target src<\/summary>/u);
|
|
assert.doesNotMatch(markdown.match(/<summary[^>]*>[\s\S]*?<\/summary>/u)?.[0] ?? "", /total=|duration=|exit=|src\/main\.ts:42:target|总耗时/u);
|
|
assert.equal(result.markdownOutput?.trim(), markdown.trim());
|
|
});
|
|
|
|
test("trace lineage reports the first non-final assistant content duplicate without printing message text", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-trace-duplicate-lineage-"));
|
|
const duplicateText = "I am checking the workspace.";
|
|
const records = [
|
|
hwlabRecord(1, { type: "assistant", eventType: "assistant", status: "running", label: "agentrun:assistant:message", itemId: "assistant_first", message: duplicateText, assistantText: duplicateText }),
|
|
hwlabRecord(2, { type: "tool", eventType: "tool", status: "completed", label: "agentrun:tool:commandExecution", toolName: "commandExecution", itemId: "tool_between", command: "rg target", exitCode: 0 }),
|
|
hwlabRecord(3, { type: "assistant", eventType: "assistant", status: "running", label: "agentrun:assistant:message", itemId: "assistant_repeated", message: duplicateText, assistantText: duplicateText }),
|
|
hwlabRecord(4, { type: "assistant", eventType: "assistant", status: "running", label: "agentrun:assistant:message", itemId: "assistant_final", message: "Done.", assistantText: "Done.", replyAuthority: true, final: true, finalResponse: { text: "Done." } }),
|
|
hwlabRecord(5, { type: "result", eventType: "terminal", status: "completed", label: "agentrun:terminal:completed", terminal: true })
|
|
];
|
|
const inputFile = path.join(cwd, "hwlab-events.jsonl");
|
|
await writeFile(inputFile, records.map((record) => JSON.stringify(record)).join("\n") + "\n", "utf8");
|
|
|
|
const result = await runKafkaCli([
|
|
"render", "trace",
|
|
"--from", "jsonl",
|
|
"--trace-id", TRACE_ID,
|
|
"--jsonl-file", inputFile,
|
|
"--format", "markdown"
|
|
], { cwd, env: {}, now: () => "2026-07-10T12:00:00.000Z" });
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.deepEqual(result.payload.replayLineage.counts, { source: 5, applied: 5, assistant: 2, tool: 1, terminal: 1, final: 1 });
|
|
assert.equal(result.payload.replayLineage.firstVisibleDuplicateLayer, "source");
|
|
assert.equal(result.payload.replayLineage.layers.source.assistantDuplicate.occurrenceCount, 1);
|
|
assert.equal(result.payload.replayLineage.layers.applied.assistantDuplicate.occurrenceCount, 1);
|
|
assert.equal(result.payload.replayLineage.layers.render.assistantDuplicate.occurrenceCount, 1);
|
|
assert.equal(result.payload.replayLineage.layers.source.assistantDuplicate.first.first.identity, "evt_render_1");
|
|
assert.equal(result.payload.replayLineage.layers.source.assistantDuplicate.first.duplicate.identity, "evt_render_3");
|
|
assert.doesNotMatch(JSON.stringify(result.payload.replayLineage), new RegExp(duplicateText.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"), "u"));
|
|
assert.match(renderKafkaCliText(result.payload), /assistant=2 tool=1 terminalRows=1 final=1 duplicateLayer=source/u);
|
|
});
|
|
|
|
test("shared row model removes late assistant progress after the same item completed", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-trace-assistant-item-lifecycle-"));
|
|
const inputFile = path.join(cwd, "hwlab-events.jsonl");
|
|
const duplicateText = "The completed assistant item must stay visible once.";
|
|
const records = [
|
|
hwlabRecord(1, { type: "assistant", eventType: "assistant", status: "running", label: "agentrun:assistant:message", itemId: "assistant_item", assistantSource: "completed-agent-message", message: duplicateText, assistantText: duplicateText }),
|
|
hwlabRecord(2, { type: "tool", eventType: "tool", status: "completed", label: "agentrun:tool:commandExecution", toolName: "commandExecution", itemId: "tool_between", command: "rg target", exitCode: 0 }),
|
|
hwlabRecord(3, { type: "assistant", eventType: "assistant", status: "running", label: "agentrun:assistant:message", itemId: "assistant_item", assistantSource: "agent-message-delta-progress", message: duplicateText, assistantText: duplicateText }),
|
|
hwlabRecord(4, { type: "assistant", eventType: "assistant", status: "running", label: "agentrun:assistant:message", itemId: "assistant_final", assistantSource: "completed-agent-message-final", message: "Done.", assistantText: "Done.", replyAuthority: true, final: true, finalResponse: { text: "Done." } }),
|
|
hwlabRecord(5, { type: "result", eventType: "terminal", status: "completed", label: "agentrun:terminal:completed", terminal: true })
|
|
];
|
|
await writeFile(inputFile, records.map((record) => JSON.stringify(record)).join("\n") + "\n", "utf8");
|
|
|
|
const result = await runKafkaCli([
|
|
"render", "trace",
|
|
"--from", "jsonl",
|
|
"--trace-id", TRACE_ID,
|
|
"--jsonl-file", inputFile,
|
|
"--format", "markdown"
|
|
], { cwd, env: {}, now: () => "2026-07-10T12:00:00.000Z" });
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.deepEqual(result.payload.replayLineage.counts, { source: 5, applied: 5, assistant: 1, tool: 1, terminal: 1, final: 1 });
|
|
assert.equal(result.payload.replayLineage.layers.source.assistantDuplicate.occurrenceCount, 1);
|
|
assert.equal(result.payload.replayLineage.layers.applied.assistantDuplicate.occurrenceCount, 1);
|
|
assert.equal(result.payload.replayLineage.layers.render.assistantDuplicate.occurrenceCount, 0);
|
|
assert.equal(result.payload.replayLineage.firstVisibleDuplicateLayer, "source");
|
|
assert.equal((result.markdownOutput?.match(new RegExp(duplicateText.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"), "gu")) ?? []).length, 1);
|
|
});
|
|
|
|
test("fixed Kafka assistant lifecycle replays 500/1003 byte progress into one 1190 byte final", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-assistant-progress-contract-"));
|
|
const inputFile = path.join(cwd, "hwlab-events.jsonl");
|
|
const itemId = "msg_0d2ec_assistant_lifecycle";
|
|
const finalText = "x".repeat(1190);
|
|
const progress500 = finalText.slice(0, 500);
|
|
const progress1003 = finalText.slice(0, 1003);
|
|
assert.deepEqual([Buffer.byteLength(progress500), Buffer.byteLength(progress1003), Buffer.byteLength(finalText)], [500, 1003, 1190]);
|
|
|
|
const agentrunRecords = [
|
|
agentrunAssistantLifecycleRecord(136, 1, "assistant_progress", {
|
|
text: progress500,
|
|
itemId,
|
|
source: "agent-message-delta-progress",
|
|
progress: true,
|
|
progressFlush: false,
|
|
replyAuthority: false,
|
|
final: false,
|
|
textBytes: 500,
|
|
outputBytes: 500,
|
|
valuesPrinted: false
|
|
}),
|
|
agentrunAssistantLifecycleRecord(137, 2, "assistant_progress", {
|
|
text: progress1003,
|
|
itemId,
|
|
source: "agent-message-delta-progress",
|
|
progress: true,
|
|
progressFlush: false,
|
|
replyAuthority: false,
|
|
final: false,
|
|
textBytes: 1003,
|
|
outputBytes: 1003,
|
|
valuesPrinted: false
|
|
}),
|
|
agentrunAssistantLifecycleRecord(138, 3, "assistant_message", {
|
|
text: finalText,
|
|
itemId,
|
|
source: "completed-agent-message",
|
|
durableText: true,
|
|
replyAuthority: false,
|
|
final: false,
|
|
textBytes: 1190,
|
|
outputBytes: 1190,
|
|
valuesPrinted: false
|
|
}),
|
|
agentrunAssistantLifecycleRecord(142, 4, "backend_status", {
|
|
phase: "assistant-message-final-sealed",
|
|
itemId,
|
|
source: "assistant-message-final-seal",
|
|
finalSeal: true,
|
|
replyAuthority: true,
|
|
final: true,
|
|
replyRef: {
|
|
eventType: "assistant_message",
|
|
itemId,
|
|
source: "completed-agent-message",
|
|
textSha256: "a".repeat(64)
|
|
},
|
|
valuesPrinted: false
|
|
}),
|
|
agentrunAssistantLifecycleRecord(143, 5, "terminal_status", {
|
|
terminalStatus: "completed",
|
|
valuesPrinted: false
|
|
})
|
|
];
|
|
const hwlabRecords = agentrunRecords.map((record, index) => hwlabOrderRecord(record, index));
|
|
const hwlabEvents = hwlabRecords.map((record) => record.value.event);
|
|
|
|
assert.deepEqual(hwlabRecords.map((record) => record.value.context.agentRunEventType), [
|
|
"assistant_progress",
|
|
"assistant_progress",
|
|
"assistant_message",
|
|
"backend_status",
|
|
"terminal_status"
|
|
]);
|
|
assert.deepEqual(hwlabEvents.slice(0, 3).map((event) => [event.itemId, event.sourceSeq, event.progress, event.assistantText.length]), [
|
|
[itemId, 136, true, 500],
|
|
[itemId, 137, true, 1003],
|
|
[itemId, 138, false, 1190]
|
|
]);
|
|
assert.equal(hwlabEvents[3]?.finalSeal, true);
|
|
assert.equal(hwlabEvents[3]?.message, "assistant-message-final-sealed");
|
|
assert.equal(hwlabEvents[3]?.assistantText, undefined, "seal must not copy the completed body");
|
|
assert.equal(hwlabEvents[4]?.assistantText, undefined, "terminal must not copy the completed body");
|
|
|
|
const runningRows = traceDisplayRows({ eventSource: "hwlab-kafka-sse" }, hwlabEvents.slice(0, 2));
|
|
assert.equal(runningRows.filter((row) => /助手/u.test(row.header)).length, 1);
|
|
assert.equal(runningRows.find((row) => /助手/u.test(row.header))?.seq, 137);
|
|
assert.equal(runningRows.find((row) => /助手/u.test(row.header))?.body, progress1003);
|
|
const completedRows = traceDisplayRows({ eventSource: "hwlab-kafka-sse" }, hwlabEvents.slice(0, 3));
|
|
assert.equal(completedRows.filter((row) => /助手/u.test(row.header)).length, 1);
|
|
assert.equal(completedRows.find((row) => /助手/u.test(row.header))?.seq, 138);
|
|
assert.equal(completedRows.find((row) => /助手/u.test(row.header))?.body, finalText);
|
|
|
|
const projected = projectHwlabRecordsThroughWorkbench(hwlabRecords, {
|
|
traceId: TRACE_ID,
|
|
sessionId: HWLAB_SESSION_ID,
|
|
observedAt: "2026-07-12T10:01:00.000Z"
|
|
});
|
|
assert.equal(projected.appliedCount, 5);
|
|
assert.equal(projected.sourceEvents.length, 5);
|
|
assert.equal(projected.message?.runnerTrace?.events?.length, 5, "raw/applied observation must retain every unfiltered event");
|
|
assert.equal(projected.message?.text, finalText);
|
|
assert.equal(projected.message?.status, "completed");
|
|
assert.equal((projected.message?.finalResponse as { text?: string } | null)?.text, finalText);
|
|
|
|
await writeFile(inputFile, hwlabRecords.map((record) => JSON.stringify(record)).join("\n") + "\n", "utf8");
|
|
const result = await runKafkaCli([
|
|
"render", "trace",
|
|
"--from", "jsonl",
|
|
"--trace-id", TRACE_ID,
|
|
"--session-id", HWLAB_SESSION_ID,
|
|
"--jsonl-file", inputFile,
|
|
"--format", "markdown"
|
|
], { cwd, env: {}, now: () => "2026-07-12T10:02:00.000Z" });
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.projection.appliedCount, 5);
|
|
assert.equal(result.payload.projection.eventCount, 5);
|
|
assert.equal(result.payload.render.assistantRowCount, 0, "the authoritative outer final replaces the progress lifecycle");
|
|
assert.equal(result.payload.render.finalResponsePresent, true);
|
|
assert.equal(result.payload.render.finalResponseInTrace, false);
|
|
assert.deepEqual(result.payload.replayLineage.counts, { source: 5, applied: 5, assistant: 0, tool: 0, terminal: 1, final: 1 });
|
|
assert.equal(result.payload.replayLineage.layers.source.assistant, 3, "raw source lineage must keep both progress snapshots and the durable completion");
|
|
assert.equal(result.payload.replayLineage.layers.applied.assistant, 3);
|
|
assert.equal(result.payload.validation.allMatchedEventsApplied, true);
|
|
assert.equal(result.payload.validation.finalResponseOutsideTrace, true);
|
|
assert.equal(result.markdownOutput?.split(finalText).length, 2, "the final body must render exactly once");
|
|
const traceMarkdown = (result.markdownOutput ?? "").split("## 运行记录")[1]?.split("## 最终回复")[0] ?? "";
|
|
assert.doesNotMatch(traceMarkdown, /x{500}/u, "progress must not remain as a separate rendered row");
|
|
});
|
|
|
|
test("HWLAB compatibility window coalesces legacy assistant_message progress before AgentRun assistant_progress rollout", () => {
|
|
const itemId = "msg_legacy_progress_contract";
|
|
const finalText = "legacy durable body";
|
|
const agentrunRecords = [
|
|
agentrunAssistantLifecycleRecord(201, 1, "assistant_message", {
|
|
text: "legacy progress 1",
|
|
itemId,
|
|
source: "agent-message-delta-progress",
|
|
progress: true,
|
|
progressFlush: false,
|
|
replyAuthority: false,
|
|
final: false
|
|
}),
|
|
agentrunAssistantLifecycleRecord(202, 2, "assistant_message", {
|
|
text: "legacy progress 2",
|
|
itemId,
|
|
source: "agent-message-delta-progress",
|
|
progress: true,
|
|
progressFlush: true,
|
|
replyAuthority: false,
|
|
final: false
|
|
}),
|
|
agentrunAssistantLifecycleRecord(203, 3, "assistant_message", {
|
|
text: finalText,
|
|
itemId,
|
|
source: "completed-agent-message",
|
|
durableText: true,
|
|
replyAuthority: false,
|
|
final: false
|
|
}),
|
|
agentrunAssistantLifecycleRecord(204, 4, "terminal_status", { terminalStatus: "completed" })
|
|
];
|
|
const hwlabRecords = agentrunRecords.map((record, index) => hwlabOrderRecord(record, index));
|
|
const events = hwlabRecords.map((record) => record.value.event);
|
|
|
|
assert.deepEqual(events.slice(0, 2).map((event) => [event.agentRunEventType, event.eventType, event.progress]), [
|
|
["assistant_message", "assistant", true],
|
|
["assistant_message", "assistant", true]
|
|
]);
|
|
const rows = traceDisplayRows({ eventSource: "hwlab-kafka-sse" }, events.slice(0, 3));
|
|
const assistantRows = rows.filter((row) => /助手/u.test(row.header));
|
|
assert.equal(assistantRows.length, 1);
|
|
assert.equal(assistantRows[0]?.seq, 203);
|
|
assert.equal(assistantRows[0]?.body, finalText);
|
|
|
|
const projected = projectHwlabRecordsThroughWorkbench(hwlabRecords, {
|
|
traceId: TRACE_ID,
|
|
sessionId: HWLAB_SESSION_ID,
|
|
observedAt: "2026-07-12T10:03:00.000Z"
|
|
});
|
|
assert.equal(projected.message?.text, finalText);
|
|
assert.equal(projected.message?.runnerTrace?.events?.length, 4);
|
|
assert.equal((projected.message?.finalResponse as { text?: string } | null)?.text, finalText);
|
|
const finalRows = traceDisplayRows(projected.message?.runnerTrace ?? {}, projected.message?.runnerTrace?.events ?? [], {
|
|
finalResponsePlacement: "outer",
|
|
outerFinalResponseText: finalText
|
|
});
|
|
assert.equal(finalRows.filter((row) => /助手/u.test(row.header)).length, 0);
|
|
});
|
|
|
|
test("shared tool summary model keeps status semantic and visible content status-free", () => {
|
|
const rows = traceDisplayRows({ eventSource: "hwlab-kafka-sse" }, [
|
|
{
|
|
sourceSeq: 1,
|
|
sourceEventId: "evt_tool_running",
|
|
type: "tool_call",
|
|
status: "running",
|
|
label: "agentrun:tool:commandExecution:started",
|
|
toolName: "commandExecution",
|
|
itemId: "tool_running",
|
|
command: "bun test active-fixture",
|
|
createdAt: "2026-07-10T11:00:01.000Z"
|
|
},
|
|
{
|
|
sourceSeq: 2,
|
|
sourceEventId: "evt_tool_success",
|
|
type: "tool_call",
|
|
status: "completed",
|
|
label: "agentrun:tool:commandExecution:completed",
|
|
toolName: "commandExecution",
|
|
itemId: "tool_success",
|
|
command: "find . -maxdepth 3 -name package.json -o -name tsconfig.json -o -name vitest.config.ts",
|
|
durationMs: 1234,
|
|
exitCode: 0,
|
|
stdoutSummary: "src/main.ts:1:success",
|
|
createdAt: "2026-07-10T11:00:02.000Z"
|
|
},
|
|
{
|
|
sourceSeq: 3,
|
|
sourceEventId: "evt_tool_failure",
|
|
type: "tool_call",
|
|
status: "completed",
|
|
label: "agentrun:tool:commandExecution:completed",
|
|
toolName: "commandExecution",
|
|
itemId: "tool_failure",
|
|
command: "bun test failure-fixture",
|
|
durationMs: 2345,
|
|
exitCode: 1,
|
|
stderrSummary: "failure output",
|
|
createdAt: "2026-07-10T11:00:03.000Z"
|
|
},
|
|
{
|
|
sourceSeq: 4,
|
|
sourceEventId: "evt_tool_output_chunk",
|
|
type: "tool_call",
|
|
status: "output_chunk",
|
|
label: "item/commandExecution/outputDelta",
|
|
outputSummary: "streamed output",
|
|
createdAt: "2026-07-10T11:00:04.000Z"
|
|
}
|
|
]).filter((row) => row.rowId.startsWith("tool:"));
|
|
|
|
assert.deepEqual(rows.map((row) => row.toolState), ["running", "success", "failure", "running"]);
|
|
assert.deepEqual(rows.map((row) => row.tone), ["warn", "ok", "blocked", "warn"]);
|
|
assert.deepEqual(rows.map((row) => row.statusLabel), ["工具调用中", "工具调用成功", "工具调用失败", "工具调用中"]);
|
|
assert.ok(rows.every((row) => /^\d{2}:\d{2}:\d{2}$/u.test(row.header)));
|
|
assert.equal(rows[1]!.preview, "find . -maxdepth 3 -name package.json -o -name tsconfig.json -o -name vitest.config.ts");
|
|
assert.equal(rows[2]!.preview, "bun test failure-fixture");
|
|
assert.doesNotMatch(rows[3]!.header, /commandExecution|outputDelta/u);
|
|
|
|
const markdown = renderTraceRowsMarkdown(rows);
|
|
const visibleSummaries = [...markdown.matchAll(/<summary[^>]*>([\s\S]*?)<\/summary>/gu)].map((match) => match[1]);
|
|
assert.equal(visibleSummaries.length, 4);
|
|
for (const [index, summary] of visibleSummaries.entries()) {
|
|
assert.equal(summary, `<code>${rows[index]!.header}</code> ${traceToolSummary(rows[index]!)}`);
|
|
assert.doesNotMatch(summary, /\b(?:ok|fail|run|running)\b|total=|duration=|exit=/u);
|
|
}
|
|
assert.match(visibleSummaries[1]!, /^<code>\d{2}:\d{2}:\d{2}<\/code> find \. -maxdepth 3/u);
|
|
assert.match(markdown, /aria-label="工具调用中:/u);
|
|
assert.match(markdown, /aria-label="工具调用成功:/u);
|
|
assert.match(markdown, /aria-label="工具调用失败:/u);
|
|
});
|
|
|
|
test("fileChange and turn diff render real paths and unified diff as valid Markdown cards", () => {
|
|
const fileDiff = "@@ -1 +1 @@\n-export const value = 1;\n+export const value = 2;";
|
|
const aggregateDiff = `${fileDiff}\n@@ -0,0 +1 @@\n+# 文件编辑可见`;
|
|
const rows = traceDisplayRows({ eventSource: "hwlab-kafka-sse" }, [
|
|
{
|
|
sourceSeq: 21,
|
|
sourceEventId: "evt_file_change_completed",
|
|
type: "tool_call",
|
|
eventType: "tool_call",
|
|
status: "completed",
|
|
label: "agentrun:tool:fileChange",
|
|
toolName: "fileChange",
|
|
toolType: "fileChange",
|
|
itemId: "item_file_change",
|
|
changes: [
|
|
{ path: "src/example.ts", kind: "update", diff: fileDiff },
|
|
{ path: "docs/说明.md", kind: "add", diff: "@@ -0,0 +1 @@\n+# 文件编辑可见" }
|
|
],
|
|
createdAt: "2026-07-12T15:00:01.000Z"
|
|
},
|
|
{
|
|
sourceSeq: 22,
|
|
sourceEventId: "evt_turn_diff_updated",
|
|
type: "diff",
|
|
eventType: "diff",
|
|
status: "updated",
|
|
label: "agentrun:diff",
|
|
diff: aggregateDiff,
|
|
message: aggregateDiff,
|
|
createdAt: "2026-07-12T15:00:02.000Z"
|
|
}
|
|
]);
|
|
const markdown = renderTraceRowsMarkdown(rows);
|
|
assert.equal(rows.length, 2);
|
|
assert.match(markdown, /2 个文件 · \+2 -1 · src\/example\.ts, docs\/说明\.md/u);
|
|
assert.match(markdown, /#### `src\/example\.ts`/u);
|
|
assert.match(markdown, /#### `docs\/说明\.md`/u);
|
|
assert.match(markdown, /\+export const value = 2;/u);
|
|
assert.match(markdown, /文件 diff 已更新/u);
|
|
assert.match(markdown, /\+# 文件编辑可见/u);
|
|
assert.equal((markdown.match(/<details>/gu) ?? []).length, 2);
|
|
assert.equal((markdown.match(/<\/details>/gu) ?? []).length, 2);
|
|
assert.ok(rows.every((row) => row.bodyFormat === "markdown"));
|
|
});
|
|
|
|
test("fileChange rendering uses preserved stats and ignores historical object coercion", () => {
|
|
const rows = traceDisplayRows({ eventSource: "hwlab-kafka-sse" }, [
|
|
{
|
|
sourceSeq: 53,
|
|
type: "tool",
|
|
eventType: "tool",
|
|
status: "completed",
|
|
label: "agentrun:tool:fileChange",
|
|
toolName: "fileChange",
|
|
itemId: "item_large_file_change",
|
|
changes: [{ path: "/agentrun-workspace/src/large.ts", kind: "add", additions: 400, deletions: 0, diffTruncated: true }],
|
|
message: "bounded file change metadata",
|
|
createdAt: "2026-07-21T08:04:30.452Z"
|
|
},
|
|
{
|
|
sourceSeq: 54,
|
|
type: "tool",
|
|
eventType: "tool",
|
|
status: "completed",
|
|
label: "agentrun:tool:fileChange",
|
|
toolName: "fileChange",
|
|
itemId: "item_historical_file_change",
|
|
message: "[object Object]",
|
|
createdAt: "2026-07-21T08:04:31.452Z"
|
|
}
|
|
]);
|
|
assert.equal(rows[0]?.preview, "src/large.ts · +400 -0");
|
|
assert.match(rows[0]?.body ?? "", /新增 · \+400 -0/u);
|
|
assert.doesNotMatch(JSON.stringify(rows), /\[object Object\]/u);
|
|
assert.equal(rows[1]?.body, null);
|
|
assert.equal(rows[1]?.preview, "fileChange");
|
|
});
|
|
|
|
test("outer final response suppression follows formal assistant identity instead of equal text", () => {
|
|
const finalText = "Hi. What do you want to work on?";
|
|
const rows = traceDisplayRows({ eventSource: "hwlab-kafka-sse" }, [
|
|
{
|
|
sourceSeq: 1,
|
|
sourceEventId: "evt_same_text",
|
|
type: "assistant",
|
|
eventType: "assistant",
|
|
status: "running",
|
|
label: "agentrun:assistant:message",
|
|
itemId: "assistant_same_text",
|
|
message: finalText,
|
|
assistantText: finalText,
|
|
assistantSource: "completed-agent-message"
|
|
},
|
|
{
|
|
sourceSeq: 2,
|
|
sourceEventId: "evt_final",
|
|
type: "assistant",
|
|
eventType: "assistant",
|
|
status: "running",
|
|
label: "agentrun:assistant:message",
|
|
itemId: "assistant_final",
|
|
message: finalText,
|
|
assistantText: finalText,
|
|
assistantSource: "completed-agent-message-final",
|
|
finalResponse: { text: finalText },
|
|
final: true,
|
|
replyAuthority: true
|
|
}
|
|
], {
|
|
finalResponsePlacement: "outer",
|
|
outerFinalResponseText: finalText
|
|
});
|
|
|
|
assert.equal(rows.length, 1);
|
|
assert.equal(rows[0]?.rowId, "event:evt_same_text");
|
|
assert.equal(rows[0]?.body, finalText, "a distinct itemId with equal text must remain visible");
|
|
});
|
|
|
|
test("trace JSON defaults to a bounded tail while the Markdown artifact stays complete", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-trace-window-"));
|
|
const inputFile = path.join(cwd, "hwlab-events.jsonl");
|
|
const records = Array.from({ length: 25 }, (_, index) => hwlabRecord(index + 1, {
|
|
type: "assistant",
|
|
eventType: "assistant",
|
|
status: "running",
|
|
label: "agentrun:assistant:message",
|
|
itemId: `assistant_${index + 1}`,
|
|
message: `progress ${index + 1}`,
|
|
assistantText: `progress ${index + 1}`
|
|
}));
|
|
records.push(hwlabRecord(26, { type: "result", eventType: "terminal", status: "completed", label: "agentrun:terminal:completed", terminal: true }));
|
|
await writeFile(inputFile, records.map((record) => JSON.stringify(record)).join("\n") + "\n", "utf8");
|
|
|
|
const result = await runKafkaCli([
|
|
"render", "trace",
|
|
"--from", "jsonl",
|
|
"--trace-id", TRACE_ID,
|
|
"--jsonl-file", inputFile,
|
|
"--row-limit", "5",
|
|
"--format", "markdown"
|
|
], { cwd, env: {}, now: () => "2026-07-10T12:00:00.000Z" });
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.render.rowCount, 25);
|
|
assert.equal(result.payload.render.rowsReturned, 5);
|
|
assert.equal(result.payload.render.rowsOmitted, 20);
|
|
assert.equal(result.payload.render.rowWindow, "tail");
|
|
assert.match(result.markdownOutput, /progress 1/u);
|
|
assert.match(result.markdownOutput, /progress 24/u);
|
|
});
|
|
|
|
test("command-scoped trace keeps attributable commandless final and terminal lifecycle events", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-command-scope-"));
|
|
const finalText = "Hi. What do you want to work on?";
|
|
const exactTool = hwlabRecord(1, {
|
|
type: "tool",
|
|
eventType: "tool",
|
|
status: "completed",
|
|
label: "agentrun:tool:commandExecution",
|
|
toolName: "commandExecution",
|
|
itemId: "tool_command_scope",
|
|
command: "rg -n target src",
|
|
exitCode: 0
|
|
});
|
|
const exactProgress = hwlabRecord(2, {
|
|
type: "assistant",
|
|
eventType: "assistant",
|
|
status: "running",
|
|
itemId: "assistant_progress",
|
|
assistantText: "Checking the workspace."
|
|
});
|
|
const unscopedFinal = withoutCommandId(hwlabRecord(3, {
|
|
type: "assistant",
|
|
eventType: "assistant",
|
|
status: "running",
|
|
itemId: "assistant_final",
|
|
assistantText: finalText,
|
|
finalResponse: { text: finalText },
|
|
final: true,
|
|
replyAuthority: true
|
|
}));
|
|
const unscopedTerminal = withoutCommandId(hwlabRecord(4, {
|
|
type: "result",
|
|
eventType: "terminal",
|
|
status: "completed",
|
|
terminal: true,
|
|
message: "AgentRun completed"
|
|
}));
|
|
const runnerShutdown = withCommandId(hwlabRecord(5, {
|
|
type: "backend",
|
|
eventType: "backend",
|
|
status: "running",
|
|
message: "runner shutdown"
|
|
}), "runner-shutdown");
|
|
const unscopedBackend = withoutCommandId(hwlabRecord(6, {
|
|
type: "backend",
|
|
eventType: "backend",
|
|
status: "running",
|
|
message: "unscoped heartbeat"
|
|
}));
|
|
let readerInput: any = null;
|
|
const result = await runKafkaCli([
|
|
"render", "trace",
|
|
"--from", "kafka",
|
|
"--trace-id", TRACE_ID,
|
|
"--session-id", HWLAB_SESSION_ID,
|
|
"--run-id", RUN_ID,
|
|
"--command-id", "cmd_render_trace",
|
|
"--group-prefix", "hwlab-v03-workbench-isolated-debug",
|
|
"--row-limit", "2",
|
|
"--format", "markdown"
|
|
], {
|
|
cwd,
|
|
env: {},
|
|
now: () => "2026-07-10T12:00:00.000Z",
|
|
async readKafka(input) {
|
|
readerInput = input;
|
|
const events = [exactTool, exactProgress, unscopedFinal, unscopedTerminal, runnerShutdown, unscopedBackend];
|
|
return {
|
|
...completeKafkaRead(events),
|
|
scannedCount: 6,
|
|
parsedCount: 6,
|
|
matchedCount: 6,
|
|
filterRejectedCount: 0
|
|
};
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(readerInput.commandId, undefined, "commandId must not be pushed down before lifecycle attribution");
|
|
assert.equal(readerInput.runId, RUN_ID);
|
|
assert.equal(result.payload.input.queryMatchedCount, 6);
|
|
assert.equal(result.payload.input.matchedCount, 4);
|
|
assert.equal(result.payload.input.refreshHandoff.status, "succeeded");
|
|
assert.equal(result.payload.input.refreshHandoff.liveSubscriptionInstalled, true);
|
|
assert.equal(result.payload.input.refreshHandoff.counts.replayed, 6);
|
|
assert.deepEqual(result.payload.input.refreshHandoff.barrier, [{ partition: 0, endOffset: "1907" }]);
|
|
assert.deepEqual(result.payload.input.commandScope.commandIdCounts, [
|
|
{ commandId: "cmd_render_trace", count: 2 },
|
|
{ commandId: "runner-shutdown", count: 1 }
|
|
]);
|
|
assert.equal(result.payload.input.commandScope.missingCommandIdCount, 3);
|
|
assert.equal(result.payload.input.commandScope.unscopedLifecycleCount, 2);
|
|
assert.equal(result.payload.input.commandScope.selectedUnscopedLifecycleCount, 2);
|
|
assert.equal(result.payload.input.commandScope.unscopedNonLifecycleCount, 1);
|
|
assert.equal(result.payload.input.commandScope.otherCommandRecordCount, 1);
|
|
assert.equal(result.payload.projection.eventCount, 4);
|
|
assert.equal(result.payload.projection.terminalObserved, true);
|
|
assert.equal(result.payload.projection.pipeline[0], "createWorkbenchKafkaRefreshHandoff");
|
|
assert.equal(result.payload.render.finalResponseInTrace, false);
|
|
assert.equal(result.payload.validation.commandLifecyclePreserved, true);
|
|
assert.equal((result.markdownOutput?.match(/Hi\. What do you want to work on\?/gu) ?? []).length, 1);
|
|
});
|
|
|
|
test("missing commandId cannot be satisfied by commandless lifecycle events from the same run", async () => {
|
|
const otherCommand = withCommandId(hwlabRecord(1, {
|
|
type: "tool",
|
|
eventType: "tool",
|
|
status: "completed",
|
|
label: "agentrun:tool:commandExecution",
|
|
itemId: "tool_other_command",
|
|
command: "rg -n target src",
|
|
exitCode: 0
|
|
}), "cmd_other");
|
|
const commandlessFinal = withoutCommandId(hwlabRecord(2, {
|
|
type: "assistant",
|
|
eventType: "assistant",
|
|
status: "running",
|
|
itemId: "assistant_final",
|
|
assistantText: "This belongs to the existing command.",
|
|
finalResponse: { text: "This belongs to the existing command." },
|
|
final: true,
|
|
replyAuthority: true
|
|
}));
|
|
const commandlessTerminal = withoutCommandId(hwlabRecord(3, {
|
|
type: "result",
|
|
eventType: "terminal",
|
|
status: "completed",
|
|
terminal: true
|
|
}));
|
|
|
|
const result = await runKafkaCli([
|
|
"render", "trace",
|
|
"--from", "kafka",
|
|
"--trace-id", TRACE_ID,
|
|
"--session-id", HWLAB_SESSION_ID,
|
|
"--run-id", RUN_ID,
|
|
"--command-id", "cmd_missing",
|
|
"--group-prefix", "hwlab-v03-workbench-isolated-debug",
|
|
"--format", "markdown"
|
|
], {
|
|
env: {},
|
|
now: () => "2026-07-10T12:00:00.000Z",
|
|
async readKafka() {
|
|
const events = [otherCommand, commandlessFinal, commandlessTerminal];
|
|
return {
|
|
...completeKafkaRead(events),
|
|
scannedCount: 3,
|
|
parsedCount: 3,
|
|
matchedCount: 3,
|
|
filterRejectedCount: 0
|
|
};
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.payload.error.code, "source_command_missing");
|
|
assert.equal(result.payload.error.details.commandScope.exactCommandCount, 0);
|
|
assert.equal(result.payload.error.details.commandScope.selectedCount, 0);
|
|
assert.equal(result.payload.error.details.commandScope.selectedUnscopedLifecycleCount, 0);
|
|
});
|
|
|
|
test("Kafka Trace render returns a typed partial result when the captured end offsets were not reached", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-trace-partial-"));
|
|
const record = hwlabRecord(1, {
|
|
type: "assistant",
|
|
eventType: "assistant",
|
|
status: "running",
|
|
itemId: "assistant_partial",
|
|
assistantText: "Partial trace row"
|
|
});
|
|
const result = await runKafkaCli([
|
|
"render", "trace",
|
|
"--from", "kafka",
|
|
"--trace-id", TRACE_ID,
|
|
"--session-id", HWLAB_SESSION_ID,
|
|
"--run-id", RUN_ID,
|
|
"--command-id", "cmd_render_trace",
|
|
"--group-prefix", "hwlab-v03-workbench-isolated-debug",
|
|
"--format", "markdown"
|
|
], {
|
|
cwd,
|
|
env: {},
|
|
now: () => "2026-07-10T12:00:00.000Z",
|
|
async readKafka() {
|
|
return {
|
|
events: [record],
|
|
scannedCount: 1,
|
|
parsedCount: 1,
|
|
matchedCount: 1,
|
|
filterRejectedCount: 0,
|
|
completionReason: "timeout",
|
|
reachedEndOffsets: false,
|
|
endOffsetsAvailable: false,
|
|
endOffsetsError: { code: "ENOTFOUND", message: "broker metadata hostname unresolved", valuesRedacted: true },
|
|
endOffsets: [],
|
|
completion: { reason: "timeout", complete: false, barrierReached: false, retentionStartVerified: true },
|
|
lastScannedOffsets: [{ partition: 0, offset: "0" }]
|
|
};
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 2);
|
|
assert.equal(result.payload.input.endOffsetsError.code, "ENOTFOUND");
|
|
assert.equal(result.payload.ok, false);
|
|
assert.equal(result.payload.status, "partial");
|
|
assert.equal(result.payload.error.code, "source_scan_incomplete");
|
|
assert.equal(result.payload.input.scanComplete, false);
|
|
assert.equal(result.payload.input.completionReason, "timeout");
|
|
assert.equal(result.payload.input.refreshHandoff.status, "failed");
|
|
assert.equal(result.payload.input.refreshHandoff.error.code, "workbench_kafka_refresh_timeout");
|
|
assert.equal(result.payload.validation.sourceScanComplete, false);
|
|
assert.equal(result.payload.validation.commandLifecyclePreserved, false);
|
|
assert.match(result.markdownOutput ?? "", /source scan: partial \(timeout\)/u);
|
|
assert.equal(typeof result.payload.output.artifact.path, "string");
|
|
assert.match(renderKafkaCliText(result.payload), /^partial action=kafka\.render\.trace code=source_scan_incomplete completion=timeout reachedEnd=false/u);
|
|
});
|
|
|
|
test("incomplete zero-match Kafka scan stays partial and never claims the trace is missing", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-trace-empty-partial-"));
|
|
const result = await runKafkaCli([
|
|
"render", "trace",
|
|
"--from", "kafka",
|
|
"--trace-id", TRACE_ID,
|
|
"--group-prefix", "hwlab-v03-workbench-isolated-debug",
|
|
"--format", "markdown"
|
|
], {
|
|
cwd,
|
|
env: {},
|
|
now: () => "2026-07-10T12:00:00.000Z",
|
|
async readKafka() {
|
|
return {
|
|
events: [],
|
|
scannedCount: 0,
|
|
parsedCount: 0,
|
|
matchedCount: 0,
|
|
filterRejectedCount: 0,
|
|
completionReason: "timeout",
|
|
reachedEndOffsets: false,
|
|
endOffsetsAvailable: true,
|
|
endOffsets: [{ partition: 0, startOffset: "0", endOffset: "12" }],
|
|
completion: { reason: "timeout", complete: false, barrierReached: false, retentionStartVerified: true },
|
|
lastScannedOffsets: []
|
|
};
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 2);
|
|
assert.equal(result.payload.status, "partial");
|
|
assert.equal(result.payload.error.code, "source_scan_incomplete");
|
|
assert.notEqual(result.payload.error.code, "source_trace_missing");
|
|
assert.equal(result.payload.input.scannedCount, 0);
|
|
assert.equal(result.payload.input.matchedCount, 0);
|
|
assert.equal(result.payload.input.scanComplete, false);
|
|
assert.equal(result.payload.input.refreshHandoff.status, "failed");
|
|
assert.equal(result.payload.input.refreshHandoff.error.code, "workbench_kafka_refresh_timeout");
|
|
assert.equal(result.payload.validation.refreshHandoffComplete, false);
|
|
assert.match(result.markdownOutput ?? "", /source scan: partial \(timeout\)/u);
|
|
assert.equal(typeof result.payload.output.artifact.path, "string");
|
|
assert.match(renderKafkaCliText(result.payload), /^partial action=kafka\.render\.trace code=source_scan_incomplete completion=timeout reachedEnd=false scanned=0 matched=0 applied=0/u);
|
|
});
|
|
|
|
test("offline JSONL maps 35 stdio reconstructions to 35 ordered HWLAB debug events without runtime dependencies", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-debug-jsonl-"));
|
|
const inputFile = path.join(cwd, "agentrun-events.jsonl");
|
|
const outputFile = path.join(cwd, "hwlab-events.jsonl");
|
|
const records = Array.from({ length: 35 }, (_, index) => reconstructionRecord(index + 1));
|
|
await writeFile(inputFile, records.map((record) => JSON.stringify(record)).join("\n") + "\n", "utf8");
|
|
|
|
const result = await runKafkaCli([
|
|
"regenerate", "hwlab",
|
|
"--from", "jsonl",
|
|
"--session-id", SESSION_ID,
|
|
"--trace-id", TRACE_ID,
|
|
"--replay-id", "rpl_offline_35",
|
|
"--jsonl-file", inputFile,
|
|
"--output-jsonl", outputFile,
|
|
"--expect-count", "35",
|
|
"--no-publish",
|
|
"--json"
|
|
], {
|
|
cwd,
|
|
env: {},
|
|
now: () => "2026-07-10T12:00:00.000Z",
|
|
async createProducer() { throw new Error("offline JSONL must not create a producer"); }
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.input.readCount, 35);
|
|
assert.equal(result.payload.output.count, 35);
|
|
assert.equal(result.payload.output.publishedCount, 0);
|
|
assert.deepEqual(result.payload.validation, {
|
|
expectedCount: 35,
|
|
sourceScanComplete: true,
|
|
oneToOne: true,
|
|
orderPreserved: true,
|
|
lineagePreserved: true,
|
|
firstSourceSeq: 1,
|
|
lastSourceSeq: 35,
|
|
inputKinds: ["stdio-derived partial reconstruction"]
|
|
});
|
|
assert.deepEqual(result.payload.runtimeDependencies, {
|
|
cloudApi: false,
|
|
database: false,
|
|
transactionalProjector: false,
|
|
kafka: false
|
|
});
|
|
|
|
const output = (await readFile(outputFile, "utf8")).trim().split("\n").map(JSON.parse);
|
|
assert.equal(output.length, 35);
|
|
assert.deepEqual(output.map((row) => row.value.debugLineage.sourceSeq), Array.from({ length: 35 }, (_, index) => index + 1));
|
|
assert.ok(output.every((row) => row.topic === "hwlab.event.debug.v1"));
|
|
assert.ok(output.every((row) => row.value.schema === "hwlab.event.debug.v1"));
|
|
assert.ok(output.every((row) => row.partition === 0));
|
|
assert.ok(output.every((row) => row.value.replayId === "rpl_offline_35"));
|
|
assert.deepEqual(output.map((row) => row.value.debugReplay.index), Array.from({ length: 35 }, (_, index) => index + 1));
|
|
assert.ok(output.every((row) => row.value.eventId === null && row.value.sourceEventId === null && row.value.outboxSeq === null));
|
|
assert.ok(output.every((row) => row.value.debugLineage.inputEventId === null && row.value.debugLineage.inputOutboxSeq === null));
|
|
assert.ok(output.every((row) => row.value.sourceEvent.partition === null && row.value.sourceEvent.offset === null));
|
|
assert.ok(output.every((row) => row.value.traceId === TRACE_ID && row.value.sessionId === HWLAB_SESSION_ID));
|
|
assert.ok(output.every((row) => row.value.debugLineage.inputSessionId === SESSION_ID));
|
|
assert.ok(output.every((row) => row.value.debugLineage.inputHwlabSessionId === HWLAB_SESSION_ID));
|
|
assert.ok(output.every((row) => row.value.reconstruction.kind === "stdio-derived partial reconstruction"));
|
|
assert.ok(output.every((row) => row.value.reconstruction.originalEventId === null && row.value.reconstruction.originalOutboxSeq === null));
|
|
assert.ok(output.every((row) => row.value.reconstruction.identityDerivation.kind === "agentrun-session-uuid-to-hwlab-session"));
|
|
assert.ok(output.every((row) => row.value.reconstruction.identityDerivation.reversible === true));
|
|
});
|
|
|
|
test("Kafka reader and producer are injected while canonical AgentRun events use strict decode and isolated output", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-debug-canonical-"));
|
|
const records = Array.from({ length: 35 }, (_, index) => canonicalRecord(index + 1));
|
|
const readerCalls: any[] = [];
|
|
const producerCalls: any[] = [];
|
|
let connected = 0;
|
|
let disconnected = 0;
|
|
const result = await runKafkaCli([
|
|
"regenerate", "hwlab",
|
|
"--from", "kafka",
|
|
"--session-id", SESSION_ID,
|
|
"--trace-id", TRACE_ID,
|
|
"--replay-id", "rpl_canonical_35",
|
|
"--input-topic", "agentrun.event.v1",
|
|
"--publish",
|
|
"--expect-count", "35",
|
|
"--output-jsonl", path.join(cwd, "canonical-output.jsonl"),
|
|
"--json"
|
|
], {
|
|
cwd,
|
|
env: {
|
|
HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092",
|
|
HWLAB_KAFKA_HWLAB_DEBUG_EVENT_TOPIC: "hwlab.event.debug.v1",
|
|
HWLAB_KAFKA_HWLAB_DEBUG_GROUP_PREFIX: "hwlab-v03-workbench-isolated-debug"
|
|
},
|
|
now: () => "2026-07-10T12:00:00.000Z",
|
|
async readKafka(input) {
|
|
readerCalls.push(input);
|
|
return { ok: true, groupId: "hwlab-v03-agentrun-event-debug-regenerate-test", events: records, completionReason: "end-offset", reachedEndOffsets: true };
|
|
},
|
|
async createProducer() {
|
|
return {
|
|
async connect() { connected += 1; },
|
|
async send(input: any) { producerCalls.push(input); return [{ topicName: input.topic, partition: 0, baseOffset: "100" }]; },
|
|
async disconnect() { disconnected += 1; }
|
|
};
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(readerCalls.length, 1);
|
|
assert.equal(readerCalls[0].groupIdPrefix, "hwlab-v03-workbench-isolated-debug");
|
|
assert.equal(readerCalls[0].sessionId, SESSION_ID);
|
|
assert.equal(readerCalls[0].limit, 36, "exact count verification must leave room to detect one extra event");
|
|
assert.equal(connected, 1);
|
|
assert.equal(disconnected, 1);
|
|
assert.equal(producerCalls.length, 1);
|
|
assert.equal(producerCalls[0].topic, "hwlab.event.debug.v1");
|
|
assert.equal(producerCalls[0].messages.length, 35);
|
|
assert.ok(producerCalls[0].messages.every((message: any) => message.partition === 0));
|
|
const envelopes = producerCalls[0].messages.map((message: any) => JSON.parse(message.value));
|
|
assert.ok(envelopes.every((event: any) => event.replayId === "rpl_canonical_35"));
|
|
assert.ok(producerCalls[0].messages.every((message: any) => message.headers["x-debug-replay-id"] === "rpl_canonical_35"));
|
|
assert.deepEqual(envelopes.map((event: any) => event.debugLineage.sourceSeq), Array.from({ length: 35 }, (_, index) => index + 1));
|
|
assert.deepEqual(envelopes.map((event: any) => event.eventId), Array.from({ length: 35 }, (_, index) => `hwlab:evt_${index + 1}`));
|
|
assert.deepEqual(envelopes.map((event: any) => event.debugLineage.inputOutboxSeq), Array.from({ length: 35 }, (_, index) => index + 1));
|
|
assert.ok(envelopes.every((event: any) => event.diagnostics.inputKind === "canonical-durable"));
|
|
assert.ok(envelopes.every((event: any) => event.schema === "hwlab.event.debug.v1"));
|
|
assert.equal(result.payload.output.publishedCount, 35);
|
|
assert.deepEqual(result.payload.config.outputTopic, { value: "hwlab.event.debug.v1", source: "env:HWLAB_KAFKA_HWLAB_DEBUG_EVENT_TOPIC" });
|
|
assert.deepEqual(result.payload.config.groupPrefix, { value: "hwlab-v03-workbench-isolated-debug", source: "env:HWLAB_KAFKA_HWLAB_DEBUG_GROUP_PREFIX" });
|
|
assert.equal(result.payload.validation.orderPreserved, true);
|
|
assert.equal(result.payload.validation.lineagePreserved, true);
|
|
assert.deepEqual(result.payload.output.offsetRanges, [{
|
|
topic: "hwlab.event.debug.v1",
|
|
partition: 0,
|
|
firstOffset: "100",
|
|
lastOffset: "134",
|
|
count: 35,
|
|
valuesPrinted: false
|
|
}]);
|
|
assert.equal(result.payload.replay.replayId, "rpl_canonical_35");
|
|
assert.equal(result.payload.replay.producerInvoked, true);
|
|
assert.equal(result.payload.replay.sourceMatched, true);
|
|
assert.deepEqual(result.payload.replay.outputBarrier, result.payload.output.offsetRanges[0]);
|
|
});
|
|
|
|
test("Kafka regeneration reports source trace missing before creating a producer", async () => {
|
|
let producerCreated = false;
|
|
let readerInput: any = null;
|
|
const result = await runKafkaCli([
|
|
"regenerate", "hwlab",
|
|
"--from", "kafka",
|
|
"--session-id", SESSION_ID,
|
|
"--trace-id", "trc_missing_debug_source",
|
|
"--replay-id", "rpl_missing_debug_source",
|
|
"--group-prefix", "hwlab-v03-workbench-isolated-debug"
|
|
], {
|
|
env: {},
|
|
now: () => "2026-07-10T12:00:00.000Z",
|
|
async readKafka(input) { readerInput = input; return { events: [canonicalRecord(1)] }; },
|
|
async createProducer() { producerCreated = true; throw new Error("must not create producer"); }
|
|
});
|
|
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.payload.error.code, "source_trace_missing");
|
|
assert.equal(readerInput.topic, "agentrun.event.v1");
|
|
assert.equal(producerCreated, false);
|
|
});
|
|
|
|
test("canonical Kafka regeneration preflights without publishing and preserves replayId in the publish hint", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-debug-preflight-"));
|
|
let producerCreated = false;
|
|
const result = await runKafkaCli([
|
|
"regenerate", "hwlab",
|
|
"--from", "kafka",
|
|
"--session-id", SESSION_ID,
|
|
"--trace-id", TRACE_ID,
|
|
"--replay-id", "rpl_canonical_preflight",
|
|
"--group-prefix", "hwlab-v03-workbench-isolated-debug",
|
|
"--output-jsonl", path.join(cwd, "output.jsonl")
|
|
], {
|
|
cwd,
|
|
env: {},
|
|
now: () => "2026-07-10T12:00:00.000Z",
|
|
async readKafka() { return { events: [canonicalRecord(1)], completionReason: "end-offset", reachedEndOffsets: true }; },
|
|
async createProducer() { producerCreated = true; throw new Error("preflight must not create a producer"); }
|
|
});
|
|
|
|
assert.equal(result.exitCode, 0);
|
|
assert.equal(result.payload.config.inputTopic.value, "agentrun.event.v1");
|
|
assert.equal(result.payload.output.published, false);
|
|
assert.equal(result.payload.output.publishedCount, 0);
|
|
assert.equal(result.payload.replay.producerInvoked, false);
|
|
assert.equal(result.payload.replay.outputBarrier, null);
|
|
assert.match(result.payload.next.command, /--replay-id rpl_canonical_preflight/u);
|
|
assert.match(result.payload.next.command, /--group-prefix hwlab-v03-workbench-isolated-debug/u);
|
|
assert.match(result.payload.next.command, /--publish/u);
|
|
assert.equal(producerCreated, false);
|
|
});
|
|
|
|
test("partial AgentRun Kafka regeneration preserves the artifact and never invokes the producer", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-debug-partial-"));
|
|
let producerCreated = false;
|
|
const result = await runKafkaCli([
|
|
"regenerate", "hwlab",
|
|
"--from", "kafka",
|
|
"--session-id", SESSION_ID,
|
|
"--trace-id", TRACE_ID,
|
|
"--replay-id", "rpl_partial_no_publish",
|
|
"--group-prefix", "hwlab-v03-workbench-isolated-debug",
|
|
"--publish",
|
|
"--output-jsonl", path.join(cwd, "partial.jsonl")
|
|
], {
|
|
cwd,
|
|
env: {},
|
|
now: () => "2026-07-10T12:00:00.000Z",
|
|
async readKafka() {
|
|
return {
|
|
events: [canonicalRecord(1)],
|
|
scannedCount: 1,
|
|
parsedCount: 1,
|
|
matchedCount: 1,
|
|
completionReason: "limit",
|
|
reachedEndOffsets: false
|
|
};
|
|
},
|
|
async createProducer() { producerCreated = true; throw new Error("partial scan must not create producer"); }
|
|
});
|
|
|
|
assert.equal(result.exitCode, 2);
|
|
assert.equal(result.payload.status, "partial");
|
|
assert.equal(result.payload.error.code, "source_scan_incomplete");
|
|
assert.equal(result.payload.input.scanComplete, false);
|
|
assert.equal(result.payload.output.published, false);
|
|
assert.equal(result.payload.output.publishedCount, 0);
|
|
assert.equal(result.payload.replay.producerInvoked, false);
|
|
assert.equal(producerCreated, false);
|
|
assert.equal(typeof result.payload.output.artifact.path, "string");
|
|
assert.match(renderKafkaCliText(result.payload), /^partial action=kafka\.regenerate\.hwlab code=source_scan_incomplete completion=limit reachedEnd=false/u);
|
|
});
|
|
|
|
test("Kafka regeneration fails closed when publish metadata cannot form an offset barrier", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-debug-barrier-"));
|
|
const result = await runKafkaCli([
|
|
"regenerate", "hwlab",
|
|
"--from", "kafka",
|
|
"--session-id", SESSION_ID,
|
|
"--trace-id", TRACE_ID,
|
|
"--replay-id", "rpl_missing_offset_barrier",
|
|
"--group-prefix", "hwlab-v03-workbench-isolated-debug",
|
|
"--publish",
|
|
"--output-jsonl", path.join(cwd, "output.jsonl")
|
|
], {
|
|
cwd,
|
|
env: {},
|
|
now: () => "2026-07-10T12:00:00.000Z",
|
|
async readKafka() { return { events: [canonicalRecord(1)], completionReason: "end-offset", reachedEndOffsets: true }; },
|
|
async createProducer() {
|
|
return {
|
|
async connect() {},
|
|
async send() { return []; },
|
|
async disconnect() {}
|
|
};
|
|
}
|
|
});
|
|
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.payload.error.code, "debug_publish_barrier_missing");
|
|
});
|
|
|
|
test("reconstruction identity fabrication and product output topic fail closed", async () => {
|
|
const cwd = await mkdtemp(path.join(os.tmpdir(), "hwlab-kafka-debug-invalid-"));
|
|
const forged = reconstructionRecord(1);
|
|
forged.value.eventId = "evt_forged";
|
|
const inputFile = path.join(cwd, "forged.jsonl");
|
|
await writeFile(inputFile, `${JSON.stringify(forged)}\n`, "utf8");
|
|
|
|
const forgedResult = await runKafkaCli([
|
|
"regenerate", "hwlab", "--from", "jsonl", "--session-id", SESSION_ID,
|
|
"--jsonl-file", inputFile, "--no-publish"
|
|
], { cwd, env: {}, now: () => "2026-07-10T12:00:00.000Z" });
|
|
assert.equal(forgedResult.exitCode, 1);
|
|
assert.equal(forgedResult.payload.error.code, "hwlab_kafka_debug_identity_invalid");
|
|
|
|
const productResult = await runKafkaCli([
|
|
"regenerate", "hwlab", "--from", "jsonl", "--session-id", SESSION_ID,
|
|
"--jsonl-file", inputFile, "--output-topic", "hwlab.event.v1", "--no-publish"
|
|
], { cwd, env: {}, now: () => "2026-07-10T12:00:00.000Z" });
|
|
assert.equal(productResult.exitCode, 1);
|
|
assert.equal(productResult.payload.error.code, "product_topic_forbidden");
|
|
|
|
const productGroupResult = await runKafkaCli([
|
|
"regenerate", "hwlab", "--from", "jsonl", "--session-id", SESSION_ID,
|
|
"--jsonl-file", inputFile, "--group-prefix", "hwlab-v03-workbench-live-sse", "--no-publish"
|
|
], { cwd, env: {}, now: () => "2026-07-10T12:00:00.000Z" });
|
|
assert.equal(productGroupResult.exitCode, 1);
|
|
assert.equal(productGroupResult.payload.error.code, "debug_group_required");
|
|
|
|
const replayIdResult = await runKafkaCli([
|
|
"regenerate", "hwlab", "--from", "jsonl", "--session-id", SESSION_ID,
|
|
"--replay-id", "not-a-replay", "--jsonl-file", inputFile, "--no-publish"
|
|
], { cwd, env: {}, now: () => "2026-07-10T12:00:00.000Z" });
|
|
assert.equal(replayIdResult.exitCode, 1);
|
|
assert.equal(replayIdResult.payload.error.code, "invalid_replay_id");
|
|
});
|
|
|
|
test("Kafka mode requires a CLI or YAML-owned isolated group prefix", async () => {
|
|
let readCalled = false;
|
|
const result = await runKafkaCli([
|
|
"regenerate", "hwlab", "--from", "kafka", "--session-id", SESSION_ID, "--no-publish"
|
|
], {
|
|
env: {},
|
|
now: () => "2026-07-10T12:00:00.000Z",
|
|
async readKafka() { readCalled = true; return { events: [] }; }
|
|
});
|
|
|
|
assert.equal(result.exitCode, 1);
|
|
assert.equal(result.payload.error.code, "kafka_debug_group_missing");
|
|
assert.equal(readCalled, false);
|
|
});
|
|
|
|
test("Kafka query exposes the isolated generated group and original value hash", async () => {
|
|
const valueText = JSON.stringify(canonicalRecord(1).value);
|
|
const consumerConfigs: any[] = [];
|
|
const consumer = {
|
|
async connect() {},
|
|
async subscribe() {},
|
|
async run({ eachMessage }: any) {
|
|
await eachMessage({
|
|
topic: "agentrun.event.v1",
|
|
partition: 0,
|
|
message: { offset: "1601", key: Buffer.from(SESSION_ID), value: Buffer.from(valueText), timestamp: "1783681200000" }
|
|
});
|
|
},
|
|
async stop() {},
|
|
async disconnect() {}
|
|
};
|
|
const queried = await queryKafkaEventStream({
|
|
env: { HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092", HWLAB_KAFKA_CLIENT_ID: "hwlab-test" },
|
|
topic: "agentrun.event.v1",
|
|
sessionId: SESSION_ID,
|
|
limit: 1,
|
|
groupIdPrefix: "hwlab-v03-workbench-isolated-debug",
|
|
kafkaFactory() {
|
|
return { consumer(config: any) { consumerConfigs.push(config); return consumer; } };
|
|
}
|
|
});
|
|
assert.match(queried.groupId, /^hwlab-v03-workbench-isolated-debug-\d+-[a-f0-9]{8}$/u);
|
|
assert.equal(consumerConfigs[0].groupId, queried.groupId);
|
|
assert.equal(queried.events[0].valueSha256, createSha256(valueText));
|
|
assert.equal(queried.scannedCount, 1);
|
|
assert.equal(queried.parsedCount, 1);
|
|
assert.equal(queried.matchedCount, 1);
|
|
assert.equal(queried.invalidJsonCount, 0);
|
|
assert.equal(queried.completionReason, "limit");
|
|
assert.equal(queried.endOffsetsAvailable, false);
|
|
});
|
|
|
|
test("Kafka query reports invalid JSON, filter rejection, and bounded end-offset completion", async () => {
|
|
const matching = canonicalRecord(1).value;
|
|
const mismatch = structuredClone(matching);
|
|
mismatch.sessionId = "ses_agentrun_other";
|
|
mismatch.run.sessionId = "ses_agentrun_other";
|
|
mismatch.event.payload.sessionId = "ses_agentrun_other";
|
|
const consumer = {
|
|
async connect() {},
|
|
async subscribe() {},
|
|
async run({ eachMessage }: any) {
|
|
await eachMessage({ topic: "agentrun.event.v1", partition: 0, message: { offset: "0", value: Buffer.from("{invalid") } });
|
|
await eachMessage({ topic: "agentrun.event.v1", partition: 0, message: { offset: "1", value: Buffer.from(JSON.stringify(mismatch)) } });
|
|
await eachMessage({ topic: "agentrun.event.v1", partition: 0, message: { offset: "2", value: Buffer.from(JSON.stringify(matching)) } });
|
|
},
|
|
async stop() {},
|
|
async disconnect() {}
|
|
};
|
|
const queried = await queryKafkaEventStream({
|
|
env: { HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092", HWLAB_KAFKA_CLIENT_ID: "hwlab-test" },
|
|
topic: "agentrun.event.v1",
|
|
sessionId: SESSION_ID,
|
|
limit: 2,
|
|
groupIdPrefix: "hwlab-v03-workbench-isolated-debug",
|
|
kafkaFactory() {
|
|
return {
|
|
admin() { return { async connect() {}, async fetchTopicOffsets() { return [{ partition: 0, low: "0", high: "3" }]; }, async disconnect() {} }; },
|
|
consumer() { return consumer; }
|
|
};
|
|
}
|
|
});
|
|
assert.equal(queried.scannedCount, 3);
|
|
assert.equal(queried.parsedCount, 2);
|
|
assert.equal(queried.invalidJsonCount, 1);
|
|
assert.equal(queried.filterRejectedCount, 1);
|
|
assert.equal(queried.matchedCount, 1);
|
|
assert.equal(queried.completionReason, "end-offset");
|
|
assert.equal(queried.reachedEndOffsets, true);
|
|
assert.deepEqual(queried.endOffsets, [{ partition: 0, startOffset: "0", endOffset: "3" }]);
|
|
assert.deepEqual(queried.firstScannedOffsets, [{ partition: 0, offset: "0" }]);
|
|
assert.deepEqual(queried.lastScannedOffsets, [{ partition: 0, offset: "2" }]);
|
|
});
|
|
|
|
test("Kafka query fails closed when retention advances between barrier capture and consumer assignment", async () => {
|
|
const matching = canonicalRecord(1).value;
|
|
const queried = await queryKafkaEventStream({
|
|
env: { HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092", HWLAB_KAFKA_CLIENT_ID: "hwlab-test" },
|
|
topic: "hwlab.event.v1",
|
|
sessionId: SESSION_ID,
|
|
limit: 10,
|
|
scanLimit: 10,
|
|
groupIdPrefix: "hwlab-v03-workbench-isolated-debug",
|
|
kafkaFactory() {
|
|
return {
|
|
admin() { return { async connect() {}, async fetchTopicOffsets() { return [{ partition: 0, low: "0", high: "3" }]; }, async disconnect() {} }; },
|
|
consumer() {
|
|
return {
|
|
async connect() {},
|
|
async subscribe() {},
|
|
async run({ eachMessage }: any) {
|
|
await eachMessage({ topic: "hwlab.event.v1", partition: 0, message: { offset: "2", value: Buffer.from(JSON.stringify(matching)) } });
|
|
},
|
|
async stop() {},
|
|
async disconnect() {}
|
|
};
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
assert.equal(queried.completionReason, "retention-start-moved");
|
|
assert.equal(queried.completion.complete, false);
|
|
assert.equal(queried.completion.retentionStartVerified, false);
|
|
assert.equal(queried.completion.retentionStartMoved, true);
|
|
assert.equal(queried.reachedEndOffsets, false);
|
|
assert.equal(queried.scannedCount, 0);
|
|
assert.deepEqual(queried.firstScannedOffsets, [{ partition: 0, offset: "2" }]);
|
|
});
|
|
|
|
test("Kafka query prefers end-offset when the final record also reaches the event limit", async () => {
|
|
const matching = canonicalRecord(1).value;
|
|
const queried = await queryKafkaEventStream({
|
|
env: { HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092", HWLAB_KAFKA_CLIENT_ID: "hwlab-test" },
|
|
topic: "agentrun.event.v1",
|
|
sessionId: SESSION_ID,
|
|
limit: 1,
|
|
groupIdPrefix: "hwlab-v03-workbench-isolated-debug",
|
|
kafkaFactory() {
|
|
return {
|
|
admin() { return { async connect() {}, async fetchTopicOffsets() { return [{ partition: 0, low: "0", high: "1" }]; }, async disconnect() {} }; },
|
|
consumer() {
|
|
return {
|
|
async connect() {},
|
|
async subscribe() {},
|
|
async run({ eachMessage }: any) {
|
|
await eachMessage({ topic: "agentrun.event.v1", partition: 0, message: { offset: "0", value: Buffer.from(JSON.stringify(matching)) } });
|
|
},
|
|
async stop() {},
|
|
async disconnect() {}
|
|
};
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
assert.equal(queried.matchedCount, 1);
|
|
assert.equal(queried.completionReason, "end-offset");
|
|
assert.equal(queried.reachedEndOffsets, true);
|
|
});
|
|
|
|
test("Kafka query session filter accepts canonical hwlabSessionId across AgentRun and HWLAB topics", async () => {
|
|
const matching = canonicalRecord(1).value;
|
|
const queried = await queryKafkaEventStream({
|
|
env: { HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092", HWLAB_KAFKA_CLIENT_ID: "hwlab-test" },
|
|
topic: "agentrun.event.v1",
|
|
sessionId: HWLAB_SESSION_ID,
|
|
limit: 10,
|
|
groupIdPrefix: "hwlab-v03-workbench-isolated-debug",
|
|
kafkaFactory() {
|
|
return {
|
|
admin() { return { async connect() {}, async fetchTopicOffsets() { return [{ partition: 0, low: "0", high: "1" }]; }, async disconnect() {} }; },
|
|
consumer() {
|
|
return {
|
|
async connect() {},
|
|
async subscribe() {},
|
|
async run({ eachMessage }: any) {
|
|
await eachMessage({ topic: "agentrun.event.v1", partition: 0, message: { offset: "0", value: Buffer.from(JSON.stringify(matching)) } });
|
|
},
|
|
async stop() {},
|
|
async disconnect() {}
|
|
};
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
assert.equal(queried.matchedCount, 1);
|
|
assert.equal(queried.filterRejectedCount, 0);
|
|
assert.equal(queried.completionReason, "end-offset");
|
|
});
|
|
|
|
test("Kafka query treats a retained empty partition as a completed bounded scan", async () => {
|
|
let runCalled = false;
|
|
let disconnected = false;
|
|
const queried = await queryKafkaEventStream({
|
|
env: { HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092", HWLAB_KAFKA_CLIENT_ID: "hwlab-test" },
|
|
topic: "hwlab.event.v1",
|
|
traceId: TRACE_ID,
|
|
timeoutMs: 250,
|
|
groupIdPrefix: "hwlab-v03-workbench-isolated-debug",
|
|
kafkaFactory() {
|
|
return {
|
|
admin() { return { async connect() {}, async fetchTopicOffsets() { return [{ partition: 0, low: "7", high: "7" }]; }, async disconnect() {} }; },
|
|
consumer() {
|
|
return {
|
|
async connect() {},
|
|
async subscribe() {},
|
|
async run() { runCalled = true; },
|
|
async stop() {},
|
|
async disconnect() { disconnected = true; }
|
|
};
|
|
}
|
|
};
|
|
}
|
|
});
|
|
assert.equal(queried.completionReason, "end-offset");
|
|
assert.equal(queried.reachedEndOffsets, true);
|
|
assert.equal(queried.scannedCount, 0);
|
|
assert.equal(runCalled, false);
|
|
assert.equal(disconnected, true);
|
|
assert.deepEqual(queried.endOffsets, [{ partition: 0, startOffset: "7", endOffset: "7" }]);
|
|
});
|
|
|
|
test("Kafka query stops every partition at the captured exclusive barrier and excludes later records", async () => {
|
|
const matching = canonicalRecord(1).value;
|
|
const mismatch = structuredClone(matching);
|
|
mismatch.sessionId = "ses_agentrun_other";
|
|
mismatch.run.sessionId = "ses_agentrun_other";
|
|
mismatch.event.payload.sessionId = "ses_agentrun_other";
|
|
const queried = await queryKafkaEventStream({
|
|
env: { HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092", HWLAB_KAFKA_CLIENT_ID: "hwlab-test" },
|
|
topic: "agentrun.event.v1",
|
|
sessionId: SESSION_ID,
|
|
limit: 10,
|
|
scanLimit: 10,
|
|
groupIdPrefix: "hwlab-v03-workbench-isolated-debug",
|
|
kafkaFactory() {
|
|
return {
|
|
admin() { return { async connect() {}, async fetchTopicOffsets() { return [{ partition: 0, low: "0", high: "2" }, { partition: 1, low: "0", high: "2" }]; }, async disconnect() {} }; },
|
|
consumer() {
|
|
return {
|
|
async connect() {},
|
|
async subscribe() {},
|
|
async run({ eachMessage }: any) {
|
|
await eachMessage({ topic: "agentrun.event.v1", partition: 0, message: { offset: "0", value: Buffer.from(JSON.stringify(matching)) } });
|
|
await eachMessage({ topic: "agentrun.event.v1", partition: 0, message: { offset: "1", value: Buffer.from(JSON.stringify(mismatch)) } });
|
|
await eachMessage({ topic: "agentrun.event.v1", partition: 0, message: { offset: "2", value: Buffer.from(JSON.stringify(matching)) } });
|
|
await eachMessage({ topic: "agentrun.event.v1", partition: 1, message: { offset: "0", value: Buffer.from(JSON.stringify(mismatch)) } });
|
|
await eachMessage({ topic: "agentrun.event.v1", partition: 1, message: { offset: "1", value: Buffer.from(JSON.stringify(matching)) } });
|
|
await eachMessage({ topic: "agentrun.event.v1", partition: 1, message: { offset: "3", value: Buffer.from(JSON.stringify(matching)) } });
|
|
},
|
|
async stop() {},
|
|
async disconnect() {}
|
|
};
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
assert.equal(queried.completionReason, "end-offset");
|
|
assert.equal(queried.completion.complete, true);
|
|
assert.equal(queried.completion.barrierReached, true);
|
|
assert.equal(queried.scannedCount, 4);
|
|
assert.equal(queried.excludedPostBarrierCount, 1);
|
|
assert.deepEqual(queried.events.map((event: any) => [event.partition, event.offset]), [[0, "0"], [1, "1"]]);
|
|
assert.deepEqual(queried.lastScannedOffsets, [{ partition: 0, offset: "1" }, { partition: 1, offset: "1" }]);
|
|
});
|
|
|
|
test("Kafka query reports scan-limit and abort as typed incomplete completion", async () => {
|
|
const matching = canonicalRecord(1).value;
|
|
const limited = await queryKafkaEventStream({
|
|
env: { HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092", HWLAB_KAFKA_CLIENT_ID: "hwlab-test" },
|
|
topic: "agentrun.event.v1",
|
|
sessionId: SESSION_ID,
|
|
limit: 10,
|
|
scanLimit: 1,
|
|
timeoutMs: 500,
|
|
groupIdPrefix: "hwlab-v03-workbench-isolated-debug",
|
|
kafkaFactory() {
|
|
return {
|
|
admin() { return { async connect() {}, async fetchTopicOffsets() { return [{ partition: 0, low: "0", high: "5" }]; }, async disconnect() {} }; },
|
|
consumer() { return { async connect() {}, async subscribe() {}, async run({ eachMessage }: any) { await eachMessage({ topic: "agentrun.event.v1", partition: 0, message: { offset: "0", value: Buffer.from(JSON.stringify(matching)) } }); }, async stop() {}, async disconnect() {} }; }
|
|
};
|
|
}
|
|
});
|
|
assert.deepEqual(limited.completion, {
|
|
reason: "scan-limit",
|
|
complete: false,
|
|
barrierReached: false,
|
|
retentionStartVerified: true,
|
|
retentionStartMoved: false,
|
|
timeout: false,
|
|
matchedEventLimitReached: false,
|
|
scanLimitReached: true,
|
|
aborted: false,
|
|
valuesRedacted: true
|
|
});
|
|
|
|
const controller = new AbortController();
|
|
let disconnected = false;
|
|
const abortedPromise = queryKafkaEventStream({
|
|
env: { HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092", HWLAB_KAFKA_CLIENT_ID: "hwlab-test" },
|
|
topic: "agentrun.event.v1",
|
|
sessionId: SESSION_ID,
|
|
limit: 10,
|
|
scanLimit: 10,
|
|
timeoutMs: 500,
|
|
signal: controller.signal,
|
|
groupIdPrefix: "hwlab-v03-workbench-isolated-debug",
|
|
kafkaFactory() {
|
|
return {
|
|
admin() { return { async connect() {}, async fetchTopicOffsets() { return [{ partition: 0, low: "0", high: "5" }]; }, async disconnect() {} }; },
|
|
consumer() { return { async connect() {}, async subscribe() {}, async run() { await new Promise(() => undefined); }, async stop() {}, async disconnect() { disconnected = true; } }; }
|
|
};
|
|
}
|
|
});
|
|
setTimeout(() => controller.abort(), 5);
|
|
const aborted = await abortedPromise;
|
|
assert.equal(aborted.completionReason, "aborted");
|
|
assert.equal(aborted.completion.aborted, true);
|
|
assert.equal(aborted.completion.complete, false);
|
|
assert.equal(disconnected, true);
|
|
});
|
|
|
|
test("Kafka query does not report an empty offset response as a completed scan", async () => {
|
|
const queried = await queryKafkaEventStream({
|
|
env: { HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092", HWLAB_KAFKA_CLIENT_ID: "hwlab-test" },
|
|
topic: "hwlab.event.v1",
|
|
traceId: TRACE_ID,
|
|
timeoutMs: 250,
|
|
groupIdPrefix: "hwlab-v03-workbench-isolated-debug",
|
|
kafkaFactory() {
|
|
return {
|
|
admin() { return { async connect() {}, async fetchTopicOffsets() { return []; }, async disconnect() {} }; },
|
|
consumer() { return { async connect() {}, async subscribe() {}, async run() {}, async stop() {}, async disconnect() {} }; }
|
|
};
|
|
}
|
|
});
|
|
assert.equal(queried.completionReason, "timeout");
|
|
assert.equal(queried.reachedEndOffsets, false);
|
|
assert.equal(queried.endOffsetsAvailable, false);
|
|
assert.equal(queried.endOffsetsError?.code, "kafka_end_offsets_empty");
|
|
});
|
|
|
|
test("Kafka query disconnects a connected consumer when subscribe fails", async () => {
|
|
let disconnected = 0;
|
|
await assert.rejects(queryKafkaEventStream({
|
|
env: { HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092", HWLAB_KAFKA_CLIENT_ID: "hwlab-test" },
|
|
topic: "hwlab.event.v1",
|
|
traceId: TRACE_ID,
|
|
timeoutMs: 250,
|
|
groupIdPrefix: "hwlab-v03-workbench-isolated-debug",
|
|
kafkaFactory() {
|
|
return {
|
|
consumer() {
|
|
return {
|
|
async connect() {},
|
|
async subscribe() { throw new Error("subscribe failed"); },
|
|
async run() {},
|
|
async stop() {},
|
|
async disconnect() { disconnected += 1; }
|
|
};
|
|
}
|
|
};
|
|
}
|
|
}), /subscribe failed/u);
|
|
assert.equal(disconnected, 1);
|
|
});
|
|
|
|
test("Kafka query setup and cleanup stay inside the command budget", async () => {
|
|
let consumerConnectCalled = false;
|
|
let consumerDisconnected = false;
|
|
const startedAt = Date.now();
|
|
const queried = await queryKafkaEventStream({
|
|
env: { HWLAB_KAFKA_BOOTSTRAP_SERVERS: "kafka.test:9092", HWLAB_KAFKA_CLIENT_ID: "hwlab-test" },
|
|
topic: "hwlab.event.v1",
|
|
traceId: TRACE_ID,
|
|
timeoutMs: 250,
|
|
groupIdPrefix: "hwlab-v03-workbench-isolated-debug",
|
|
kafkaFactory() {
|
|
return {
|
|
admin() {
|
|
return {
|
|
async connect() { await new Promise(() => undefined); },
|
|
async fetchTopicOffsets() { throw new Error("must not fetch after setup timeout"); },
|
|
async disconnect() {}
|
|
};
|
|
},
|
|
consumer() {
|
|
return {
|
|
async connect() { consumerConnectCalled = true; },
|
|
async subscribe() {},
|
|
async run() {},
|
|
async stop() {},
|
|
async disconnect() { consumerDisconnected = true; }
|
|
};
|
|
}
|
|
};
|
|
}
|
|
});
|
|
const elapsedMs = Date.now() - startedAt;
|
|
assert.equal(queried.completionReason, "timeout");
|
|
assert.equal(queried.endOffsetsAvailable, false);
|
|
assert.equal(queried.endOffsetsError?.code, "kafka_query_timeout");
|
|
assert.equal(consumerConnectCalled, false);
|
|
assert.equal(consumerDisconnected, true);
|
|
assert.ok(elapsedMs < 500, `query exceeded bounded setup budget: ${elapsedMs}ms`);
|
|
});
|
|
|
|
test("non-reversible AgentRun session identity stays only in debug lineage", () => {
|
|
const rawSessionId = "ses_agentrun_not_uuid";
|
|
const record = reconstructionRecord(1);
|
|
record.key = rawSessionId;
|
|
record.value.sessionId = rawSessionId;
|
|
record.value.hwlabSessionId = null;
|
|
record.value.run.sessionId = rawSessionId;
|
|
record.value.run.hwlabSessionId = null;
|
|
record.value.event.payload.sessionId = rawSessionId;
|
|
record.value.reconstruction.identityDerivation = {
|
|
kind: "none",
|
|
sourceField: "sessionId",
|
|
targetField: "hwlabSessionId",
|
|
reversible: false,
|
|
reason: "source sessionId is not ses_agentrun_<uuid-with-underscores>",
|
|
valuesPrinted: false
|
|
};
|
|
const mapped = mapAgentRunRecordsToHwlabDebugEvents([record], {
|
|
sessionId: rawSessionId,
|
|
traceId: TRACE_ID,
|
|
expectedCount: 1,
|
|
producedAt: "2026-07-10T12:00:00.000Z"
|
|
});
|
|
assert.equal(mapped.events[0].sessionId, null);
|
|
assert.equal(mapped.events[0].hwlabSessionId, null);
|
|
assert.equal(mapped.events[0].event.sessionId, null);
|
|
assert.equal(mapped.events[0].debugLineage.inputSessionId, rawSessionId);
|
|
assert.equal(mapped.events[0].reconstruction.identityDerivation.kind, "none");
|
|
});
|
|
|
|
function completeKafkaRead(events: any[]) {
|
|
const byPartition = new Map<number, bigint[]>();
|
|
for (const event of events) {
|
|
const partition = Number(event.partition);
|
|
const offset = BigInt(String(event.offset));
|
|
const offsets = byPartition.get(partition) ?? [];
|
|
offsets.push(offset);
|
|
byPartition.set(partition, offsets);
|
|
}
|
|
const endOffsets = [...byPartition.entries()].map(([partition, offsets]) => ({
|
|
partition,
|
|
startOffset: String(offsets.reduce((left, right) => left < right ? left : right)),
|
|
endOffset: String(offsets.reduce((left, right) => left > right ? left : right) + 1n)
|
|
})).sort((left, right) => left.partition - right.partition);
|
|
return {
|
|
topic: events[0]?.topic ?? "hwlab.event.v1",
|
|
events,
|
|
completionReason: "end-offset",
|
|
reachedEndOffsets: true,
|
|
endOffsetsAvailable: true,
|
|
endOffsets,
|
|
completion: { reason: "end-offset", complete: true, barrierReached: true, retentionStartVerified: true }
|
|
};
|
|
}
|
|
|
|
function reconstructionRecord(seq: number): any {
|
|
const value = {
|
|
schema: "agentrun.event.reconstruction.v1",
|
|
eventType: "agentrun.event.reconstructed",
|
|
source: "agentrun-cli-stdio-reconstruction",
|
|
eventId: null,
|
|
outboxSeq: null,
|
|
sourceSeq: seq,
|
|
observedAt: `2026-07-10T11:00:${String(seq).padStart(2, "0")}.000Z`,
|
|
traceId: TRACE_ID,
|
|
sessionId: SESSION_ID,
|
|
hwlabSessionId: HWLAB_SESSION_ID,
|
|
runId: RUN_ID,
|
|
commandId: "cmd_9fff03fc124b4203b065d04bbb7c4f1e",
|
|
run: { runId: RUN_ID, sessionId: SESSION_ID, hwlabSessionId: HWLAB_SESSION_ID, status: "running", valuesPrinted: false },
|
|
command: { commandId: "cmd_9fff03fc124b4203b065d04bbb7c4f1e", runId: RUN_ID, seq: 1, type: "run", state: "running", valuesPrinted: false },
|
|
event: {
|
|
id: null,
|
|
runId: RUN_ID,
|
|
seq,
|
|
type: "backend_status",
|
|
payload: {
|
|
traceId: TRACE_ID,
|
|
sessionId: SESSION_ID,
|
|
phase: `reconstructed-${seq}`,
|
|
reconstructed: true,
|
|
reconstructionKind: "stdio-derived partial reconstruction"
|
|
},
|
|
createdAt: `2026-07-10T11:00:${String(seq).padStart(2, "0")}.000Z`
|
|
},
|
|
reconstruction: {
|
|
kind: "stdio-derived partial reconstruction",
|
|
completeLifecycle: false,
|
|
originalEventId: null,
|
|
originalOutboxSeq: null,
|
|
sourceTopic: "codex-stdio.raw.v1",
|
|
sourcePartition: 0,
|
|
sourceOffset: String(3500 + seq),
|
|
sourceFrameSeq: seq,
|
|
sourceValueSha256: `sha256:${String(seq).padStart(64, "0")}`,
|
|
identityDerivation: {
|
|
kind: "agentrun-session-uuid-to-hwlab-session",
|
|
sourceField: "sessionId",
|
|
targetField: "hwlabSessionId",
|
|
reversible: true,
|
|
sourcePrefix: "ses_agentrun_",
|
|
targetPrefix: "ses_",
|
|
separatorTransform: "underscore-to-hyphen",
|
|
valuesPrinted: false
|
|
}
|
|
},
|
|
valuesPrinted: false
|
|
};
|
|
return { topic: "agentrun.event.debug.v1", partition: null, offset: null, key: SESSION_ID, value, headers: { "x-trace-id": TRACE_ID } };
|
|
}
|
|
|
|
function agentrunOrderRecord(seq: number, traceId: string, type: string, payload: Record<string, unknown>): any {
|
|
const record = canonicalRecord(seq);
|
|
const eventId = `evt_order_${seq}`;
|
|
record.offset = String(1600 + seq);
|
|
record.value.eventId = eventId;
|
|
record.value.sourceSeq = seq;
|
|
record.value.traceId = traceId;
|
|
record.value.hwlabSessionId = HWLAB_SESSION_ID;
|
|
record.value.run.hwlabSessionId = HWLAB_SESSION_ID;
|
|
record.value.event = {
|
|
id: eventId,
|
|
runId: RUN_ID,
|
|
seq,
|
|
type,
|
|
payload: {
|
|
traceId,
|
|
sessionId: SESSION_ID,
|
|
hwlabSessionId: HWLAB_SESSION_ID,
|
|
...payload
|
|
},
|
|
createdAt: `2026-07-11T11:00:${String(seq).padStart(2, "0")}.000Z`
|
|
};
|
|
return record;
|
|
}
|
|
|
|
function agentrunAssistantLifecycleRecord(seq: number, ordinal: number, type: string, payload: Record<string, unknown>): any {
|
|
const record = agentrunOrderRecord(seq, TRACE_ID, type, payload);
|
|
const createdAt = `2026-07-12T10:00:0${ordinal}.000Z`;
|
|
record.value.committedAt = createdAt;
|
|
record.value.event.createdAt = createdAt;
|
|
return record;
|
|
}
|
|
|
|
function hwlabOrderRecord(source: any, index: number): any {
|
|
const valueText = JSON.stringify(source.value);
|
|
const projected = projectAgentRunKafkaEventToHwlabEvent(source.value, {
|
|
producedAt: `2026-07-11T11:01:${String(index).padStart(2, "0")}.000Z`,
|
|
sourceTopic: source.topic,
|
|
sourcePartition: source.partition,
|
|
sourceOffset: source.offset,
|
|
sourceKey: source.key,
|
|
inputSha256: createSha256(valueText)
|
|
});
|
|
return {
|
|
topic: "hwlab.event.v1",
|
|
partition: 0,
|
|
offset: String(1901 + index),
|
|
key: HWLAB_SESSION_ID,
|
|
valueSha256: createSha256(JSON.stringify(projected)),
|
|
value: projected
|
|
};
|
|
}
|
|
|
|
function canonicalRecord(seq: number): any {
|
|
const eventId = `evt_${seq}`;
|
|
const value = {
|
|
schema: "agentrun.event.v1",
|
|
eventType: "agentrun.event.committed",
|
|
source: "agentrun-manager",
|
|
eventId,
|
|
outboxSeq: seq,
|
|
sourceSeq: seq,
|
|
committedAt: `2026-07-10T11:00:${String(seq).padStart(2, "0")}.000Z`,
|
|
traceId: TRACE_ID,
|
|
sessionId: SESSION_ID,
|
|
hwlabSessionId: HWLAB_SESSION_ID,
|
|
runId: RUN_ID,
|
|
commandId: "cmd_9fff03fc124b4203b065d04bbb7c4f1e",
|
|
run: {
|
|
runId: RUN_ID,
|
|
status: "running",
|
|
terminalStatus: null,
|
|
failureKind: null,
|
|
tenantId: "tenant-test",
|
|
projectId: "project-test",
|
|
providerId: "gpt.pika",
|
|
backendProfile: "gpt.pika",
|
|
sessionId: SESSION_ID,
|
|
hwlabSessionId: HWLAB_SESSION_ID,
|
|
conversationId: null,
|
|
threadId: null,
|
|
valuesPrinted: false
|
|
},
|
|
command: {
|
|
commandId: "cmd_9fff03fc124b4203b065d04bbb7c4f1e",
|
|
runId: RUN_ID,
|
|
seq: 1,
|
|
type: "run",
|
|
state: "running",
|
|
payloadHash: "hash",
|
|
valuesPrinted: false
|
|
},
|
|
event: {
|
|
id: eventId,
|
|
runId: RUN_ID,
|
|
seq,
|
|
type: "backend_status",
|
|
payload: { traceId: TRACE_ID, sessionId: SESSION_ID, phase: `canonical-${seq}` },
|
|
createdAt: `2026-07-10T11:00:${String(seq).padStart(2, "0")}.000Z`
|
|
},
|
|
valuesPrinted: false
|
|
};
|
|
return { topic: "agentrun.event.v1", partition: 0, offset: String(1600 + seq), key: SESSION_ID, value };
|
|
}
|
|
|
|
function hwlabRecord(sourceSeq: number, event: Record<string, unknown>): any {
|
|
return {
|
|
topic: "hwlab.event.v1",
|
|
partition: 0,
|
|
offset: String(1900 + sourceSeq),
|
|
key: HWLAB_SESSION_ID,
|
|
value: {
|
|
schema: "hwlab.event.v1",
|
|
eventType: "hwlab.trace.event.projected",
|
|
eventId: `hwlab:evt_render_${sourceSeq}`,
|
|
sourceEventId: `evt_render_${sourceSeq}`,
|
|
traceId: TRACE_ID,
|
|
hwlabSessionId: HWLAB_SESSION_ID,
|
|
sessionId: HWLAB_SESSION_ID,
|
|
runId: RUN_ID,
|
|
commandId: "cmd_render_trace",
|
|
serverSentAt: `2026-07-10T11:00:0${sourceSeq}.000Z`,
|
|
event: {
|
|
traceId: TRACE_ID,
|
|
sessionId: HWLAB_SESSION_ID,
|
|
source: "agentrun.kafka",
|
|
sourceSeq,
|
|
sourceEventId: `evt_render_${sourceSeq}`,
|
|
runId: RUN_ID,
|
|
commandId: "cmd_render_trace",
|
|
createdAt: `2026-07-10T11:00:0${sourceSeq}.000Z`,
|
|
...event
|
|
},
|
|
valuesPrinted: false
|
|
}
|
|
};
|
|
}
|
|
|
|
function withoutCommandId(record: any) {
|
|
delete record.value.commandId;
|
|
delete record.value.event.commandId;
|
|
return record;
|
|
}
|
|
|
|
function withCommandId(record: any, commandId: string) {
|
|
record.value.commandId = commandId;
|
|
record.value.event.commandId = commandId;
|
|
return record;
|
|
}
|
|
|
|
function createSha256(value: string) {
|
|
return new Bun.CryptoHasher("sha256").update(value).digest("hex");
|
|
}
|