open-multi-agent-kit 0.80.5 → 0.80.6

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.
Files changed (105) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/config.d.ts +4 -9
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +46 -15
  5. package/dist/config.js.map +1 -1
  6. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  7. package/dist/core/compaction/branch-summarization.js +6 -1
  8. package/dist/core/compaction/branch-summarization.js.map +1 -1
  9. package/dist/core/compaction/compaction.d.ts +82 -1
  10. package/dist/core/compaction/compaction.d.ts.map +1 -1
  11. package/dist/core/compaction/compaction.js +606 -10
  12. package/dist/core/compaction/compaction.js.map +1 -1
  13. package/dist/core/harness-control-events.d.ts +79 -0
  14. package/dist/core/harness-control-events.d.ts.map +1 -0
  15. package/dist/core/harness-control-events.js +339 -0
  16. package/dist/core/harness-control-events.js.map +1 -0
  17. package/dist/core/harness-control-replay.d.ts +26 -0
  18. package/dist/core/harness-control-replay.d.ts.map +1 -0
  19. package/dist/core/harness-control-replay.js +155 -0
  20. package/dist/core/harness-control-replay.js.map +1 -0
  21. package/dist/core/keybindings.d.ts +12 -0
  22. package/dist/core/keybindings.d.ts.map +1 -1
  23. package/dist/core/keybindings.js +31 -1
  24. package/dist/core/keybindings.js.map +1 -1
  25. package/dist/core/mcp-inventory.d.ts +40 -0
  26. package/dist/core/mcp-inventory.d.ts.map +1 -0
  27. package/dist/core/mcp-inventory.js +104 -0
  28. package/dist/core/mcp-inventory.js.map +1 -0
  29. package/dist/core/settings-manager.d.ts +3 -0
  30. package/dist/core/settings-manager.d.ts.map +1 -1
  31. package/dist/core/settings-manager.js +4 -0
  32. package/dist/core/settings-manager.js.map +1 -1
  33. package/dist/core/slash-commands.d.ts.map +1 -1
  34. package/dist/core/slash-commands.js +4 -1
  35. package/dist/core/slash-commands.js.map +1 -1
  36. package/dist/core/spec-kit/compiler.d.ts +67 -0
  37. package/dist/core/spec-kit/compiler.d.ts.map +1 -0
  38. package/dist/core/spec-kit/compiler.js +268 -0
  39. package/dist/core/spec-kit/compiler.js.map +1 -0
  40. package/dist/core/transaction-coordinator.d.ts +30 -0
  41. package/dist/core/transaction-coordinator.d.ts.map +1 -0
  42. package/dist/core/transaction-coordinator.js +173 -0
  43. package/dist/core/transaction-coordinator.js.map +1 -0
  44. package/dist/migrations.d.ts +35 -0
  45. package/dist/migrations.d.ts.map +1 -1
  46. package/dist/migrations.js +409 -3
  47. package/dist/migrations.js.map +1 -1
  48. package/dist/modes/interactive/components/control-panel.d.ts +29 -0
  49. package/dist/modes/interactive/components/control-panel.d.ts.map +1 -0
  50. package/dist/modes/interactive/components/control-panel.js +90 -0
  51. package/dist/modes/interactive/components/control-panel.js.map +1 -0
  52. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  53. package/dist/modes/interactive/components/custom-editor.js +21 -13
  54. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  55. package/dist/modes/interactive/components/index.d.ts +3 -0
  56. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  57. package/dist/modes/interactive/components/index.js +3 -0
  58. package/dist/modes/interactive/components/index.js.map +1 -1
  59. package/dist/modes/interactive/components/mcp-selector.d.ts +28 -0
  60. package/dist/modes/interactive/components/mcp-selector.d.ts.map +1 -0
  61. package/dist/modes/interactive/components/mcp-selector.js +138 -0
  62. package/dist/modes/interactive/components/mcp-selector.js.map +1 -0
  63. package/dist/modes/interactive/components/model-selector.d.ts +12 -3
  64. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  65. package/dist/modes/interactive/components/model-selector.js +120 -27
  66. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  67. package/dist/modes/interactive/components/settings-selector.d.ts +19 -1
  68. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  69. package/dist/modes/interactive/components/settings-selector.js +21 -8
  70. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  71. package/dist/modes/interactive/components/skills-selector.d.ts +32 -0
  72. package/dist/modes/interactive/components/skills-selector.d.ts.map +1 -0
  73. package/dist/modes/interactive/components/skills-selector.js +130 -0
  74. package/dist/modes/interactive/components/skills-selector.js.map +1 -0
  75. package/dist/modes/interactive/components/thinking-selector.d.ts +7 -8
  76. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  77. package/dist/modes/interactive/components/thinking-selector.js +7 -48
  78. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  79. package/dist/modes/interactive/interactive-mode.d.ts +9 -0
  80. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  81. package/dist/modes/interactive/interactive-mode.js +340 -97
  82. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  83. package/dist/modes/interactive/theme/catppuccin-mocha.json +86 -0
  84. package/dist/modes/interactive/theme/dracula.json +86 -0
  85. package/dist/modes/interactive/theme/gruvbox-dark.json +86 -0
  86. package/dist/modes/interactive/theme/kanagawa-dragon.json +86 -0
  87. package/dist/modes/interactive/theme/omk-control-panel.json +91 -0
  88. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  89. package/dist/modes/interactive/theme/theme.js +41 -16
  90. package/dist/modes/interactive/theme/theme.js.map +1 -1
  91. package/dist/modes/interactive/theme/tokyo-night-storm.json +86 -0
  92. package/docs/compaction.md +21 -8
  93. package/docs/quickstart.md +1 -1
  94. package/docs/themes.md +10 -2
  95. package/docs/usage.md +2 -1
  96. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  97. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  98. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  99. package/examples/extensions/gondolin/package-lock.json +2 -2
  100. package/examples/extensions/gondolin/package.json +1 -1
  101. package/examples/extensions/sandbox/package-lock.json +2 -2
  102. package/examples/extensions/sandbox/package.json +1 -1
  103. package/examples/extensions/with-deps/package-lock.json +2 -2
  104. package/examples/extensions/with-deps/package.json +1 -1
  105. package/package.json +1 -1
