fix: 校正实时探针可选工具事件合同

This commit is contained in:
Codex
2026-07-11 20:56:01 +02:00
parent 127cd8c220
commit 98e4cde51f
6 changed files with 196 additions and 15 deletions
@@ -36,6 +36,7 @@ test("realtime fanout keeps the YAML terminal timeout when durationMs is absent"
},
forbiddenRequestPaths: [],
requiredEventTypes: { agentrun: ["terminal_status"], hwlab: ["terminal"] },
conditionalEventTypePairs: { "tool-call": { agentrun: "tool_call", hwlab: "tool" } },
expectedKafka: {
topics: { stdio: "codex-stdio.raw.v1", agentrun: "agentrun.event.v1", hwlab: "hwlab.event.v1" },
groups: { directPublish: "direct", transactionalProjector: "projector", liveSse: "live" },
@@ -46,6 +47,91 @@ test("realtime fanout keeps the YAML terminal timeout when durationMs is absent"
assert.equal(effectiveProfile(profile, null).terminalTimeoutMs, 480_000);
assert.equal(effectiveProfile(profile, undefined).terminalTimeoutMs, 480_000);
assert.equal(effectiveProfile(profile, 5_000).terminalTimeoutMs, 5_000);
assert.deepEqual(effectiveProfile(profile, null).conditionalEventTypePairs, profile.conditionalEventTypePairs);
});
test("realtime fanout terminal completion does not require absent optional tool families", () => {
const source = nodeWebObserveRunnerRealtimeSource();
const build = new Function(`${source}\nreturn realtimeDebugTurnCompletion;`) as () => (
snapshot: Record<string, any>, traceId: string, profile: Record<string, any>,
) => Record<string, any>;
const completion = build();
const traceId = "trc_direct_answer";
const profile = {
debugStreams: ["stdio", "agentrun", "hwlab"],
requiredEventTypes: {
agentrun: ["user_message", "assistant_message", "terminal_status"],
hwlab: ["user", "assistant", "terminal"],
},
conditionalEventTypePairs: {
"tool-call": { agentrun: "tool_call", hwlab: "tool" },
"command-output": { agentrun: "command_output", hwlab: "status" },
},
};
const snapshot = {
records: {
stdio: [{ traceId, eventType: "codex.stdio.stdout" }],
agentrun: ["user_message", "backend_status", "assistant_message", "terminal_status"].map((eventType) => ({ traceId, eventType })),
hwlab: ["user", "backend", "assistant", "terminal"].map((eventType) => ({ traceId, eventType })),
},
};
const directAnswer = completion(snapshot, traceId, profile);
assert.equal(directAnswer.complete, true);
assert.deepEqual(directAnswer.missingEventTypes, { agentrun: [], hwlab: [] });
const missingTerminal = completion({
records: {
...snapshot.records,
agentrun: snapshot.records.agentrun.filter((row: Record<string, string>) => row.eventType !== "terminal_status"),
hwlab: snapshot.records.hwlab.filter((row: Record<string, string>) => row.eventType !== "terminal"),
},
}, traceId, profile);
assert.equal(missingTerminal.complete, false);
assert.deepEqual(missingTerminal.missingEventTypes, { agentrun: ["terminal_status"], hwlab: ["terminal"] });
assert.ok(missingTerminal.observedEventTypes.agentrun.includes("assistant_message"));
});
test("realtime fanout validates optional tool events only when they occur", () => {
const source = nodeWebObserveRunnerRealtimeSource();
const build = new Function(`${source}\nreturn realtimeValidateConditionalEventTypePairs;`) as () => (
input: Record<string, any>,
) => void;
const validate = build();
const traceId = "trc_optional_tool";
const base = {
turn: 1,
traceId,
profile: {
conditionalEventTypePairs: {
"tool-call": { agentrun: "tool_call", hwlab: "tool" },
"command-output": { agentrun: "command_output", hwlab: "status" },
},
},
};
assert.doesNotThrow(() => validate({ ...base, debug: { records: { agentrun: [], hwlab: [] } } }));
assert.doesNotThrow(() => validate({
...base,
debug: {
records: {
agentrun: [{ traceId, eventType: "tool_call", eventId: "evt_tool" }],
hwlab: [{ traceId, eventType: "tool", sourceEventId: "evt_tool" }],
},
},
}));
assert.throws(
() => validate({
...base,
debug: {
records: {
agentrun: [{ traceId, eventType: "command_output", eventId: "evt_output" }],
hwlab: [],
},
},
}),
/conditional event pair command-output count differs/u,
);
});
test("realtime fanout retries a YAML-bounded transient health navigation", async () => {