@fusionkit/protocol 0.1.0

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 (45) hide show
  1. package/dist/api.d.ts +106 -0
  2. package/dist/api.js +6 -0
  3. package/dist/chain.d.ts +14 -0
  4. package/dist/chain.js +49 -0
  5. package/dist/constants.d.ts +25 -0
  6. package/dist/constants.js +36 -0
  7. package/dist/contract.d.ts +6 -0
  8. package/dist/contract.js +32 -0
  9. package/dist/execution.d.ts +67 -0
  10. package/dist/execution.js +27 -0
  11. package/dist/generated/model-fusion-openapi.d.ts +44 -0
  12. package/dist/generated/model-fusion-openapi.js +23 -0
  13. package/dist/hash.d.ts +10 -0
  14. package/dist/hash.js +31 -0
  15. package/dist/index.d.ts +42 -0
  16. package/dist/index.js +30 -0
  17. package/dist/jcs.d.ts +14 -0
  18. package/dist/jcs.js +49 -0
  19. package/dist/keys.d.ts +14 -0
  20. package/dist/keys.js +36 -0
  21. package/dist/model-fusion.d.ts +167 -0
  22. package/dist/model-fusion.js +596 -0
  23. package/dist/receipt-story.d.ts +27 -0
  24. package/dist/receipt-story.js +127 -0
  25. package/dist/receipt.d.ts +20 -0
  26. package/dist/receipt.js +162 -0
  27. package/dist/test/model-fusion.test.d.ts +1 -0
  28. package/dist/test/model-fusion.test.js +213 -0
  29. package/dist/test/protocol.test.d.ts +1 -0
  30. package/dist/test/protocol.test.js +240 -0
  31. package/dist/test/tool-executor.test.d.ts +1 -0
  32. package/dist/test/tool-executor.test.js +86 -0
  33. package/dist/test/trace.test.d.ts +1 -0
  34. package/dist/test/trace.test.js +75 -0
  35. package/dist/tool-executor.d.ts +58 -0
  36. package/dist/tool-executor.js +80 -0
  37. package/dist/trace.d.ts +119 -0
  38. package/dist/trace.js +248 -0
  39. package/dist/types.d.ts +375 -0
  40. package/dist/types.js +14 -0
  41. package/dist/validators.d.ts +7 -0
  42. package/dist/validators.js +32 -0
  43. package/dist/vocabulary.d.ts +12 -0
  44. package/dist/vocabulary.js +79 -0
  45. package/package.json +27 -0