@@ -0,0 +1,339 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, writeSync, } from "node:fs";
3
+ import { dirname, join, resolve, sep } from "node:path";
4
+ const MAX_EVENT_DEPTH = 6;
5
+ const MAX_EVENT_OBJECT_KEYS = 80;
6
+ const MAX_EVENT_ARRAY_ITEMS = 80;
7
+ const MAX_EVENT_STRING_LENGTH = 2000;
8
+ const REDACTED = "[redacted]";
9
+ const GENESIS_EVENT_HASH = "0".repeat(64);
10
+ const DEFAULT_RUN_ID = "default-run";
11
+ const DEFAULT_SESSION_ID = "sessionless";
12
+ const LOCK_STALE_MS = 30000;
13
+ function isSensitiveKey(key) {
14
+ return /(?:secret|token|api[_-]?key|authorization|password|credential|private[_-]?key)/i.test(key);
15
+ }
16
+ function redactSensitiveValue(value) {
17
+ let redacted = value;
18
+ redacted = redacted.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, `Bearer ${REDACTED}`);
19
+ redacted = redacted.replace(/sk-(?:proj-|ant-)?[A-Za-z0-9_-]{16,}/g, REDACTED);
20
+ redacted = redacted.replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, REDACTED);
21
+ redacted = redacted.replace(/-----BEGIN [^-]+PRIVATE KEY-----[\s\S]*?-----END [^-]+PRIVATE KEY-----/g, REDACTED);
22
+ redacted = redacted.replace(/\b[A-Fa-f0-9]{64,}\b/g, REDACTED);
23
+ return redacted;
24
+ }
25
+ function truncateString(value) {
26
+ const redacted = redactSensitiveValue(value);
27
+ if (redacted.length <= MAX_EVENT_STRING_LENGTH)
28
+ return redacted;
29
+ return `${redacted.slice(0, MAX_EVENT_STRING_LENGTH)}…[truncated]`;
30
+ }
31
+ function sanitizeValue(value, depth, seen) {
32
+ if (value === null)
33
+ return null;
34
+ if (typeof value === "string")
35
+ return truncateString(value);
36
+ if (typeof value === "number")
37
+ return Number.isFinite(value) ? value : String(value);
38
+ if (typeof value === "boolean")
39
+ return value;
40
+ if (typeof value === "bigint")
41
+ return value.toString();
42
+ if (typeof value === "undefined" || typeof value === "function" || typeof value === "symbol")
43
+ return String(value);
44
+ if (depth >= MAX_EVENT_DEPTH)
45
+ return "[max-depth]";
46
+ if (Array.isArray(value)) {
47
+ return value.slice(0, MAX_EVENT_ARRAY_ITEMS).map((entry) => sanitizeValue(entry, depth + 1, seen));
48
+ }
49
+ if (value instanceof Date)
50
+ return value.toISOString();
51
+ if (value instanceof Error) {
52
+ return {
53
+ name: value.name,
54
+ message: truncateString(value.message),
55
+ };
56
+ }
57
+ if (typeof value === "object") {
58
+ if (seen.has(value))
59
+ return "[circular]";
60
+ seen.add(value);
61
+ const result = {};
62
+ for (const [key, entry] of Object.entries(value).slice(0, MAX_EVENT_OBJECT_KEYS)) {
63
+ result[key] = isSensitiveKey(key) ? REDACTED : sanitizeValue(entry, depth + 1, seen);
64
+ }
65
+ seen.delete(value);
66
+ return result;
67
+ }
68
+ return String(value);
69
+ }
70
+ export function sanitizeHarnessControlEventData(data) {
71
+ const sanitized = sanitizeValue(data ?? {}, 0, new WeakSet());
72
+ return typeof sanitized === "object" && sanitized !== null && !Array.isArray(sanitized)
73
+ ? sanitized
74
+ : { value: sanitized };
75
+ }
76
+ export function canonicalJson(value) {
77
+ if (value === null)
78
+ return "null";
79
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
80
+ return JSON.stringify(value);
81
+ }
82
+ if (Array.isArray(value)) {
83
+ return `[${value.map((entry) => canonicalJson(entry)).join(",")}]`;
84
+ }
85
+ return `{${Object.keys(value)
86
+ .sort()
87
+ .map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key] ?? null)}`)
88
+ .join(",")}}`;
89
+ }
90
+ export function hashCanonical(value) {
91
+ return createHash("sha256")
92
+ .update(canonicalJson(sanitizeValue(value, 0, new WeakSet())))
93
+ .digest("hex");
94
+ }
95
+ function sha256Text(value) {
96
+ return createHash("sha256").update(value).digest("hex");
97
+ }
98
+ function sha256File(path) {
99
+ return createHash("sha256").update(readFileSync(path)).digest("hex");
100
+ }
101
+ function isPathWithin(parent, child) {
102
+ const resolvedParent = resolve(parent);
103
+ const resolvedChild = resolve(child);
104
+ return resolvedChild === resolvedParent || resolvedChild.startsWith(`${resolvedParent}${sep}`);
105
+ }
106
+ function isSensitivePath(path) {
107
+ return /(?:^|[/\\])(?:\.env[^/\\]*|auth\.json|oauth\.json|.*(?:secret|token|credential|private[_-]?key).*)$/i.test(path);
108
+ }
109
+ function defaultAllowedArtifactRoots(cwd, logPath) {
110
+ return [cwd, dirname(logPath)];
111
+ }
112
+ function createArtifactManifest(artifactRefs, cwd, logPath, allowedArtifactRoots) {
113
+ if (!artifactRefs || artifactRefs.length === 0)
114
+ return [];
115
+ const roots = (allowedArtifactRoots ?? defaultAllowedArtifactRoots(cwd, logPath)).map((root) => resolve(root));
116
+ return artifactRefs.map((artifactPath) => {
117
+ const resolvedPath = resolve(cwd, artifactPath);
118
+ const allowed = roots.some((root) => isPathWithin(root, resolvedPath)) && !isSensitivePath(resolvedPath);
119
+ if (!allowed)
120
+ return { path: artifactPath, exists: existsSync(resolvedPath), allowed: false };
121
+ if (!existsSync(resolvedPath))
122
+ return { path: artifactPath, exists: false, allowed: true };
123
+ try {
124
+ const stats = statSync(resolvedPath);
125
+ if (!stats.isFile())
126
+ return { path: artifactPath, exists: true, allowed: true, sizeBytes: stats.size };
127
+ return {
128
+ path: artifactPath,
129
+ exists: true,
130
+ allowed: true,
131
+ sizeBytes: stats.size,
132
+ sha256: sha256File(resolvedPath),
133
+ };
134
+ }
135
+ catch (error) {
136
+ return {
137
+ path: artifactPath,
138
+ exists: existsSync(resolvedPath),
139
+ allowed: true,
140
+ error: error instanceof Error ? error.message : String(error),
141
+ };
142
+ }
143
+ });
144
+ }
145
+ export function resolveHarnessControlEventLogPath(options = {}) {
146
+ if (process.env.OMK_HARNESS_CONTROL_EVENTS === "0")
147
+ return undefined;
148
+ if (options.logPath)
149
+ return options.logPath;
150
+ if (process.env.OMK_HARNESS_CONTROL_EVENT_LOG)
151
+ return process.env.OMK_HARNESS_CONTROL_EVENT_LOG;
152
+ const runId = options.runId ?? DEFAULT_RUN_ID;
153
+ return join(options.cwd ?? process.cwd(), ".omk", "runs", runId, "harness-control", "events.jsonl");
154
+ }
155
+ function eventHashInput(eventWithoutHash) {
156
+ return `${canonicalJson(eventWithoutHash)}${eventWithoutHash.previousEventHash}`;
157
+ }
158
+ function computeEventHash(eventWithoutHash) {
159
+ return sha256Text(eventHashInput(eventWithoutHash));
160
+ }
161
+ function readPreviousEventState(logPath) {
162
+ if (!existsSync(logPath))
163
+ return { sequence: 0, eventHash: GENESIS_EVENT_HASH };
164
+ const lines = readFileSync(logPath, "utf-8")
165
+ .split("\n")
166
+ .map((line) => line.trim())
167
+ .filter((line) => line.length > 0);
168
+ if (lines.length === 0)
169
+ return { sequence: 0, eventHash: GENESIS_EVENT_HASH };
170
+ const parsed = JSON.parse(lines.at(-1));
171
+ if (parsed.schemaVersion !== "omk.harness-control.event.v2") {
172
+ return { sequence: 0, eventHash: GENESIS_EVENT_HASH };
173
+ }
174
+ if (typeof parsed.sequence !== "number" || typeof parsed.eventHash !== "string") {
175
+ throw new Error("Previous harness event is missing sequence or eventHash");
176
+ }
177
+ return { sequence: parsed.sequence, eventHash: parsed.eventHash };
178
+ }
179
+ function acquireLock(lockPath, timeoutMs) {
180
+ const start = Date.now();
181
+ while (true) {
182
+ try {
183
+ return openSync(lockPath, "wx", 0o600);
184
+ }
185
+ catch (error) {
186
+ if (existsSync(lockPath)) {
187
+ try {
188
+ const stats = statSync(lockPath);
189
+ if (Date.now() - stats.mtimeMs > LOCK_STALE_MS)
190
+ rmSync(lockPath, { force: true });
191
+ }
192
+ catch { }
193
+ }
194
+ if (Date.now() - start >= timeoutMs) {
195
+ throw error;
196
+ }
197
+ }
198
+ }
199
+ }
200
+ function formatRotationTimestamp(date) {
201
+ return date.toISOString().replace(/[:.]/g, "-");
202
+ }
203
+ function rotateLedgerIfNeeded(path, maxLedgerBytes, now) {
204
+ if (!Number.isFinite(maxLedgerBytes) || (maxLedgerBytes ?? 0) <= 0 || !existsSync(path))
205
+ return undefined;
206
+ const stats = statSync(path);
207
+ if (stats.size < Math.floor(maxLedgerBytes ?? 0))
208
+ return undefined;
209
+ const rotatedPath = `${path}.${formatRotationTimestamp(now)}.rotated`;
210
+ renameSync(path, rotatedPath);
211
+ return rotatedPath;
212
+ }
213
+ function appendJsonLineWithFsync(path, line) {
214
+ const fd = openSync(path, "a", 0o600);
215
+ try {
216
+ writeSync(fd, `${line}\n`, undefined, "utf-8");
217
+ fsyncSync(fd);
218
+ }
219
+ finally {
220
+ closeSync(fd);
221
+ }
222
+ try {
223
+ const dirFd = openSync(dirname(path), "r");
224
+ try {
225
+ fsyncSync(dirFd);
226
+ }
227
+ finally {
228
+ closeSync(dirFd);
229
+ }
230
+ }
231
+ catch {
232
+ // Some platforms/filesystems do not support directory fsync; file fsync above is still mandatory.
233
+ }
234
+ }
235
+ export function createHarnessControlEvent(kind, status, data = {}, options = {}, previous = { sequence: 0, eventHash: GENESIS_EVENT_HASH }) {
236
+ const eventId = options.eventId ?? options.id ?? randomUUID();
237
+ const operationId = options.operationId ?? eventId;
238
+ const logPath = resolveHarnessControlEventLogPath(options) ?? "";
239
+ const cwd = options.cwd ?? process.cwd();
240
+ const sanitizedData = sanitizeHarnessControlEventData(data);
241
+ const eventWithoutHash = {
242
+ schemaVersion: "omk.harness-control.event.v2",
243
+ eventId,
244
+ runId: options.runId ?? DEFAULT_RUN_ID,
245
+ sessionId: options.sessionId ?? DEFAULT_SESSION_ID,
246
+ operationId,
247
+ causationId: options.causationId ?? null,
248
+ correlationId: options.correlationId ?? operationId,
249
+ sequence: previous.sequence + 1,
250
+ timestamp: (options.now ?? new Date()).toISOString(),
251
+ kind,
252
+ status,
253
+ cwd,
254
+ beforeStateHash: hashCanonical(options.beforeState ?? null),
255
+ afterStateHash: hashCanonical(options.afterState ?? null),
256
+ data: sanitizedData,
257
+ dataHash: hashCanonical(sanitizedData),
258
+ artifacts: createArtifactManifest(options.artifactRefs, cwd, logPath, options.allowedArtifactRoots),
259
+ previousEventHash: previous.eventHash,
260
+ };
261
+ return { ...eventWithoutHash, eventHash: computeEventHash(eventWithoutHash) };
262
+ }
263
+ export function recordHarnessControlEvent(kind, status, data = {}, options = {}) {
264
+ const logPath = resolveHarnessControlEventLogPath(options);
265
+ if (!logPath)
266
+ return { ok: false, error: "harness control event logging disabled" };
267
+ const lockPath = `${logPath}.lock`;
268
+ let lockFd;
269
+ try {
270
+ mkdirSync(dirname(logPath), { recursive: true });
271
+ lockFd = acquireLock(lockPath, options.lockTimeoutMs ?? 1000);
272
+ rotateLedgerIfNeeded(logPath, options.maxLedgerBytes, options.now ?? new Date());
273
+ const previous = readPreviousEventState(logPath);
274
+ const event = createHarnessControlEvent(kind, status, data, options, previous);
275
+ appendJsonLineWithFsync(logPath, JSON.stringify(event));
276
+ return { ok: true, path: logPath, event };
277
+ }
278
+ catch (error) {
279
+ return { ok: false, path: logPath, error: error instanceof Error ? error.message : String(error) };
280
+ }
281
+ finally {
282
+ if (lockFd !== undefined) {
283
+ try {
284
+ closeSync(lockFd);
285
+ }
286
+ catch { }
287
+ rmSync(lockPath, { force: true });
288
+ }
289
+ }
290
+ }
291
+ function validateEventHash(event) {
292
+ const { eventHash: _eventHash, ...eventWithoutHash } = event;
293
+ const expectedEventHash = computeEventHash(eventWithoutHash);
294
+ if (expectedEventHash !== event.eventHash) {
295
+ return `event ${event.eventId} hash mismatch`;
296
+ }
297
+ if (hashCanonical(event.data) !== event.dataHash) {
298
+ return `event ${event.eventId} dataHash mismatch`;
299
+ }
300
+ return undefined;
301
+ }
302
+ export function verifyHarnessControlLedger(logPath) {
303
+ const errors = [];
304
+ const events = [];
305
+ if (!existsSync(logPath))
306
+ return { ok: true, events, errors };
307
+ const lines = readFileSync(logPath, "utf-8")
308
+ .split("\n")
309
+ .map((line) => line.trim())
310
+ .filter((line) => line.length > 0);
311
+ let previousSequence = 0;
312
+ let previousEventHash = GENESIS_EVENT_HASH;
313
+ for (let index = 0; index < lines.length; index++) {
314
+ try {
315
+ const event = JSON.parse(lines[index]);
316
+ events.push(event);
317
+ if (event.schemaVersion !== "omk.harness-control.event.v2") {
318
+ errors.push(`line ${index + 1}: unsupported schemaVersion`);
319
+ continue;
320
+ }
321
+ if (event.sequence !== previousSequence + 1) {
322
+ errors.push(`line ${index + 1}: sequence gap`);
323
+ }
324
+ if (event.previousEventHash !== previousEventHash) {
325
+ errors.push(`line ${index + 1}: previousEventHash mismatch`);
326
+ }
327
+ const hashError = validateEventHash(event);
328
+ if (hashError)
329
+ errors.push(`line ${index + 1}: ${hashError}`);
330
+ previousSequence = event.sequence;
331
+ previousEventHash = event.eventHash;
332
+ }
333
+ catch (error) {
334
+ errors.push(`line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`);
335
+ }
336
+ }
337
+ return { ok: errors.length === 0, events, errors };
338
+ }
339
+ //# sourceMappingURL=harness-control-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness-control-events.js","sourceRoot":"","sources":["../../src/core/harness-control-events.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport {\n\tcloseSync,\n\texistsSync,\n\tfsyncSync,\n\tmkdirSync,\n\topenSync,\n\treadFileSync,\n\trenameSync,\n\trmSync,\n\tstatSync,\n\twriteSync,\n} from \"node:fs\";\nimport { dirname, join, resolve, sep } from \"node:path\";\n\nexport type HarnessControlEventStatus =\n\t| \"prepared\"\n\t| \"started\"\n\t| \"applying\"\n\t| \"verifying\"\n\t| \"completed\"\n\t| \"failed\"\n\t| \"blocked\"\n\t| \"rolled_back\"\n\t| \"in_doubt\";\n\nexport type HarnessControlEventKind =\n\t| \"compaction.summary.generated\"\n\t| \"extension.migration.plan\"\n\t| \"extension.migration.apply\"\n\t| \"interactive.model_thinking.commit\"\n\t| \"interactive.theme.apply\"\n\t| \"keybinding.conflict\"\n\t| \"spec.compile\"\n\t| \"spec.verify\"\n\t| (string & {});\n\nexport type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };\nexport type JsonObject = { [key: string]: JsonValue };\n\nexport interface HarnessControlArtifactManifestEntry {\n\tpath: string;\n\texists: boolean;\n\tallowed: boolean;\n\tsizeBytes?: number;\n\tsha256?: string;\n\terror?: string;\n}\n\nexport interface HarnessControlEvent {\n\tschemaVersion: \"omk.harness-control.event.v2\";\n\teventId: string;\n\trunId: string;\n\tsessionId: string;\n\toperationId: string;\n\tcausationId: string | null;\n\tcorrelationId: string;\n\tsequence: number;\n\ttimestamp: string;\n\tkind: HarnessControlEventKind;\n\tstatus: HarnessControlEventStatus;\n\tcwd: string;\n\tbeforeStateHash: string;\n\tafterStateHash: string;\n\tdata: JsonObject;\n\tdataHash: string;\n\tartifacts: HarnessControlArtifactManifestEntry[];\n\tpreviousEventHash: string;\n\teventHash: string;\n}\n\nexport interface HarnessControlEventWriteResult {\n\tok: boolean;\n\tpath?: string;\n\tevent?: HarnessControlEvent;\n\terror?: string;\n}\n\nexport interface HarnessControlEventOptions {\n\tcwd?: string;\n\tlogPath?: string;\n\tnow?: Date;\n\tid?: string;\n\teventId?: string;\n\trunId?: string;\n\tsessionId?: string;\n\toperationId?: string;\n\tcausationId?: string | null;\n\tcorrelationId?: string;\n\tbeforeState?: unknown;\n\tafterState?: unknown;\n\tartifactRefs?: string[];\n\tallowedArtifactRoots?: string[];\n\tlockTimeoutMs?: number;\n\tmaxLedgerBytes?: number;\n}\n\nexport interface HarnessControlLedgerVerificationResult {\n\tok: boolean;\n\tevents: HarnessControlEvent[];\n\terrors: string[];\n}\n\ninterface PreviousEventState {\n\tsequence: number;\n\teventHash: string;\n}\n\nconst MAX_EVENT_DEPTH = 6;\nconst MAX_EVENT_OBJECT_KEYS = 80;\nconst MAX_EVENT_ARRAY_ITEMS = 80;\nconst MAX_EVENT_STRING_LENGTH = 2000;\nconst REDACTED = \"[redacted]\";\nconst GENESIS_EVENT_HASH = \"0\".repeat(64);\nconst DEFAULT_RUN_ID = \"default-run\";\nconst DEFAULT_SESSION_ID = \"sessionless\";\nconst LOCK_STALE_MS = 30000;\n\nfunction isSensitiveKey(key: string): boolean {\n\treturn /(?:secret|token|api[_-]?key|authorization|password|credential|private[_-]?key)/i.test(key);\n}\n\nfunction redactSensitiveValue(value: string): string {\n\tlet redacted = value;\n\tredacted = redacted.replace(/Bearer\\s+[A-Za-z0-9._~+/=-]+/gi, `Bearer ${REDACTED}`);\n\tredacted = redacted.replace(/sk-(?:proj-|ant-)?[A-Za-z0-9_-]{16,}/g, REDACTED);\n\tredacted = redacted.replace(/\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/g, REDACTED);\n\tredacted = redacted.replace(/-----BEGIN [^-]+PRIVATE KEY-----[\\s\\S]*?-----END [^-]+PRIVATE KEY-----/g, REDACTED);\n\tredacted = redacted.replace(/\\b[A-Fa-f0-9]{64,}\\b/g, REDACTED);\n\treturn redacted;\n}\n\nfunction truncateString(value: string): string {\n\tconst redacted = redactSensitiveValue(value);\n\tif (redacted.length <= MAX_EVENT_STRING_LENGTH) return redacted;\n\treturn `${redacted.slice(0, MAX_EVENT_STRING_LENGTH)}…[truncated]`;\n}\n\nfunction sanitizeValue(value: unknown, depth: number, seen: WeakSet<object>): JsonValue {\n\tif (value === null) return null;\n\tif (typeof value === \"string\") return truncateString(value);\n\tif (typeof value === \"number\") return Number.isFinite(value) ? value : String(value);\n\tif (typeof value === \"boolean\") return value;\n\tif (typeof value === \"bigint\") return value.toString();\n\tif (typeof value === \"undefined\" || typeof value === \"function\" || typeof value === \"symbol\") return String(value);\n\tif (depth >= MAX_EVENT_DEPTH) return \"[max-depth]\";\n\n\tif (Array.isArray(value)) {\n\t\treturn value.slice(0, MAX_EVENT_ARRAY_ITEMS).map((entry) => sanitizeValue(entry, depth + 1, seen));\n\t}\n\n\tif (value instanceof Date) return value.toISOString();\n\tif (value instanceof Error) {\n\t\treturn {\n\t\t\tname: value.name,\n\t\t\tmessage: truncateString(value.message),\n\t\t};\n\t}\n\n\tif (typeof value === \"object\") {\n\t\tif (seen.has(value)) return \"[circular]\";\n\t\tseen.add(value);\n\t\tconst result: JsonObject = {};\n\t\tfor (const [key, entry] of Object.entries(value).slice(0, MAX_EVENT_OBJECT_KEYS)) {\n\t\t\tresult[key] = isSensitiveKey(key) ? REDACTED : sanitizeValue(entry, depth + 1, seen);\n\t\t}\n\t\tseen.delete(value);\n\t\treturn result;\n\t}\n\n\treturn String(value);\n}\n\nexport function sanitizeHarnessControlEventData(data: unknown): JsonObject {\n\tconst sanitized = sanitizeValue(data ?? {}, 0, new WeakSet<object>());\n\treturn typeof sanitized === \"object\" && sanitized !== null && !Array.isArray(sanitized)\n\t\t? sanitized\n\t\t: { value: sanitized };\n}\n\nexport function canonicalJson(value: JsonValue): string {\n\tif (value === null) return \"null\";\n\tif (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n\t\treturn JSON.stringify(value);\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn `[${value.map((entry) => canonicalJson(entry)).join(\",\")}]`;\n\t}\n\treturn `{${Object.keys(value)\n\t\t.sort()\n\t\t.map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key] ?? null)}`)\n\t\t.join(\",\")}}`;\n}\n\nexport function hashCanonical(value: unknown): string {\n\treturn createHash(\"sha256\")\n\t\t.update(canonicalJson(sanitizeValue(value, 0, new WeakSet<object>())))\n\t\t.digest(\"hex\");\n}\n\nfunction sha256Text(value: string): string {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nfunction sha256File(path: string): string {\n\treturn createHash(\"sha256\").update(readFileSync(path)).digest(\"hex\");\n}\n\nfunction isPathWithin(parent: string, child: string): boolean {\n\tconst resolvedParent = resolve(parent);\n\tconst resolvedChild = resolve(child);\n\treturn resolvedChild === resolvedParent || resolvedChild.startsWith(`${resolvedParent}${sep}`);\n}\n\nfunction isSensitivePath(path: string): boolean {\n\treturn /(?:^|[/\\\\])(?:\\.env[^/\\\\]*|auth\\.json|oauth\\.json|.*(?:secret|token|credential|private[_-]?key).*)$/i.test(\n\t\tpath,\n\t);\n}\n\nfunction defaultAllowedArtifactRoots(cwd: string, logPath: string): string[] {\n\treturn [cwd, dirname(logPath)];\n}\n\nfunction createArtifactManifest(\n\tartifactRefs: string[] | undefined,\n\tcwd: string,\n\tlogPath: string,\n\tallowedArtifactRoots: string[] | undefined,\n): HarnessControlArtifactManifestEntry[] {\n\tif (!artifactRefs || artifactRefs.length === 0) return [];\n\tconst roots = (allowedArtifactRoots ?? defaultAllowedArtifactRoots(cwd, logPath)).map((root) => resolve(root));\n\treturn artifactRefs.map((artifactPath) => {\n\t\tconst resolvedPath = resolve(cwd, artifactPath);\n\t\tconst allowed = roots.some((root) => isPathWithin(root, resolvedPath)) && !isSensitivePath(resolvedPath);\n\t\tif (!allowed) return { path: artifactPath, exists: existsSync(resolvedPath), allowed: false };\n\t\tif (!existsSync(resolvedPath)) return { path: artifactPath, exists: false, allowed: true };\n\t\ttry {\n\t\t\tconst stats = statSync(resolvedPath);\n\t\t\tif (!stats.isFile()) return { path: artifactPath, exists: true, allowed: true, sizeBytes: stats.size };\n\t\t\treturn {\n\t\t\t\tpath: artifactPath,\n\t\t\t\texists: true,\n\t\t\t\tallowed: true,\n\t\t\t\tsizeBytes: stats.size,\n\t\t\t\tsha256: sha256File(resolvedPath),\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tpath: artifactPath,\n\t\t\t\texists: existsSync(resolvedPath),\n\t\t\t\tallowed: true,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t}\n\t});\n}\n\nexport function resolveHarnessControlEventLogPath(options: HarnessControlEventOptions = {}): string | undefined {\n\tif (process.env.OMK_HARNESS_CONTROL_EVENTS === \"0\") return undefined;\n\tif (options.logPath) return options.logPath;\n\tif (process.env.OMK_HARNESS_CONTROL_EVENT_LOG) return process.env.OMK_HARNESS_CONTROL_EVENT_LOG;\n\tconst runId = options.runId ?? DEFAULT_RUN_ID;\n\treturn join(options.cwd ?? process.cwd(), \".omk\", \"runs\", runId, \"harness-control\", \"events.jsonl\");\n}\n\nfunction eventHashInput(eventWithoutHash: Omit<HarnessControlEvent, \"eventHash\">): string {\n\treturn `${canonicalJson(eventWithoutHash as unknown as JsonObject)}${eventWithoutHash.previousEventHash}`;\n}\n\nfunction computeEventHash(eventWithoutHash: Omit<HarnessControlEvent, \"eventHash\">): string {\n\treturn sha256Text(eventHashInput(eventWithoutHash));\n}\n\nfunction readPreviousEventState(logPath: string): PreviousEventState {\n\tif (!existsSync(logPath)) return { sequence: 0, eventHash: GENESIS_EVENT_HASH };\n\tconst lines = readFileSync(logPath, \"utf-8\")\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.filter((line) => line.length > 0);\n\tif (lines.length === 0) return { sequence: 0, eventHash: GENESIS_EVENT_HASH };\n\tconst parsed = JSON.parse(lines.at(-1)!) as Partial<HarnessControlEvent>;\n\tif (parsed.schemaVersion !== \"omk.harness-control.event.v2\") {\n\t\treturn { sequence: 0, eventHash: GENESIS_EVENT_HASH };\n\t}\n\tif (typeof parsed.sequence !== \"number\" || typeof parsed.eventHash !== \"string\") {\n\t\tthrow new Error(\"Previous harness event is missing sequence or eventHash\");\n\t}\n\treturn { sequence: parsed.sequence, eventHash: parsed.eventHash };\n}\n\nfunction acquireLock(lockPath: string, timeoutMs: number): number {\n\tconst start = Date.now();\n\twhile (true) {\n\t\ttry {\n\t\t\treturn openSync(lockPath, \"wx\", 0o600);\n\t\t} catch (error) {\n\t\t\tif (existsSync(lockPath)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(lockPath);\n\t\t\t\t\tif (Date.now() - stats.mtimeMs > LOCK_STALE_MS) rmSync(lockPath, { force: true });\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t\tif (Date.now() - start >= timeoutMs) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction formatRotationTimestamp(date: Date): string {\n\treturn date.toISOString().replace(/[:.]/g, \"-\");\n}\n\nfunction rotateLedgerIfNeeded(path: string, maxLedgerBytes: number | undefined, now: Date): string | undefined {\n\tif (!Number.isFinite(maxLedgerBytes) || (maxLedgerBytes ?? 0) <= 0 || !existsSync(path)) return undefined;\n\tconst stats = statSync(path);\n\tif (stats.size < Math.floor(maxLedgerBytes ?? 0)) return undefined;\n\tconst rotatedPath = `${path}.${formatRotationTimestamp(now)}.rotated`;\n\trenameSync(path, rotatedPath);\n\treturn rotatedPath;\n}\n\nfunction appendJsonLineWithFsync(path: string, line: string): void {\n\tconst fd = openSync(path, \"a\", 0o600);\n\ttry {\n\t\twriteSync(fd, `${line}\\n`, undefined, \"utf-8\");\n\t\tfsyncSync(fd);\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\ttry {\n\t\tconst dirFd = openSync(dirname(path), \"r\");\n\t\ttry {\n\t\t\tfsyncSync(dirFd);\n\t\t} finally {\n\t\t\tcloseSync(dirFd);\n\t\t}\n\t} catch {\n\t\t// Some platforms/filesystems do not support directory fsync; file fsync above is still mandatory.\n\t}\n}\n\nexport function createHarnessControlEvent(\n\tkind: HarnessControlEventKind,\n\tstatus: HarnessControlEventStatus,\n\tdata: unknown = {},\n\toptions: HarnessControlEventOptions = {},\n\tprevious: PreviousEventState = { sequence: 0, eventHash: GENESIS_EVENT_HASH },\n): HarnessControlEvent {\n\tconst eventId = options.eventId ?? options.id ?? randomUUID();\n\tconst operationId = options.operationId ?? eventId;\n\tconst logPath = resolveHarnessControlEventLogPath(options) ?? \"\";\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst sanitizedData = sanitizeHarnessControlEventData(data);\n\tconst eventWithoutHash: Omit<HarnessControlEvent, \"eventHash\"> = {\n\t\tschemaVersion: \"omk.harness-control.event.v2\",\n\t\teventId,\n\t\trunId: options.runId ?? DEFAULT_RUN_ID,\n\t\tsessionId: options.sessionId ?? DEFAULT_SESSION_ID,\n\t\toperationId,\n\t\tcausationId: options.causationId ?? null,\n\t\tcorrelationId: options.correlationId ?? operationId,\n\t\tsequence: previous.sequence + 1,\n\t\ttimestamp: (options.now ?? new Date()).toISOString(),\n\t\tkind,\n\t\tstatus,\n\t\tcwd,\n\t\tbeforeStateHash: hashCanonical(options.beforeState ?? null),\n\t\tafterStateHash: hashCanonical(options.afterState ?? null),\n\t\tdata: sanitizedData,\n\t\tdataHash: hashCanonical(sanitizedData),\n\t\tartifacts: createArtifactManifest(options.artifactRefs, cwd, logPath, options.allowedArtifactRoots),\n\t\tpreviousEventHash: previous.eventHash,\n\t};\n\treturn { ...eventWithoutHash, eventHash: computeEventHash(eventWithoutHash) };\n}\n\nexport function recordHarnessControlEvent(\n\tkind: HarnessControlEventKind,\n\tstatus: HarnessControlEventStatus,\n\tdata: unknown = {},\n\toptions: HarnessControlEventOptions = {},\n): HarnessControlEventWriteResult {\n\tconst logPath = resolveHarnessControlEventLogPath(options);\n\tif (!logPath) return { ok: false, error: \"harness control event logging disabled\" };\n\tconst lockPath = `${logPath}.lock`;\n\tlet lockFd: number | undefined;\n\ttry {\n\t\tmkdirSync(dirname(logPath), { recursive: true });\n\t\tlockFd = acquireLock(lockPath, options.lockTimeoutMs ?? 1000);\n\t\trotateLedgerIfNeeded(logPath, options.maxLedgerBytes, options.now ?? new Date());\n\t\tconst previous = readPreviousEventState(logPath);\n\t\tconst event = createHarnessControlEvent(kind, status, data, options, previous);\n\t\tappendJsonLineWithFsync(logPath, JSON.stringify(event));\n\t\treturn { ok: true, path: logPath, event };\n\t} catch (error) {\n\t\treturn { ok: false, path: logPath, error: error instanceof Error ? error.message : String(error) };\n\t} finally {\n\t\tif (lockFd !== undefined) {\n\t\t\ttry {\n\t\t\t\tcloseSync(lockFd);\n\t\t\t} catch {}\n\t\t\trmSync(lockPath, { force: true });\n\t\t}\n\t}\n}\n\nfunction validateEventHash(event: HarnessControlEvent): string | undefined {\n\tconst { eventHash: _eventHash, ...eventWithoutHash } = event;\n\tconst expectedEventHash = computeEventHash(eventWithoutHash);\n\tif (expectedEventHash !== event.eventHash) {\n\t\treturn `event ${event.eventId} hash mismatch`;\n\t}\n\tif (hashCanonical(event.data) !== event.dataHash) {\n\t\treturn `event ${event.eventId} dataHash mismatch`;\n\t}\n\treturn undefined;\n}\n\nexport function verifyHarnessControlLedger(logPath: string): HarnessControlLedgerVerificationResult {\n\tconst errors: string[] = [];\n\tconst events: HarnessControlEvent[] = [];\n\tif (!existsSync(logPath)) return { ok: true, events, errors };\n\tconst lines = readFileSync(logPath, \"utf-8\")\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.filter((line) => line.length > 0);\n\tlet previousSequence = 0;\n\tlet previousEventHash = GENESIS_EVENT_HASH;\n\tfor (let index = 0; index < lines.length; index++) {\n\t\ttry {\n\t\t\tconst event = JSON.parse(lines[index]!) as HarnessControlEvent;\n\t\t\tevents.push(event);\n\t\t\tif (event.schemaVersion !== \"omk.harness-control.event.v2\") {\n\t\t\t\terrors.push(`line ${index + 1}: unsupported schemaVersion`);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (event.sequence !== previousSequence + 1) {\n\t\t\t\terrors.push(`line ${index + 1}: sequence gap`);\n\t\t\t}\n\t\t\tif (event.previousEventHash !== previousEventHash) {\n\t\t\t\terrors.push(`line ${index + 1}: previousEventHash mismatch`);\n\t\t\t}\n\t\t\tconst hashError = validateEventHash(event);\n\t\t\tif (hashError) errors.push(`line ${index + 1}: ${hashError}`);\n\t\t\tpreviousSequence = event.sequence;\n\t\t\tpreviousEventHash = event.eventHash;\n\t\t} catch (error) {\n\t\t\terrors.push(`line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`);\n\t\t}\n\t}\n\treturn { ok: errors.length === 0, events, errors };\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import type { HarnessControlEvent, HarnessControlEventStatus } from "./harness-control-events.ts";
2
+ export interface HarnessControlOperationReplay {
3
+ operationId: string;
4
+ kind: string;
5
+ started: boolean;
6
+ terminalStatus?: HarnessControlEventStatus;
7
+ terminalEventIds: string[];
8
+ eventIds: string[];
9
+ sequenceRange: [number, number];
10
+ stateTransitions: Array<{
11
+ beforeStateHash: string;
12
+ afterStateHash: string;
13
+ }>;
14
+ finalStateHash?: string;
15
+ }
16
+ export interface HarnessControlReplayReport {
17
+ ok: boolean;
18
+ events: HarnessControlEvent[];
19
+ operations: HarnessControlOperationReplay[];
20
+ reconstructedStateHash: string;
21
+ errors: string[];
22
+ warnings: string[];
23
+ }
24
+ export declare function replayHarnessControlEvents(events: readonly HarnessControlEvent[]): HarnessControlReplayReport;
25
+ export declare function verifyHarnessControlReplay(logPath: string): HarnessControlReplayReport;
26
+ //# sourceMappingURL=harness-control-replay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness-control-replay.d.ts","sourceRoot":"","sources":["../../src/core/harness-control-replay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAGlG,MAAM,WAAW,6BAA6B;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,yBAAyB,CAAC;IAC3C,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,gBAAgB,EAAE,KAAK,CAAC;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,0BAA0B;IAC1C,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC9B,UAAU,EAAE,6BAA6B,EAAE,CAAC;IAC5C,sBAAsB,EAAE,MAAM,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AA2HD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,GAAG,0BAA0B,CAoC7G;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,0BAA0B,CAWtF","sourcesContent":["import type { HarnessControlEvent, HarnessControlEventStatus } from \"./harness-control-events.ts\";\nimport { hashCanonical, verifyHarnessControlLedger } from \"./harness-control-events.ts\";\n\nexport interface HarnessControlOperationReplay {\n\toperationId: string;\n\tkind: string;\n\tstarted: boolean;\n\tterminalStatus?: HarnessControlEventStatus;\n\tterminalEventIds: string[];\n\teventIds: string[];\n\tsequenceRange: [number, number];\n\tstateTransitions: Array<{ beforeStateHash: string; afterStateHash: string }>;\n\tfinalStateHash?: string;\n}\n\nexport interface HarnessControlReplayReport {\n\tok: boolean;\n\tevents: HarnessControlEvent[];\n\toperations: HarnessControlOperationReplay[];\n\treconstructedStateHash: string;\n\terrors: string[];\n\twarnings: string[];\n}\n\nconst TERMINAL_STATUSES = new Set<HarnessControlEventStatus>([\n\t\"completed\",\n\t\"failed\",\n\t\"blocked\",\n\t\"rolled_back\",\n\t\"in_doubt\",\n]);\n\nconst NON_TERMINAL_STATUS_ORDER = new Map<HarnessControlEventStatus, number>([\n\t[\"prepared\", 0],\n\t[\"started\", 1],\n\t[\"applying\", 2],\n\t[\"verifying\", 3],\n]);\n\nfunction isTerminalStatus(status: HarnessControlEventStatus): boolean {\n\treturn TERMINAL_STATUSES.has(status);\n}\n\nfunction createOperationReplay(operationId: string, events: HarnessControlEvent[]): HarnessControlOperationReplay {\n\tconst sortedEvents = [...events].sort((a, b) => a.sequence - b.sequence);\n\tconst terminalEvents = sortedEvents.filter((event) => isTerminalStatus(event.status));\n\tconst terminal = terminalEvents.at(-1);\n\treturn {\n\t\toperationId,\n\t\tkind: sortedEvents[0]?.kind ?? \"unknown\",\n\t\tstarted: sortedEvents.some((event) => event.status === \"started\"),\n\t\tterminalStatus: terminal?.status,\n\t\tterminalEventIds: terminalEvents.map((event) => event.eventId),\n\t\teventIds: sortedEvents.map((event) => event.eventId),\n\t\tsequenceRange: [sortedEvents[0]?.sequence ?? 0, sortedEvents.at(-1)?.sequence ?? 0],\n\t\tstateTransitions: sortedEvents.map((event) => ({\n\t\t\tbeforeStateHash: event.beforeStateHash,\n\t\t\tafterStateHash: event.afterStateHash,\n\t\t})),\n\t\tfinalStateHash: terminal?.afterStateHash ?? sortedEvents.at(-1)?.afterStateHash,\n\t};\n}\n\nfunction validateUniqueEventIds(events: readonly HarnessControlEvent[]): string[] {\n\tconst errors: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const event of events) {\n\t\tif (seen.has(event.eventId)) errors.push(`event ${event.eventId} is duplicated`);\n\t\tseen.add(event.eventId);\n\t}\n\treturn errors;\n}\n\nfunction validateCausation(events: readonly HarnessControlEvent[]): string[] {\n\tconst errors: string[] = [];\n\tconst byId = new Map(events.map((event) => [event.eventId, event]));\n\tfor (const event of events) {\n\t\tif (!event.causationId) continue;\n\t\tconst causationEvent = byId.get(event.causationId);\n\t\tif (\n\t\t\t!causationEvent ||\n\t\t\tcausationEvent.sequence >= event.sequence ||\n\t\t\tcausationEvent.correlationId !== event.correlationId\n\t\t) {\n\t\t\terrors.push(\n\t\t\t\t`event causation ${event.causationId} for operation ${event.operationId} does not reference an earlier event`,\n\t\t\t);\n\t\t}\n\t}\n\treturn errors;\n}\n\nfunction reconstructHarnessStateHash(operations: readonly HarnessControlOperationReplay[]): string {\n\treturn hashCanonical(\n\t\toperations.map((operation) => ({\n\t\t\toperationId: operation.operationId,\n\t\t\tkind: operation.kind,\n\t\t\tfinalStateHash: operation.finalStateHash,\n\t\t\tterminalStatus: operation.terminalStatus,\n\t\t})),\n\t);\n}\n\nfunction validateOperationStateMachine(\n\toperation: HarnessControlOperationReplay,\n\tevents: HarnessControlEvent[],\n): string[] {\n\tconst errors: string[] = [];\n\tconst sortedEvents = [...events].sort((a, b) => a.sequence - b.sequence);\n\tconst kinds = new Set(sortedEvents.map((event) => event.kind));\n\tconst correlations = new Set(sortedEvents.map((event) => event.correlationId));\n\tconst startedCount = sortedEvents.filter((event) => event.status === \"started\").length;\n\tlet lastNonTerminalOrder = -1;\n\tlet sawTerminal = false;\n\tlet sawEventAfterTerminal = false;\n\n\tif (kinds.size > 1) errors.push(`operation ${operation.operationId} mixes event kinds`);\n\tif (correlations.size > 1) errors.push(`operation ${operation.operationId} mixes correlation ids`);\n\tif (startedCount > 1) errors.push(`operation ${operation.operationId} has multiple started events`);\n\tif (operation.terminalEventIds.length > 1)\n\t\terrors.push(`operation ${operation.operationId} has multiple terminal events`);\n\n\tfor (const event of sortedEvents) {\n\t\tif (sawTerminal) {\n\t\t\tsawEventAfterTerminal = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isTerminalStatus(event.status)) {\n\t\t\tsawTerminal = true;\n\t\t\tcontinue;\n\t\t}\n\t\tconst order = NON_TERMINAL_STATUS_ORDER.get(event.status);\n\t\tif (order === undefined) {\n\t\t\terrors.push(`operation ${operation.operationId} has unsupported status ${event.status}`);\n\t\t\tcontinue;\n\t\t}\n\t\tif (order < lastNonTerminalOrder) {\n\t\t\terrors.push(`operation ${operation.operationId} has invalid status transition to ${event.status}`);\n\t\t}\n\t\tlastNonTerminalOrder = order;\n\t}\n\tif (sawEventAfterTerminal) errors.push(`operation ${operation.operationId} has event after terminal event`);\n\treturn errors;\n}\n\nexport function replayHarnessControlEvents(events: readonly HarnessControlEvent[]): HarnessControlReplayReport {\n\tconst errors: string[] = [];\n\tconst warnings: string[] = [];\n\tconst operationEvents = new Map<string, HarnessControlEvent[]>();\n\tfor (const event of events) {\n\t\tconst group = operationEvents.get(event.operationId) ?? [];\n\t\tgroup.push(event);\n\t\toperationEvents.set(event.operationId, group);\n\t}\n\n\tconst operations = [...operationEvents]\n\t\t.sort(([, a], [, b]) => (a[0]?.sequence ?? 0) - (b[0]?.sequence ?? 0))\n\t\t.map(([operationId, groupedEvents]) => createOperationReplay(operationId, groupedEvents));\n\n\terrors.push(...validateUniqueEventIds(events));\n\terrors.push(...validateCausation(events));\n\n\tfor (const operation of operations) {\n\t\tconst groupedEvents = operationEvents.get(operation.operationId) ?? [];\n\t\terrors.push(...validateOperationStateMachine(operation, groupedEvents));\n\t\tif (!operation.started) {\n\t\t\twarnings.push(`operation ${operation.operationId} has no started event`);\n\t\t}\n\t\tif (!operation.terminalStatus) {\n\t\t\terrors.push(`operation ${operation.operationId} has no terminal event`);\n\t\t}\n\t}\n\n\treturn {\n\t\tok: errors.length === 0,\n\t\tevents: [...events],\n\t\toperations,\n\t\treconstructedStateHash: reconstructHarnessStateHash(operations),\n\t\terrors,\n\t\twarnings,\n\t};\n}\n\nexport function verifyHarnessControlReplay(logPath: string): HarnessControlReplayReport {\n\tconst ledger = verifyHarnessControlLedger(logPath);\n\tconst replay = replayHarnessControlEvents(ledger.events);\n\treturn {\n\t\tok: ledger.ok && replay.ok,\n\t\tevents: ledger.events,\n\t\toperations: replay.operations,\n\t\treconstructedStateHash: replay.reconstructedStateHash,\n\t\terrors: [...ledger.errors, ...replay.errors],\n\t\twarnings: replay.warnings,\n\t};\n}\n"]}
@@ -0,0 +1,155 @@
1
+ import { hashCanonical, verifyHarnessControlLedger } from "./harness-control-events.js";
2
+ const TERMINAL_STATUSES = new Set([
3
+ "completed",
4
+ "failed",
5
+ "blocked",
6
+ "rolled_back",
7
+ "in_doubt",
8
+ ]);
9
+ const NON_TERMINAL_STATUS_ORDER = new Map([
10
+ ["prepared", 0],
11
+ ["started", 1],
12
+ ["applying", 2],
13
+ ["verifying", 3],
14
+ ]);
15
+ function isTerminalStatus(status) {
16
+ return TERMINAL_STATUSES.has(status);
17
+ }
18
+ function createOperationReplay(operationId, events) {
19
+ const sortedEvents = [...events].sort((a, b) => a.sequence - b.sequence);
20
+ const terminalEvents = sortedEvents.filter((event) => isTerminalStatus(event.status));
21
+ const terminal = terminalEvents.at(-1);
22
+ return {
23
+ operationId,
24
+ kind: sortedEvents[0]?.kind ?? "unknown",
25
+ started: sortedEvents.some((event) => event.status === "started"),
26
+ terminalStatus: terminal?.status,
27
+ terminalEventIds: terminalEvents.map((event) => event.eventId),
28
+ eventIds: sortedEvents.map((event) => event.eventId),
29
+ sequenceRange: [sortedEvents[0]?.sequence ?? 0, sortedEvents.at(-1)?.sequence ?? 0],
30
+ stateTransitions: sortedEvents.map((event) => ({
31
+ beforeStateHash: event.beforeStateHash,
32
+ afterStateHash: event.afterStateHash,
33
+ })),
34
+ finalStateHash: terminal?.afterStateHash ?? sortedEvents.at(-1)?.afterStateHash,
35
+ };
36
+ }
37
+ function validateUniqueEventIds(events) {
38
+ const errors = [];
39
+ const seen = new Set();
40
+ for (const event of events) {
41
+ if (seen.has(event.eventId))
42
+ errors.push(`event ${event.eventId} is duplicated`);
43
+ seen.add(event.eventId);
44
+ }
45
+ return errors;
46
+ }
47
+ function validateCausation(events) {
48
+ const errors = [];
49
+ const byId = new Map(events.map((event) => [event.eventId, event]));
50
+ for (const event of events) {
51
+ if (!event.causationId)
52
+ continue;
53
+ const causationEvent = byId.get(event.causationId);
54
+ if (!causationEvent ||
55
+ causationEvent.sequence >= event.sequence ||
56
+ causationEvent.correlationId !== event.correlationId) {
57
+ errors.push(`event causation ${event.causationId} for operation ${event.operationId} does not reference an earlier event`);
58
+ }
59
+ }
60
+ return errors;
61
+ }
62
+ function reconstructHarnessStateHash(operations) {
63
+ return hashCanonical(operations.map((operation) => ({
64
+ operationId: operation.operationId,
65
+ kind: operation.kind,
66
+ finalStateHash: operation.finalStateHash,
67
+ terminalStatus: operation.terminalStatus,
68
+ })));
69
+ }
70
+ function validateOperationStateMachine(operation, events) {
71
+ const errors = [];
72
+ const sortedEvents = [...events].sort((a, b) => a.sequence - b.sequence);
73
+ const kinds = new Set(sortedEvents.map((event) => event.kind));
74
+ const correlations = new Set(sortedEvents.map((event) => event.correlationId));
75
+ const startedCount = sortedEvents.filter((event) => event.status === "started").length;
76
+ let lastNonTerminalOrder = -1;
77
+ let sawTerminal = false;
78
+ let sawEventAfterTerminal = false;
79
+ if (kinds.size > 1)
80
+ errors.push(`operation ${operation.operationId} mixes event kinds`);
81
+ if (correlations.size > 1)
82
+ errors.push(`operation ${operation.operationId} mixes correlation ids`);
83
+ if (startedCount > 1)
84
+ errors.push(`operation ${operation.operationId} has multiple started events`);
85
+ if (operation.terminalEventIds.length > 1)
86
+ errors.push(`operation ${operation.operationId} has multiple terminal events`);
87
+ for (const event of sortedEvents) {
88
+ if (sawTerminal) {
89
+ sawEventAfterTerminal = true;
90
+ continue;
91
+ }
92
+ if (isTerminalStatus(event.status)) {
93
+ sawTerminal = true;
94
+ continue;
95
+ }
96
+ const order = NON_TERMINAL_STATUS_ORDER.get(event.status);
97
+ if (order === undefined) {
98
+ errors.push(`operation ${operation.operationId} has unsupported status ${event.status}`);
99
+ continue;
100
+ }
101
+ if (order < lastNonTerminalOrder) {
102
+ errors.push(`operation ${operation.operationId} has invalid status transition to ${event.status}`);
103
+ }
104
+ lastNonTerminalOrder = order;
105
+ }
106
+ if (sawEventAfterTerminal)
107
+ errors.push(`operation ${operation.operationId} has event after terminal event`);
108
+ return errors;
109
+ }
110
+ export function replayHarnessControlEvents(events) {
111
+ const errors = [];
112
+ const warnings = [];
113
+ const operationEvents = new Map();
114
+ for (const event of events) {
115
+ const group = operationEvents.get(event.operationId) ?? [];
116
+ group.push(event);
117
+ operationEvents.set(event.operationId, group);
118
+ }
119
+ const operations = [...operationEvents]
120
+ .sort(([, a], [, b]) => (a[0]?.sequence ?? 0) - (b[0]?.sequence ?? 0))
121
+ .map(([operationId, groupedEvents]) => createOperationReplay(operationId, groupedEvents));
122
+ errors.push(...validateUniqueEventIds(events));
123
+ errors.push(...validateCausation(events));
124
+ for (const operation of operations) {
125
+ const groupedEvents = operationEvents.get(operation.operationId) ?? [];
126
+ errors.push(...validateOperationStateMachine(operation, groupedEvents));
127
+ if (!operation.started) {
128
+ warnings.push(`operation ${operation.operationId} has no started event`);
129
+ }
130
+ if (!operation.terminalStatus) {
131
+ errors.push(`operation ${operation.operationId} has no terminal event`);
132
+ }
133
+ }
134
+ return {
135
+ ok: errors.length === 0,
136
+ events: [...events],
137
+ operations,
138
+ reconstructedStateHash: reconstructHarnessStateHash(operations),
139
+ errors,
140
+ warnings,
141
+ };
142
+ }
143
+ export function verifyHarnessControlReplay(logPath) {
144
+ const ledger = verifyHarnessControlLedger(logPath);
145
+ const replay = replayHarnessControlEvents(ledger.events);
146
+ return {
147
+ ok: ledger.ok && replay.ok,
148
+ events: ledger.events,
149
+ operations: replay.operations,
150
+ reconstructedStateHash: replay.reconstructedStateHash,
151
+ errors: [...ledger.errors, ...replay.errors],
152
+ warnings: replay.warnings,
153
+ };
154
+ }
155
+ //# sourceMappingURL=harness-control-replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness-control-replay.js","sourceRoot":"","sources":["../../src/core/harness-control-replay.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HarnessControlEvent, HarnessControlEventStatus } from \"./harness-control-events.ts\";\nimport { hashCanonical, verifyHarnessControlLedger } from \"./harness-control-events.ts\";\n\nexport interface HarnessControlOperationReplay {\n\toperationId: string;\n\tkind: string;\n\tstarted: boolean;\n\tterminalStatus?: HarnessControlEventStatus;\n\tterminalEventIds: string[];\n\teventIds: string[];\n\tsequenceRange: [number, number];\n\tstateTransitions: Array<{ beforeStateHash: string; afterStateHash: string }>;\n\tfinalStateHash?: string;\n}\n\nexport interface HarnessControlReplayReport {\n\tok: boolean;\n\tevents: HarnessControlEvent[];\n\toperations: HarnessControlOperationReplay[];\n\treconstructedStateHash: string;\n\terrors: string[];\n\twarnings: string[];\n}\n\nconst TERMINAL_STATUSES = new Set<HarnessControlEventStatus>([\n\t\"completed\",\n\t\"failed\",\n\t\"blocked\",\n\t\"rolled_back\",\n\t\"in_doubt\",\n]);\n\nconst NON_TERMINAL_STATUS_ORDER = new Map<HarnessControlEventStatus, number>([\n\t[\"prepared\", 0],\n\t[\"started\", 1],\n\t[\"applying\", 2],\n\t[\"verifying\", 3],\n]);\n\nfunction isTerminalStatus(status: HarnessControlEventStatus): boolean {\n\treturn TERMINAL_STATUSES.has(status);\n}\n\nfunction createOperationReplay(operationId: string, events: HarnessControlEvent[]): HarnessControlOperationReplay {\n\tconst sortedEvents = [...events].sort((a, b) => a.sequence - b.sequence);\n\tconst terminalEvents = sortedEvents.filter((event) => isTerminalStatus(event.status));\n\tconst terminal = terminalEvents.at(-1);\n\treturn {\n\t\toperationId,\n\t\tkind: sortedEvents[0]?.kind ?? \"unknown\",\n\t\tstarted: sortedEvents.some((event) => event.status === \"started\"),\n\t\tterminalStatus: terminal?.status,\n\t\tterminalEventIds: terminalEvents.map((event) => event.eventId),\n\t\teventIds: sortedEvents.map((event) => event.eventId),\n\t\tsequenceRange: [sortedEvents[0]?.sequence ?? 0, sortedEvents.at(-1)?.sequence ?? 0],\n\t\tstateTransitions: sortedEvents.map((event) => ({\n\t\t\tbeforeStateHash: event.beforeStateHash,\n\t\t\tafterStateHash: event.afterStateHash,\n\t\t})),\n\t\tfinalStateHash: terminal?.afterStateHash ?? sortedEvents.at(-1)?.afterStateHash,\n\t};\n}\n\nfunction validateUniqueEventIds(events: readonly HarnessControlEvent[]): string[] {\n\tconst errors: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const event of events) {\n\t\tif (seen.has(event.eventId)) errors.push(`event ${event.eventId} is duplicated`);\n\t\tseen.add(event.eventId);\n\t}\n\treturn errors;\n}\n\nfunction validateCausation(events: readonly HarnessControlEvent[]): string[] {\n\tconst errors: string[] = [];\n\tconst byId = new Map(events.map((event) => [event.eventId, event]));\n\tfor (const event of events) {\n\t\tif (!event.causationId) continue;\n\t\tconst causationEvent = byId.get(event.causationId);\n\t\tif (\n\t\t\t!causationEvent ||\n\t\t\tcausationEvent.sequence >= event.sequence ||\n\t\t\tcausationEvent.correlationId !== event.correlationId\n\t\t) {\n\t\t\terrors.push(\n\t\t\t\t`event causation ${event.causationId} for operation ${event.operationId} does not reference an earlier event`,\n\t\t\t);\n\t\t}\n\t}\n\treturn errors;\n}\n\nfunction reconstructHarnessStateHash(operations: readonly HarnessControlOperationReplay[]): string {\n\treturn hashCanonical(\n\t\toperations.map((operation) => ({\n\t\t\toperationId: operation.operationId,\n\t\t\tkind: operation.kind,\n\t\t\tfinalStateHash: operation.finalStateHash,\n\t\t\tterminalStatus: operation.terminalStatus,\n\t\t})),\n\t);\n}\n\nfunction validateOperationStateMachine(\n\toperation: HarnessControlOperationReplay,\n\tevents: HarnessControlEvent[],\n): string[] {\n\tconst errors: string[] = [];\n\tconst sortedEvents = [...events].sort((a, b) => a.sequence - b.sequence);\n\tconst kinds = new Set(sortedEvents.map((event) => event.kind));\n\tconst correlations = new Set(sortedEvents.map((event) => event.correlationId));\n\tconst startedCount = sortedEvents.filter((event) => event.status === \"started\").length;\n\tlet lastNonTerminalOrder = -1;\n\tlet sawTerminal = false;\n\tlet sawEventAfterTerminal = false;\n\n\tif (kinds.size > 1) errors.push(`operation ${operation.operationId} mixes event kinds`);\n\tif (correlations.size > 1) errors.push(`operation ${operation.operationId} mixes correlation ids`);\n\tif (startedCount > 1) errors.push(`operation ${operation.operationId} has multiple started events`);\n\tif (operation.terminalEventIds.length > 1)\n\t\terrors.push(`operation ${operation.operationId} has multiple terminal events`);\n\n\tfor (const event of sortedEvents) {\n\t\tif (sawTerminal) {\n\t\t\tsawEventAfterTerminal = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (isTerminalStatus(event.status)) {\n\t\t\tsawTerminal = true;\n\t\t\tcontinue;\n\t\t}\n\t\tconst order = NON_TERMINAL_STATUS_ORDER.get(event.status);\n\t\tif (order === undefined) {\n\t\t\terrors.push(`operation ${operation.operationId} has unsupported status ${event.status}`);\n\t\t\tcontinue;\n\t\t}\n\t\tif (order < lastNonTerminalOrder) {\n\t\t\terrors.push(`operation ${operation.operationId} has invalid status transition to ${event.status}`);\n\t\t}\n\t\tlastNonTerminalOrder = order;\n\t}\n\tif (sawEventAfterTerminal) errors.push(`operation ${operation.operationId} has event after terminal event`);\n\treturn errors;\n}\n\nexport function replayHarnessControlEvents(events: readonly HarnessControlEvent[]): HarnessControlReplayReport {\n\tconst errors: string[] = [];\n\tconst warnings: string[] = [];\n\tconst operationEvents = new Map<string, HarnessControlEvent[]>();\n\tfor (const event of events) {\n\t\tconst group = operationEvents.get(event.operationId) ?? [];\n\t\tgroup.push(event);\n\t\toperationEvents.set(event.operationId, group);\n\t}\n\n\tconst operations = [...operationEvents]\n\t\t.sort(([, a], [, b]) => (a[0]?.sequence ?? 0) - (b[0]?.sequence ?? 0))\n\t\t.map(([operationId, groupedEvents]) => createOperationReplay(operationId, groupedEvents));\n\n\terrors.push(...validateUniqueEventIds(events));\n\terrors.push(...validateCausation(events));\n\n\tfor (const operation of operations) {\n\t\tconst groupedEvents = operationEvents.get(operation.operationId) ?? [];\n\t\terrors.push(...validateOperationStateMachine(operation, groupedEvents));\n\t\tif (!operation.started) {\n\t\t\twarnings.push(`operation ${operation.operationId} has no started event`);\n\t\t}\n\t\tif (!operation.terminalStatus) {\n\t\t\terrors.push(`operation ${operation.operationId} has no terminal event`);\n\t\t}\n\t}\n\n\treturn {\n\t\tok: errors.length === 0,\n\t\tevents: [...events],\n\t\toperations,\n\t\treconstructedStateHash: reconstructHarnessStateHash(operations),\n\t\terrors,\n\t\twarnings,\n\t};\n}\n\nexport function verifyHarnessControlReplay(logPath: string): HarnessControlReplayReport {\n\tconst ledger = verifyHarnessControlLedger(logPath);\n\tconst replay = replayHarnessControlEvents(ledger.events);\n\treturn {\n\t\tok: ledger.ok && replay.ok,\n\t\tevents: ledger.events,\n\t\toperations: replay.operations,\n\t\treconstructedStateHash: replay.reconstructedStateHash,\n\t\terrors: [...ledger.errors, ...replay.errors],\n\t\twarnings: replay.warnings,\n\t};\n}\n"]}