@fluxpointstudios/orynq-sdk-process-trace 0.2.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,1339 @@
1
+ 'use strict';
2
+
3
+ var utils = require('@fluxpointstudios/orynq-sdk-core/utils');
4
+
5
+ // src/types.ts
6
+ var DEFAULT_EVENT_VISIBILITY = {
7
+ command: "public",
8
+ output: "private",
9
+ decision: "private",
10
+ observation: "public",
11
+ error: "private",
12
+ custom: "private"
13
+ };
14
+ var HASH_DOMAIN_PREFIXES = {
15
+ event: "poi-trace:event:v1|",
16
+ roll: "poi-trace:roll:v1|",
17
+ span: "poi-trace:span:v1|",
18
+ leaf: "poi-trace:leaf:v1|",
19
+ node: "poi-trace:node:v1|",
20
+ manifest: "poi-trace:manifest:v1|",
21
+ root: "poi-trace:root:v1|"
22
+ };
23
+ var GENESIS_SEED = "genesis";
24
+ async function computeEventHash(event) {
25
+ const eventWithoutHash = removeHashField(event);
26
+ const canonical = utils.canonicalize(eventWithoutHash);
27
+ const prefixedData = HASH_DOMAIN_PREFIXES.event + canonical;
28
+ return utils.sha256StringHex(prefixedData);
29
+ }
30
+ function removeHashField(event) {
31
+ const { hash: _, ...rest } = event;
32
+ return rest;
33
+ }
34
+ async function initRollingHash() {
35
+ const genesisInput = HASH_DOMAIN_PREFIXES.roll + GENESIS_SEED;
36
+ const genesisHash = await utils.sha256StringHex(genesisInput);
37
+ return {
38
+ currentHash: genesisHash,
39
+ itemCount: 0
40
+ };
41
+ }
42
+ async function updateRollingHash(state, eventHash) {
43
+ const input = HASH_DOMAIN_PREFIXES.roll + state.currentHash + "|" + eventHash;
44
+ const newHash = await utils.sha256StringHex(input);
45
+ return {
46
+ currentHash: newHash,
47
+ itemCount: state.itemCount + 1
48
+ };
49
+ }
50
+ async function computeRollingHash(events) {
51
+ const sortedEvents = [...events].sort((a, b) => a.seq - b.seq);
52
+ let state = await initRollingHash();
53
+ for (const event of sortedEvents) {
54
+ const eventHash = await computeEventHash(event);
55
+ state = await updateRollingHash(state, eventHash);
56
+ }
57
+ return state.currentHash;
58
+ }
59
+ async function verifyRollingHash(events, expectedHash) {
60
+ const computedHash = await computeRollingHash(events);
61
+ return constantTimeCompare(computedHash, expectedHash.toLowerCase());
62
+ }
63
+ function constantTimeCompare(a, b) {
64
+ if (a.length !== b.length) {
65
+ return false;
66
+ }
67
+ let result = 0;
68
+ for (let i = 0; i < a.length; i++) {
69
+ result |= a.charCodeAt(i) ^ b.charCodeAt(i);
70
+ }
71
+ return result === 0;
72
+ }
73
+ async function computeRootHash(rollingHash, spans) {
74
+ const sortedSpans = [...spans].sort((a, b) => a.spanSeq - b.spanSeq);
75
+ const spanHashes = sortedSpans.map((span) => {
76
+ if (!span.hash) {
77
+ throw new Error(`Span ${span.id} is missing hash field`);
78
+ }
79
+ return span.hash;
80
+ });
81
+ let input = HASH_DOMAIN_PREFIXES.root + rollingHash;
82
+ if (spanHashes.length > 0) {
83
+ input += "|" + spanHashes.join("|");
84
+ }
85
+ return utils.sha256StringHex(input);
86
+ }
87
+ async function computeEventHashes(events) {
88
+ const sortedEvents = [...events].sort((a, b) => a.seq - b.seq);
89
+ const hashPromises = sortedEvents.map((event) => computeEventHash(event));
90
+ return Promise.all(hashPromises);
91
+ }
92
+ async function getGenesisHash() {
93
+ const state = await initRollingHash();
94
+ return state.currentHash;
95
+ }
96
+ async function computeSpanHash(span, eventHashes) {
97
+ const spanHeader = {
98
+ id: span.id,
99
+ spanSeq: span.spanSeq,
100
+ name: span.name,
101
+ status: span.status,
102
+ visibility: span.visibility,
103
+ startedAt: span.startedAt,
104
+ eventIds: span.eventIds,
105
+ childSpanIds: span.childSpanIds
106
+ };
107
+ if (span.parentSpanId !== void 0) {
108
+ spanHeader.parentSpanId = span.parentSpanId;
109
+ }
110
+ if (span.endedAt !== void 0) {
111
+ spanHeader.endedAt = span.endedAt;
112
+ }
113
+ if (span.durationMs !== void 0) {
114
+ spanHeader.durationMs = span.durationMs;
115
+ }
116
+ if (span.metadata !== void 0) {
117
+ spanHeader.metadata = span.metadata;
118
+ }
119
+ const canonicalHeader = utils.canonicalize(spanHeader, { removeNulls: true });
120
+ let hashInput = HASH_DOMAIN_PREFIXES.span + canonicalHeader;
121
+ if (eventHashes.length > 0) {
122
+ hashInput += "|" + eventHashes.join("|");
123
+ }
124
+ return utils.sha256StringHex(hashInput);
125
+ }
126
+ async function buildSpanMerkleTree(spans, events) {
127
+ if (spans.length === 0) {
128
+ return {
129
+ rootHash: "",
130
+ leafCount: 0,
131
+ depth: 0,
132
+ leafHashes: []
133
+ };
134
+ }
135
+ const sortedSpans = [...spans].sort((a, b) => a.spanSeq - b.spanSeq);
136
+ const eventMap = /* @__PURE__ */ new Map();
137
+ for (const event of events) {
138
+ eventMap.set(event.id, event);
139
+ }
140
+ const leafHashes = [];
141
+ for (const span of sortedSpans) {
142
+ const spanEvents = span.eventIds.map((id) => eventMap.get(id)).filter((e) => e !== void 0).sort((a, b) => a.seq - b.seq);
143
+ const eventHashes = spanEvents.map((e) => e.hash ?? "");
144
+ const spanHash = await computeSpanHash(span, eventHashes);
145
+ const leafHash = await utils.sha256StringHex(HASH_DOMAIN_PREFIXES.leaf + spanHash);
146
+ leafHashes.push(leafHash);
147
+ }
148
+ const tree = await buildTreeFromLeaves(leafHashes);
149
+ return {
150
+ rootHash: tree.rootHash,
151
+ leafCount: leafHashes.length,
152
+ depth: tree.depth,
153
+ leafHashes
154
+ };
155
+ }
156
+ async function buildTreeFromLeaves(leafHashes) {
157
+ if (leafHashes.length === 1) {
158
+ const rootHash = leafHashes[0];
159
+ if (rootHash === void 0) {
160
+ throw new Error("Unexpected empty leaf hash array");
161
+ }
162
+ return {
163
+ rootHash,
164
+ depth: 0
165
+ };
166
+ }
167
+ let currentLevel = [...leafHashes];
168
+ let depth = 0;
169
+ while (currentLevel.length > 1) {
170
+ const nextLevel = [];
171
+ for (let i = 0; i < currentLevel.length; i += 2) {
172
+ const left = currentLevel[i];
173
+ if (left === void 0) {
174
+ throw new Error("Unexpected undefined hash in tree level");
175
+ }
176
+ const right = i + 1 < currentLevel.length ? currentLevel[i + 1] ?? left : left;
177
+ const parentHash = await utils.sha256StringHex(
178
+ HASH_DOMAIN_PREFIXES.node + left + "|" + right
179
+ );
180
+ nextLevel.push(parentHash);
181
+ }
182
+ currentLevel = nextLevel;
183
+ depth++;
184
+ }
185
+ const finalRoot = currentLevel[0];
186
+ if (finalRoot === void 0) {
187
+ throw new Error("Unexpected empty tree level");
188
+ }
189
+ return {
190
+ rootHash: finalRoot,
191
+ depth
192
+ };
193
+ }
194
+ function generateMerkleProof(tree, spanIndex) {
195
+ if (spanIndex < 0 || spanIndex >= tree.leafCount) {
196
+ throw new Error(
197
+ `Span index ${spanIndex} is out of bounds (0-${tree.leafCount - 1})`
198
+ );
199
+ }
200
+ if (tree.leafCount === 1) {
201
+ const leafHash2 = tree.leafHashes[0];
202
+ if (leafHash2 === void 0) {
203
+ throw new Error("Unexpected empty leaf hash array in tree");
204
+ }
205
+ return {
206
+ leafHash: leafHash2,
207
+ leafIndex: 0,
208
+ siblings: [],
209
+ rootHash: tree.rootHash
210
+ };
211
+ }
212
+ const siblings = [];
213
+ let currentLevel = [...tree.leafHashes];
214
+ let currentIndex = spanIndex;
215
+ while (currentLevel.length > 1) {
216
+ const isLeftChild = currentIndex % 2 === 0;
217
+ const siblingIndex = isLeftChild ? currentIndex + 1 : currentIndex - 1;
218
+ let siblingHash;
219
+ if (siblingIndex >= currentLevel.length) {
220
+ const currentHash = currentLevel[currentIndex];
221
+ if (currentHash === void 0) {
222
+ throw new Error("Unexpected undefined hash at current index");
223
+ }
224
+ siblingHash = currentHash;
225
+ } else {
226
+ const hash = currentLevel[siblingIndex];
227
+ if (hash === void 0) {
228
+ throw new Error("Unexpected undefined hash at sibling index");
229
+ }
230
+ siblingHash = hash;
231
+ }
232
+ siblings.push({
233
+ hash: siblingHash,
234
+ position: isLeftChild ? "right" : "left"
235
+ });
236
+ const nextLevel = [];
237
+ for (let i = 0; i < currentLevel.length; i += 2) {
238
+ const left = currentLevel[i] ?? "";
239
+ const right = i + 1 < currentLevel.length ? currentLevel[i + 1] ?? left : left;
240
+ nextLevel.push(`${left}|${right}`);
241
+ }
242
+ currentLevel = nextLevel;
243
+ currentIndex = Math.floor(currentIndex / 2);
244
+ }
245
+ const leafHash = tree.leafHashes[spanIndex];
246
+ if (leafHash === void 0) {
247
+ throw new Error(`Unexpected undefined leaf hash at index ${spanIndex}`);
248
+ }
249
+ return {
250
+ leafHash,
251
+ leafIndex: spanIndex,
252
+ siblings,
253
+ rootHash: tree.rootHash
254
+ };
255
+ }
256
+ async function verifyMerkleProof(proof) {
257
+ let currentHash = proof.leafHash;
258
+ for (const sibling of proof.siblings) {
259
+ let left;
260
+ let right;
261
+ if (sibling.position === "left") {
262
+ left = sibling.hash;
263
+ right = currentHash;
264
+ } else {
265
+ left = currentHash;
266
+ right = sibling.hash;
267
+ }
268
+ currentHash = await utils.sha256StringHex(
269
+ HASH_DOMAIN_PREFIXES.node + left + "|" + right
270
+ );
271
+ }
272
+ return currentHash === proof.rootHash;
273
+ }
274
+ async function verifySpanInclusion(proof, span, events) {
275
+ const sortedEvents = [...events].sort((a, b) => a.seq - b.seq);
276
+ const eventHashes = sortedEvents.map((e) => e.hash ?? "");
277
+ const spanHash = await computeSpanHash(span, eventHashes);
278
+ const computedLeafHash = await utils.sha256StringHex(
279
+ HASH_DOMAIN_PREFIXES.leaf + spanHash
280
+ );
281
+ if (computedLeafHash !== proof.leafHash) {
282
+ return false;
283
+ }
284
+ return verifyMerkleProof(proof);
285
+ }
286
+
287
+ // src/trace-builder.ts
288
+ async function createTrace(opts) {
289
+ if (!opts.agentId || typeof opts.agentId !== "string") {
290
+ throw new Error("agentId is required and must be a non-empty string");
291
+ }
292
+ const runId = crypto.randomUUID();
293
+ const hashState = await initRollingHash();
294
+ const run = {
295
+ id: runId,
296
+ schemaVersion: "1.0",
297
+ agentId: opts.agentId,
298
+ status: "running",
299
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
300
+ events: [],
301
+ spans: [],
302
+ rollingHash: hashState.currentHash,
303
+ nextSeq: 0,
304
+ nextSpanSeq: 0
305
+ };
306
+ if (opts.metadata !== void 0) {
307
+ run.metadata = { ...opts.metadata };
308
+ }
309
+ if (opts.description !== void 0) {
310
+ run.metadata = {
311
+ ...run.metadata,
312
+ description: opts.description
313
+ };
314
+ }
315
+ return run;
316
+ }
317
+ function addSpan(run, opts) {
318
+ if (isFinalized(run)) {
319
+ throw new Error("Cannot add span to a finalized trace run");
320
+ }
321
+ if (!opts.name || typeof opts.name !== "string") {
322
+ throw new Error("name is required and must be a non-empty string");
323
+ }
324
+ if (opts.parentSpanId !== void 0) {
325
+ const parentSpan = getSpan(run, opts.parentSpanId);
326
+ if (!parentSpan) {
327
+ throw new Error(`Parent span not found: ${opts.parentSpanId}`);
328
+ }
329
+ if (parentSpan.status !== "running") {
330
+ throw new Error(`Parent span is not running: ${opts.parentSpanId}`);
331
+ }
332
+ }
333
+ const spanId = crypto.randomUUID();
334
+ const spanSeq = run.nextSpanSeq++;
335
+ const visibility = opts.visibility ?? "private";
336
+ const span = {
337
+ id: spanId,
338
+ spanSeq,
339
+ name: opts.name,
340
+ status: "running",
341
+ visibility,
342
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
343
+ eventIds: [],
344
+ childSpanIds: []
345
+ };
346
+ if (opts.parentSpanId !== void 0) {
347
+ span.parentSpanId = opts.parentSpanId;
348
+ }
349
+ if (opts.metadata !== void 0) {
350
+ span.metadata = { ...opts.metadata };
351
+ }
352
+ run.spans.push(span);
353
+ if (opts.parentSpanId !== void 0) {
354
+ const parentSpan = getSpan(run, opts.parentSpanId);
355
+ if (parentSpan) {
356
+ parentSpan.childSpanIds.push(spanId);
357
+ }
358
+ }
359
+ return span;
360
+ }
361
+ function getSpan(run, spanId) {
362
+ return run.spans.find((s) => s.id === spanId);
363
+ }
364
+ function getSpanEvents(run, spanId) {
365
+ const span = getSpan(run, spanId);
366
+ if (!span) {
367
+ return [];
368
+ }
369
+ const eventMap = new Map(run.events.map((e) => [e.id, e]));
370
+ const spanEvents = span.eventIds.map((id) => eventMap.get(id)).filter((e) => e !== void 0);
371
+ return spanEvents.sort((a, b) => a.seq - b.seq);
372
+ }
373
+ async function addEvent(run, spanId, event) {
374
+ if (isFinalized(run)) {
375
+ throw new Error("Cannot add event to a finalized trace run");
376
+ }
377
+ const span = getSpan(run, spanId);
378
+ if (!span) {
379
+ throw new Error(`Span not found: ${spanId}`);
380
+ }
381
+ if (span.status !== "running") {
382
+ throw new Error(`Cannot add event to closed span: ${spanId} (status: ${span.status})`);
383
+ }
384
+ if (!event.kind || typeof event.kind !== "string") {
385
+ throw new Error("Event kind is required and must be a non-empty string");
386
+ }
387
+ const eventId = crypto.randomUUID();
388
+ const seq = run.nextSeq++;
389
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
390
+ const visibility = event.visibility ?? DEFAULT_EVENT_VISIBILITY[event.kind] ?? "private";
391
+ const completeEvent = {
392
+ ...event,
393
+ id: eventId,
394
+ seq,
395
+ timestamp,
396
+ visibility
397
+ };
398
+ const eventHash = await computeEventHash(completeEvent);
399
+ completeEvent.hash = eventHash;
400
+ const currentState = {
401
+ currentHash: run.rollingHash,
402
+ itemCount: run.events.length
403
+ };
404
+ const newState = await updateRollingHash(currentState, eventHash);
405
+ run.rollingHash = newState.currentHash;
406
+ span.eventIds.push(eventId);
407
+ run.events.push(completeEvent);
408
+ return completeEvent;
409
+ }
410
+ async function closeSpan(run, spanId, status = "completed") {
411
+ const span = getSpan(run, spanId);
412
+ if (!span) {
413
+ throw new Error(`Span not found: ${spanId}`);
414
+ }
415
+ if (span.status !== "running") {
416
+ throw new Error(`Span already closed: ${spanId} (status: ${span.status})`);
417
+ }
418
+ span.status = status;
419
+ const endedAt = (/* @__PURE__ */ new Date()).toISOString();
420
+ span.endedAt = endedAt;
421
+ const startTime = new Date(span.startedAt).getTime();
422
+ const endTime = new Date(endedAt).getTime();
423
+ span.durationMs = endTime - startTime;
424
+ const spanEvents = getSpanEvents(run, spanId);
425
+ const eventHashes = spanEvents.map((e) => e.hash ?? "");
426
+ span.hash = await computeSpanHash(span, eventHashes);
427
+ }
428
+ function isFinalized(run) {
429
+ return run.rootHash !== void 0;
430
+ }
431
+ async function finalizeTrace(run) {
432
+ if (isFinalized(run)) {
433
+ throw new Error("Trace run is already finalized");
434
+ }
435
+ for (const span of run.spans) {
436
+ if (span.status === "running") {
437
+ await closeSpan(run, span.id, "completed");
438
+ }
439
+ }
440
+ run.status = "completed";
441
+ const endedAt = (/* @__PURE__ */ new Date()).toISOString();
442
+ run.endedAt = endedAt;
443
+ const startTime = new Date(run.startedAt).getTime();
444
+ const endTime = new Date(endedAt).getTime();
445
+ run.durationMs = endTime - startTime;
446
+ const merkleTree = await buildSpanMerkleTree(run.spans, run.events);
447
+ const rootHash = await computeRootHash(run.rollingHash, run.spans);
448
+ run.rootHash = rootHash;
449
+ const publicView = createPublicView(run, merkleTree.rootHash);
450
+ const bundle = {
451
+ formatVersion: "1.0",
452
+ publicView,
453
+ privateRun: run,
454
+ merkleRoot: merkleTree.rootHash,
455
+ rootHash
456
+ };
457
+ return bundle;
458
+ }
459
+ function createPublicView(run, merkleRoot) {
460
+ const eventMap = new Map(run.events.map((e) => [e.id, e]));
461
+ const publicSpans = [];
462
+ const redactedSpanHashes = [];
463
+ for (const span of run.spans) {
464
+ if (span.visibility === "public") {
465
+ const spanEvents = span.eventIds.map((id) => eventMap.get(id)).filter((e) => e !== void 0).filter((e) => e.visibility === "public").sort((a, b) => a.seq - b.seq);
466
+ const annotatedSpan = {
467
+ ...span,
468
+ events: spanEvents
469
+ };
470
+ publicSpans.push(annotatedSpan);
471
+ } else {
472
+ if (span.hash) {
473
+ redactedSpanHashes.push({
474
+ spanId: span.id,
475
+ hash: span.hash
476
+ });
477
+ }
478
+ }
479
+ }
480
+ publicSpans.sort((a, b) => a.spanSeq - b.spanSeq);
481
+ redactedSpanHashes.sort((a, b) => a.spanId.localeCompare(b.spanId));
482
+ const publicView = {
483
+ runId: run.id,
484
+ agentId: run.agentId,
485
+ schemaVersion: run.schemaVersion,
486
+ startedAt: run.startedAt,
487
+ endedAt: run.endedAt ?? run.startedAt,
488
+ // Fallback for safety
489
+ durationMs: run.durationMs ?? 0,
490
+ status: run.status,
491
+ totalEvents: run.events.length,
492
+ totalSpans: run.spans.length,
493
+ rootHash: run.rootHash ?? "",
494
+ merkleRoot,
495
+ publicSpans,
496
+ redactedSpanHashes
497
+ };
498
+ return publicView;
499
+ }
500
+ function getEventCount(run) {
501
+ return run.events.length;
502
+ }
503
+ function getSpanCount(run) {
504
+ return run.spans.length;
505
+ }
506
+ function getRootSpans(run) {
507
+ return run.spans.filter((s) => s.parentSpanId === void 0);
508
+ }
509
+ function getChildSpans(run, parentSpanId) {
510
+ return run.spans.filter((s) => s.parentSpanId === parentSpanId);
511
+ }
512
+ function getEvent(run, eventId) {
513
+ return run.events.find((e) => e.id === eventId);
514
+ }
515
+ function getEventsByKind(run, kind) {
516
+ return run.events.filter(
517
+ (e) => e.kind === kind
518
+ );
519
+ }
520
+ function isPublicSpan(span) {
521
+ return span.visibility === "public";
522
+ }
523
+ function isPublicEvent(event) {
524
+ return event.visibility === "public";
525
+ }
526
+ function filterPublicEvents(events) {
527
+ return events.filter(isPublicEvent);
528
+ }
529
+ async function createBundle(run) {
530
+ if (!run.rootHash) {
531
+ throw new Error(
532
+ "Cannot create bundle from non-finalized run: rootHash is missing. Call finalizeTraceRun() before creating a bundle."
533
+ );
534
+ }
535
+ if (run.status === "running") {
536
+ throw new Error(
537
+ "Cannot create bundle from running trace. The trace must be completed, failed, or cancelled."
538
+ );
539
+ }
540
+ for (const event of run.events) {
541
+ if (!event.hash) {
542
+ throw new Error(
543
+ `Event ${event.id} (seq ${event.seq}) is missing hash. All events must have hashes computed before creating a bundle.`
544
+ );
545
+ }
546
+ }
547
+ for (const span of run.spans) {
548
+ if (!span.hash) {
549
+ throw new Error(
550
+ `Span ${span.id} (spanSeq ${span.spanSeq}) is missing hash. All spans must have hashes computed before creating a bundle.`
551
+ );
552
+ }
553
+ }
554
+ const merkleTree = await buildSpanMerkleTree(run.spans, run.events);
555
+ const publicView = createPublicView2(run, merkleTree.rootHash);
556
+ const bundle = {
557
+ formatVersion: run.schemaVersion,
558
+ publicView,
559
+ privateRun: run,
560
+ merkleRoot: merkleTree.rootHash,
561
+ rootHash: run.rootHash
562
+ };
563
+ return bundle;
564
+ }
565
+ function createPublicView2(run, merkleRoot) {
566
+ const eventMap = /* @__PURE__ */ new Map();
567
+ for (const event of run.events) {
568
+ eventMap.set(event.id, event);
569
+ }
570
+ const publicSpans = [];
571
+ const redactedSpanHashes = [];
572
+ for (const span of run.spans) {
573
+ if (isPublicSpan(span)) {
574
+ const spanEvents = span.eventIds.map((id) => eventMap.get(id)).filter((e) => e !== void 0).filter(isPublicEvent).sort((a, b) => a.seq - b.seq);
575
+ const annotatedSpan = {
576
+ ...span,
577
+ events: spanEvents
578
+ };
579
+ publicSpans.push(annotatedSpan);
580
+ } else {
581
+ redactedSpanHashes.push({
582
+ spanId: span.id,
583
+ hash: span.hash ?? ""
584
+ });
585
+ }
586
+ }
587
+ publicSpans.sort((a, b) => a.spanSeq - b.spanSeq);
588
+ redactedSpanHashes.sort((a, b) => a.spanId.localeCompare(b.spanId));
589
+ return {
590
+ runId: run.id,
591
+ agentId: run.agentId,
592
+ schemaVersion: run.schemaVersion,
593
+ startedAt: run.startedAt,
594
+ endedAt: run.endedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
595
+ durationMs: run.durationMs ?? 0,
596
+ status: run.status,
597
+ totalEvents: run.events.length,
598
+ totalSpans: run.spans.length,
599
+ rootHash: run.rootHash ?? "",
600
+ merkleRoot,
601
+ publicSpans,
602
+ redactedSpanHashes
603
+ };
604
+ }
605
+ function extractPublicView(bundle) {
606
+ return bundle.publicView;
607
+ }
608
+ async function verifyBundle(bundle) {
609
+ const errors = [];
610
+ const warnings = [];
611
+ const checks = {
612
+ rollingHashValid: false,
613
+ rootHashValid: false,
614
+ merkleRootValid: false,
615
+ spanHashesValid: false,
616
+ eventHashesValid: false,
617
+ sequenceValid: false
618
+ };
619
+ const run = bundle.privateRun;
620
+ const sequenceErrors = verifySequences(run);
621
+ if (sequenceErrors.length === 0) {
622
+ checks.sequenceValid = true;
623
+ } else {
624
+ errors.push(...sequenceErrors);
625
+ }
626
+ const eventHashErrors = await verifyEventHashes(run.events);
627
+ if (eventHashErrors.length === 0) {
628
+ checks.eventHashesValid = true;
629
+ } else {
630
+ errors.push(...eventHashErrors);
631
+ }
632
+ const spanHashErrors = await verifySpanHashes(run.spans, run.events);
633
+ if (spanHashErrors.length === 0) {
634
+ checks.spanHashesValid = true;
635
+ } else {
636
+ errors.push(...spanHashErrors);
637
+ }
638
+ try {
639
+ const computedRollingHash = await computeRollingHash(run.events);
640
+ if (computedRollingHash === run.rollingHash) {
641
+ checks.rollingHashValid = true;
642
+ } else {
643
+ errors.push(
644
+ `Rolling hash mismatch: expected ${run.rollingHash}, computed ${computedRollingHash}`
645
+ );
646
+ }
647
+ } catch (error) {
648
+ errors.push(
649
+ `Failed to compute rolling hash: ${error instanceof Error ? error.message : String(error)}`
650
+ );
651
+ }
652
+ try {
653
+ const computedRootHash = await computeRootHash(run.rollingHash, run.spans);
654
+ if (computedRootHash === bundle.rootHash) {
655
+ checks.rootHashValid = true;
656
+ } else {
657
+ errors.push(
658
+ `Root hash mismatch: expected ${bundle.rootHash}, computed ${computedRootHash}`
659
+ );
660
+ }
661
+ } catch (error) {
662
+ errors.push(
663
+ `Failed to compute root hash: ${error instanceof Error ? error.message : String(error)}`
664
+ );
665
+ }
666
+ try {
667
+ const merkleTree = await buildSpanMerkleTree(run.spans, run.events);
668
+ if (merkleTree.rootHash === bundle.merkleRoot) {
669
+ checks.merkleRootValid = true;
670
+ } else {
671
+ errors.push(
672
+ `Merkle root mismatch: expected ${bundle.merkleRoot}, computed ${merkleTree.rootHash}`
673
+ );
674
+ }
675
+ } catch (error) {
676
+ errors.push(
677
+ `Failed to compute Merkle root: ${error instanceof Error ? error.message : String(error)}`
678
+ );
679
+ }
680
+ if (bundle.publicView.publicSpans.length === 0 && run.spans.length > 0) {
681
+ warnings.push(
682
+ "No public spans in bundle. The public view will be empty. Consider marking some spans as public for transparency."
683
+ );
684
+ }
685
+ if (bundle.publicView.status !== run.status) {
686
+ warnings.push(
687
+ `Status mismatch between publicView (${bundle.publicView.status}) and privateRun (${run.status})`
688
+ );
689
+ }
690
+ const valid = checks.rollingHashValid && checks.rootHashValid && checks.merkleRootValid && checks.spanHashesValid && checks.eventHashesValid && checks.sequenceValid;
691
+ return {
692
+ valid,
693
+ errors,
694
+ warnings,
695
+ checks
696
+ };
697
+ }
698
+ function verifySequences(run) {
699
+ const errors = [];
700
+ const sortedEvents = [...run.events].sort((a, b) => a.seq - b.seq);
701
+ for (let i = 0; i < sortedEvents.length; i++) {
702
+ const event = sortedEvents[i];
703
+ if (event !== void 0 && event.seq !== i) {
704
+ errors.push(
705
+ `Event sequence gap: expected seq ${i}, found ${event.seq} for event ${event.id}`
706
+ );
707
+ }
708
+ }
709
+ const sortedSpans = [...run.spans].sort((a, b) => a.spanSeq - b.spanSeq);
710
+ for (let i = 0; i < sortedSpans.length; i++) {
711
+ const span = sortedSpans[i];
712
+ if (span !== void 0 && span.spanSeq !== i) {
713
+ errors.push(
714
+ `Span sequence gap: expected spanSeq ${i}, found ${span.spanSeq} for span ${span.id}`
715
+ );
716
+ }
717
+ }
718
+ return errors;
719
+ }
720
+ async function verifyEventHashes(events) {
721
+ const errors = [];
722
+ for (const event of events) {
723
+ if (!event.hash) {
724
+ errors.push(`Event ${event.id} (seq ${event.seq}) is missing hash`);
725
+ continue;
726
+ }
727
+ try {
728
+ const computedHash = await computeEventHash(event);
729
+ if (computedHash !== event.hash) {
730
+ errors.push(
731
+ `Event hash mismatch for ${event.id} (seq ${event.seq}): expected ${event.hash}, computed ${computedHash}`
732
+ );
733
+ }
734
+ } catch (error) {
735
+ errors.push(
736
+ `Failed to compute hash for event ${event.id}: ${error instanceof Error ? error.message : String(error)}`
737
+ );
738
+ }
739
+ }
740
+ return errors;
741
+ }
742
+ async function verifySpanHashes(spans, events) {
743
+ const errors = [];
744
+ const eventMap = /* @__PURE__ */ new Map();
745
+ for (const event of events) {
746
+ eventMap.set(event.id, event);
747
+ }
748
+ for (const span of spans) {
749
+ if (!span.hash) {
750
+ errors.push(
751
+ `Span ${span.id} (spanSeq ${span.spanSeq}) is missing hash`
752
+ );
753
+ continue;
754
+ }
755
+ try {
756
+ const spanEvents = span.eventIds.map((id) => eventMap.get(id)).filter((e) => e !== void 0).sort((a, b) => a.seq - b.seq);
757
+ const eventHashes = spanEvents.map((e) => e.hash ?? "");
758
+ const computedHash = await computeSpanHash(span, eventHashes);
759
+ if (computedHash !== span.hash) {
760
+ errors.push(
761
+ `Span hash mismatch for ${span.id} (spanSeq ${span.spanSeq}): expected ${span.hash}, computed ${computedHash}`
762
+ );
763
+ }
764
+ } catch (error) {
765
+ errors.push(
766
+ `Failed to compute hash for span ${span.id}: ${error instanceof Error ? error.message : String(error)}`
767
+ );
768
+ }
769
+ }
770
+ return errors;
771
+ }
772
+ async function signBundle(bundle, provider) {
773
+ const signingPayload = {
774
+ rootHash: bundle.rootHash,
775
+ merkleRoot: bundle.merkleRoot
776
+ };
777
+ if (bundle.manifestHash) {
778
+ signingPayload.manifestHash = bundle.manifestHash;
779
+ }
780
+ const canonicalPayload = utils.canonicalize(signingPayload);
781
+ const payloadBytes = new TextEncoder().encode(canonicalPayload);
782
+ const signatureBytes = await provider.sign(payloadBytes);
783
+ const signatureHex = utils.bytesToHex(signatureBytes);
784
+ return {
785
+ ...bundle,
786
+ signerId: provider.signerId,
787
+ signature: signatureHex
788
+ };
789
+ }
790
+ async function verifyBundleSignature(bundle, provider) {
791
+ if (!bundle.signature) {
792
+ return false;
793
+ }
794
+ if (!bundle.signerId) {
795
+ return false;
796
+ }
797
+ try {
798
+ const signingPayload = {
799
+ rootHash: bundle.rootHash,
800
+ merkleRoot: bundle.merkleRoot
801
+ };
802
+ if (bundle.manifestHash) {
803
+ signingPayload.manifestHash = bundle.manifestHash;
804
+ }
805
+ const canonicalPayload = utils.canonicalize(signingPayload);
806
+ const payloadBytes = new TextEncoder().encode(canonicalPayload);
807
+ const signatureBytes = utils.hexToBytes(bundle.signature);
808
+ return await provider.verify(payloadBytes, signatureBytes, bundle.signerId);
809
+ } catch (error) {
810
+ return false;
811
+ }
812
+ }
813
+ function getSpanEvents2(span, events) {
814
+ const eventMap = /* @__PURE__ */ new Map();
815
+ for (const event of events) {
816
+ eventMap.set(event.id, event);
817
+ }
818
+ return span.eventIds.map((id) => eventMap.get(id)).filter((e) => e !== void 0).sort((a, b) => a.seq - b.seq);
819
+ }
820
+ function countEventsByVisibility(run) {
821
+ const counts = {
822
+ public: 0,
823
+ private: 0,
824
+ secret: 0
825
+ };
826
+ for (const event of run.events) {
827
+ counts[event.visibility]++;
828
+ }
829
+ return counts;
830
+ }
831
+ function countSpansByVisibility(run) {
832
+ const counts = {
833
+ public: 0,
834
+ private: 0,
835
+ secret: 0
836
+ };
837
+ for (const span of run.spans) {
838
+ counts[span.visibility]++;
839
+ }
840
+ return counts;
841
+ }
842
+ function canDisclose(bundle, spanId) {
843
+ return bundle.privateRun.spans.some((span) => span.id === spanId);
844
+ }
845
+ function getSpanIndex(bundle, spanId) {
846
+ const sortedSpans = [...bundle.privateRun.spans].sort(
847
+ (a, b) => a.spanSeq - b.spanSeq
848
+ );
849
+ const index = sortedSpans.findIndex((span) => span.id === spanId);
850
+ if (index === -1) {
851
+ throw new Error(
852
+ `Span with ID "${spanId}" not found in bundle. Available span IDs: ${sortedSpans.map((s) => s.id).join(", ")}`
853
+ );
854
+ }
855
+ return index;
856
+ }
857
+ function getSpanEventsFromBundle(bundle, span) {
858
+ const eventMap = /* @__PURE__ */ new Map();
859
+ for (const event of bundle.privateRun.events) {
860
+ eventMap.set(event.id, event);
861
+ }
862
+ return span.eventIds.map((id) => eventMap.get(id)).filter((e) => e !== void 0).sort((a, b) => a.seq - b.seq);
863
+ }
864
+ function createDisclosureRequest(bundle, spanIds, mode) {
865
+ return {
866
+ bundleRootHash: bundle.rootHash,
867
+ bundleMerkleRoot: bundle.merkleRoot,
868
+ spanIds: [...spanIds],
869
+ // Create a copy to prevent external mutation
870
+ mode
871
+ };
872
+ }
873
+ async function selectiveDisclose(bundle, spanIds, mode) {
874
+ const missingSpanIds = [];
875
+ for (const spanId of spanIds) {
876
+ if (!canDisclose(bundle, spanId)) {
877
+ missingSpanIds.push(spanId);
878
+ }
879
+ }
880
+ if (missingSpanIds.length > 0) {
881
+ throw new Error(
882
+ `Cannot disclose spans that do not exist in bundle: ${missingSpanIds.join(", ")}`
883
+ );
884
+ }
885
+ const merkleTree = await buildSpanMerkleTree(
886
+ bundle.privateRun.spans,
887
+ bundle.privateRun.events
888
+ );
889
+ const sortedSpans = [...bundle.privateRun.spans].sort(
890
+ (a, b) => a.spanSeq - b.spanSeq
891
+ );
892
+ const spanMap = /* @__PURE__ */ new Map();
893
+ for (const span of sortedSpans) {
894
+ spanMap.set(span.id, span);
895
+ }
896
+ const disclosedSpans = [];
897
+ for (const spanId of spanIds) {
898
+ const span = spanMap.get(spanId);
899
+ if (!span) {
900
+ throw new Error(`Span "${spanId}" not found after validation`);
901
+ }
902
+ const spanIndex = getSpanIndex(bundle, spanId);
903
+ const proof = generateMerkleProof(merkleTree, spanIndex);
904
+ if (mode === "full") {
905
+ const events = getSpanEventsFromBundle(bundle, span);
906
+ disclosedSpans.push({
907
+ spanId,
908
+ proof,
909
+ span: { ...span },
910
+ // Clone to prevent external mutation
911
+ events: events.map((e) => ({ ...e }))
912
+ // Clone events
913
+ });
914
+ } else {
915
+ disclosedSpans.push({
916
+ spanId,
917
+ proof
918
+ // span and events are undefined in membership mode
919
+ });
920
+ }
921
+ }
922
+ return {
923
+ mode,
924
+ rootHash: bundle.rootHash,
925
+ merkleRoot: bundle.merkleRoot,
926
+ disclosedSpans
927
+ };
928
+ }
929
+ async function verifyDisclosure(disclosure, expectedRootHash, expectedMerkleRoot) {
930
+ const errors = [];
931
+ if (disclosure.rootHash !== expectedRootHash) {
932
+ errors.push(
933
+ `Root hash mismatch: disclosure has "${disclosure.rootHash}", expected "${expectedRootHash}"`
934
+ );
935
+ }
936
+ if (disclosure.merkleRoot !== expectedMerkleRoot) {
937
+ errors.push(
938
+ `Merkle root mismatch: disclosure has "${disclosure.merkleRoot}", expected "${expectedMerkleRoot}"`
939
+ );
940
+ }
941
+ for (const disclosed of disclosure.disclosedSpans) {
942
+ const proofValid = await verifyMerkleProof(disclosed.proof);
943
+ if (!proofValid) {
944
+ errors.push(
945
+ `Merkle proof verification failed for span "${disclosed.spanId}"`
946
+ );
947
+ continue;
948
+ }
949
+ if (disclosed.proof.rootHash !== expectedMerkleRoot) {
950
+ errors.push(
951
+ `Proof root hash mismatch for span "${disclosed.spanId}": proof has "${disclosed.proof.rootHash}", expected "${expectedMerkleRoot}"`
952
+ );
953
+ }
954
+ if (disclosure.mode === "full" && disclosed.span && disclosed.events) {
955
+ const spanVerification = await verifySpanDisclosure(
956
+ {
957
+ spanId: disclosed.spanId,
958
+ proof: disclosed.proof,
959
+ span: disclosed.span,
960
+ events: disclosed.events
961
+ },
962
+ expectedMerkleRoot
963
+ );
964
+ if (!spanVerification.valid) {
965
+ errors.push(...spanVerification.errors);
966
+ }
967
+ }
968
+ }
969
+ return {
970
+ valid: errors.length === 0,
971
+ errors
972
+ };
973
+ }
974
+ async function verifySpanDisclosure(disclosed, expectedMerkleRoot) {
975
+ const errors = [];
976
+ const sortedEvents = [...disclosed.events].sort((a, b) => a.seq - b.seq);
977
+ const eventHashes = sortedEvents.map((e) => e.hash ?? "");
978
+ const computedSpanHash = await computeSpanHash(disclosed.span, eventHashes);
979
+ const computedLeafHash = await utils.sha256StringHex(
980
+ HASH_DOMAIN_PREFIXES.leaf + computedSpanHash
981
+ );
982
+ if (computedLeafHash !== disclosed.proof.leafHash) {
983
+ errors.push(
984
+ `Span hash verification failed for "${disclosed.spanId}": computed leaf hash "${computedLeafHash}" does not match proof leaf hash "${disclosed.proof.leafHash}". The span data may have been modified.`
985
+ );
986
+ }
987
+ const proofValid = await verifyMerkleProof(disclosed.proof);
988
+ if (!proofValid) {
989
+ errors.push(
990
+ `Merkle proof verification failed for span "${disclosed.spanId}"`
991
+ );
992
+ }
993
+ if (disclosed.proof.rootHash !== expectedMerkleRoot) {
994
+ errors.push(
995
+ `Proof root hash mismatch for span "${disclosed.spanId}": proof has "${disclosed.proof.rootHash}", expected "${expectedMerkleRoot}"`
996
+ );
997
+ }
998
+ return {
999
+ valid: errors.length === 0,
1000
+ errors
1001
+ };
1002
+ }
1003
+ var DEFAULT_CHUNK_SIZE = 1e6;
1004
+ async function createManifest(bundle, options) {
1005
+ const chunkSize = options?.chunkSize ?? DEFAULT_CHUNK_SIZE;
1006
+ const compression = options?.compression ?? "none";
1007
+ const run = bundle.privateRun;
1008
+ const eventMap = /* @__PURE__ */ new Map();
1009
+ for (const event of run.events) {
1010
+ eventMap.set(event.id, event);
1011
+ }
1012
+ const chunks = [];
1013
+ let currentChunkSpans = [];
1014
+ let currentChunkEvents = [];
1015
+ let currentChunkSize = 0;
1016
+ let chunkIndex = 0;
1017
+ const sortedSpans = [...run.spans].sort((a, b) => a.spanSeq - b.spanSeq);
1018
+ for (const span of sortedSpans) {
1019
+ const spanEvents = span.eventIds.map((id) => eventMap.get(id)).filter((e) => e !== void 0).sort((a, b) => a.seq - b.seq);
1020
+ const spanJson = JSON.stringify(span);
1021
+ const eventsJson = spanEvents.map((e) => JSON.stringify(e)).join("");
1022
+ const spanSize = spanJson.length + eventsJson.length;
1023
+ if (currentChunkSize + spanSize > chunkSize && currentChunkSpans.length > 0) {
1024
+ const chunk = await createChunk(
1025
+ chunkIndex,
1026
+ currentChunkSpans,
1027
+ currentChunkEvents,
1028
+ compression
1029
+ );
1030
+ chunks.push(chunk);
1031
+ chunkIndex++;
1032
+ currentChunkSpans = [];
1033
+ currentChunkEvents = [];
1034
+ currentChunkSize = 0;
1035
+ }
1036
+ currentChunkSpans.push(span);
1037
+ currentChunkEvents.push(...spanEvents);
1038
+ currentChunkSize += spanSize;
1039
+ }
1040
+ if (currentChunkSpans.length > 0) {
1041
+ const chunk = await createChunk(
1042
+ chunkIndex,
1043
+ currentChunkSpans,
1044
+ currentChunkEvents,
1045
+ compression
1046
+ );
1047
+ chunks.push(chunk);
1048
+ }
1049
+ const manifestWithoutHash = {
1050
+ formatVersion: bundle.formatVersion,
1051
+ runId: run.id,
1052
+ agentId: run.agentId,
1053
+ rootHash: bundle.rootHash,
1054
+ merkleRoot: bundle.merkleRoot,
1055
+ totalEvents: run.events.length,
1056
+ totalSpans: run.spans.length,
1057
+ startedAt: run.startedAt,
1058
+ endedAt: run.endedAt ?? run.startedAt,
1059
+ durationMs: run.durationMs ?? 0,
1060
+ chunks: chunks.map((c) => c.info),
1061
+ publicView: bundle.publicView
1062
+ };
1063
+ const manifestHash = await computeManifestHash(manifestWithoutHash);
1064
+ const manifest = {
1065
+ ...manifestWithoutHash,
1066
+ manifestHash
1067
+ };
1068
+ return { manifest, chunks };
1069
+ }
1070
+ async function createChunk(index, spans, events, compression) {
1071
+ const content = {
1072
+ spans,
1073
+ events
1074
+ };
1075
+ const contentJson = utils.canonicalize(content);
1076
+ const hash = await utils.sha256StringHex(contentJson);
1077
+ const spanIds = spans.map((s) => s.id);
1078
+ const info = {
1079
+ index,
1080
+ hash,
1081
+ size: contentJson.length,
1082
+ compression,
1083
+ spanIds
1084
+ };
1085
+ return {
1086
+ info,
1087
+ content: contentJson
1088
+ };
1089
+ }
1090
+ async function computeManifestHash(manifest) {
1091
+ const canonical = utils.canonicalize(manifest);
1092
+ const prefixedData = HASH_DOMAIN_PREFIXES.manifest + canonical;
1093
+ return utils.sha256StringHex(prefixedData);
1094
+ }
1095
+ async function verifyManifest(manifest, chunks) {
1096
+ const errors = [];
1097
+ const warnings = [];
1098
+ const checks = {
1099
+ manifestHashValid: false,
1100
+ chunkHashesValid: false,
1101
+ rootHashMatches: false,
1102
+ merkleRootMatches: false
1103
+ };
1104
+ try {
1105
+ const manifestWithoutHash = {
1106
+ formatVersion: manifest.formatVersion,
1107
+ runId: manifest.runId,
1108
+ agentId: manifest.agentId,
1109
+ rootHash: manifest.rootHash,
1110
+ merkleRoot: manifest.merkleRoot,
1111
+ totalEvents: manifest.totalEvents,
1112
+ totalSpans: manifest.totalSpans,
1113
+ startedAt: manifest.startedAt,
1114
+ endedAt: manifest.endedAt,
1115
+ durationMs: manifest.durationMs,
1116
+ chunks: manifest.chunks,
1117
+ publicView: manifest.publicView
1118
+ };
1119
+ const computedHash = await computeManifestHash(manifestWithoutHash);
1120
+ if (manifest.manifestHash === computedHash) {
1121
+ checks.manifestHashValid = true;
1122
+ } else {
1123
+ errors.push(
1124
+ `Manifest hash mismatch: expected ${manifest.manifestHash}, computed ${computedHash}`
1125
+ );
1126
+ }
1127
+ } catch (error) {
1128
+ errors.push(
1129
+ `Failed to compute manifest hash: ${error instanceof Error ? error.message : String(error)}`
1130
+ );
1131
+ }
1132
+ const chunkByIndex = /* @__PURE__ */ new Map();
1133
+ for (const chunk of chunks) {
1134
+ chunkByIndex.set(chunk.info.index, chunk);
1135
+ }
1136
+ let allChunkHashesValid = true;
1137
+ for (const chunkInfo of manifest.chunks) {
1138
+ const chunk = chunkByIndex.get(chunkInfo.index);
1139
+ if (!chunk) {
1140
+ errors.push(`Missing chunk at index ${chunkInfo.index}`);
1141
+ allChunkHashesValid = false;
1142
+ continue;
1143
+ }
1144
+ try {
1145
+ const computedHash = await utils.sha256StringHex(chunk.content);
1146
+ if (computedHash !== chunkInfo.hash) {
1147
+ errors.push(
1148
+ `Chunk ${chunkInfo.index} hash mismatch: expected ${chunkInfo.hash}, computed ${computedHash}`
1149
+ );
1150
+ allChunkHashesValid = false;
1151
+ }
1152
+ if (chunk.content.length !== chunkInfo.size) {
1153
+ errors.push(
1154
+ `Chunk ${chunkInfo.index} size mismatch: expected ${chunkInfo.size}, got ${chunk.content.length}`
1155
+ );
1156
+ allChunkHashesValid = false;
1157
+ }
1158
+ } catch (error) {
1159
+ errors.push(
1160
+ `Failed to verify chunk ${chunkInfo.index}: ${error instanceof Error ? error.message : String(error)}`
1161
+ );
1162
+ allChunkHashesValid = false;
1163
+ }
1164
+ }
1165
+ for (const chunk of chunks) {
1166
+ const inManifest = manifest.chunks.some((c) => c.index === chunk.info.index);
1167
+ if (!inManifest) {
1168
+ warnings.push(`Extra chunk at index ${chunk.info.index} not referenced in manifest`);
1169
+ }
1170
+ }
1171
+ checks.chunkHashesValid = allChunkHashesValid;
1172
+ if (manifest.rootHash && manifest.rootHash.length > 0) {
1173
+ checks.rootHashMatches = true;
1174
+ } else {
1175
+ errors.push("Manifest is missing rootHash");
1176
+ }
1177
+ if (manifest.merkleRoot && manifest.merkleRoot.length > 0) {
1178
+ checks.merkleRootMatches = true;
1179
+ } else {
1180
+ errors.push("Manifest is missing merkleRoot");
1181
+ }
1182
+ if (manifest.chunks.length === 0) {
1183
+ warnings.push("Manifest has no chunks - trace data may be empty");
1184
+ }
1185
+ if (manifest.totalSpans === 0 && manifest.chunks.length > 0) {
1186
+ warnings.push("Manifest reports 0 spans but has chunks");
1187
+ }
1188
+ const valid = checks.manifestHashValid && checks.chunkHashesValid && checks.rootHashMatches && checks.merkleRootMatches;
1189
+ return {
1190
+ valid,
1191
+ errors,
1192
+ warnings,
1193
+ checks
1194
+ };
1195
+ }
1196
+ async function reconstructBundleFromManifest(manifest, chunks) {
1197
+ const chunkByIndex = /* @__PURE__ */ new Map();
1198
+ for (const chunk of chunks) {
1199
+ chunkByIndex.set(chunk.info.index, chunk);
1200
+ }
1201
+ const allSpans = [];
1202
+ const allEvents = [];
1203
+ const sortedChunkInfos = [...manifest.chunks].sort((a, b) => a.index - b.index);
1204
+ for (const chunkInfo of sortedChunkInfos) {
1205
+ const chunk = chunkByIndex.get(chunkInfo.index);
1206
+ if (!chunk) {
1207
+ throw new Error(`Missing chunk at index ${chunkInfo.index}`);
1208
+ }
1209
+ const { spans, events } = parseChunkContent(chunk.content);
1210
+ allSpans.push(...spans);
1211
+ allEvents.push(...events);
1212
+ }
1213
+ allSpans.sort((a, b) => a.spanSeq - b.spanSeq);
1214
+ allEvents.sort((a, b) => a.seq - b.seq);
1215
+ const nextSeq = allEvents.length > 0 ? Math.max(...allEvents.map((e) => e.seq)) + 1 : 0;
1216
+ const nextSpanSeq = allSpans.length > 0 ? Math.max(...allSpans.map((s) => s.spanSeq)) + 1 : 0;
1217
+ const privateRun = {
1218
+ id: manifest.runId,
1219
+ schemaVersion: manifest.formatVersion,
1220
+ agentId: manifest.agentId,
1221
+ status: manifest.publicView.status,
1222
+ startedAt: manifest.startedAt,
1223
+ endedAt: manifest.endedAt,
1224
+ durationMs: manifest.durationMs,
1225
+ events: allEvents,
1226
+ spans: allSpans,
1227
+ rollingHash: "",
1228
+ // Would need to be recomputed for full verification
1229
+ rootHash: manifest.rootHash,
1230
+ nextSeq,
1231
+ nextSpanSeq
1232
+ };
1233
+ const bundle = {
1234
+ formatVersion: manifest.formatVersion,
1235
+ publicView: manifest.publicView,
1236
+ privateRun,
1237
+ merkleRoot: manifest.merkleRoot,
1238
+ rootHash: manifest.rootHash
1239
+ };
1240
+ if (manifest.manifestHash !== void 0) {
1241
+ bundle.manifestHash = manifest.manifestHash;
1242
+ }
1243
+ return bundle;
1244
+ }
1245
+ function getChunkPath(chunkInfo) {
1246
+ return `chunks/${chunkInfo.hash}.json`;
1247
+ }
1248
+ function parseChunkContent(content) {
1249
+ try {
1250
+ const parsed = JSON.parse(content);
1251
+ if (typeof parsed !== "object" || parsed === null) {
1252
+ throw new Error("Chunk content must be an object");
1253
+ }
1254
+ const obj = parsed;
1255
+ if (!Array.isArray(obj.spans)) {
1256
+ throw new Error("Chunk content must have a 'spans' array");
1257
+ }
1258
+ if (!Array.isArray(obj.events)) {
1259
+ throw new Error("Chunk content must have an 'events' array");
1260
+ }
1261
+ const spans = obj.spans;
1262
+ const events = obj.events;
1263
+ for (const span of spans) {
1264
+ if (typeof span.id !== "string" || typeof span.spanSeq !== "number") {
1265
+ throw new Error("Invalid span structure in chunk content");
1266
+ }
1267
+ }
1268
+ for (const event of events) {
1269
+ if (typeof event.id !== "string" || typeof event.seq !== "number") {
1270
+ throw new Error("Invalid event structure in chunk content");
1271
+ }
1272
+ }
1273
+ return { spans, events };
1274
+ } catch (error) {
1275
+ if (error instanceof SyntaxError) {
1276
+ throw new Error(`Invalid JSON in chunk content: ${error.message}`);
1277
+ }
1278
+ throw error;
1279
+ }
1280
+ }
1281
+
1282
+ // src/index.ts
1283
+ var VERSION = "0.1.0";
1284
+
1285
+ exports.DEFAULT_EVENT_VISIBILITY = DEFAULT_EVENT_VISIBILITY;
1286
+ exports.HASH_DOMAIN_PREFIXES = HASH_DOMAIN_PREFIXES;
1287
+ exports.VERSION = VERSION;
1288
+ exports.addEvent = addEvent;
1289
+ exports.addSpan = addSpan;
1290
+ exports.buildSpanMerkleTree = buildSpanMerkleTree;
1291
+ exports.canDisclose = canDisclose;
1292
+ exports.closeSpan = closeSpan;
1293
+ exports.computeEventHash = computeEventHash;
1294
+ exports.computeEventHashes = computeEventHashes;
1295
+ exports.computeManifestHash = computeManifestHash;
1296
+ exports.computeRollingHash = computeRollingHash;
1297
+ exports.computeRootHash = computeRootHash;
1298
+ exports.computeSpanHash = computeSpanHash;
1299
+ exports.countEventsByVisibility = countEventsByVisibility;
1300
+ exports.countSpansByVisibility = countSpansByVisibility;
1301
+ exports.createBundle = createBundle;
1302
+ exports.createDisclosureRequest = createDisclosureRequest;
1303
+ exports.createManifest = createManifest;
1304
+ exports.createTrace = createTrace;
1305
+ exports.extractPublicView = extractPublicView;
1306
+ exports.filterPublicEvents = filterPublicEvents;
1307
+ exports.finalizeTrace = finalizeTrace;
1308
+ exports.generateMerkleProof = generateMerkleProof;
1309
+ exports.getBundleSpanEvents = getSpanEvents2;
1310
+ exports.getChildSpans = getChildSpans;
1311
+ exports.getChunkPath = getChunkPath;
1312
+ exports.getEvent = getEvent;
1313
+ exports.getEventCount = getEventCount;
1314
+ exports.getEventsByKind = getEventsByKind;
1315
+ exports.getGenesisHash = getGenesisHash;
1316
+ exports.getRootSpans = getRootSpans;
1317
+ exports.getSpan = getSpan;
1318
+ exports.getSpanCount = getSpanCount;
1319
+ exports.getSpanEvents = getSpanEvents;
1320
+ exports.getSpanIndex = getSpanIndex;
1321
+ exports.initRollingHash = initRollingHash;
1322
+ exports.isFinalized = isFinalized;
1323
+ exports.isPublicEvent = isPublicEvent;
1324
+ exports.isPublicSpan = isPublicSpan;
1325
+ exports.parseChunkContent = parseChunkContent;
1326
+ exports.reconstructBundleFromManifest = reconstructBundleFromManifest;
1327
+ exports.selectiveDisclose = selectiveDisclose;
1328
+ exports.signBundle = signBundle;
1329
+ exports.updateRollingHash = updateRollingHash;
1330
+ exports.verifyBundle = verifyBundle;
1331
+ exports.verifyBundleSignature = verifyBundleSignature;
1332
+ exports.verifyDisclosure = verifyDisclosure;
1333
+ exports.verifyManifest = verifyManifest;
1334
+ exports.verifyMerkleProof = verifyMerkleProof;
1335
+ exports.verifyRollingHash = verifyRollingHash;
1336
+ exports.verifySpanDisclosure = verifySpanDisclosure;
1337
+ exports.verifySpanInclusion = verifySpanInclusion;
1338
+ //# sourceMappingURL=index.cjs.map
1339
+ //# sourceMappingURL=index.cjs.map