@@ -0,0 +1,596 @@
1
+ export const MODEL_FUSION_SCHEMA_NAMES = [
2
+ "model-call-record.v1",
3
+ "harness-run-request.v1",
4
+ "harness-run-result.v1",
5
+ "harness-candidate-record.v1",
6
+ "judge-synthesis-record.v1",
7
+ "benchmark-task-record.v1",
8
+ "artifact-ref.v1",
9
+ "tool-call-plan.v1",
10
+ "tool-execution-record.v1",
11
+ "ensemble-receipt.v1"
12
+ ];
13
+ export const MODEL_FUSION_SCHEMA_BUNDLE_HASH = "sha256:75792f89c091b6ab4fd317a15fb03fd73438563dceff5ccf9f5d7c752dbf35f3";
14
+ const HASH_PATTERN = /^sha256:[a-f0-9]{64}$/;
15
+ const GIT_SHA_PATTERN = /^[a-f0-9]{40}$/;
16
+ const STATUSES = [
17
+ "pending",
18
+ "running",
19
+ "succeeded",
20
+ "failed",
21
+ "canceled",
22
+ "requires_action",
23
+ "skipped",
24
+ "unsupported"
25
+ ];
26
+ const SIDE_EFFECTS = [
27
+ "none",
28
+ "read_only",
29
+ "writes_workspace",
30
+ "network",
31
+ "tool_execution",
32
+ "unknown"
33
+ ];
34
+ const HARNESS_KINDS = [
35
+ "generic",
36
+ "cursor",
37
+ "claude_code",
38
+ "codex",
39
+ "openai_responses"
40
+ ];
41
+ const CAPABILITY_STATUSES = [
42
+ "supported",
43
+ "unsupported",
44
+ "degraded",
45
+ "unknown"
46
+ ];
47
+ const ARTIFACT_KINDS = [
48
+ "patch",
49
+ "log",
50
+ "transcript",
51
+ "metrics",
52
+ "benchmark_task",
53
+ "worktree",
54
+ "receipt",
55
+ "other"
56
+ ];
57
+ const REDACTION_STATUSES = [
58
+ "synthetic",
59
+ "redacted",
60
+ "raw"
61
+ ];
62
+ const ERROR_KINDS = [
63
+ "none",
64
+ "provider_error",
65
+ "validation_error",
66
+ "timeout",
67
+ "rate_limited",
68
+ "tool_denied",
69
+ "secret_denied",
70
+ "capability_missing",
71
+ "internal_error"
72
+ ];
73
+ const CHAT_ROLES = ["system", "user", "assistant", "tool"];
74
+ const BENCHMARK_TASK_KINDS = ["model_fusion", "harness_coding"];
75
+ const BENCHMARK_SOURCE_REPOS = ["fusionkit", "handoffkit", "cursorkit", "mlx-lm"];
76
+ const BENCHMARK_SCORER_KINDS = ["exact", "contains", "record_join", "custom"];
77
+ const JUDGE_DECISIONS = [
78
+ "synthesize",
79
+ "select_candidate",
80
+ "repair_required",
81
+ "failed"
82
+ ];
83
+ function hasString(values, value) {
84
+ return typeof value === "string" && values.includes(value);
85
+ }
86
+ function assertObject(value, context) {
87
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
88
+ throw new Error(`${context} must be an object`);
89
+ }
90
+ }
91
+ function assertAllowedKeys(value, allowedKeys, context) {
92
+ const allowed = new Set(allowedKeys);
93
+ const unexpected = Object.keys(value).filter((key) => !allowed.has(key));
94
+ if (unexpected.length > 0) {
95
+ throw new Error(`${context} contains unsupported field(s): ${unexpected.join(", ")}`);
96
+ }
97
+ }
98
+ function assertString(value, context) {
99
+ if (typeof value !== "string" || value.length === 0) {
100
+ throw new Error(`${context} must be a non-empty string`);
101
+ }
102
+ }
103
+ function assertOptionalString(value, context) {
104
+ if (value !== undefined)
105
+ assertString(value, context);
106
+ }
107
+ function assertNumber(value, context) {
108
+ if (typeof value !== "number" || !Number.isFinite(value)) {
109
+ throw new Error(`${context} must be a finite number`);
110
+ }
111
+ }
112
+ function assertOptionalNumber(value, context) {
113
+ if (value !== undefined)
114
+ assertNumber(value, context);
115
+ }
116
+ function assertBoolean(value, context) {
117
+ if (typeof value !== "boolean")
118
+ throw new Error(`${context} must be a boolean`);
119
+ }
120
+ function assertHash(value, context) {
121
+ if (typeof value !== "string" || !HASH_PATTERN.test(value)) {
122
+ throw new Error(`${context} must be a sha256:<64 hex> hash`);
123
+ }
124
+ }
125
+ function assertGitSha(value, context) {
126
+ if (typeof value !== "string" || !GIT_SHA_PATTERN.test(value)) {
127
+ throw new Error(`${context} must be a 40-character git SHA`);
128
+ }
129
+ }
130
+ function assertDateTime(value, context) {
131
+ assertString(value, context);
132
+ if (Number.isNaN(Date.parse(value))) {
133
+ throw new Error(`${context} must be an RFC 3339 date-time string`);
134
+ }
135
+ }
136
+ function assertStringArray(value, context, minItems = 0) {
137
+ if (!Array.isArray(value) || value.length < minItems) {
138
+ throw new Error(`${context} must be an array with at least ${minItems} item(s)`);
139
+ }
140
+ value.forEach((item, index) => assertString(item, `${context}[${index}]`));
141
+ }
142
+ function assertEnum(value, values, context) {
143
+ if (!hasString(values, value)) {
144
+ throw new Error(`${context} is not a supported value`);
145
+ }
146
+ }
147
+ function assertStatus(value, context) {
148
+ assertEnum(value, STATUSES, context);
149
+ }
150
+ function assertSideEffects(value, context) {
151
+ assertEnum(value, SIDE_EFFECTS, context);
152
+ }
153
+ function assertHarnessKind(value, context) {
154
+ assertEnum(value, HARNESS_KINDS, context);
155
+ }
156
+ function assertCapabilityMap(value, context) {
157
+ assertObject(value, context);
158
+ for (const [key, status] of Object.entries(value)) {
159
+ assertEnum(status, CAPABILITY_STATUSES, `${context}.${key}`);
160
+ }
161
+ }
162
+ function assertError(value, context) {
163
+ assertObject(value, context);
164
+ assertAllowedKeys(value, ["kind", "message", "retryable"], context);
165
+ assertEnum(value.kind, ERROR_KINDS, `${context}.kind`);
166
+ assertOptionalString(value.message, `${context}.message`);
167
+ if (value.retryable !== undefined && typeof value.retryable !== "boolean") {
168
+ throw new Error(`${context}.retryable must be a boolean`);
169
+ }
170
+ }
171
+ function assertArtifact(value, context, includeMetadata = false) {
172
+ assertObject(value, context);
173
+ assertAllowedKeys(value, [
174
+ ...(includeMetadata
175
+ ? [
176
+ "schema",
177
+ "schema_version",
178
+ "schema_bundle_hash",
179
+ "producer",
180
+ "producer_version",
181
+ "producer_git_sha",
182
+ "created_at"
183
+ ]
184
+ : []),
185
+ "artifact_id",
186
+ "kind",
187
+ "uri",
188
+ "hash",
189
+ "redaction_status"
190
+ ], context);
191
+ assertString(value.artifact_id, `${context}.artifact_id`);
192
+ assertEnum(value.kind, ARTIFACT_KINDS, `${context}.kind`);
193
+ assertOptionalString(value.uri, `${context}.uri`);
194
+ assertHash(value.hash, `${context}.hash`);
195
+ if (value.redaction_status !== undefined) {
196
+ assertEnum(value.redaction_status, REDACTION_STATUSES, `${context}.redaction_status`);
197
+ }
198
+ }
199
+ function assertArtifacts(value, context) {
200
+ if (!Array.isArray(value))
201
+ throw new Error(`${context} must be an array`);
202
+ value.forEach((item, index) => assertArtifact(item, `${context}[${index}]`));
203
+ }
204
+ function assertMetadata(value, schema) {
205
+ if (value.schema !== schema)
206
+ throw new Error(`schema must be ${schema}`);
207
+ if (value.schema_version !== "v1")
208
+ throw new Error("schema_version must be v1");
209
+ assertHash(value.schema_bundle_hash, "schema_bundle_hash");
210
+ assertString(value.producer, "producer");
211
+ assertString(value.producer_version, "producer_version");
212
+ assertGitSha(value.producer_git_sha, "producer_git_sha");
213
+ assertDateTime(value.created_at, "created_at");
214
+ }
215
+ function assertChatMessages(value, context) {
216
+ if (!Array.isArray(value) || value.length === 0) {
217
+ throw new Error(`${context} must include at least one chat message`);
218
+ }
219
+ value.forEach((item, index) => {
220
+ assertObject(item, `${context}[${index}]`);
221
+ assertAllowedKeys(item, ["role", "content"], `${context}[${index}]`);
222
+ assertEnum(item.role, CHAT_ROLES, `${context}[${index}].role`);
223
+ assertString(item.content, `${context}[${index}].content`);
224
+ });
225
+ }
226
+ function assertUsage(value, context) {
227
+ assertObject(value, context);
228
+ assertAllowedKeys(value, ["prompt_tokens", "completion_tokens", "total_tokens"], context);
229
+ for (const key of ["prompt_tokens", "completion_tokens", "total_tokens"]) {
230
+ const candidate = value[key];
231
+ if (candidate !== undefined &&
232
+ (!Number.isInteger(candidate) || candidate < 0)) {
233
+ throw new Error(`${context}.${key} must be a non-negative integer`);
234
+ }
235
+ }
236
+ }
237
+ export function assertArtifactRefV1(value) {
238
+ assertObject(value, "artifact-ref.v1");
239
+ assertMetadata(value, "artifact-ref.v1");
240
+ assertArtifact(value, "artifact-ref.v1", true);
241
+ }
242
+ export function assertModelCallRecordV1(value) {
243
+ assertObject(value, "model-call-record.v1");
244
+ assertAllowedKeys(value, [
245
+ "schema",
246
+ "schema_version",
247
+ "schema_bundle_hash",
248
+ "producer",
249
+ "producer_version",
250
+ "producer_git_sha",
251
+ "created_at",
252
+ "call_id",
253
+ "endpoint_id",
254
+ "provider_request_id",
255
+ "model",
256
+ "request_hash",
257
+ "response_hash",
258
+ "messages",
259
+ "status",
260
+ "side_effects",
261
+ "started_at",
262
+ "finished_at",
263
+ "latency_ms",
264
+ "usage",
265
+ "output_text",
266
+ "error",
267
+ "metadata"
268
+ ], "model-call-record.v1");
269
+ assertMetadata(value, "model-call-record.v1");
270
+ assertString(value.call_id, "call_id");
271
+ assertString(value.endpoint_id, "endpoint_id");
272
+ assertOptionalString(value.provider_request_id, "provider_request_id");
273
+ assertString(value.model, "model");
274
+ assertHash(value.request_hash, "request_hash");
275
+ if (value.response_hash !== undefined)
276
+ assertHash(value.response_hash, "response_hash");
277
+ assertChatMessages(value.messages, "messages");
278
+ assertStatus(value.status, "status");
279
+ assertSideEffects(value.side_effects, "side_effects");
280
+ assertDateTime(value.started_at, "started_at");
281
+ if (value.finished_at !== undefined)
282
+ assertDateTime(value.finished_at, "finished_at");
283
+ assertOptionalNumber(value.latency_ms, "latency_ms");
284
+ if (value.usage !== undefined)
285
+ assertUsage(value.usage, "usage");
286
+ assertOptionalString(value.output_text, "output_text");
287
+ if (value.error !== undefined)
288
+ assertError(value.error, "error");
289
+ if (value.metadata !== undefined)
290
+ assertObject(value.metadata, "metadata");
291
+ }
292
+ export function assertHarnessRunRequestV1(value) {
293
+ assertObject(value, "harness-run-request.v1");
294
+ assertAllowedKeys(value, [
295
+ "schema",
296
+ "schema_version",
297
+ "schema_bundle_hash",
298
+ "producer",
299
+ "producer_version",
300
+ "producer_git_sha",
301
+ "created_at",
302
+ "request_id",
303
+ "harness_kind",
304
+ "source_repo",
305
+ "base_git_sha",
306
+ "prompt",
307
+ "prompt_hash",
308
+ "allowed_tools",
309
+ "side_effects",
310
+ "requested_capabilities",
311
+ "metadata"
312
+ ], "harness-run-request.v1");
313
+ assertMetadata(value, "harness-run-request.v1");
314
+ assertString(value.request_id, "request_id");
315
+ assertHarnessKind(value.harness_kind, "harness_kind");
316
+ assertString(value.source_repo, "source_repo");
317
+ assertGitSha(value.base_git_sha, "base_git_sha");
318
+ assertString(value.prompt, "prompt");
319
+ assertHash(value.prompt_hash, "prompt_hash");
320
+ if (value.allowed_tools !== undefined)
321
+ assertStringArray(value.allowed_tools, "allowed_tools");
322
+ assertSideEffects(value.side_effects, "side_effects");
323
+ assertCapabilityMap(value.requested_capabilities, "requested_capabilities");
324
+ if (value.metadata !== undefined)
325
+ assertObject(value.metadata, "metadata");
326
+ }
327
+ export function assertHarnessRunResultV1(value) {
328
+ assertObject(value, "harness-run-result.v1");
329
+ assertAllowedKeys(value, [
330
+ "schema",
331
+ "schema_version",
332
+ "schema_bundle_hash",
333
+ "producer",
334
+ "producer_version",
335
+ "producer_git_sha",
336
+ "created_at",
337
+ "result_id",
338
+ "request_id",
339
+ "harness_kind",
340
+ "status",
341
+ "candidate_ids",
342
+ "output_summary",
343
+ "artifacts",
344
+ "capabilities",
345
+ "started_at",
346
+ "finished_at",
347
+ "errors",
348
+ "metadata"
349
+ ], "harness-run-result.v1");
350
+ assertMetadata(value, "harness-run-result.v1");
351
+ assertString(value.result_id, "result_id");
352
+ assertString(value.request_id, "request_id");
353
+ assertHarnessKind(value.harness_kind, "harness_kind");
354
+ assertStatus(value.status, "status");
355
+ assertStringArray(value.candidate_ids, "candidate_ids");
356
+ assertOptionalString(value.output_summary, "output_summary");
357
+ if (value.artifacts !== undefined)
358
+ assertArtifacts(value.artifacts, "artifacts");
359
+ assertCapabilityMap(value.capabilities, "capabilities");
360
+ assertDateTime(value.started_at, "started_at");
361
+ if (value.finished_at !== undefined)
362
+ assertDateTime(value.finished_at, "finished_at");
363
+ if (value.errors !== undefined) {
364
+ if (!Array.isArray(value.errors))
365
+ throw new Error("errors must be an array");
366
+ value.errors.forEach((error, index) => assertError(error, `errors[${index}]`));
367
+ }
368
+ if (value.metadata !== undefined)
369
+ assertObject(value.metadata, "metadata");
370
+ }
371
+ export function assertHarnessCandidateRecordV1(value) {
372
+ assertObject(value, "harness-candidate-record.v1");
373
+ assertAllowedKeys(value, [
374
+ "schema",
375
+ "schema_version",
376
+ "schema_bundle_hash",
377
+ "producer",
378
+ "producer_version",
379
+ "producer_git_sha",
380
+ "created_at",
381
+ "candidate_id",
382
+ "request_id",
383
+ "harness_kind",
384
+ "model_call_id",
385
+ "status",
386
+ "side_effects",
387
+ "branch_name",
388
+ "worktree_path",
389
+ "artifacts",
390
+ "score",
391
+ "error",
392
+ "metadata"
393
+ ], "harness-candidate-record.v1");
394
+ assertMetadata(value, "harness-candidate-record.v1");
395
+ assertString(value.candidate_id, "candidate_id");
396
+ assertString(value.request_id, "request_id");
397
+ assertHarnessKind(value.harness_kind, "harness_kind");
398
+ assertOptionalString(value.model_call_id, "model_call_id");
399
+ assertStatus(value.status, "status");
400
+ assertSideEffects(value.side_effects, "side_effects");
401
+ assertOptionalString(value.branch_name, "branch_name");
402
+ assertOptionalString(value.worktree_path, "worktree_path");
403
+ if (value.artifacts !== undefined)
404
+ assertArtifacts(value.artifacts, "artifacts");
405
+ assertOptionalNumber(value.score, "score");
406
+ if (value.error !== undefined)
407
+ assertError(value.error, "error");
408
+ if (value.metadata !== undefined)
409
+ assertObject(value.metadata, "metadata");
410
+ }
411
+ export function assertJudgeSynthesisRecordV1(value) {
412
+ assertObject(value, "judge-synthesis-record.v1");
413
+ assertAllowedKeys(value, [
414
+ "schema",
415
+ "schema_version",
416
+ "schema_bundle_hash",
417
+ "producer",
418
+ "producer_version",
419
+ "producer_git_sha",
420
+ "created_at",
421
+ "synthesis_id",
422
+ "judge_model_call_id",
423
+ "input_candidate_ids",
424
+ "status",
425
+ "decision",
426
+ "selected_candidate_id",
427
+ "rationale",
428
+ "final_output",
429
+ "score",
430
+ "metrics"
431
+ ], "judge-synthesis-record.v1");
432
+ assertMetadata(value, "judge-synthesis-record.v1");
433
+ assertString(value.synthesis_id, "synthesis_id");
434
+ assertOptionalString(value.judge_model_call_id, "judge_model_call_id");
435
+ assertStringArray(value.input_candidate_ids, "input_candidate_ids", 1);
436
+ assertStatus(value.status, "status");
437
+ assertEnum(value.decision, JUDGE_DECISIONS, "decision");
438
+ assertOptionalString(value.selected_candidate_id, "selected_candidate_id");
439
+ assertOptionalString(value.rationale, "rationale");
440
+ assertString(value.final_output, "final_output");
441
+ assertOptionalNumber(value.score, "score");
442
+ if (value.metrics !== undefined)
443
+ assertObject(value.metrics, "metrics");
444
+ }
445
+ export function assertBenchmarkTaskRecordV1(value) {
446
+ assertObject(value, "benchmark-task-record.v1");
447
+ assertAllowedKeys(value, [
448
+ "schema",
449
+ "schema_version",
450
+ "schema_bundle_hash",
451
+ "producer",
452
+ "producer_version",
453
+ "producer_git_sha",
454
+ "created_at",
455
+ "task_id",
456
+ "task_kind",
457
+ "source_repo",
458
+ "source_sha",
459
+ "prompt",
460
+ "prompt_hash",
461
+ "setup_hash",
462
+ "expected_evidence",
463
+ "scorer",
464
+ "holdout",
465
+ "contamination_notes",
466
+ "allowed_tools"
467
+ ], "benchmark-task-record.v1");
468
+ assertMetadata(value, "benchmark-task-record.v1");
469
+ assertString(value.task_id, "task_id");
470
+ assertEnum(value.task_kind, BENCHMARK_TASK_KINDS, "task_kind");
471
+ assertEnum(value.source_repo, BENCHMARK_SOURCE_REPOS, "source_repo");
472
+ assertGitSha(value.source_sha, "source_sha");
473
+ assertOptionalString(value.prompt, "prompt");
474
+ assertHash(value.prompt_hash, "prompt_hash");
475
+ assertHash(value.setup_hash, "setup_hash");
476
+ assertStringArray(value.expected_evidence, "expected_evidence", 1);
477
+ assertObject(value.scorer, "scorer");
478
+ assertAllowedKeys(value.scorer, ["kind", "params"], "scorer");
479
+ assertEnum(value.scorer.kind, BENCHMARK_SCORER_KINDS, "scorer.kind");
480
+ if (value.scorer.params !== undefined)
481
+ assertObject(value.scorer.params, "scorer.params");
482
+ assertBoolean(value.holdout, "holdout");
483
+ assertString(value.contamination_notes, "contamination_notes");
484
+ assertStringArray(value.allowed_tools, "allowed_tools");
485
+ }
486
+ export function assertToolCallPlanV1(value) {
487
+ assertObject(value, "tool-call-plan.v1");
488
+ assertAllowedKeys(value, [
489
+ "schema",
490
+ "schema_version",
491
+ "schema_bundle_hash",
492
+ "producer",
493
+ "producer_version",
494
+ "producer_git_sha",
495
+ "created_at",
496
+ "plan_id",
497
+ "tool_name",
498
+ "arguments_hash",
499
+ "side_effects",
500
+ "status"
501
+ ], "tool-call-plan.v1");
502
+ assertMetadata(value, "tool-call-plan.v1");
503
+ assertString(value.plan_id, "plan_id");
504
+ assertString(value.tool_name, "tool_name");
505
+ assertHash(value.arguments_hash, "arguments_hash");
506
+ assertSideEffects(value.side_effects, "side_effects");
507
+ assertStatus(value.status, "status");
508
+ }
509
+ export function assertToolExecutionRecordV1(value) {
510
+ assertObject(value, "tool-execution-record.v1");
511
+ assertAllowedKeys(value, [
512
+ "schema",
513
+ "schema_version",
514
+ "schema_bundle_hash",
515
+ "producer",
516
+ "producer_version",
517
+ "producer_git_sha",
518
+ "created_at",
519
+ "execution_id",
520
+ "plan_id",
521
+ "status",
522
+ "output_hash",
523
+ "error"
524
+ ], "tool-execution-record.v1");
525
+ assertMetadata(value, "tool-execution-record.v1");
526
+ assertString(value.execution_id, "execution_id");
527
+ assertString(value.plan_id, "plan_id");
528
+ assertStatus(value.status, "status");
529
+ if (value.output_hash !== undefined)
530
+ assertHash(value.output_hash, "output_hash");
531
+ if (value.error !== undefined)
532
+ assertError(value.error, "error");
533
+ }
534
+ export function assertEnsembleReceiptV1(value) {
535
+ assertObject(value, "ensemble-receipt.v1");
536
+ assertAllowedKeys(value, [
537
+ "schema",
538
+ "schema_version",
539
+ "schema_bundle_hash",
540
+ "producer",
541
+ "producer_version",
542
+ "producer_git_sha",
543
+ "created_at",
544
+ "receipt_id",
545
+ "run_id",
546
+ "status",
547
+ "artifact_hashes"
548
+ ], "ensemble-receipt.v1");
549
+ assertMetadata(value, "ensemble-receipt.v1");
550
+ assertString(value.receipt_id, "receipt_id");
551
+ assertString(value.run_id, "run_id");
552
+ assertStatus(value.status, "status");
553
+ if (!Array.isArray(value.artifact_hashes)) {
554
+ throw new Error("artifact_hashes must be an array");
555
+ }
556
+ value.artifact_hashes.forEach((hash, index) => {
557
+ assertHash(hash, `artifact_hashes[${index}]`);
558
+ });
559
+ }
560
+ export function assertModelFusionRecord(value) {
561
+ assertObject(value, "model-fusion record");
562
+ switch (value.schema) {
563
+ case "model-call-record.v1":
564
+ assertModelCallRecordV1(value);
565
+ return;
566
+ case "harness-run-request.v1":
567
+ assertHarnessRunRequestV1(value);
568
+ return;
569
+ case "harness-run-result.v1":
570
+ assertHarnessRunResultV1(value);
571
+ return;
572
+ case "harness-candidate-record.v1":
573
+ assertHarnessCandidateRecordV1(value);
574
+ return;
575
+ case "judge-synthesis-record.v1":
576
+ assertJudgeSynthesisRecordV1(value);
577
+ return;
578
+ case "benchmark-task-record.v1":
579
+ assertBenchmarkTaskRecordV1(value);
580
+ return;
581
+ case "artifact-ref.v1":
582
+ assertArtifactRefV1(value);
583
+ return;
584
+ case "tool-call-plan.v1":
585
+ assertToolCallPlanV1(value);
586
+ return;
587
+ case "tool-execution-record.v1":
588
+ assertToolExecutionRecordV1(value);
589
+ return;
590
+ case "ensemble-receipt.v1":
591
+ assertEnsembleReceiptV1(value);
592
+ return;
593
+ default:
594
+ throw new Error(`unsupported model-fusion schema: ${String(value.schema)}`);
595
+ }
596
+ }
@@ -0,0 +1,27 @@
1
+ import type { ReceiptBundle, RunEvent, SessionIsolation } from "./types.js";
2
+ export type EventSummary = {
3
+ tone: "plain" | "info" | "ok" | "warn" | "err";
4
+ label: string;
5
+ detail: string;
6
+ };
7
+ export type ReceiptStory = {
8
+ runId: string;
9
+ status: ReceiptBundle["receipt"]["status"];
10
+ actor: string;
11
+ agent: string;
12
+ isolation: SessionIsolation;
13
+ workspace: {
14
+ baseRef: string;
15
+ manifestHash: string;
16
+ diffHash: string;
17
+ artifactCount: number;
18
+ };
19
+ policyHash: string;
20
+ secrets: string[];
21
+ network: string[];
22
+ eventCount: number;
23
+ eventsHead: string;
24
+ verificationCommand: string;
25
+ };
26
+ export declare function summarizeRunEvent(event: RunEvent): EventSummary;
27
+ export declare function buildReceiptStory(bundle: ReceiptBundle): ReceiptStory;