@nodetool-ai/reliability-harness 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/cli.d.ts +73 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +201 -0
- package/dist/cli.js.map +1 -0
- package/dist/compare.d.ts +98 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +267 -0
- package/dist/compare.js.map +1 -0
- package/dist/core/diff.d.ts +39 -0
- package/dist/core/diff.d.ts.map +1 -0
- package/dist/core/diff.js +114 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/golden.d.ts +50 -0
- package/dist/core/golden.d.ts.map +1 -0
- package/dist/core/golden.js +150 -0
- package/dist/core/golden.js.map +1 -0
- package/dist/core/invariants/index.d.ts +32 -0
- package/dist/core/invariants/index.d.ts.map +1 -0
- package/dist/core/invariants/index.js +44 -0
- package/dist/core/invariants/index.js.map +1 -0
- package/dist/core/invariants/leaks.d.ts +22 -0
- package/dist/core/invariants/leaks.d.ts.map +1 -0
- package/dist/core/invariants/leaks.js +55 -0
- package/dist/core/invariants/leaks.js.map +1 -0
- package/dist/core/invariants/lifecycle.d.ts +19 -0
- package/dist/core/invariants/lifecycle.d.ts.map +1 -0
- package/dist/core/invariants/lifecycle.js +95 -0
- package/dist/core/invariants/lifecycle.js.map +1 -0
- package/dist/core/invariants/protocol-validity.d.ts +4 -0
- package/dist/core/invariants/protocol-validity.d.ts.map +1 -0
- package/dist/core/invariants/protocol-validity.js +131 -0
- package/dist/core/invariants/protocol-validity.js.map +1 -0
- package/dist/core/invariants/state-machine.d.ts +29 -0
- package/dist/core/invariants/state-machine.d.ts.map +1 -0
- package/dist/core/invariants/state-machine.js +79 -0
- package/dist/core/invariants/state-machine.js.map +1 -0
- package/dist/core/invariants/status-sets.d.ts +27 -0
- package/dist/core/invariants/status-sets.d.ts.map +1 -0
- package/dist/core/invariants/status-sets.js +51 -0
- package/dist/core/invariants/status-sets.js.map +1 -0
- package/dist/core/invariants/terminal-uniqueness.d.ts +23 -0
- package/dist/core/invariants/terminal-uniqueness.d.ts.map +1 -0
- package/dist/core/invariants/terminal-uniqueness.js +99 -0
- package/dist/core/invariants/terminal-uniqueness.js.map +1 -0
- package/dist/core/invariants/types.d.ts +25 -0
- package/dist/core/invariants/types.d.ts.map +1 -0
- package/dist/core/invariants/types.js +2 -0
- package/dist/core/invariants/types.js.map +1 -0
- package/dist/core/journey.d.ts +121 -0
- package/dist/core/journey.d.ts.map +1 -0
- package/dist/core/journey.js +166 -0
- package/dist/core/journey.js.map +1 -0
- package/dist/core/normalize.d.ts +86 -0
- package/dist/core/normalize.d.ts.map +1 -0
- package/dist/core/normalize.js +182 -0
- package/dist/core/normalize.js.map +1 -0
- package/dist/core/record.d.ts +160 -0
- package/dist/core/record.d.ts.map +1 -0
- package/dist/core/record.js +74 -0
- package/dist/core/record.js.map +1 -0
- package/dist/core/stable-json.d.ts +13 -0
- package/dist/core/stable-json.d.ts.map +1 -0
- package/dist/core/stable-json.js +28 -0
- package/dist/core/stable-json.js.map +1 -0
- package/dist/drivers/anchors.d.ts +29 -0
- package/dist/drivers/anchors.d.ts.map +1 -0
- package/dist/drivers/anchors.js +48 -0
- package/dist/drivers/anchors.js.map +1 -0
- package/dist/drivers/app-headless.d.ts +39 -0
- package/dist/drivers/app-headless.d.ts.map +1 -0
- package/dist/drivers/app-headless.js +123 -0
- package/dist/drivers/app-headless.js.map +1 -0
- package/dist/drivers/browser.d.ts +34 -0
- package/dist/drivers/browser.d.ts.map +1 -0
- package/dist/drivers/browser.js +176 -0
- package/dist/drivers/browser.js.map +1 -0
- package/dist/drivers/cli.d.ts +13 -0
- package/dist/drivers/cli.d.ts.map +1 -0
- package/dist/drivers/cli.js +82 -0
- package/dist/drivers/cli.js.map +1 -0
- package/dist/drivers/fixture-nodes.d.ts +124 -0
- package/dist/drivers/fixture-nodes.d.ts.map +1 -0
- package/dist/drivers/fixture-nodes.js +189 -0
- package/dist/drivers/fixture-nodes.js.map +1 -0
- package/dist/drivers/index.d.ts +18 -0
- package/dist/drivers/index.d.ts.map +1 -0
- package/dist/drivers/index.js +12 -0
- package/dist/drivers/index.js.map +1 -0
- package/dist/drivers/kernel.d.ts +8 -0
- package/dist/drivers/kernel.d.ts.map +1 -0
- package/dist/drivers/kernel.js +247 -0
- package/dist/drivers/kernel.js.map +1 -0
- package/dist/drivers/packaged.d.ts +28 -0
- package/dist/drivers/packaged.d.ts.map +1 -0
- package/dist/drivers/packaged.js +303 -0
- package/dist/drivers/packaged.js.map +1 -0
- package/dist/drivers/python-bridge.d.ts +22 -0
- package/dist/drivers/python-bridge.d.ts.map +1 -0
- package/dist/drivers/python-bridge.js +48 -0
- package/dist/drivers/python-bridge.js.map +1 -0
- package/dist/drivers/registry.d.ts +11 -0
- package/dist/drivers/registry.d.ts.map +1 -0
- package/dist/drivers/registry.js +18 -0
- package/dist/drivers/registry.js.map +1 -0
- package/dist/drivers/relay-fields.d.ts +70 -0
- package/dist/drivers/relay-fields.d.ts.map +1 -0
- package/dist/drivers/relay-fields.js +104 -0
- package/dist/drivers/relay-fields.js.map +1 -0
- package/dist/drivers/repo-root.d.ts +9 -0
- package/dist/drivers/repo-root.d.ts.map +1 -0
- package/dist/drivers/repo-root.js +39 -0
- package/dist/drivers/repo-root.js.map +1 -0
- package/dist/drivers/spawn-cli.d.ts +8 -0
- package/dist/drivers/spawn-cli.d.ts.map +1 -0
- package/dist/drivers/spawn-cli.js +41 -0
- package/dist/drivers/spawn-cli.js.map +1 -0
- package/dist/drivers/types.d.ts +18 -0
- package/dist/drivers/types.d.ts.map +1 -0
- package/dist/drivers/types.js +2 -0
- package/dist/drivers/types.js.map +1 -0
- package/dist/drivers/ws-server.d.ts +8 -0
- package/dist/drivers/ws-server.d.ts.map +1 -0
- package/dist/drivers/ws-server.js +382 -0
- package/dist/drivers/ws-server.js.map +1 -0
- package/dist/faults/bridge-faults.d.ts +7 -0
- package/dist/faults/bridge-faults.d.ts.map +1 -0
- package/dist/faults/bridge-faults.js +90 -0
- package/dist/faults/bridge-faults.js.map +1 -0
- package/dist/faults/host-faults.d.ts +40 -0
- package/dist/faults/host-faults.d.ts.map +1 -0
- package/dist/faults/host-faults.js +70 -0
- package/dist/faults/host-faults.js.map +1 -0
- package/dist/faults/index.d.ts +21 -0
- package/dist/faults/index.d.ts.map +1 -0
- package/dist/faults/index.js +29 -0
- package/dist/faults/index.js.map +1 -0
- package/dist/faults/net-proxy.d.ts +75 -0
- package/dist/faults/net-proxy.d.ts.map +1 -0
- package/dist/faults/net-proxy.js +305 -0
- package/dist/faults/net-proxy.js.map +1 -0
- package/dist/faults/provider-faults.d.ts +12 -0
- package/dist/faults/provider-faults.d.ts.map +1 -0
- package/dist/faults/provider-faults.js +110 -0
- package/dist/faults/provider-faults.js.map +1 -0
- package/dist/faults/registry.d.ts +17 -0
- package/dist/faults/registry.d.ts.map +1 -0
- package/dist/faults/registry.js +15 -0
- package/dist/faults/registry.js.map +1 -0
- package/dist/faults/types.d.ts +42 -0
- package/dist/faults/types.d.ts.map +1 -0
- package/dist/faults/types.js +2 -0
- package/dist/faults/types.js.map +1 -0
- package/dist/faults/ws-faults.d.ts +6 -0
- package/dist/faults/ws-faults.d.ts.map +1 -0
- package/dist/faults/ws-faults.js +48 -0
- package/dist/faults/ws-faults.js.map +1 -0
- package/dist/faults/ws-proxy.d.ts +36 -0
- package/dist/faults/ws-proxy.d.ts.map +1 -0
- package/dist/faults/ws-proxy.js +40 -0
- package/dist/faults/ws-proxy.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/** Field names (anywhere in a message, at any depth) treated as ids that get
|
|
2
|
+
* remapped to a stable `<class:N>` placeholder, keyed by their remap class. */
|
|
3
|
+
export const ID_FIELD_CLASSES = {
|
|
4
|
+
job_id: "job",
|
|
5
|
+
workflow_id: "workflow",
|
|
6
|
+
node_id: "node",
|
|
7
|
+
edge_id: "edge",
|
|
8
|
+
provider_request_id: "provider-request"
|
|
9
|
+
};
|
|
10
|
+
/** Field names replaced wholesale with `<ts>` — the value never matters, only presence. */
|
|
11
|
+
export const TIMESTAMP_FIELDS = [
|
|
12
|
+
"timestamp",
|
|
13
|
+
"ts",
|
|
14
|
+
"created_at",
|
|
15
|
+
"generatedAt"
|
|
16
|
+
];
|
|
17
|
+
/** Field names replaced wholesale with `<duration>`. */
|
|
18
|
+
export const DURATION_FIELDS = [
|
|
19
|
+
"duration",
|
|
20
|
+
"durationMs",
|
|
21
|
+
"duration_ms"
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* Field values treated as interchangeable "nothing here" signals (added by
|
|
25
|
+
* C2): a node with zero declared `@prop` fields (the harness's own fixture
|
|
26
|
+
* nodes in `drivers/fixture-nodes.ts` — no shipped node has none) serializes
|
|
27
|
+
* its `properties` as `null` on the raw kernel stream but as `{}` once
|
|
28
|
+
* relayed through the ws-server. Canonicalizing `null` to `{}` for these
|
|
29
|
+
* keys make the two the same "no properties" value.
|
|
30
|
+
*/
|
|
31
|
+
export const NULLABLE_OBJECT_FIELDS = ["properties"];
|
|
32
|
+
/**
|
|
33
|
+
* Top-level fields the WebSocket transport stamps on a frame so a dropped
|
|
34
|
+
* client can replay what it missed (`job-run-registry.ts`,
|
|
35
|
+
* `chat-turn-registry.ts`). They describe the delivery, not the run, and the
|
|
36
|
+
* raw kernel stream has no counterpart — dropped from the comparison view so
|
|
37
|
+
* a resumable surface still diffs clean against a non-resumable one.
|
|
38
|
+
*/
|
|
39
|
+
export const TRANSPORT_ONLY_FIELDS = [
|
|
40
|
+
"job_seq",
|
|
41
|
+
"chat_seq"
|
|
42
|
+
];
|
|
43
|
+
/**
|
|
44
|
+
* Node type prefixes whose `node_update`/`generation_complete` frames are
|
|
45
|
+
* dropped from comparison (added by C2). This mirrors a real, documented,
|
|
46
|
+
* intentional ws-server behavior (`websocket-client-session.ts`: "Skip
|
|
47
|
+
* messages for constant/input nodes — they produce trivial outputs that
|
|
48
|
+
* don't need to be relayed to the frontend") that the raw kernel stream has
|
|
49
|
+
* no equivalent for — the kernel emits every node's lifecycle unconditionally.
|
|
50
|
+
* Normalizing both sides down to what a real frontend actually receives is
|
|
51
|
+
* the honest comparison; dropping the frames here (not filtering in
|
|
52
|
+
* `diff.ts`) keeps the diff itself surface-agnostic.
|
|
53
|
+
*/
|
|
54
|
+
export const TRIVIAL_NODE_TYPE_PREFIXES = [
|
|
55
|
+
"nodetool.constant.",
|
|
56
|
+
"nodetool.input."
|
|
57
|
+
];
|
|
58
|
+
const TRIVIAL_FRAME_MESSAGE_TYPES = new Set([
|
|
59
|
+
"node_update",
|
|
60
|
+
"generation_complete"
|
|
61
|
+
]);
|
|
62
|
+
/** True when `frame` is one `TRIVIAL_NODE_TYPE_PREFIXES` says to drop. */
|
|
63
|
+
function isTrivialNodeFrame(message) {
|
|
64
|
+
if (!TRIVIAL_FRAME_MESSAGE_TYPES.has(String(message["type"])))
|
|
65
|
+
return false;
|
|
66
|
+
const nodeType = message["node_type"];
|
|
67
|
+
return (typeof nodeType === "string" &&
|
|
68
|
+
TRIVIAL_NODE_TYPE_PREFIXES.some((prefix) => nodeType.startsWith(prefix)));
|
|
69
|
+
}
|
|
70
|
+
/** Asset references: `asset://…`, an `/assets/…` URL, or an inline data URI. */
|
|
71
|
+
const ASSET_URL_PATTERN = /^(asset:\/\/|https?:\/\/[^\s]+\/assets\/|data:[^;]+;base64,)/;
|
|
72
|
+
/**
|
|
73
|
+
* Assigns each distinct original id value a stable, order-of-first-sight
|
|
74
|
+
* placeholder within one normalization pass. Two records normalized
|
|
75
|
+
* independently line up as long as ids first appear in the same relative
|
|
76
|
+
* order — which is exactly the "same journey, different surface" case this
|
|
77
|
+
* exists for.
|
|
78
|
+
*/
|
|
79
|
+
export class IdMapper {
|
|
80
|
+
maps = new Map();
|
|
81
|
+
placeholderFor(cls, value) {
|
|
82
|
+
let map = this.maps.get(cls);
|
|
83
|
+
if (!map) {
|
|
84
|
+
map = new Map();
|
|
85
|
+
this.maps.set(cls, map);
|
|
86
|
+
}
|
|
87
|
+
let placeholder = map.get(value);
|
|
88
|
+
if (!placeholder) {
|
|
89
|
+
placeholder = `<${cls}:${map.size}>`;
|
|
90
|
+
map.set(value, placeholder);
|
|
91
|
+
}
|
|
92
|
+
return placeholder;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function normalizeValue(value, key, mapper) {
|
|
96
|
+
if (key && key in ID_FIELD_CLASSES && typeof value === "string") {
|
|
97
|
+
return mapper.placeholderFor(ID_FIELD_CLASSES[key], value);
|
|
98
|
+
}
|
|
99
|
+
if (key && TIMESTAMP_FIELDS.includes(key)) {
|
|
100
|
+
return value == null ? value : "<ts>";
|
|
101
|
+
}
|
|
102
|
+
if (key && DURATION_FIELDS.includes(key)) {
|
|
103
|
+
return value == null ? value : "<duration>";
|
|
104
|
+
}
|
|
105
|
+
if (key && NULLABLE_OBJECT_FIELDS.includes(key) && value === null) {
|
|
106
|
+
return {};
|
|
107
|
+
}
|
|
108
|
+
if (typeof value === "string" && ASSET_URL_PATTERN.test(value)) {
|
|
109
|
+
return "<asset>";
|
|
110
|
+
}
|
|
111
|
+
if (Array.isArray(value)) {
|
|
112
|
+
return value.map((item) => normalizeValue(item, null, mapper));
|
|
113
|
+
}
|
|
114
|
+
if (value !== null && typeof value === "object") {
|
|
115
|
+
const out = {};
|
|
116
|
+
for (const [k, v] of Object.entries(value)) {
|
|
117
|
+
out[k] = normalizeValue(v, k, mapper);
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* `job_update`'s optional `result` field (added by C2): the ws-server relay
|
|
125
|
+
* appends an "authoritative terminal snapshot" `job_update` carrying the
|
|
126
|
+
* full `result.outputs` whenever the relayed kernel terminal frame lacked
|
|
127
|
+
* one (`websocket-client-session.ts`'s `terminalWithResultSeen` check) — the
|
|
128
|
+
* kernel's own `job_update` emissions (`runner.ts`) never set `result` at
|
|
129
|
+
* all. Dropped only on `job_update` (not `node_update`, whose own `result`
|
|
130
|
+
* is real per-node data the outputs assertion depends on).
|
|
131
|
+
*/
|
|
132
|
+
function stripJobUpdateResult(message) {
|
|
133
|
+
if (message["type"] !== "job_update" || !("result" in message))
|
|
134
|
+
return message;
|
|
135
|
+
const rest = { ...message };
|
|
136
|
+
delete rest["result"];
|
|
137
|
+
return rest;
|
|
138
|
+
}
|
|
139
|
+
/** Normalizes one message (a frame's payload) in place-equivalent (returns a new object). */
|
|
140
|
+
export function normalizeMessage(message, mapper) {
|
|
141
|
+
const payload = { ...stripJobUpdateResult(message) };
|
|
142
|
+
for (const field of TRANSPORT_ONLY_FIELDS)
|
|
143
|
+
delete payload[field];
|
|
144
|
+
return normalizeValue(payload, null, mapper);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Normalizes a whole `RunRecord`: every frame's message, plus the record's
|
|
148
|
+
* own `jobId`/`workflowId` (through the same mapper, so they match the
|
|
149
|
+
* placeholders used inside the frames). `startedAt`/`finishedAt`/`durationMs`
|
|
150
|
+
* are dropped — wall-clock fields carry no comparison value once
|
|
151
|
+
* normalized, per §4.
|
|
152
|
+
*/
|
|
153
|
+
export function normalizeRunRecord(record, mapper = new IdMapper()) {
|
|
154
|
+
const frames = record.frames
|
|
155
|
+
// Frames a driver tagged as its surface's own documented redundancy
|
|
156
|
+
// (`RunFrame.redundant`) have no counterpart on the kernel's raw stream,
|
|
157
|
+
// so they are dropped here — in the comparison view only. The record
|
|
158
|
+
// itself keeps them, and the invariants read the record.
|
|
159
|
+
.filter((frame) => frame.redundant === undefined)
|
|
160
|
+
.filter((frame) => !isTrivialNodeFrame(frame.message))
|
|
161
|
+
.map((frame) => ({
|
|
162
|
+
seq: frame.seq,
|
|
163
|
+
channel: frame.channel,
|
|
164
|
+
direction: frame.direction,
|
|
165
|
+
surface: frame.surface,
|
|
166
|
+
message: normalizeMessage(frame.message, mapper)
|
|
167
|
+
}));
|
|
168
|
+
return {
|
|
169
|
+
journeyId: record.journeyId,
|
|
170
|
+
surface: record.surface,
|
|
171
|
+
status: record.status,
|
|
172
|
+
error: record.error,
|
|
173
|
+
jobId: record.jobId !== null
|
|
174
|
+
? mapper.placeholderFor(ID_FIELD_CLASSES.job_id, record.jobId)
|
|
175
|
+
: null,
|
|
176
|
+
workflowId: record.workflowId !== null
|
|
177
|
+
? mapper.placeholderFor(ID_FIELD_CLASSES.workflow_id, record.workflowId)
|
|
178
|
+
: null,
|
|
179
|
+
frames
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/core/normalize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RunRecord`: the universal currency of the reliability harness (§12).
|
|
3
|
+
*
|
|
4
|
+
* Every driver — kernel, ws-server, cli, browser, app-headless, packaged,
|
|
5
|
+
* electron — produces the same shape: every frame, both directions,
|
|
6
|
+
* timestamped, surface-tagged. It is a superset of two existing shapes:
|
|
7
|
+
*
|
|
8
|
+
* - the debug harness's bundle (`packages/cli/src/debug/`: `report.json` +
|
|
9
|
+
* `server/messages.jsonl`) — one direction (server → client), no per-frame
|
|
10
|
+
* timestamps, one `ProcessingMessage` per JSONL line;
|
|
11
|
+
* - the e2e_runner's `record.json` (`web/src/e2e_runner/types.ts`'s
|
|
12
|
+
* `RunRecord`) — one direction, a flat `events: WsEvent[]` array plus
|
|
13
|
+
* distilled `outputs`/`logs`/`nodeIO`.
|
|
14
|
+
*
|
|
15
|
+
* Invariants and diffs (§6, `diff.ts`) are pure functions over this shape,
|
|
16
|
+
* so they run identically whether the record came from a live driver run or
|
|
17
|
+
* from a user's field debug bundle.
|
|
18
|
+
*/
|
|
19
|
+
import type { ProcessingMessage } from "@nodetool-ai/protocol";
|
|
20
|
+
/** Which side of the wire a frame crossed. Debug bundles and today's
|
|
21
|
+
* e2e_runner records only ever carry `server_to_client`; a real ws-server
|
|
22
|
+
* driver also records `client_to_server` control frames (run/cancel/
|
|
23
|
+
* stream_input), which is what makes the record a strict superset. */
|
|
24
|
+
export type FrameDirection = "server_to_client" | "client_to_server";
|
|
25
|
+
/** A single frame in a run's stream, normalized to one shared shape. */
|
|
26
|
+
export interface RunFrame {
|
|
27
|
+
/** 0-based arrival order within this record — the tiebreaker when two
|
|
28
|
+
* frames share a timestamp, and the only ordering source for records
|
|
29
|
+
* whose origin (e.g. a JSONL bundle) never carried per-frame timestamps. */
|
|
30
|
+
seq: number;
|
|
31
|
+
/** Capture time, epoch ms. Synthetic (derived from `seq`) when the source
|
|
32
|
+
* record didn't preserve a real one — see `tsSource`. */
|
|
33
|
+
ts: number;
|
|
34
|
+
/** Whether `ts` is a real capture time or a synthetic ordering stand-in. */
|
|
35
|
+
tsSource: "captured" | "synthetic";
|
|
36
|
+
direction: FrameDirection;
|
|
37
|
+
/** Driver/converter that produced this frame (e.g. "kernel", "ws-server", "debug-bundle"). */
|
|
38
|
+
surface: string;
|
|
39
|
+
/** Per-node-channel diff key: `node:<id>` when the message names a node,
|
|
40
|
+
* `job` for job-level frames, `control` otherwise (§4's "per-node-channel,
|
|
41
|
+
* not globally" comparison). */
|
|
42
|
+
channel: string;
|
|
43
|
+
/** The raw frame payload. A `ProcessingMessage` for server→client frames;
|
|
44
|
+
* an untyped record for client→server control frames (run/cancel/…). */
|
|
45
|
+
message: ProcessingMessage | Record<string, unknown>;
|
|
46
|
+
/**
|
|
47
|
+
* Set when a driver recognizes this frame as its own surface's *documented*
|
|
48
|
+
* redundancy — a repeat of information an earlier frame already carried,
|
|
49
|
+
* emitted for client convenience (the ws-server's eager "running" ack and
|
|
50
|
+
* its authoritative terminal snapshot; see `drivers/ws-server.ts`). The
|
|
51
|
+
* value is the reason tag.
|
|
52
|
+
*
|
|
53
|
+
* The frame is still recorded: dropping repeats outright, as this driver
|
|
54
|
+
* used to, makes "exactly one terminal job_update" unfalsifiable on the one
|
|
55
|
+
* surface that has a wire. Cross-surface normalization skips tagged frames
|
|
56
|
+
* (they have no kernel counterpart), and the invariants that assert
|
|
57
|
+
* uniqueness discount them — so an *undocumented* duplicate, which is never
|
|
58
|
+
* tagged, still fails both.
|
|
59
|
+
*/
|
|
60
|
+
redundant?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Point-in-time snapshot of the leak-accounting counters §6's "Cleanup and
|
|
64
|
+
* leaks" section names: zero live actors, zero pending control responses,
|
|
65
|
+
* zero timers beyond the trigger timeout, zero pending Python-bridge
|
|
66
|
+
* requests, and WS slot accounting (`activeJobs`/`startingJobs`) back to
|
|
67
|
+
* empty. `at` distinguishes a baseline snapshot (taken before the run, so a
|
|
68
|
+
* driver can express "returned to its starting count" rather than
|
|
69
|
+
* "reached exactly zero" for a long-lived process) from the post-run one
|
|
70
|
+
* `leaks.ts` actually asserts against.
|
|
71
|
+
*/
|
|
72
|
+
export interface ResourceCounterSnapshot {
|
|
73
|
+
at: "before" | "after";
|
|
74
|
+
liveActors: number;
|
|
75
|
+
pendingControlResponses: number;
|
|
76
|
+
pendingTimers: number;
|
|
77
|
+
pythonBridgePendingRequests: number;
|
|
78
|
+
activeJobs: number;
|
|
79
|
+
startingJobs: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* One recorded client `WebSocketManager` state transition (§6 "State
|
|
83
|
+
* transitions", `WebSocketManager.ts:95`'s `STATE_TRANSITIONS`). `action` and
|
|
84
|
+
* `from`/`to` mirror that table's shape so `state-machine.ts` can replay the
|
|
85
|
+
* sequence against the declared machine.
|
|
86
|
+
*/
|
|
87
|
+
export interface RecordedTransition {
|
|
88
|
+
/** Ordering key — a frame `seq` this transition is anchored to, or a plain
|
|
89
|
+
* incrementing index when the driver has no frame to anchor on. */
|
|
90
|
+
at: number;
|
|
91
|
+
action: string;
|
|
92
|
+
from: string;
|
|
93
|
+
to: string;
|
|
94
|
+
}
|
|
95
|
+
export interface RunRecord {
|
|
96
|
+
/** Journey this record was captured for, when known. */
|
|
97
|
+
journeyId?: string;
|
|
98
|
+
/** The driver/surface that produced this record. */
|
|
99
|
+
surface: string;
|
|
100
|
+
jobId: string | null;
|
|
101
|
+
workflowId: string | null;
|
|
102
|
+
/** Epoch ms; null when the source never recorded a start. */
|
|
103
|
+
startedAt: number | null;
|
|
104
|
+
finishedAt: number | null;
|
|
105
|
+
durationMs: number | null;
|
|
106
|
+
/** Terminal status ("completed" | "failed" | "cancelled" | …), or "unknown". */
|
|
107
|
+
status: string;
|
|
108
|
+
error: string | null;
|
|
109
|
+
params: Record<string, unknown>;
|
|
110
|
+
frames: RunFrame[];
|
|
111
|
+
/**
|
|
112
|
+
* Leak-accounting snapshots (§6), additive over C1's shape. Absent when the
|
|
113
|
+
* driver that produced this record doesn't instrument these counters (e.g.
|
|
114
|
+
* a debug-bundle conversion) — `leaks.ts` reports no violations rather than
|
|
115
|
+
* treating absence itself as a leak.
|
|
116
|
+
*/
|
|
117
|
+
resourceCounters?: ResourceCounterSnapshot[];
|
|
118
|
+
/**
|
|
119
|
+
* Client `WebSocketManager` transitions observed during this run (§6),
|
|
120
|
+
* additive over C1's shape. Absent when the driver never touches a real
|
|
121
|
+
* client connection (e.g. the kernel driver) — `state-machine.ts` reports
|
|
122
|
+
* no violations rather than treating absence itself as a violation.
|
|
123
|
+
*/
|
|
124
|
+
transitions?: RecordedTransition[];
|
|
125
|
+
}
|
|
126
|
+
/** Per-node-channel diff key for a message (§4, §12). */
|
|
127
|
+
export declare function channelForMessage(message: ProcessingMessage | Record<string, unknown>): string;
|
|
128
|
+
/** Builds one `RunFrame`, filling in `channel` and a synthetic `ts` when none is given. */
|
|
129
|
+
export declare function makeFrame(seq: number, surface: string, direction: FrameDirection, message: ProcessingMessage | Record<string, unknown>, ts?: number): RunFrame;
|
|
130
|
+
/** Minimal shape the debug harness's `report.json` needs to expose for conversion. */
|
|
131
|
+
interface DebugBundleReportLike {
|
|
132
|
+
target?: {
|
|
133
|
+
workflowId?: string | null;
|
|
134
|
+
} | null;
|
|
135
|
+
server?: {
|
|
136
|
+
status?: string;
|
|
137
|
+
error?: string | null;
|
|
138
|
+
durationMs?: number | null;
|
|
139
|
+
} | null;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Converts a decoded debug-bundle `report.json` + its `server/messages.jsonl`
|
|
143
|
+
* lines into a `RunRecord`. Pure — the IO (reading the bundle directory off
|
|
144
|
+
* disk) lives in {@link loadRunRecordFromDebugBundle}, so this half is
|
|
145
|
+
* unit-testable without touching the filesystem.
|
|
146
|
+
*/
|
|
147
|
+
export declare function runRecordFromDebugBundle(report: DebugBundleReportLike, messages: Array<ProcessingMessage | Record<string, unknown>>, options?: {
|
|
148
|
+
journeyId?: string;
|
|
149
|
+
startedAt?: number;
|
|
150
|
+
}): RunRecord;
|
|
151
|
+
/**
|
|
152
|
+
* Reads a debug bundle directory (`report.json` + `server/messages.jsonl`)
|
|
153
|
+
* off disk and converts it into a `RunRecord`. This is the "debug bundle
|
|
154
|
+
* round-trips into a RunRecord" half of C1's done-when.
|
|
155
|
+
*/
|
|
156
|
+
export declare function loadRunRecordFromDebugBundle(bundleDir: string, options?: {
|
|
157
|
+
journeyId?: string;
|
|
158
|
+
}): Promise<RunRecord>;
|
|
159
|
+
export {};
|
|
160
|
+
//# sourceMappingURL=record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/core/record.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
function messageNodeId(message) {
|
|
2
|
+
const nodeId = message["node_id"];
|
|
3
|
+
return typeof nodeId === "string" && nodeId.length > 0 ? nodeId : null;
|
|
4
|
+
}
|
|
5
|
+
/** Per-node-channel diff key for a message (§4, §12). */
|
|
6
|
+
export function channelForMessage(message) {
|
|
7
|
+
const record = message;
|
|
8
|
+
const nodeId = messageNodeId(record);
|
|
9
|
+
if (nodeId)
|
|
10
|
+
return `node:${nodeId}`;
|
|
11
|
+
if (record["type"] === "job_update")
|
|
12
|
+
return "job";
|
|
13
|
+
return "control";
|
|
14
|
+
}
|
|
15
|
+
/** Builds one `RunFrame`, filling in `channel` and a synthetic `ts` when none is given. */
|
|
16
|
+
export function makeFrame(seq, surface, direction, message, ts) {
|
|
17
|
+
return {
|
|
18
|
+
seq,
|
|
19
|
+
ts: ts ?? seq,
|
|
20
|
+
tsSource: ts === undefined ? "synthetic" : "captured",
|
|
21
|
+
direction,
|
|
22
|
+
surface,
|
|
23
|
+
channel: channelForMessage(message),
|
|
24
|
+
message
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Converts a decoded debug-bundle `report.json` + its `server/messages.jsonl`
|
|
29
|
+
* lines into a `RunRecord`. Pure — the IO (reading the bundle directory off
|
|
30
|
+
* disk) lives in {@link loadRunRecordFromDebugBundle}, so this half is
|
|
31
|
+
* unit-testable without touching the filesystem.
|
|
32
|
+
*/
|
|
33
|
+
export function runRecordFromDebugBundle(report, messages, options = {}) {
|
|
34
|
+
const startedAt = options.startedAt ?? 0;
|
|
35
|
+
const frames = messages.map((message, index) => makeFrame(index, "debug-bundle", "server_to_client", message, startedAt + index));
|
|
36
|
+
const jobUpdates = frames.filter((f) => f.channel === "job");
|
|
37
|
+
const firstJob = jobUpdates[0]?.message;
|
|
38
|
+
const lastJob = jobUpdates[jobUpdates.length - 1]?.message;
|
|
39
|
+
return {
|
|
40
|
+
journeyId: options.journeyId,
|
|
41
|
+
surface: "debug-bundle",
|
|
42
|
+
jobId: firstJob?.["job_id"] ??
|
|
43
|
+
lastJob?.["job_id"] ??
|
|
44
|
+
null,
|
|
45
|
+
workflowId: report.target?.workflowId ?? null,
|
|
46
|
+
startedAt: frames.length > 0 ? startedAt : null,
|
|
47
|
+
finishedAt: frames.length > 0 ? startedAt + (frames.length - 1) : null,
|
|
48
|
+
durationMs: report.server?.durationMs ?? null,
|
|
49
|
+
status: report.server?.status ?? "unknown",
|
|
50
|
+
error: report.server?.error ?? null,
|
|
51
|
+
params: {},
|
|
52
|
+
frames
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Reads a debug bundle directory (`report.json` + `server/messages.jsonl`)
|
|
57
|
+
* off disk and converts it into a `RunRecord`. This is the "debug bundle
|
|
58
|
+
* round-trips into a RunRecord" half of C1's done-when.
|
|
59
|
+
*/
|
|
60
|
+
export async function loadRunRecordFromDebugBundle(bundleDir, options = {}) {
|
|
61
|
+
const { readFile } = await import("node:fs/promises");
|
|
62
|
+
const { join } = await import("node:path");
|
|
63
|
+
const reportRaw = await readFile(join(bundleDir, "report.json"), "utf8");
|
|
64
|
+
const report = JSON.parse(reportRaw);
|
|
65
|
+
const messagesFile = join(bundleDir, "server", "messages.jsonl");
|
|
66
|
+
const messagesRaw = await readFile(messagesFile, "utf8");
|
|
67
|
+
const messages = messagesRaw
|
|
68
|
+
.split("\n")
|
|
69
|
+
.map((line) => line.trim())
|
|
70
|
+
.filter((line) => line.length > 0)
|
|
71
|
+
.map((line) => JSON.parse(line));
|
|
72
|
+
return runRecordFromDebugBundle(report, messages, options);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/core/record.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Order-independent JSON serialization, recursive at every depth — the
|
|
3
|
+
* canonical form both the stream diff (`diff.ts`) and the golden comparisons
|
|
4
|
+
* (`golden.ts`) key messages by.
|
|
5
|
+
*
|
|
6
|
+
* `JSON.stringify(value, keyArray)` cannot do this: its array form is a key
|
|
7
|
+
* *whitelist* applied at every nesting level, so a top-level key list erases
|
|
8
|
+
* every nested object's contents — `{result: {output: "A"}}` and
|
|
9
|
+
* `{result: {output: "B"}}` both serialize to `{"result":{}}`, and output,
|
|
10
|
+
* metadata, and nested error divergences compare equal.
|
|
11
|
+
*/
|
|
12
|
+
export declare function stableStringify(value: unknown): string;
|
|
13
|
+
//# sourceMappingURL=stable-json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stable-json.d.ts","sourceRoot":"","sources":["../../src/core/stable-json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CActD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Order-independent JSON serialization, recursive at every depth — the
|
|
3
|
+
* canonical form both the stream diff (`diff.ts`) and the golden comparisons
|
|
4
|
+
* (`golden.ts`) key messages by.
|
|
5
|
+
*
|
|
6
|
+
* `JSON.stringify(value, keyArray)` cannot do this: its array form is a key
|
|
7
|
+
* *whitelist* applied at every nesting level, so a top-level key list erases
|
|
8
|
+
* every nested object's contents — `{result: {output: "A"}}` and
|
|
9
|
+
* `{result: {output: "B"}}` both serialize to `{"result":{}}`, and output,
|
|
10
|
+
* metadata, and nested error divergences compare equal.
|
|
11
|
+
*/
|
|
12
|
+
export function stableStringify(value) {
|
|
13
|
+
if (value === undefined)
|
|
14
|
+
return "null";
|
|
15
|
+
if (value === null || typeof value !== "object") {
|
|
16
|
+
return JSON.stringify(value) ?? "null";
|
|
17
|
+
}
|
|
18
|
+
if (Array.isArray(value)) {
|
|
19
|
+
return `[${value.map(stableStringify).join(",")}]`;
|
|
20
|
+
}
|
|
21
|
+
const entries = Object.entries(value)
|
|
22
|
+
.filter(([, v]) => v !== undefined)
|
|
23
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
24
|
+
return `{${entries
|
|
25
|
+
.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`)
|
|
26
|
+
.join(",")}}`;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=stable-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stable-json.js","sourceRoot":"","sources":["../../src/core/stable-json.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACvC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC;IACzC,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACrD,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;SAC7D,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;SAClC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,OAAO,IAAI,OAAO;SACf,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;SAC7D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event-anchor matching for scripted `JourneyInteraction`s (§4: "event-
|
|
3
|
+
* anchored, not time-anchored"). An interaction's `at` string names a
|
|
4
|
+
* message to wait for before firing — never a wall-clock delay — so both
|
|
5
|
+
* drivers cancel/stream-input at the same logical point in the run
|
|
6
|
+
* regardless of how fast either surface actually executes.
|
|
7
|
+
*
|
|
8
|
+
* Supported anchor shapes (the only ones the four C2 journeys need; extend
|
|
9
|
+
* here, not per-driver, when a later journey needs more):
|
|
10
|
+
* - `"node_update:<nodeId>:<status>"` — a `node_update` for that node id
|
|
11
|
+
* reaching that status (e.g. `"node_update:probe:running"`).
|
|
12
|
+
* - `"job_update:<status>"` — a `job_update` reaching that status.
|
|
13
|
+
* - `"<type>"` — any message of that `type`, e.g. `"chunk"`.
|
|
14
|
+
*/
|
|
15
|
+
export type AnchoredMessage = Record<string, unknown>;
|
|
16
|
+
export declare function matchesAnchor(message: AnchoredMessage, anchor: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Registers anchor waiters and resolves them as matching messages flow
|
|
19
|
+
* through a driver's message pump. Registration must happen synchronously
|
|
20
|
+
* (no `await` in between) relative to the pump starting to consume already-
|
|
21
|
+
* queued messages, or a fast-arriving anchor could be missed — both drivers
|
|
22
|
+
* follow that rule (see the comment at each `run()`'s interaction loop).
|
|
23
|
+
*/
|
|
24
|
+
export declare class AnchorWaiter {
|
|
25
|
+
private waiters;
|
|
26
|
+
wait(anchor: string): Promise<void>;
|
|
27
|
+
notify(message: AnchoredMessage): void;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=anchors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchors.d.ts","sourceRoot":"","sources":["../../src/drivers/anchors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD,wBAAgB,aAAa,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAoB/E;AAOD;;;;;;GAMG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAgB;IAE/B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIlC;IAED,MAAM,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAOrC;CACF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export function matchesAnchor(message, anchor) {
|
|
2
|
+
const parts = anchor.split(":");
|
|
3
|
+
const type = parts[0];
|
|
4
|
+
if (message["type"] !== type)
|
|
5
|
+
return false;
|
|
6
|
+
if (type === "node_update") {
|
|
7
|
+
const [, nodeId, status] = parts;
|
|
8
|
+
if (nodeId !== undefined && message["node_id"] !== nodeId)
|
|
9
|
+
return false;
|
|
10
|
+
if (status !== undefined && message["status"] !== status)
|
|
11
|
+
return false;
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
if (type === "job_update") {
|
|
15
|
+
const [, status] = parts;
|
|
16
|
+
if (status !== undefined && message["status"] !== status)
|
|
17
|
+
return false;
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
// Bare type match (e.g. "chunk") — no further qualifiers supported yet.
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Registers anchor waiters and resolves them as matching messages flow
|
|
25
|
+
* through a driver's message pump. Registration must happen synchronously
|
|
26
|
+
* (no `await` in between) relative to the pump starting to consume already-
|
|
27
|
+
* queued messages, or a fast-arriving anchor could be missed — both drivers
|
|
28
|
+
* follow that rule (see the comment at each `run()`'s interaction loop).
|
|
29
|
+
*/
|
|
30
|
+
export class AnchorWaiter {
|
|
31
|
+
waiters = [];
|
|
32
|
+
wait(anchor) {
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
this.waiters.push({ test: (m) => matchesAnchor(m, anchor), resolve });
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
notify(message) {
|
|
38
|
+
const remaining = [];
|
|
39
|
+
for (const waiter of this.waiters) {
|
|
40
|
+
if (waiter.test(message))
|
|
41
|
+
waiter.resolve();
|
|
42
|
+
else
|
|
43
|
+
remaining.push(waiter);
|
|
44
|
+
}
|
|
45
|
+
this.waiters = remaining;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=anchors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchors.js","sourceRoot":"","sources":["../../src/drivers/anchors.ts"],"names":[],"mappings":"AAgBA,MAAM,UAAU,aAAa,CAAC,OAAwB,EAAE,MAAc;IACpE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAE3C,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;QAC3B,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;QACjC,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,KAAK,MAAM;YAAE,OAAO,KAAK,CAAC;QACxE,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,MAAM;YAAE,OAAO,KAAK,CAAC;QACvE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC1B,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;QACzB,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,MAAM;YAAE,OAAO,KAAK,CAAC;QACvE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wEAAwE;IACxE,OAAO,IAAI,CAAC;AACd,CAAC;AAOD;;;;;;GAMG;AACH,MAAM,OAAO,YAAY;IACf,OAAO,GAAa,EAAE,CAAC;IAE/B,IAAI,CAAC,MAAc;QACjB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,OAAwB;QAC7B,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;gBAAE,MAAM,CAAC,OAAO,EAAE,CAAC;;gBACtC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ProcessingMessage } from "@nodetool-ai/protocol";
|
|
2
|
+
import type { Journey } from "../core/journey.js";
|
|
3
|
+
import { type RunRecord } from "../core/record.js";
|
|
4
|
+
import type { RunDriver } from "./types.js";
|
|
5
|
+
/** Minimal shape this driver needs from an `AppDebugReport`'s `report.json`
|
|
6
|
+
* (`packages/cli/src/app-debug/types.ts`). */
|
|
7
|
+
export interface AppDebugReportLike {
|
|
8
|
+
target?: {
|
|
9
|
+
workflowId?: string | null;
|
|
10
|
+
} | null;
|
|
11
|
+
runs?: Array<{
|
|
12
|
+
status?: string;
|
|
13
|
+
error?: string | null;
|
|
14
|
+
durationMs?: number | null;
|
|
15
|
+
messagesFile?: string;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
/** True when a journey's workflow carries an `app_doc` the app-headless
|
|
19
|
+
* driver can run — pure, no IO, so `compare.ts` and tests can check
|
|
20
|
+
* applicability without spawning a process. */
|
|
21
|
+
export declare function journeyHasAppDoc(journey: Journey): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Folds a decoded `AppDebugReport` (`report.json`) plus each triggered run's
|
|
24
|
+
* own `server/run-N.messages.jsonl` lines (already read, in `runs[]` order)
|
|
25
|
+
* into one `RunRecord`. Pure — mirrors `record.ts`'s
|
|
26
|
+
* `runRecordFromDebugBundle` split between the pure conversion and the IO
|
|
27
|
+
* that reads the bundle off disk, so this half is unit-testable without a
|
|
28
|
+
* real app document or a spawned process.
|
|
29
|
+
*/
|
|
30
|
+
export declare function runRecordFromAppDebugReport(report: AppDebugReportLike, runMessages: Array<Array<ProcessingMessage | Record<string, unknown>>>, options?: {
|
|
31
|
+
journeyId?: string;
|
|
32
|
+
startedAt?: number;
|
|
33
|
+
}): RunRecord;
|
|
34
|
+
export declare class AppHeadlessDriver implements RunDriver {
|
|
35
|
+
readonly name = "app-headless";
|
|
36
|
+
supports(journey: Journey): boolean;
|
|
37
|
+
run(journey: Journey): Promise<RunRecord>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=app-headless.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-headless.d.ts","sourceRoot":"","sources":["../../src/drivers/app-headless.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAA4B,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;8CAC8C;AAC9C,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/C,IAAI,CAAC,EAAE,KAAK,CAAC;QACX,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC,CAAC;CACJ;AAED;;+CAE+C;AAC/C,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAE1D;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,kBAAkB,EAC1B,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EACtE,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACvD,SAAS,CA6BX;AAED,qBAAa,iBAAkB,YAAW,SAAS;IACjD,QAAQ,CAAC,IAAI,kBAAkB;IAE/B,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAElC;IAEK,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAiE9C;CACF"}
|