@rasputin-ai/node 0.5.0-alpha.1 → 0.5.0-alpha.11

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 (41) hide show
  1. package/README.md +15 -15
  2. package/dist/auto-instrumentation/instrument-compiled-esm.d.ts +2 -0
  3. package/dist/auto-instrumentation/instrument-compiled-esm.d.ts.map +1 -1
  4. package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts +2 -1
  5. package/dist/auto-instrumentation/instrumentation-manifest-delta.d.ts.map +1 -1
  6. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +1 -1
  7. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
  8. package/dist/auto-instrumentation/transform-source.d.ts +6 -0
  9. package/dist/auto-instrumentation/transform-source.d.ts.map +1 -1
  10. package/dist/auto-instrumentation/warn-production-runtime-transform.d.ts +8 -0
  11. package/dist/auto-instrumentation/warn-production-runtime-transform.d.ts.map +1 -0
  12. package/dist/derivation/analyze-function.d.ts +42 -0
  13. package/dist/derivation/analyze-function.d.ts.map +1 -0
  14. package/dist/derivation/build-dependency-graph.d.ts +21 -0
  15. package/dist/derivation/build-dependency-graph.d.ts.map +1 -0
  16. package/dist/derivation/derivation-ir.d.ts +33 -0
  17. package/dist/derivation/derivation-ir.d.ts.map +1 -0
  18. package/dist/derivation/derivation-proof.d.ts +14 -0
  19. package/dist/derivation/derivation-proof.d.ts.map +1 -0
  20. package/dist/derivation/emit-capture-plan.d.ts +8 -0
  21. package/dist/derivation/emit-capture-plan.d.ts.map +1 -0
  22. package/dist/execution-recorder/automatic-runtime.d.ts +12 -3
  23. package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
  24. package/dist/execution-recorder/execution-journal.d.ts +84 -0
  25. package/dist/execution-recorder/execution-journal.d.ts.map +1 -0
  26. package/dist/execution-recorder/execution-recorder-types.d.ts +23 -4
  27. package/dist/execution-recorder/execution-recorder-types.d.ts.map +1 -1
  28. package/dist/execution-recorder/execution-recorder.d.ts.map +1 -1
  29. package/dist/execution-recorder/recorder-memory-budget.d.ts +1 -0
  30. package/dist/execution-recorder/recorder-memory-budget.d.ts.map +1 -1
  31. package/dist/execution-recorder/safe-serialize.d.ts +1 -0
  32. package/dist/execution-recorder/safe-serialize.d.ts.map +1 -1
  33. package/dist/index.js +1181 -330
  34. package/dist/instrument/build.js +795 -72
  35. package/dist/instrument/bun.js +790 -67
  36. package/dist/instrument/node.js +790 -67
  37. package/dist/sdk-meta.d.ts +1 -1
  38. package/dist/sdk-meta.d.ts.map +1 -1
  39. package/package.json +2 -2
  40. package/dist/execution-recorder/call-aware-event-buffer.d.ts +0 -24
  41. package/dist/execution-recorder/call-aware-event-buffer.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -133,7 +133,7 @@ var definitionFromEncodedPlan = (plan) => {
133
133
  var estimatedCallBytesFor = (functionId) => EVENT_PAIR_BASE_ESTIMATED_BYTES + Buffer.byteLength(functionId, "utf8") * 2;
134
134
 
135
135
  // src/auto-instrumentation/instrumentation-manifest-registry.ts
136
- var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v2";
136
+ var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v3";
137
137
  var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
138
138
  var getRegistry = () => {
139
139
  const existing = Reflect.get(globalThis, registrySymbol);
@@ -190,6 +190,7 @@ var snapshotInstrumentationManifest = (options = {}) => {
190
190
  const functions = [];
191
191
  const callSites = [];
192
192
  const destructures = [];
193
+ const derivationPlans = [];
193
194
  for (const delta of getRegistry().files.values()) {
194
195
  for (const fn of delta.functions) {
195
196
  const definition = definitionFromEncodedPlan(fn.plan);
@@ -241,6 +242,7 @@ var snapshotInstrumentationManifest = (options = {}) => {
241
242
  binding: destructure.binding
242
243
  });
243
244
  }
245
+ derivationPlans.push(...delta.derivationPlans ?? []);
244
246
  }
245
247
  const registry = getRegistry();
246
248
  if (registry.prebuilt && registry.files.size === 0) {
@@ -268,7 +270,8 @@ var snapshotInstrumentationManifest = (options = {}) => {
268
270
  } : {},
269
271
  functions,
270
272
  callSites,
271
- destructures
273
+ destructures,
274
+ derivationPlans
272
275
  };
273
276
  };
274
277
  var markInstrumentationManifestUploaded = (generation) => {
@@ -384,90 +387,393 @@ var scheduleInstrumentationManifestUpload = (options) => {
384
387
 
385
388
  // src/execution-recorder/execution-recorder.ts
386
389
  import { AsyncLocalStorage } from "node:async_hooks";
387
- import { runtimeFunctionId as runtimeFunctionId2 } from "@rasputin-ai/core";
390
+ import {
391
+ capturedValueCompleteness,
392
+ hashInstrumentationManifest,
393
+ rasputinOmitted,
394
+ rasputinTagKind,
395
+ runtimeFunctionId as runtimeFunctionId2
396
+ } from "@rasputin-ai/core";
388
397
 
389
- // src/execution-recorder/call-aware-event-buffer.ts
390
- var CallAwareEventBuffer = class {
398
+ // src/execution-recorder/execution-journal.ts
399
+ var KIND_ENTER = 1;
400
+ var KIND_EXIT = 2;
401
+ var KIND_THROW = 3;
402
+ var KIND_SUPPRESSED = 4;
403
+ var NO_PARENT = 4294967295;
404
+ var NO_SUPPRESSION = 4294967295;
405
+ var MAX_POOLED_JOURNALS = 8;
406
+ var MAX_POOLED_JOURNAL_CAPACITY = 4096;
407
+ var INITIAL_JOURNAL_STORAGE = 32;
408
+ var BYTES_PER_EVENT = Uint8Array.BYTES_PER_ELEMENT + Uint32Array.BYTES_PER_ELEMENT * 4 + BigInt64Array.BYTES_PER_ELEMENT * 2;
409
+ var DEFAULT_MAX_EVENTS_PER_EXECUTION = 500;
410
+ var MAX_JOURNAL_EVENTS = 16384;
411
+ var elapsedMs = (startedAtNs, endedAtNs) => Number(endedAtNs - startedAtNs) / 1e6;
412
+ var roundedMilliseconds = (nanoseconds) => Math.round(Number(nanoseconds) / 1e6 * 1e4) / 1e4;
413
+ var relativeMs = (relativeNs) => Number(relativeNs) / 1e6;
414
+ var sanitizeJournalCapacity = (value) => {
415
+ const requested = value ?? DEFAULT_MAX_EVENTS_PER_EXECUTION;
416
+ if (!Number.isFinite(requested)) return DEFAULT_MAX_EVENTS_PER_EXECUTION;
417
+ return Math.min(MAX_JOURNAL_EVENTS, Math.max(2, Math.floor(requested)));
418
+ };
419
+ var ExecutionJournal = class {
420
+ dropped = 0;
421
+ allocatedBytes = 0;
422
+ onGrow;
423
+ length = 0;
424
+ kind = new Uint8Array(0);
425
+ planId = new Uint32Array(0);
426
+ callId = new Uint32Array(0);
427
+ parentCallId = new Uint32Array(0);
428
+ timestampNs = new BigInt64Array(0);
429
+ durationNs = new BigInt64Array(0);
430
+ suppressionIndex = new Uint32Array(0);
431
+ payloads = [];
432
+ suppressions = [];
433
+ openCallIds = /* @__PURE__ */ new Set();
434
+ notedPlanIds = [];
435
+ notedPlanSet = /* @__PURE__ */ new Set();
436
+ maxCapacity;
391
437
  constructor(capacity) {
392
- this.capacity = capacity;
438
+ this.maxCapacity = sanitizeJournalCapacity(capacity);
439
+ this.reallocate(Math.min(this.maxCapacity, INITIAL_JOURNAL_STORAGE));
440
+ }
441
+ get capacity() {
442
+ return this.maxCapacity;
443
+ }
444
+ get hasOpenCalls() {
445
+ return this.openCallIds.size > 0;
446
+ }
447
+ get eventCount() {
448
+ return this.length;
449
+ }
450
+ get payloadSlotCount() {
451
+ return this.payloads.length;
393
452
  }
394
- items = [];
395
- openCallIds = /* @__PURE__ */ new Set();
396
- retainedSummaries = /* @__PURE__ */ new WeakSet();
397
- dropped = 0;
398
453
  canStartCall() {
399
- return this.items.length + this.openCallIds.size + 2 <= this.capacity;
454
+ if (this.length + this.openCallIds.size + 2 > this.maxCapacity) return false;
455
+ return this.ensureCapacity(this.length + 2);
400
456
  }
401
- startCall(event) {
402
- this.items.push(event);
403
- this.openCallIds.add(event.callId);
457
+ notePlan(runtimePlanId) {
458
+ if (this.notedPlanSet.has(runtimePlanId)) return;
459
+ this.notedPlanSet.add(runtimePlanId);
460
+ this.notedPlanIds.push(runtimePlanId);
404
461
  }
405
- finishCall(event) {
406
- if (event.type !== "function_exit" && event.type !== "function_throw") return;
407
- if (!this.openCallIds.delete(event.callId)) return;
408
- this.items.push(event);
462
+ registerSuppression(group) {
463
+ if (group.storageIndex >= 0) return group;
464
+ group.storageIndex = this.suppressions.length;
465
+ this.suppressions.push(group);
466
+ return group;
467
+ }
468
+ startCall(runtimePlanId, callId, parentCallId, timestampNs, args) {
469
+ if (!this.write(
470
+ KIND_ENTER,
471
+ runtimePlanId,
472
+ callId,
473
+ parentCallId,
474
+ timestampNs,
475
+ 0n,
476
+ args,
477
+ NO_SUPPRESSION
478
+ )) {
479
+ return;
480
+ }
481
+ this.openCallIds.add(callId);
482
+ }
483
+ finishCall(kind, callId, timestampNs, durationNs, payload) {
484
+ if (!this.openCallIds.delete(callId)) return;
485
+ this.write(kind, 0, callId, void 0, timestampNs, durationNs, payload, NO_SUPPRESSION);
409
486
  }
410
487
  dropCall() {
411
488
  this.dropped += 2;
412
489
  }
413
- append(event) {
414
- if (this.items.length + this.openCallIds.size + 1 > this.capacity) {
490
+ appendSuppressed(group) {
491
+ if (this.length + this.openCallIds.size + 1 > this.maxCapacity) {
492
+ this.dropped++;
493
+ return false;
494
+ }
495
+ this.registerSuppression(group);
496
+ group.retainedIndex = this.length;
497
+ if (!this.write(
498
+ KIND_SUPPRESSED,
499
+ group.runtimePlanId,
500
+ 0,
501
+ group.parentCallId,
502
+ 0n,
503
+ 0n,
504
+ void 0,
505
+ group.storageIndex
506
+ )) {
507
+ group.retainedIndex = -1;
415
508
  this.dropped++;
416
509
  return false;
417
510
  }
418
- this.items.push(event);
419
- if (event.type === "function_calls_suppressed") this.retainedSummaries.add(event);
420
511
  return true;
421
512
  }
422
- hasSummary(event) {
423
- return this.retainedSummaries.has(event);
424
- }
425
- forceCompletedCall(enter, terminal) {
426
- while (this.items.length + this.openCallIds.size + 2 > this.capacity) {
513
+ forceCompletedCall(runtimePlanId, callId, parentCallId, enterTimestampNs, args, terminalKind, terminalTimestampNs, durationNs, payload) {
514
+ while (this.length + this.openCallIds.size + 2 > this.maxCapacity) {
427
515
  if (!this.evictCompletedLeafOrSummary()) {
428
516
  this.dropCall();
429
517
  return false;
430
518
  }
431
519
  }
432
- this.items.push(enter, terminal);
520
+ if (!this.ensureCapacity(this.length + 2)) {
521
+ this.dropCall();
522
+ return false;
523
+ }
524
+ this.write(
525
+ KIND_ENTER,
526
+ runtimePlanId,
527
+ callId,
528
+ parentCallId,
529
+ enterTimestampNs,
530
+ 0n,
531
+ args,
532
+ NO_SUPPRESSION
533
+ );
534
+ this.write(
535
+ terminalKind,
536
+ 0,
537
+ callId,
538
+ void 0,
539
+ terminalTimestampNs,
540
+ durationNs,
541
+ payload,
542
+ NO_SUPPRESSION
543
+ );
544
+ return true;
545
+ }
546
+ materialize(plansByRuntimeId, executionStartedAtNs) {
547
+ const events = [];
548
+ const referencedFunctionIds = /* @__PURE__ */ new Set();
549
+ for (let index = 0; index < this.length; index++) {
550
+ const event = this.eventAt(index, plansByRuntimeId, executionStartedAtNs);
551
+ if (!event) continue;
552
+ events.push(event);
553
+ if (event.type === "function_enter" || event.type === "function_calls_suppressed") {
554
+ referencedFunctionIds.add(event.functionId);
555
+ }
556
+ }
557
+ const functions = [];
558
+ const seenFunctionIds = /* @__PURE__ */ new Set();
559
+ for (const runtimePlanId of this.notedPlanIds) {
560
+ const definition = plansByRuntimeId[runtimePlanId]?.definition;
561
+ if (!definition || !referencedFunctionIds.has(definition.functionId)) continue;
562
+ if (seenFunctionIds.has(definition.functionId)) continue;
563
+ seenFunctionIds.add(definition.functionId);
564
+ functions.push(definition);
565
+ }
566
+ return { events, functions };
567
+ }
568
+ clear() {
569
+ this.length = 0;
570
+ this.dropped = 0;
571
+ this.onGrow = void 0;
572
+ this.openCallIds.clear();
573
+ this.notedPlanIds.length = 0;
574
+ this.notedPlanSet.clear();
575
+ for (let index = 0; index < this.payloads.length; index++) this.payloads[index] = void 0;
576
+ this.payloads.length = 0;
577
+ for (const group of this.suppressions) {
578
+ group.retainedIndex = -1;
579
+ group.storageIndex = -1;
580
+ }
581
+ this.suppressions.length = 0;
582
+ }
583
+ eventAt(index, plansByRuntimeId, executionStartedAtNs) {
584
+ const kind = this.kind[index];
585
+ if (kind === KIND_ENTER) {
586
+ const functionId = plansByRuntimeId[this.planId[index] ?? 0]?.functionId;
587
+ if (!functionId) return void 0;
588
+ const parentCallId = this.parentCallId[index] ?? NO_PARENT;
589
+ const payload = this.payloads[index];
590
+ return {
591
+ type: "function_enter",
592
+ callId: this.callId[index] ?? 0,
593
+ functionId,
594
+ timestampMs: relativeMs(this.timestampNs[index] ?? 0n),
595
+ args: Array.isArray(payload) ? payload : [],
596
+ ...parentCallId === NO_PARENT ? {} : { parentCallId }
597
+ };
598
+ }
599
+ if (kind === KIND_EXIT) {
600
+ return {
601
+ type: "function_exit",
602
+ callId: this.callId[index] ?? 0,
603
+ timestampMs: relativeMs(this.timestampNs[index] ?? 0n),
604
+ durationMs: relativeMs(this.durationNs[index] ?? 0n),
605
+ returnValue: this.payloads[index]
606
+ };
607
+ }
608
+ if (kind === KIND_THROW) {
609
+ return {
610
+ type: "function_throw",
611
+ callId: this.callId[index] ?? 0,
612
+ timestampMs: relativeMs(this.timestampNs[index] ?? 0n),
613
+ durationMs: relativeMs(this.durationNs[index] ?? 0n),
614
+ error: this.payloads[index]
615
+ };
616
+ }
617
+ if (kind === KIND_SUPPRESSED) {
618
+ const group = this.suppressions[this.suppressionIndex[index] ?? 0];
619
+ if (!group) return void 0;
620
+ return {
621
+ type: "function_calls_suppressed",
622
+ functionId: group.functionId,
623
+ timestampMs: elapsedMs(executionStartedAtNs, group.firstTimestampNs),
624
+ lastTimestampMs: elapsedMs(executionStartedAtNs, group.lastTimestampNs),
625
+ suppressedCallCount: group.count,
626
+ totalDurationMs: roundedMilliseconds(group.totalDurationNs),
627
+ ...group.parentCallId === void 0 ? {} : { parentCallId: group.parentCallId }
628
+ };
629
+ }
630
+ return void 0;
631
+ }
632
+ write(kind, runtimePlanId, callId, parentCallId, timestampNs, durationNs, payload, suppressionIndex) {
633
+ if (!this.ensureCapacity(this.length + 1)) return false;
634
+ const index = this.length;
635
+ this.kind[index] = kind;
636
+ this.planId[index] = runtimePlanId;
637
+ this.callId[index] = callId;
638
+ this.parentCallId[index] = parentCallId === void 0 ? NO_PARENT : parentCallId;
639
+ this.timestampNs[index] = timestampNs;
640
+ this.durationNs[index] = durationNs;
641
+ this.payloads[index] = payload;
642
+ this.suppressionIndex[index] = suppressionIndex;
643
+ this.length++;
433
644
  return true;
434
645
  }
435
- values() {
436
- return [...this.items];
646
+ ensureCapacity(needed) {
647
+ if (needed <= this.kind.length) return true;
648
+ if (needed > this.maxCapacity) return false;
649
+ let next = this.kind.length === 0 ? Math.min(INITIAL_JOURNAL_STORAGE, this.maxCapacity) : this.kind.length;
650
+ while (next < needed) {
651
+ const grown = Math.min(this.maxCapacity, next * 2);
652
+ if (grown <= next) return false;
653
+ next = grown;
654
+ }
655
+ const extraBytes = (next - this.kind.length) * BYTES_PER_EVENT;
656
+ if (this.onGrow && extraBytes > 0 && !this.onGrow(extraBytes)) return false;
657
+ this.reallocate(next);
658
+ return true;
659
+ }
660
+ reallocate(size) {
661
+ const nextKind = new Uint8Array(size);
662
+ const nextPlanId = new Uint32Array(size);
663
+ const nextCallId = new Uint32Array(size);
664
+ const nextParentCallId = new Uint32Array(size);
665
+ const nextTimestampNs = new BigInt64Array(size);
666
+ const nextDurationNs = new BigInt64Array(size);
667
+ const nextSuppressionIndex = new Uint32Array(size);
668
+ if (this.length > 0) {
669
+ nextKind.set(this.kind.subarray(0, this.length));
670
+ nextPlanId.set(this.planId.subarray(0, this.length));
671
+ nextCallId.set(this.callId.subarray(0, this.length));
672
+ nextParentCallId.set(this.parentCallId.subarray(0, this.length));
673
+ nextTimestampNs.set(this.timestampNs.subarray(0, this.length));
674
+ nextDurationNs.set(this.durationNs.subarray(0, this.length));
675
+ nextSuppressionIndex.set(this.suppressionIndex.subarray(0, this.length));
676
+ }
677
+ this.kind = nextKind;
678
+ this.planId = nextPlanId;
679
+ this.callId = nextCallId;
680
+ this.parentCallId = nextParentCallId;
681
+ this.timestampNs = nextTimestampNs;
682
+ this.durationNs = nextDurationNs;
683
+ this.suppressionIndex = nextSuppressionIndex;
684
+ this.allocatedBytes = size * BYTES_PER_EVENT;
437
685
  }
438
686
  evictCompletedLeafOrSummary() {
439
- const summaryIndex = this.items.findIndex(
440
- (event) => event.type === "function_calls_suppressed"
441
- );
442
- if (summaryIndex >= 0) {
443
- const summary = this.items[summaryIndex];
444
- if (summary) this.retainedSummaries.delete(summary);
445
- this.items.splice(summaryIndex, 1);
687
+ for (let index = 0; index < this.length; index++) {
688
+ if (this.kind[index] !== KIND_SUPPRESSED) continue;
689
+ const group = this.suppressions[this.suppressionIndex[index] ?? 0];
690
+ if (group) group.retainedIndex = -1;
691
+ this.removeAt(index);
446
692
  this.dropped++;
447
693
  return true;
448
694
  }
449
695
  const parentCallIds = /* @__PURE__ */ new Set();
450
- for (const event of this.items) {
451
- if ((event.type === "function_enter" || event.type === "function_calls_suppressed") && event.parentCallId !== void 0) {
452
- parentCallIds.add(event.parentCallId);
453
- }
696
+ for (let index = 0; index < this.length; index++) {
697
+ const kind = this.kind[index];
698
+ if (kind !== KIND_ENTER && kind !== KIND_SUPPRESSED) continue;
699
+ const parentCallId = this.parentCallId[index] ?? NO_PARENT;
700
+ if (parentCallId !== NO_PARENT) parentCallIds.add(parentCallId);
454
701
  }
455
- for (let index = 0; index < this.items.length; index++) {
456
- const event = this.items[index];
457
- if (event?.type !== "function_enter" || this.openCallIds.has(event.callId) || parentCallIds.has(event.callId)) {
458
- continue;
702
+ for (let index = 0; index < this.length; index++) {
703
+ if (this.kind[index] !== KIND_ENTER) continue;
704
+ const eventCallId = this.callId[index] ?? 0;
705
+ if (this.openCallIds.has(eventCallId) || parentCallIds.has(eventCallId)) continue;
706
+ let terminalIndex = -1;
707
+ for (let candidate = 0; candidate < this.length; candidate++) {
708
+ const kind = this.kind[candidate];
709
+ if ((kind === KIND_EXIT || kind === KIND_THROW) && this.callId[candidate] === eventCallId) {
710
+ terminalIndex = candidate;
711
+ break;
712
+ }
459
713
  }
460
- const terminalIndex = this.items.findIndex(
461
- (candidate) => (candidate.type === "function_exit" || candidate.type === "function_throw") && candidate.callId === event.callId
462
- );
463
714
  if (terminalIndex < 0) continue;
464
- this.items.splice(Math.max(index, terminalIndex), 1);
465
- this.items.splice(Math.min(index, terminalIndex), 1);
715
+ const later = Math.max(index, terminalIndex);
716
+ const earlier = Math.min(index, terminalIndex);
717
+ this.removeAt(later);
718
+ this.removeAt(earlier);
466
719
  this.dropped += 2;
467
720
  return true;
468
721
  }
469
722
  return false;
470
723
  }
724
+ removeAt(index) {
725
+ this.payloads[index] = void 0;
726
+ const next = index + 1;
727
+ if (next < this.length) {
728
+ this.kind.copyWithin(index, next, this.length);
729
+ this.planId.copyWithin(index, next, this.length);
730
+ this.callId.copyWithin(index, next, this.length);
731
+ this.parentCallId.copyWithin(index, next, this.length);
732
+ this.timestampNs.copyWithin(index, next, this.length);
733
+ this.durationNs.copyWithin(index, next, this.length);
734
+ this.suppressionIndex.copyWithin(index, next, this.length);
735
+ this.payloads.copyWithin(index, next, this.length);
736
+ }
737
+ this.payloads[this.length - 1] = void 0;
738
+ this.length--;
739
+ this.payloads.length = this.length;
740
+ for (const group of this.suppressions) {
741
+ if (group.retainedIndex > index) group.retainedIndex--;
742
+ }
743
+ }
744
+ };
745
+ var ExecutionJournalPool = class {
746
+ constructor(capacity, maxPooled = MAX_POOLED_JOURNALS) {
747
+ this.capacity = capacity;
748
+ this.maxPooled = maxPooled;
749
+ }
750
+ hits = 0;
751
+ misses = 0;
752
+ available = [];
753
+ acquire() {
754
+ const pooled = this.available.pop();
755
+ if (pooled) {
756
+ this.hits++;
757
+ return pooled;
758
+ }
759
+ this.misses++;
760
+ return new ExecutionJournal(this.capacity);
761
+ }
762
+ release(journal) {
763
+ journal.clear();
764
+ if (this.available.length >= this.maxPooled || journal.capacity > MAX_POOLED_JOURNAL_CAPACITY || journal.capacity !== this.capacity) {
765
+ return;
766
+ }
767
+ this.available.push(journal);
768
+ }
769
+ stats() {
770
+ return {
771
+ journalPoolHits: this.hits,
772
+ journalPoolMisses: this.misses,
773
+ journalPoolSize: this.available.length,
774
+ journalAllocatedCapacity: this.capacity
775
+ };
776
+ }
471
777
  };
472
778
 
473
779
  // src/execution-recorder/recorder-memory-budget.ts
@@ -545,6 +851,12 @@ var RecorderMemoryBudget = class {
545
851
  this.dropEvents(allocation, 1);
546
852
  return false;
547
853
  }
854
+ reserveStorage(allocation, bytes) {
855
+ if (allocation.released || allocation.mode === "metadata-only") return false;
856
+ if (this.reserve(allocation, bytes)) return true;
857
+ this.markPressure(allocation, "metadata-only");
858
+ return false;
859
+ }
548
860
  release(allocation) {
549
861
  if (allocation.released) return;
550
862
  allocation.released = true;
@@ -605,6 +917,7 @@ var defaults = {
605
917
  maxStringLength: 500,
606
918
  maxSerializedValueBytes: 8 * 1024
607
919
  };
920
+ var REDACTED = "[RASPUTIN_REDACTED]";
608
921
  var truncation = (reason, extra = {}) => ({
609
922
  __rasputin_truncated: true,
610
923
  __reason: reason,
@@ -614,7 +927,83 @@ var typeMarker = (type, value) => ({
614
927
  __rasputin_type: type,
615
928
  ...value === void 0 ? {} : { value }
616
929
  });
617
- var REDACTED = "[RASPUTIN_REDACTED]";
930
+ var jsonStringUtf8Bytes = (value) => {
931
+ let bytes = 2;
932
+ for (let index = 0; index < value.length; index++) {
933
+ const code = value.charCodeAt(index);
934
+ if (code === 34 || code === 92) {
935
+ bytes += 2;
936
+ continue;
937
+ }
938
+ if (code === 8 || code === 9 || code === 10 || code === 12 || code === 13) {
939
+ bytes += 2;
940
+ continue;
941
+ }
942
+ if (code < 32) {
943
+ bytes += 6;
944
+ continue;
945
+ }
946
+ if (code < 128) {
947
+ bytes += 1;
948
+ continue;
949
+ }
950
+ if (code < 2048) {
951
+ bytes += 2;
952
+ continue;
953
+ }
954
+ if (code >= 55296 && code <= 56319) {
955
+ const next = value.charCodeAt(index + 1);
956
+ if (index + 1 < value.length && next >= 56320 && next <= 57343) {
957
+ bytes += 4;
958
+ index++;
959
+ continue;
960
+ }
961
+ bytes += 6;
962
+ continue;
963
+ }
964
+ if (code >= 56320 && code <= 57343) {
965
+ bytes += 6;
966
+ continue;
967
+ }
968
+ bytes += 3;
969
+ }
970
+ return bytes;
971
+ };
972
+ var jsonLiteralUtf8Bytes = (value) => {
973
+ if (value === null) return 4;
974
+ switch (typeof value) {
975
+ case "boolean":
976
+ return value ? 4 : 5;
977
+ case "number":
978
+ if (!Number.isFinite(value)) return 4;
979
+ if (Object.is(value, -0)) return 1;
980
+ return String(value).length;
981
+ case "string":
982
+ return jsonStringUtf8Bytes(value);
983
+ case "object": {
984
+ if (Array.isArray(value)) {
985
+ let bytes2 = 2;
986
+ for (let index = 0; index < value.length; index++) {
987
+ if (index > 0) bytes2 += 1;
988
+ bytes2 += jsonLiteralUtf8Bytes(value[index]);
989
+ }
990
+ return bytes2;
991
+ }
992
+ const record = value;
993
+ let bytes = 2;
994
+ let first = true;
995
+ for (const key of Object.keys(record)) {
996
+ if (!first) bytes += 1;
997
+ first = false;
998
+ bytes += jsonStringUtf8Bytes(key) + 1;
999
+ bytes += jsonLiteralUtf8Bytes(record[key]);
1000
+ }
1001
+ return bytes;
1002
+ }
1003
+ default:
1004
+ return Buffer2.byteLength(JSON.stringify(value) ?? "null", "utf8");
1005
+ }
1006
+ };
618
1007
  var compileLimits = (options) => ({
619
1008
  maxDepth: options.maxDepth ?? defaults.maxDepth,
620
1009
  maxObjectKeys: options.maxObjectKeys ?? defaults.maxObjectKeys,
@@ -626,135 +1015,340 @@ var compileRedactKeys = (options) => {
626
1015
  if (!options.redactKeys?.length) return new Set(DEFAULT_REDACT_KEYS);
627
1016
  return /* @__PURE__ */ new Set([...DEFAULT_REDACT_KEYS, ...options.redactKeys.map((key) => key.toLowerCase())]);
628
1017
  };
1018
+ var keyNeedsCaseFold = (key) => {
1019
+ for (let index = 0; index < key.length; index++) {
1020
+ const code = key.charCodeAt(index);
1021
+ if (code > 127 || code >= 65 && code <= 90) return true;
1022
+ }
1023
+ return false;
1024
+ };
1025
+ var isJsonSafeString = (value) => {
1026
+ for (let index = 0; index < value.length; index++) {
1027
+ const code = value.charCodeAt(index);
1028
+ if (code < 32 || code === 34 || code === 92) return false;
1029
+ if (code >= 55296 && code <= 56319) {
1030
+ const next = value.charCodeAt(index + 1);
1031
+ if (index + 1 >= value.length || next < 56320 || next > 57343) return false;
1032
+ index++;
1033
+ continue;
1034
+ }
1035
+ if (code >= 56320 && code <= 57343) return false;
1036
+ }
1037
+ return true;
1038
+ };
1039
+ var primitiveFastPath = (value, maxStringLength) => {
1040
+ if (value === null) return { value: null, bytes: 4, visitedNodes: 1 };
1041
+ if (typeof value === "boolean") return { value, bytes: value ? 4 : 5, visitedNodes: 1 };
1042
+ if (typeof value === "number") {
1043
+ if (!Number.isFinite(value)) return { value, bytes: 4, visitedNodes: 1 };
1044
+ if (Object.is(value, -0)) return { value, bytes: 1, visitedNodes: 1 };
1045
+ return { value, bytes: String(value).length, visitedNodes: 1 };
1046
+ }
1047
+ if (typeof value === "string") {
1048
+ if (value.length > maxStringLength || !isJsonSafeString(value)) return void 0;
1049
+ return { value, bytes: 2 + Buffer2.byteLength(value, "utf8"), visitedNodes: 1 };
1050
+ }
1051
+ if (typeof value === "undefined") {
1052
+ const serialized = typeMarker("undefined");
1053
+ return { value: serialized, bytes: jsonLiteralUtf8Bytes(serialized), visitedNodes: 1 };
1054
+ }
1055
+ if (typeof value === "bigint") {
1056
+ const serialized = typeMarker("bigint", value.toString());
1057
+ return { value: serialized, bytes: jsonLiteralUtf8Bytes(serialized), visitedNodes: 1 };
1058
+ }
1059
+ if (typeof value === "symbol") {
1060
+ const text = String(value);
1061
+ if (!isJsonSafeString(text)) return void 0;
1062
+ const serialized = typeMarker("symbol", text);
1063
+ return { value: serialized, bytes: jsonLiteralUtf8Bytes(serialized), visitedNodes: 1 };
1064
+ }
1065
+ if (typeof value === "function") {
1066
+ const name = value.name || "anonymous";
1067
+ if (!isJsonSafeString(name)) return void 0;
1068
+ const serialized = typeMarker("function", name);
1069
+ return { value: serialized, bytes: jsonLiteralUtf8Bytes(serialized), visitedNodes: 1 };
1070
+ }
1071
+ return void 0;
1072
+ };
629
1073
  var createSafeSerializer = (options = {}) => {
630
1074
  const limits = compileLimits(options);
631
1075
  const redactKeys = compileRedactKeys(options);
632
- const serializeInner = (value, depth, seen, metrics) => {
1076
+ const sharedPath = /* @__PURE__ */ new Set();
1077
+ let serializing = false;
1078
+ const isRedactedKey = (key) => {
1079
+ if (redactKeys.has(key)) return true;
1080
+ if (!keyNeedsCaseFold(key)) return false;
1081
+ return redactKeys.has(key.toLowerCase());
1082
+ };
1083
+ const addBytes = (metrics, count) => {
1084
+ metrics.bytes += count;
1085
+ if (metrics.bytes > limits.maxSerializedValueBytes) metrics.oversize = true;
1086
+ };
1087
+ const boundedOversize = (bytes, visitedNodes) => {
1088
+ const bounded = truncation("max_serialized_value_bytes", { __serialized_bytes: bytes });
1089
+ return { value: bounded, bytes: jsonLiteralUtf8Bytes(bounded), visitedNodes };
1090
+ };
1091
+ const openObject = (metrics, allocate) => {
1092
+ addBytes(metrics, 2);
1093
+ return {
1094
+ target: allocate && !metrics.oversize ? {} : void 0,
1095
+ first: true
1096
+ };
1097
+ };
1098
+ const openArray = (metrics, allocate) => {
1099
+ addBytes(metrics, 2);
1100
+ return {
1101
+ items: allocate && !metrics.oversize ? [] : void 0,
1102
+ first: true
1103
+ };
1104
+ };
1105
+ const beginField = (metrics, object, key) => {
1106
+ if (!object.first) addBytes(metrics, 1);
1107
+ object.first = false;
1108
+ addBytes(metrics, jsonStringUtf8Bytes(key) + 1);
1109
+ };
1110
+ const assignField = (object, key, fieldValue) => {
1111
+ if (object.target) object.target[key] = fieldValue;
1112
+ };
1113
+ const writeLiteralField = (metrics, object, key, jsonValue) => {
1114
+ beginField(metrics, object, key);
1115
+ addBytes(metrics, jsonLiteralUtf8Bytes(jsonValue));
1116
+ assignField(object, key, jsonValue);
1117
+ };
1118
+ const writeTypeMarker = (metrics, allocate, type, nestedValue) => {
1119
+ const object = openObject(metrics, allocate);
1120
+ writeLiteralField(metrics, object, "__rasputin_type", type);
1121
+ if (nestedValue !== void 0) writeLiteralField(metrics, object, "value", nestedValue);
1122
+ return object.target;
1123
+ };
1124
+ const writeTruncation = (metrics, allocate, reason, extra = {}) => {
1125
+ const object = openObject(metrics, allocate);
1126
+ writeLiteralField(metrics, object, "__rasputin_truncated", true);
1127
+ writeLiteralField(metrics, object, "__reason", reason);
1128
+ for (const key of Object.keys(extra)) writeLiteralField(metrics, object, key, extra[key]);
1129
+ return object.target;
1130
+ };
1131
+ const serializeInner = (value, depth, path, allocate, metrics) => {
633
1132
  metrics.visitedNodes++;
634
- if (value === null || typeof value === "boolean" || typeof value === "number") return value;
1133
+ const store = allocate && !metrics.oversize;
1134
+ if (value === null) {
1135
+ addBytes(metrics, 4);
1136
+ return store ? null : void 0;
1137
+ }
1138
+ if (typeof value === "boolean") {
1139
+ addBytes(metrics, value ? 4 : 5);
1140
+ return store ? value : void 0;
1141
+ }
1142
+ if (typeof value === "number") {
1143
+ if (!Number.isFinite(value)) addBytes(metrics, 4);
1144
+ else if (Object.is(value, -0)) addBytes(metrics, 1);
1145
+ else addBytes(metrics, String(value).length);
1146
+ return store ? value : void 0;
1147
+ }
635
1148
  if (typeof value === "string") {
636
- if (value.length <= limits.maxStringLength) return value;
637
- return truncation("max_string_length", {
1149
+ if (value.length <= limits.maxStringLength) {
1150
+ addBytes(metrics, jsonStringUtf8Bytes(value));
1151
+ return store ? value : void 0;
1152
+ }
1153
+ return writeTruncation(metrics, store, "max_string_length", {
638
1154
  __original_length: value.length,
639
1155
  value: value.slice(0, limits.maxStringLength)
640
1156
  });
641
1157
  }
642
- if (typeof value === "undefined") return typeMarker("undefined");
643
- if (typeof value === "bigint") return typeMarker("bigint", value.toString());
644
- if (typeof value === "symbol") return typeMarker("symbol", String(value));
645
- if (typeof value === "function") return typeMarker("function", value.name || "anonymous");
646
- if (depth >= limits.maxDepth) return truncation("max_depth");
647
- if (typeof value !== "object") return typeMarker(typeof value);
648
- const tracking = seen ?? /* @__PURE__ */ new WeakSet();
649
- if (tracking.has(value)) return typeMarker("circular");
650
- tracking.add(value);
1158
+ if (typeof value === "undefined") return writeTypeMarker(metrics, store, "undefined");
1159
+ if (typeof value === "bigint") {
1160
+ return writeTypeMarker(metrics, store, "bigint", value.toString());
1161
+ }
1162
+ if (typeof value === "symbol") {
1163
+ return writeTypeMarker(metrics, store, "symbol", String(value));
1164
+ }
1165
+ if (typeof value === "function") {
1166
+ return writeTypeMarker(metrics, store, "function", value.name || "anonymous");
1167
+ }
1168
+ if (depth >= limits.maxDepth) return writeTruncation(metrics, store, "max_depth");
1169
+ if (typeof value !== "object") return writeTypeMarker(metrics, store, typeof value);
1170
+ if (path.has(value)) return writeTypeMarker(metrics, store, "circular");
1171
+ path.add(value);
1172
+ const bytesBefore = metrics.bytes;
1173
+ const oversizeBefore = metrics.oversize;
651
1174
  try {
1175
+ const childAllocate = allocate && !metrics.oversize;
652
1176
  if (value instanceof Error) {
653
- return {
654
- __rasputin_type: "Error",
655
- name: value.name,
656
- message: serializeInner(value.message, depth + 1, tracking, metrics),
657
- ...value.stack ? { stack: serializeInner(value.stack, depth + 1, tracking, metrics) } : {}
658
- };
1177
+ const object2 = openObject(metrics, childAllocate);
1178
+ writeLiteralField(metrics, object2, "__rasputin_type", "Error");
1179
+ writeLiteralField(metrics, object2, "name", value.name);
1180
+ beginField(metrics, object2, "message");
1181
+ assignField(
1182
+ object2,
1183
+ "message",
1184
+ serializeInner(value.message, depth + 1, path, childAllocate, metrics)
1185
+ );
1186
+ if (value.stack) {
1187
+ beginField(metrics, object2, "stack");
1188
+ assignField(
1189
+ object2,
1190
+ "stack",
1191
+ serializeInner(value.stack, depth + 1, path, childAllocate, metrics)
1192
+ );
1193
+ }
1194
+ return object2.target;
1195
+ }
1196
+ if (value instanceof Date) {
1197
+ return writeTypeMarker(metrics, childAllocate, "Date", value.toISOString());
659
1198
  }
660
- if (value instanceof Date) return typeMarker("Date", value.toISOString());
661
1199
  if (Buffer2.isBuffer(value)) {
662
- return typeMarker("Buffer", {
1200
+ return writeTypeMarker(metrics, childAllocate, "Buffer", {
663
1201
  byteLength: value.byteLength,
664
1202
  base64: value.subarray(0, limits.maxStringLength).toString("base64"),
665
1203
  ...value.byteLength > limits.maxStringLength ? { __rasputin_truncated: true } : {}
666
1204
  });
667
1205
  }
668
- if (value instanceof Promise) return typeMarker("Promise");
1206
+ if (value instanceof Promise) return writeTypeMarker(metrics, childAllocate, "Promise");
669
1207
  if (value instanceof Map) {
670
- const entries = [];
1208
+ const object2 = openObject(metrics, childAllocate);
1209
+ writeLiteralField(metrics, object2, "__rasputin_type", "Map");
1210
+ beginField(metrics, object2, "entries");
1211
+ const entries = openArray(metrics, childAllocate);
1212
+ let counted2 = 0;
671
1213
  for (const entry of value.entries()) {
672
- if (entries.length >= limits.maxArrayElements) break;
673
- entries.push([
674
- serializeInner(entry[0], depth + 1, tracking, metrics),
675
- serializeInner(entry[1], depth + 1, tracking, metrics)
676
- ]);
1214
+ if (counted2 >= limits.maxArrayElements) break;
1215
+ if (!entries.first) addBytes(metrics, 1);
1216
+ entries.first = false;
1217
+ addBytes(metrics, 2);
1218
+ const key = serializeInner(entry[0], depth + 1, path, childAllocate, metrics);
1219
+ addBytes(metrics, 1);
1220
+ const mapped = serializeInner(entry[1], depth + 1, path, childAllocate, metrics);
1221
+ entries.items?.push([key, mapped]);
1222
+ counted2++;
677
1223
  }
678
- return {
679
- __rasputin_type: "Map",
680
- entries,
681
- ...value.size > limits.maxArrayElements ? truncation("max_array_elements", { __original_length: value.size }) : {}
682
- };
1224
+ assignField(object2, "entries", entries.items ?? []);
1225
+ if (value.size > limits.maxArrayElements) {
1226
+ writeLiteralField(metrics, object2, "__rasputin_truncated", true);
1227
+ writeLiteralField(metrics, object2, "__reason", "max_array_elements");
1228
+ writeLiteralField(metrics, object2, "__original_length", value.size);
1229
+ }
1230
+ return object2.target;
683
1231
  }
684
1232
  if (value instanceof Set) {
685
- const items = [];
1233
+ const object2 = openObject(metrics, childAllocate);
1234
+ writeLiteralField(metrics, object2, "__rasputin_type", "Set");
1235
+ beginField(metrics, object2, "values");
1236
+ const items = openArray(metrics, childAllocate);
1237
+ let counted2 = 0;
686
1238
  for (const item of value.values()) {
687
- if (items.length >= limits.maxArrayElements) break;
688
- items.push(serializeInner(item, depth + 1, tracking, metrics));
1239
+ if (counted2 >= limits.maxArrayElements) break;
1240
+ if (!items.first) addBytes(metrics, 1);
1241
+ items.first = false;
1242
+ const serializedItem = serializeInner(item, depth + 1, path, childAllocate, metrics);
1243
+ items.items?.push(serializedItem);
1244
+ counted2++;
1245
+ }
1246
+ assignField(object2, "values", items.items ?? []);
1247
+ if (value.size > limits.maxArrayElements) {
1248
+ writeLiteralField(metrics, object2, "__rasputin_truncated", true);
1249
+ writeLiteralField(metrics, object2, "__reason", "max_array_elements");
1250
+ writeLiteralField(metrics, object2, "__original_length", value.size);
689
1251
  }
690
- return {
691
- __rasputin_type: "Set",
692
- values: items,
693
- ...value.size > limits.maxArrayElements ? truncation("max_array_elements", { __original_length: value.size }) : {}
694
- };
1252
+ return object2.target;
695
1253
  }
696
1254
  if (Array.isArray(value)) {
1255
+ const items = openArray(metrics, childAllocate);
697
1256
  const maxItems = value.length > limits.maxArrayElements ? limits.maxArrayElements - 1 : value.length;
698
- const items = [];
699
1257
  for (let index = 0; index < maxItems; index++) {
700
- items.push(serializeInner(value[index], depth + 1, tracking, metrics));
1258
+ if (!items.first) addBytes(metrics, 1);
1259
+ items.first = false;
1260
+ items.items?.push(serializeInner(value[index], depth + 1, path, childAllocate, metrics));
701
1261
  }
702
1262
  if (value.length > limits.maxArrayElements) {
703
- items.push(truncation("max_array_elements", { __original_length: value.length }));
1263
+ if (!items.first) addBytes(metrics, 1);
1264
+ items.first = false;
1265
+ items.items?.push(
1266
+ writeTruncation(metrics, childAllocate, "max_array_elements", {
1267
+ __original_length: value.length
1268
+ })
1269
+ );
704
1270
  }
705
- return items;
1271
+ return items.items;
706
1272
  }
1273
+ const object = openObject(metrics, childAllocate);
707
1274
  const prototype = Object.getPrototypeOf(value);
708
- const serialized = {};
709
1275
  if (prototype && prototype !== Object.prototype) {
710
- serialized.__rasputin_type = prototype.constructor?.name ?? "instance";
1276
+ writeLiteralField(
1277
+ metrics,
1278
+ object,
1279
+ "__rasputin_type",
1280
+ prototype.constructor?.name ?? "instance"
1281
+ );
711
1282
  }
712
1283
  const keys = Object.keys(value);
713
1284
  const maxKeys = keys.length > limits.maxObjectKeys ? limits.maxObjectKeys - 1 : keys.length;
714
1285
  for (let index = 0; index < maxKeys; index++) {
715
1286
  const key = keys[index];
716
1287
  if (key === void 0) continue;
717
- if (redactKeys.has(key.toLowerCase())) {
718
- serialized[key] = REDACTED;
1288
+ if (isRedactedKey(key)) {
1289
+ writeLiteralField(metrics, object, key, REDACTED);
719
1290
  continue;
720
1291
  }
721
1292
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
722
- serialized[key] = descriptor && "value" in descriptor ? serializeInner(descriptor.value, depth + 1, tracking, metrics) : typeMarker("accessor");
1293
+ if (descriptor && "value" in descriptor) {
1294
+ beginField(metrics, object, key);
1295
+ assignField(
1296
+ object,
1297
+ key,
1298
+ serializeInner(descriptor.value, depth + 1, path, childAllocate, metrics)
1299
+ );
1300
+ } else {
1301
+ beginField(metrics, object, key);
1302
+ assignField(object, key, writeTypeMarker(metrics, childAllocate, "accessor"));
1303
+ }
723
1304
  }
724
1305
  if (keys.length > limits.maxObjectKeys) {
725
- Object.assign(
726
- serialized,
727
- truncation("max_object_keys", { __original_length: keys.length })
728
- );
1306
+ writeLiteralField(metrics, object, "__rasputin_truncated", true);
1307
+ writeLiteralField(metrics, object, "__reason", "max_object_keys");
1308
+ writeLiteralField(metrics, object, "__original_length", keys.length);
729
1309
  }
730
- return serialized;
1310
+ return object.target;
731
1311
  } catch (error) {
732
- return typeMarker("unserializable", error instanceof Error ? error.message : void 0);
1312
+ metrics.bytes = bytesBefore;
1313
+ metrics.oversize = oversizeBefore;
1314
+ return writeTypeMarker(
1315
+ metrics,
1316
+ allocate && !metrics.oversize,
1317
+ "unserializable",
1318
+ error instanceof Error ? error.message : void 0
1319
+ );
733
1320
  } finally {
734
- tracking.delete(value);
1321
+ path.delete(value);
735
1322
  }
736
1323
  };
737
1324
  const serializeWithBytes = (value) => {
738
- const metrics = { visitedNodes: 0 };
1325
+ const fast = primitiveFastPath(value, limits.maxStringLength);
1326
+ if (fast) {
1327
+ return fast.bytes <= limits.maxSerializedValueBytes ? fast : boundedOversize(fast.bytes, fast.visitedNodes);
1328
+ }
1329
+ const metrics = { visitedNodes: 0, bytes: 0, oversize: false };
1330
+ const reentrant = serializing;
1331
+ const path = reentrant ? /* @__PURE__ */ new Set() : sharedPath;
1332
+ if (!reentrant) {
1333
+ serializing = true;
1334
+ sharedPath.clear();
1335
+ }
739
1336
  try {
740
- const serialized = serializeInner(value, 0, void 0, metrics);
741
- const bytes = Buffer2.byteLength(JSON.stringify(serialized), "utf8");
742
- if (bytes <= limits.maxSerializedValueBytes) {
743
- return { value: serialized, bytes, visitedNodes: metrics.visitedNodes };
1337
+ const serialized = serializeInner(value, 0, path, true, metrics);
1338
+ if (metrics.bytes > limits.maxSerializedValueBytes) {
1339
+ return boundedOversize(metrics.bytes, metrics.visitedNodes);
744
1340
  }
745
- const bounded = truncation("max_serialized_value_bytes", { __serialized_bytes: bytes });
746
- return {
747
- value: bounded,
748
- bytes: Buffer2.byteLength(JSON.stringify(bounded), "utf8"),
749
- visitedNodes: metrics.visitedNodes
750
- };
1341
+ return { value: serialized, bytes: metrics.bytes, visitedNodes: metrics.visitedNodes };
751
1342
  } catch {
752
1343
  const failed = typeMarker("unserializable");
753
1344
  return {
754
1345
  value: failed,
755
- bytes: Buffer2.byteLength(JSON.stringify(failed), "utf8"),
1346
+ bytes: jsonLiteralUtf8Bytes(failed),
756
1347
  visitedNodes: metrics.visitedNodes
757
1348
  };
1349
+ } finally {
1350
+ path.clear();
1351
+ if (!reentrant) serializing = false;
758
1352
  }
759
1353
  };
760
1354
  return {
@@ -830,6 +1424,8 @@ var createSourceExclusionMatcher = (exclusions) => {
830
1424
 
831
1425
  // src/execution-recorder/execution-recorder.ts
832
1426
  var DEFAULT_MAX_ACTIVE_MEMORY_BYTES = 64 * 1024 * 1024;
1427
+ var DERIVATION_ROOT_RECORD_BYTES = 64;
1428
+ var phaseTimingEnabled = process.env.RASPUTIN_RECORDER_PHASE_TIMING === "1";
833
1429
  var manualFunctionDefinition = (value) => {
834
1430
  const located = /^(.+):([^:@]+)@(\d+):(\d+)$/.exec(value);
835
1431
  const named = located ? void 0 : /^(.+):([^:@]+)$/.exec(value);
@@ -844,8 +1440,8 @@ var manualFunctionDefinition = (value) => {
844
1440
  return { ...definition, functionId: runtimeFunctionId2(definition) };
845
1441
  };
846
1442
  var normalizeFunctionDefinition = (value) => typeof value === "string" ? manualFunctionDefinition(value) : value;
847
- var elapsedMs = (startedAtNs, endedAtNs = process.hrtime.bigint()) => Number(endedAtNs - startedAtNs) / 1e6;
848
- var roundedMilliseconds = (nanoseconds) => Math.round(Number(nanoseconds) / 1e6 * 1e4) / 1e4;
1443
+ var elapsedMs2 = (startedAtNs, endedAtNs = process.hrtime.bigint()) => Number(endedAtNs - startedAtNs) / 1e6;
1444
+ var roundedMilliseconds2 = (nanoseconds) => Math.round(Number(nanoseconds) / 1e6 * 1e4) / 1e4;
849
1445
  var addRecorderTime = (recording, nanoseconds) => {
850
1446
  if (nanoseconds > 0n) recording.inlineRecorderTimeNs += nanoseconds;
851
1447
  };
@@ -937,7 +1533,20 @@ var emptyStats = {
937
1533
  totalInlineWallTimeMs: 0,
938
1534
  maxInlineWallTimeMs: 0,
939
1535
  activeEstimatedBytes: 0,
940
- peakActiveEstimatedBytes: 0
1536
+ peakActiveEstimatedBytes: 0,
1537
+ journalPoolHits: 0,
1538
+ journalPoolMisses: 0,
1539
+ journalPoolSize: 0,
1540
+ journalAllocatedCapacity: 0,
1541
+ derivationCandidateFunctions: 0,
1542
+ derivationElidedFunctionCalls: 0,
1543
+ derivationRootsCaptured: 0,
1544
+ derivationControlFactsCaptured: 0,
1545
+ derivationValuesAvoided: 0,
1546
+ derivationFallbackFunctions: 0,
1547
+ derivationFallbackExpressions: 0,
1548
+ derivationFallbackReasons: {},
1549
+ derivationRootBytes: 0
941
1550
  };
942
1551
  var noOpExecution = {
943
1552
  createScope: () => ({
@@ -950,19 +1559,37 @@ var noOpExecution = {
950
1559
  getErrorState: () => void 0,
951
1560
  runFunction: (_functionId, _args, callback) => callback(),
952
1561
  runFunctionPlans: (_plans, _planIndex, _args, callback) => callback(),
1562
+ prepareFunctionPlans: () => [],
953
1563
  runPreparedFunction: (_plan, _args, callback) => callback(),
954
1564
  trace: (_functionId, fn) => fn,
955
- getStats: () => ({ ...emptyStats })
1565
+ getStats: () => ({ ...emptyStats, derivationFallbackReasons: {} }),
1566
+ captureDerivationRoot: (_rootId, value) => value
956
1567
  };
957
1568
  var mergeExecutionMetadata = (target, metadata) => {
958
1569
  if (metadata.kind) target.kind = metadata.kind;
959
1570
  if (metadata.name !== void 0) target.name = metadata.name;
960
1571
  if (metadata.request) target.request = { ...target.request, ...metadata.request };
961
1572
  };
1573
+ var newRepeatedGroup = (plan) => ({
1574
+ observedCalls: 0,
1575
+ functionId: plan.functionId,
1576
+ runtimePlanId: plan.runtimePlanId
1577
+ });
1578
+ var newSuppression = (group, parentCallId, startedAtNs, endedAtNs) => ({
1579
+ count: 1,
1580
+ firstTimestampNs: startedAtNs,
1581
+ lastTimestampNs: endedAtNs,
1582
+ totalDurationNs: endedAtNs - startedAtNs,
1583
+ functionId: group.functionId,
1584
+ runtimePlanId: group.runtimePlanId,
1585
+ ...parentCallId === void 0 ? {} : { parentCallId },
1586
+ storageIndex: -1,
1587
+ retainedIndex: -1
1588
+ });
962
1589
  var createExecutionRecorder = (options) => {
963
1590
  if (!options?.enabled) return noOpExecution;
964
1591
  const storage = new AsyncLocalStorage();
965
- const maxEventsPerExecution = Math.max(2, Math.floor(options.maxEventsPerExecution ?? 500));
1592
+ const maxEventsPerExecution = sanitizeJournalCapacity(options.maxEventsPerExecution);
966
1593
  const maxCapturedCallsPerFunction = Math.max(
967
1594
  1,
968
1595
  Math.floor(options.maxCapturedCallsPerFunction ?? 3)
@@ -977,11 +1604,14 @@ var createExecutionRecorder = (options) => {
977
1604
  );
978
1605
  const serializer = createSafeSerializer(options);
979
1606
  const excludesSource = createSourceExclusionMatcher(options.excludeSources);
1607
+ const journalPool = new ExecutionJournalPool(maxEventsPerExecution);
1608
+ const plansByRuntimeId = [];
980
1609
  let executionCounter = 0;
981
1610
  let nextRuntimePlanId = 1;
982
1611
  const internedPlans = /* @__PURE__ */ new Map();
983
1612
  const preparedModules = /* @__PURE__ */ new WeakMap();
984
1613
  const errorExecutions = /* @__PURE__ */ new WeakMap();
1614
+ const phases = phaseTimingEnabled ? { alsNs: 0n, materializeNs: 0n } : void 0;
985
1615
  const stats = {
986
1616
  functionCallsObserved: 0,
987
1617
  functionCallsCaptured: 0,
@@ -998,10 +1628,22 @@ var createExecutionRecorder = (options) => {
998
1628
  partialErrorSnapshots: 0,
999
1629
  measuredExecutions: 0,
1000
1630
  totalInlineWallTimeMs: 0,
1001
- maxInlineWallTimeMs: 0
1631
+ maxInlineWallTimeMs: 0,
1632
+ derivationCandidateFunctions: 0,
1633
+ derivationElidedFunctionCalls: 0,
1634
+ derivationRootsCaptured: 0,
1635
+ derivationControlFactsCaptured: 0,
1636
+ derivationValuesAvoided: 0,
1637
+ derivationFallbackFunctions: 0,
1638
+ derivationFallbackExpressions: 0,
1639
+ derivationFallbackReasons: {},
1640
+ derivationRootBytes: 0
1641
+ };
1642
+ const addPhase = (key, nanoseconds) => {
1643
+ if (phases && nanoseconds > 0n) phases[key] += nanoseconds;
1002
1644
  };
1003
1645
  const recordInlineTiming = (recording) => {
1004
- const inlineWallTimeMs = roundedMilliseconds(recording.inlineRecorderTimeNs);
1646
+ const inlineWallTimeMs = roundedMilliseconds2(recording.inlineRecorderTimeNs);
1005
1647
  stats.measuredExecutions++;
1006
1648
  stats.totalInlineWallTimeMs = Math.round((stats.totalInlineWallTimeMs + inlineWallTimeMs) * 1e4) / 1e4;
1007
1649
  stats.maxInlineWallTimeMs = Math.max(stats.maxInlineWallTimeMs, inlineWallTimeMs);
@@ -1014,16 +1656,20 @@ var createExecutionRecorder = (options) => {
1014
1656
  return serialized;
1015
1657
  };
1016
1658
  const captureValue = (recording, value) => memoryBudget.captureValue(recording.memory, () => serializeAndCount(value));
1659
+ const storePlan = (plan) => {
1660
+ plansByRuntimeId[plan.runtimePlanId] = plan;
1661
+ return plan;
1662
+ };
1017
1663
  const preparePlan = (definition) => {
1018
1664
  const existing = internedPlans.get(definition.functionId);
1019
1665
  if (existing) return existing;
1020
- const plan = {
1666
+ const plan = storePlan({
1021
1667
  runtimePlanId: nextRuntimePlanId++,
1022
1668
  definition,
1023
1669
  functionId: definition.functionId,
1024
1670
  excluded: excludesSource(definition),
1025
1671
  estimatedCallBytes: estimatedCallBytesFor(definition.functionId)
1026
- };
1672
+ });
1027
1673
  internedPlans.set(definition.functionId, plan);
1028
1674
  return plan;
1029
1675
  };
@@ -1032,58 +1678,135 @@ var createExecutionRecorder = (options) => {
1032
1678
  if (cached) return cached;
1033
1679
  const prepared = plans.map((encoded) => {
1034
1680
  const definition = definitionFromEncodedPlan(encoded);
1035
- return {
1681
+ return storePlan({
1036
1682
  runtimePlanId: nextRuntimePlanId++,
1037
1683
  definition,
1038
1684
  functionId: definition.functionId,
1039
1685
  excluded: excludesSource(definition),
1040
1686
  estimatedCallBytes: estimatedCallBytesFor(definition.functionId)
1041
- };
1687
+ });
1042
1688
  });
1043
1689
  preparedModules.set(plans, prepared);
1044
1690
  return prepared;
1045
1691
  };
1692
+ const derivationMetrics = (recording) => {
1693
+ const manifest = snapshotInstrumentationManifest();
1694
+ const plans = manifest?.derivationPlans ?? [];
1695
+ const reasons = { ...stats.derivationFallbackReasons };
1696
+ return {
1697
+ derivationCandidateFunctions: plans.filter((plan) => !plan.fallbackReason).length,
1698
+ derivationElidedFunctionCalls: stats.derivationElidedFunctionCalls,
1699
+ derivationRootsCaptured: recording.derivationRoots.length,
1700
+ derivationControlFactsCaptured: stats.derivationControlFactsCaptured,
1701
+ derivationValuesAvoided: stats.derivationValuesAvoided,
1702
+ derivationFallbackFunctions: plans.filter((plan) => plan.fallbackReason).length,
1703
+ derivationFallbackExpressions: recording.derivationRoots.length,
1704
+ derivationFallbackReasons: reasons,
1705
+ derivationRootBytes: recording.derivationRootBytes
1706
+ };
1707
+ };
1708
+ const eventsHaveOmittedValues = (events) => {
1709
+ for (const event of events) {
1710
+ if (event.type === "function_enter" && event.args.some((arg) => rasputinTagKind(arg) === "omitted")) {
1711
+ return true;
1712
+ }
1713
+ if (event.type === "function_exit" && rasputinTagKind(event.returnValue) === "omitted") {
1714
+ return true;
1715
+ }
1716
+ }
1717
+ return false;
1718
+ };
1719
+ const resolveCaptureProfile = (recording, events, truncated, capture, derivation) => {
1720
+ const source = recording.sawAutomatic ? "automatic" : recording.sawManual ? "manual" : "scope-only";
1721
+ if (source === "scope-only") {
1722
+ return {
1723
+ source,
1724
+ functionBoundaries: "none",
1725
+ functionValues: "none",
1726
+ timings: "none",
1727
+ derivation: derivation ? "available" : "none"
1728
+ };
1729
+ }
1730
+ const droppedEvents = Boolean(truncated?.eventsDropped || capture?.eventsDropped);
1731
+ return {
1732
+ source,
1733
+ functionBoundaries: droppedEvents ? "partial" : "full",
1734
+ functionValues: eventsHaveOmittedValues(events) ? "selective" : "full",
1735
+ timings: droppedEvents ? "partial" : "full",
1736
+ derivation: derivation ? "available" : "none"
1737
+ };
1738
+ };
1739
+ const derivationCapture = (recording, functions) => {
1740
+ const manifest = snapshotInstrumentationManifest();
1741
+ if (!manifest) return void 0;
1742
+ const plans = manifest.derivationPlans ?? [];
1743
+ const recordedIds = new Set(functions.map((fn) => fn.functionId));
1744
+ if (!plans.some((plan) => recordedIds.has(plan.functionId))) {
1745
+ return void 0;
1746
+ }
1747
+ return {
1748
+ manifestHash: hashInstrumentationManifest(manifest),
1749
+ roots: recording.derivationRoots.slice(),
1750
+ controlFacts: []
1751
+ };
1752
+ };
1046
1753
  const stateFrom = (recording, endedAtNs = process.hrtime.bigint()) => {
1047
1754
  const recorderStartedAtNs = process.hrtime.bigint();
1048
- for (const byParent of recording.repeatedCalls.values()) {
1049
- for (const group of byParent.values()) {
1050
- if (group.summary) {
1051
- group.summary.totalDurationMs = roundedMilliseconds(group.totalDurationNs);
1052
- }
1755
+ let events = [];
1756
+ let functions = [];
1757
+ let truncated;
1758
+ let capture;
1759
+ let derivation;
1760
+ if (recording.cachedState) {
1761
+ events = recording.cachedState.events;
1762
+ functions = recording.cachedState.functions;
1763
+ truncated = recording.cachedState.truncated;
1764
+ capture = recording.cachedState.capture;
1765
+ derivation = recording.cachedState.derivation;
1766
+ } else {
1767
+ if (recording.journal) {
1768
+ const materialized = recording.journal.materialize(plansByRuntimeId, recording.startedAtNs);
1769
+ events = materialized.events;
1770
+ functions = materialized.functions;
1771
+ truncated = recording.journal.dropped > 0 ? { eventsDropped: recording.journal.dropped } : void 0;
1772
+ }
1773
+ capture = recording.memory.underPressure ? {
1774
+ completeness: "partial",
1775
+ reason: "process_memory_budget",
1776
+ valuesDropped: recording.memory.valuesDropped,
1777
+ eventsDropped: recording.memory.eventsDropped
1778
+ } : void 0;
1779
+ derivation = derivationCapture(recording, functions);
1780
+ if (recording.finished && recording.inFlightCalls === 0 && recording.journal) {
1781
+ recording.cachedState = { events, functions, truncated, capture, derivation };
1782
+ journalPool.release(recording.journal);
1783
+ recording.journal = void 0;
1053
1784
  }
1054
1785
  }
1055
- const events = recording.events.values().map((event) => event.type === "function_calls_suppressed" ? { ...event } : event);
1056
- const referencedFunctionIds = new Set(
1057
- events.flatMap(
1058
- (event) => event.type === "function_enter" || event.type === "function_calls_suppressed" ? [event.functionId] : []
1059
- )
1060
- );
1061
1786
  const state = {
1062
- version: 2,
1787
+ version: 4,
1788
+ captureProfile: resolveCaptureProfile(recording, events, truncated, capture, derivation),
1063
1789
  executionId: recording.executionId,
1064
1790
  startedAt: recording.startedAt,
1065
- durationMs: elapsedMs(recording.startedAtNs, endedAtNs),
1066
- recorder: { inlineWallTimeMs: 0 },
1791
+ durationMs: elapsedMs2(recording.startedAtNs, endedAtNs),
1792
+ recorder: { inlineWallTimeMs: 0, derivation: derivationMetrics(recording) },
1067
1793
  execution: {
1068
1794
  ...recording.execution,
1069
1795
  ...recording.execution.request ? { request: { ...recording.execution.request } } : {}
1070
1796
  },
1071
- functions: [...recording.functions.values()].filter(
1072
- (definition) => referencedFunctionIds.has(definition.functionId)
1073
- ),
1074
- events,
1075
- ...recording.events.dropped > 0 ? { truncated: { eventsDropped: recording.events.dropped } } : {},
1076
- ...recording.memory.underPressure ? {
1077
- capture: {
1078
- completeness: "partial",
1079
- reason: "process_memory_budget",
1080
- valuesDropped: recording.memory.valuesDropped,
1081
- eventsDropped: recording.memory.eventsDropped
1082
- }
1083
- } : {}
1797
+ functions: functions.slice(),
1798
+ events: events.slice(),
1799
+ ...truncated ? { truncated: { ...truncated } } : {},
1800
+ ...capture ? { capture: { ...capture } } : {},
1801
+ ...derivation ? { derivation: { ...derivation, roots: derivation.roots.slice() } } : {}
1084
1802
  };
1803
+ if (phaseTimingEnabled)
1804
+ addPhase("materializeNs", process.hrtime.bigint() - recorderStartedAtNs);
1085
1805
  addRecorderTime(recording, process.hrtime.bigint() - recorderStartedAtNs);
1086
- state.recorder = { inlineWallTimeMs: roundedMilliseconds(recording.inlineRecorderTimeNs) };
1806
+ state.recorder = {
1807
+ inlineWallTimeMs: roundedMilliseconds2(recording.inlineRecorderTimeNs),
1808
+ derivation: derivationMetrics(recording)
1809
+ };
1087
1810
  return state;
1088
1811
  };
1089
1812
  const rememberError = (error, recording) => {
@@ -1099,25 +1822,38 @@ var createExecutionRecorder = (options) => {
1099
1822
  errorExecutions.set(error, recording);
1100
1823
  };
1101
1824
  const countDroppedEvents = (recording, droppedBefore) => {
1102
- stats.truncatedEvents += recording.events.dropped - droppedBefore;
1825
+ const dropped = recording.journal?.dropped ?? droppedBefore;
1826
+ stats.truncatedEvents += dropped - droppedBefore;
1103
1827
  };
1104
- const appendStandaloneEvent = (recording, event) => {
1828
+ const appendSuppressedPlaceholder = (recording, group) => {
1829
+ if (!recording.journal) return;
1105
1830
  if (!memoryBudget.reserveStandaloneEvent(recording.memory)) return;
1106
- const droppedBefore = recording.events.dropped;
1107
- recording.events.append(event);
1831
+ const droppedBefore = recording.journal.dropped;
1832
+ recording.journal.appendSuppressed(group);
1108
1833
  countDroppedEvents(recording, droppedBefore);
1109
1834
  };
1110
- const repeatedCallGroup = (recording, runtimePlanId, parentCallId) => {
1111
- let byParent = recording.repeatedCalls.get(runtimePlanId);
1112
- if (!byParent) {
1113
- byParent = /* @__PURE__ */ new Map();
1114
- recording.repeatedCalls.set(runtimePlanId, byParent);
1835
+ const repeatedCallGroup = (recording, plan, parentCallId) => {
1836
+ const slots = recording.repeatedCalls;
1837
+ let slot = slots[plan.runtimePlanId];
1838
+ if (!slot) {
1839
+ slot = {};
1840
+ slots[plan.runtimePlanId] = slot;
1841
+ }
1842
+ if (parentCallId === void 0) {
1843
+ if (!slot.root) slot.root = newRepeatedGroup(plan);
1844
+ return slot.root;
1845
+ }
1846
+ if (slot.lastParentGroup && slot.lastParentCallId === parentCallId) {
1847
+ return slot.lastParentGroup;
1115
1848
  }
1116
- let group = byParent.get(parentCallId);
1849
+ if (!slot.byParent) slot.byParent = /* @__PURE__ */ new Map();
1850
+ let group = slot.byParent.get(parentCallId);
1117
1851
  if (!group) {
1118
- group = { observedCalls: 0, totalDurationNs: 0n };
1119
- byParent.set(parentCallId, group);
1852
+ group = newRepeatedGroup(plan);
1853
+ slot.byParent.set(parentCallId, group);
1120
1854
  }
1855
+ slot.lastParentCallId = parentCallId;
1856
+ slot.lastParentGroup = group;
1121
1857
  return group;
1122
1858
  };
1123
1859
  const serializedError = (recording, error, callId) => {
@@ -1139,30 +1875,31 @@ var createExecutionRecorder = (options) => {
1139
1875
  }
1140
1876
  };
1141
1877
  };
1142
- const captureCompletedThrow = (recording, plan, args, parentCallId, startedAtNs, startedAtMs, error) => {
1878
+ const captureCompletedThrow = (recording, plan, args, parentCallId, startedAtNs, error) => {
1879
+ if (!recording.journal) {
1880
+ rememberError(error, recording);
1881
+ return;
1882
+ }
1143
1883
  if (!memoryBudget.reserveCall(recording.memory, plan.estimatedCallBytes)) {
1144
1884
  rememberError(error, recording);
1145
1885
  return;
1146
1886
  }
1147
1887
  const callId = ++recording.nextCallId;
1148
1888
  const capturedError = serializedError(recording, error, callId);
1149
- const enter = {
1150
- type: "function_enter",
1889
+ const capturedArgs = Array.from(args, (argument) => captureValue(recording, argument));
1890
+ const endedAtNs = process.hrtime.bigint();
1891
+ const droppedBefore = recording.journal.dropped;
1892
+ const retained = recording.journal.forceCompletedCall(
1893
+ plan.runtimePlanId,
1151
1894
  callId,
1152
- functionId: plan.functionId,
1153
- timestampMs: startedAtMs,
1154
- args: Array.from(args, (argument) => captureValue(recording, argument)),
1155
- ...parentCallId === void 0 ? {} : { parentCallId }
1156
- };
1157
- const terminal = {
1158
- type: "function_throw",
1159
- callId,
1160
- timestampMs: elapsedMs(recording.startedAtNs),
1161
- durationMs: elapsedMs(startedAtNs),
1162
- error: capturedError.value
1163
- };
1164
- const droppedBefore = recording.events.dropped;
1165
- const retained = recording.events.forceCompletedCall(enter, terminal);
1895
+ parentCallId,
1896
+ startedAtNs - recording.startedAtNs,
1897
+ capturedArgs,
1898
+ KIND_THROW,
1899
+ endedAtNs - recording.startedAtNs,
1900
+ endedAtNs - startedAtNs,
1901
+ capturedError.value
1902
+ );
1166
1903
  if (retained) {
1167
1904
  stats.functionCallsCaptured++;
1168
1905
  capturedError.commit();
@@ -1170,29 +1907,46 @@ var createExecutionRecorder = (options) => {
1170
1907
  countDroppedEvents(recording, droppedBefore);
1171
1908
  rememberError(error, recording);
1172
1909
  };
1173
- const recordSuppressedCall = (recording, group, functionId, parentCallId, startedAtMs, startedAtNs, endedAtNs) => {
1910
+ const recordSuppressedCall = (recording, group, parentCallId, startedAtNs, endedAtNs) => {
1174
1911
  stats.repeatedCallsSuppressed++;
1175
- group.totalDurationNs += endedAtNs - startedAtNs;
1176
- const endedAtMs = elapsedMs(recording.startedAtNs, endedAtNs);
1177
- if (!group.summary) {
1178
- group.summary = {
1179
- type: "function_calls_suppressed",
1180
- functionId,
1181
- timestampMs: startedAtMs,
1182
- lastTimestampMs: endedAtMs,
1183
- suppressedCallCount: 1,
1184
- totalDurationMs: 0,
1185
- ...parentCallId === void 0 ? {} : { parentCallId }
1186
- };
1187
- appendStandaloneEvent(recording, group.summary);
1912
+ if (!group.suppressed) {
1913
+ group.suppressed = newSuppression(group, parentCallId, startedAtNs, endedAtNs);
1914
+ appendSuppressedPlaceholder(recording, group.suppressed);
1188
1915
  return;
1189
1916
  }
1190
- group.summary.lastTimestampMs = endedAtMs;
1191
- group.summary.suppressedCallCount++;
1192
- if (!recording.events.hasSummary(group.summary)) {
1193
- appendStandaloneEvent(recording, group.summary);
1917
+ const suppressed = group.suppressed;
1918
+ suppressed.count++;
1919
+ suppressed.lastTimestampNs = endedAtNs;
1920
+ suppressed.totalDurationNs += endedAtNs - startedAtNs;
1921
+ if (suppressed.retainedIndex < 0) {
1922
+ appendSuppressedPlaceholder(recording, suppressed);
1194
1923
  }
1195
1924
  };
1925
+ const releaseJournalIfIdle = (recording) => {
1926
+ if (!recording.finished || recording.hasError || recording.inFlightCalls > 0) return;
1927
+ if (!recording.journal) return;
1928
+ journalPool.release(recording.journal);
1929
+ recording.journal = void 0;
1930
+ };
1931
+ const acquireJournal = (recording) => {
1932
+ if (recording.journal) return recording.journal;
1933
+ try {
1934
+ const journal = journalPool.acquire();
1935
+ if (!memoryBudget.reserveStorage(recording.memory, journal.allocatedBytes)) {
1936
+ journalPool.release(journal);
1937
+ return void 0;
1938
+ }
1939
+ journal.onGrow = (deltaBytes) => memoryBudget.reserveStorage(recording.memory, deltaBytes);
1940
+ recording.journal = journal;
1941
+ return journal;
1942
+ } catch {
1943
+ return void 0;
1944
+ }
1945
+ };
1946
+ const settleInFlight = (recording) => {
1947
+ if (recording.inFlightCalls > 0) recording.inFlightCalls--;
1948
+ releaseJournalIfIdle(recording);
1949
+ };
1196
1950
  const finish = (store, metadata) => {
1197
1951
  const { recording } = store;
1198
1952
  if (recording.finished) return;
@@ -1201,11 +1955,22 @@ var createExecutionRecorder = (options) => {
1201
1955
  if (metadata) mergeExecutionMetadata(recording.execution, metadata);
1202
1956
  memoryBudget.release(recording.memory);
1203
1957
  stats.activeExecutions = Math.max(0, stats.activeExecutions - 1);
1204
- if (!recording.hasError) stats.successfulExecutionsDiscarded++;
1958
+ if (!recording.hasError) {
1959
+ stats.successfulExecutionsDiscarded++;
1960
+ releaseJournalIfIdle(recording);
1961
+ }
1205
1962
  recording.finishedAtNs = process.hrtime.bigint();
1206
1963
  addRecorderTime(recording, recording.finishedAtNs - recorderStartedAtNs);
1207
1964
  recordInlineTiming(recording);
1208
1965
  };
1966
+ const runStore = (store, callback) => {
1967
+ if (!phaseTimingEnabled) return storage.run(store, callback);
1968
+ const alsStartedAtNs = process.hrtime.bigint();
1969
+ return storage.run(store, () => {
1970
+ addPhase("alsNs", process.hrtime.bigint() - alsStartedAtNs);
1971
+ return callback();
1972
+ });
1973
+ };
1209
1974
  const createScope = (metadata) => {
1210
1975
  const recorderStartedAtNs = process.hrtime.bigint();
1211
1976
  try {
@@ -1218,15 +1983,19 @@ var createExecutionRecorder = (options) => {
1218
1983
  ...metadata,
1219
1984
  ...metadata.request ? { request: { ...metadata.request } } : {}
1220
1985
  },
1221
- events: new CallAwareEventBuffer(maxEventsPerExecution),
1222
- functions: /* @__PURE__ */ new Map(),
1223
- repeatedCalls: /* @__PURE__ */ new Map(),
1986
+ journal: void 0,
1987
+ repeatedCalls: [],
1224
1988
  capturedErrors: /* @__PURE__ */ new WeakMap(),
1225
1989
  nextCallId: 0,
1990
+ inFlightCalls: 0,
1226
1991
  finished: false,
1227
1992
  hasError: false,
1228
1993
  memory: memoryBudget.startExecution(),
1229
- partialSnapshotCounted: false
1994
+ partialSnapshotCounted: false,
1995
+ derivationRoots: [],
1996
+ derivationRootBytes: 0,
1997
+ sawAutomatic: false,
1998
+ sawManual: false
1230
1999
  };
1231
2000
  stats.activeExecutions++;
1232
2001
  stats.peakActiveExecutions = Math.max(stats.peakActiveExecutions, stats.activeExecutions);
@@ -1235,7 +2004,7 @@ var createExecutionRecorder = (options) => {
1235
2004
  return {
1236
2005
  run: (callback) => {
1237
2006
  const runStartedAtNs = process.hrtime.bigint();
1238
- return storage.run(store, () => {
2007
+ return runStore(store, () => {
1239
2008
  addRecorderTime(recording, process.hrtime.bigint() - runStartedAtNs);
1240
2009
  return callback();
1241
2010
  });
@@ -1288,215 +2057,282 @@ var createExecutionRecorder = (options) => {
1288
2057
  return void 0;
1289
2058
  }
1290
2059
  };
1291
- const runPreparedFunction = (plan, args, callback) => {
1292
- const store = storage.getStore();
2060
+ const runPreparedFunctionFromStore = (store, plan, args, callback, omitValues) => {
1293
2061
  if (!store || store.recording.finished) return callback();
1294
2062
  stats.functionCallsObserved++;
1295
2063
  const recorderStartedAtNs = process.hrtime.bigint();
1296
2064
  if (store.suppressCapture || plan.excluded) {
1297
2065
  stats.functionCallsExcluded++;
1298
- return storage.run({ ...store, suppressCapture: true }, () => {
2066
+ return runStore({ ...store, suppressCapture: true }, () => {
1299
2067
  addRecorderTime(store.recording, process.hrtime.bigint() - recorderStartedAtNs);
1300
2068
  return callback();
1301
2069
  });
1302
2070
  }
2071
+ const recording = store.recording;
2072
+ recording.inFlightCalls++;
1303
2073
  const parentCallId = store.currentCallId;
1304
- const group = repeatedCallGroup(store.recording, plan.runtimePlanId, parentCallId);
2074
+ const group = repeatedCallGroup(recording, plan, parentCallId);
1305
2075
  group.observedCalls++;
1306
- if (group.observedCalls === 1) {
1307
- store.recording.functions.set(plan.functionId, plan.definition);
1308
- }
2076
+ const journal = acquireJournal(recording);
2077
+ if (group.observedCalls === 1) journal?.notePlan(plan.runtimePlanId);
1309
2078
  if (group.observedCalls > maxCapturedCallsPerFunction) {
1310
2079
  const startedAtNs2 = process.hrtime.bigint();
1311
- const startedAtMs = elapsedMs(store.recording.startedAtNs, startedAtNs2);
1312
2080
  try {
1313
2081
  const result = callback();
1314
2082
  if (isPromiseLike(result)) {
1315
- addRecorderTime(store.recording, startedAtNs2 - recorderStartedAtNs);
2083
+ addRecorderTime(recording, startedAtNs2 - recorderStartedAtNs);
1316
2084
  return result.then(
1317
2085
  (value) => {
1318
2086
  const callbackEndedAtNs2 = process.hrtime.bigint();
1319
- recordSuppressedCall(
1320
- store.recording,
1321
- group,
1322
- plan.functionId,
1323
- parentCallId,
1324
- startedAtMs,
1325
- startedAtNs2,
1326
- callbackEndedAtNs2
1327
- );
1328
- addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs2);
2087
+ recordSuppressedCall(recording, group, parentCallId, startedAtNs2, callbackEndedAtNs2);
2088
+ addRecorderTime(recording, process.hrtime.bigint() - callbackEndedAtNs2);
2089
+ settleInFlight(recording);
1329
2090
  return value;
1330
2091
  },
1331
2092
  (error) => {
1332
2093
  const callbackEndedAtNs2 = process.hrtime.bigint();
1333
- captureCompletedThrow(
1334
- store.recording,
1335
- plan,
1336
- args,
1337
- parentCallId,
1338
- startedAtNs2,
1339
- startedAtMs,
1340
- error
1341
- );
1342
- addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs2);
2094
+ captureCompletedThrow(recording, plan, args, parentCallId, startedAtNs2, error);
2095
+ addRecorderTime(recording, process.hrtime.bigint() - callbackEndedAtNs2);
2096
+ settleInFlight(recording);
1343
2097
  throw error;
1344
2098
  }
1345
2099
  );
1346
2100
  }
1347
2101
  const callbackEndedAtNs = process.hrtime.bigint();
1348
- recordSuppressedCall(
1349
- store.recording,
1350
- group,
1351
- plan.functionId,
1352
- parentCallId,
1353
- startedAtMs,
1354
- startedAtNs2,
1355
- callbackEndedAtNs
1356
- );
1357
- addRecorderTime(store.recording, startedAtNs2 - recorderStartedAtNs);
1358
- addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs);
2102
+ recordSuppressedCall(recording, group, parentCallId, startedAtNs2, callbackEndedAtNs);
2103
+ addRecorderTime(recording, startedAtNs2 - recorderStartedAtNs);
2104
+ addRecorderTime(recording, process.hrtime.bigint() - callbackEndedAtNs);
2105
+ settleInFlight(recording);
1359
2106
  return result;
1360
2107
  } catch (error) {
1361
2108
  const callbackEndedAtNs = process.hrtime.bigint();
1362
- captureCompletedThrow(
1363
- store.recording,
1364
- plan,
1365
- args,
1366
- parentCallId,
1367
- startedAtNs2,
1368
- startedAtMs,
1369
- error
1370
- );
1371
- addRecorderTime(store.recording, startedAtNs2 - recorderStartedAtNs);
1372
- addRecorderTime(store.recording, process.hrtime.bigint() - callbackEndedAtNs);
2109
+ captureCompletedThrow(recording, plan, args, parentCallId, startedAtNs2, error);
2110
+ addRecorderTime(recording, startedAtNs2 - recorderStartedAtNs);
2111
+ addRecorderTime(recording, process.hrtime.bigint() - callbackEndedAtNs);
2112
+ settleInFlight(recording);
1373
2113
  throw error;
1374
2114
  }
1375
2115
  }
1376
2116
  const startedAtNs = process.hrtime.bigint();
1377
- if (!store.recording.events.canStartCall()) {
1378
- const startedAtMs = elapsedMs(store.recording.startedAtNs, startedAtNs);
2117
+ if (!journal) {
2118
+ memoryBudget.reserveCall(recording.memory, plan.estimatedCallBytes);
2119
+ return runStore(
2120
+ store,
2121
+ () => observeRecordedCallback(
2122
+ recording,
2123
+ recorderStartedAtNs,
2124
+ callback,
2125
+ () => settleInFlight(recording),
2126
+ (error) => {
2127
+ rememberError(error, recording);
2128
+ settleInFlight(recording);
2129
+ }
2130
+ )
2131
+ );
2132
+ }
2133
+ if (!journal.canStartCall()) {
1379
2134
  const captureSuccess = () => {
1380
- const droppedBefore = store.recording.events.dropped;
1381
- store.recording.events.dropCall();
1382
- countDroppedEvents(store.recording, droppedBefore);
2135
+ const droppedBefore = recording.journal?.dropped ?? 0;
2136
+ recording.journal?.dropCall();
2137
+ countDroppedEvents(recording, droppedBefore);
2138
+ settleInFlight(recording);
1383
2139
  };
1384
- return storage.run(
2140
+ return runStore(
1385
2141
  store,
1386
2142
  () => observeRecordedCallback(
1387
- store.recording,
2143
+ recording,
1388
2144
  recorderStartedAtNs,
1389
2145
  callback,
1390
2146
  captureSuccess,
1391
- (error) => captureCompletedThrow(
1392
- store.recording,
1393
- plan,
1394
- args,
1395
- parentCallId,
1396
- startedAtNs,
1397
- startedAtMs,
1398
- error
1399
- )
2147
+ (error) => {
2148
+ captureCompletedThrow(recording, plan, args, parentCallId, startedAtNs, error);
2149
+ settleInFlight(recording);
2150
+ }
1400
2151
  )
1401
2152
  );
1402
2153
  }
1403
- if (!memoryBudget.reserveCall(store.recording.memory, plan.estimatedCallBytes)) {
2154
+ if (!memoryBudget.reserveCall(recording.memory, plan.estimatedCallBytes)) {
1404
2155
  return observeRecordedCallback(
1405
- store.recording,
2156
+ recording,
1406
2157
  recorderStartedAtNs,
1407
2158
  callback,
1408
- () => {
1409
- },
1410
- (error) => rememberError(error, store.recording)
2159
+ () => settleInFlight(recording),
2160
+ (error) => {
2161
+ rememberError(error, recording);
2162
+ settleInFlight(recording);
2163
+ }
1411
2164
  );
1412
2165
  }
1413
- const callId = ++store.recording.nextCallId;
1414
- const enter = {
1415
- type: "function_enter",
1416
- callId,
1417
- functionId: plan.functionId,
1418
- timestampMs: elapsedMs(store.recording.startedAtNs),
1419
- args: Array.from(args, (argument) => captureValue(store.recording, argument)),
1420
- ...parentCallId === void 0 ? {} : { parentCallId }
1421
- };
1422
- store.recording.events.startCall(enter);
1423
- const callStore = { recording: store.recording, currentCallId: callId };
1424
- return storage.run(
2166
+ const callId = ++recording.nextCallId;
2167
+ const enterTimestampNs = process.hrtime.bigint() - recording.startedAtNs;
2168
+ const capturedArgs = omitValues ? Array.from(args, () => rasputinOmitted()) : Array.from(args, (argument) => captureValue(recording, argument));
2169
+ journal.startCall(plan.runtimePlanId, callId, parentCallId, enterTimestampNs, capturedArgs);
2170
+ const callStore = { recording, currentCallId: callId };
2171
+ return runStore(
1425
2172
  callStore,
1426
2173
  () => observeRecordedCallback(
1427
- store.recording,
2174
+ recording,
1428
2175
  recorderStartedAtNs,
1429
2176
  callback,
1430
2177
  (value) => {
1431
- store.recording.events.finishCall({
1432
- type: "function_exit",
1433
- callId,
1434
- timestampMs: elapsedMs(store.recording.startedAtNs),
1435
- durationMs: elapsedMs(startedAtNs),
1436
- returnValue: captureValue(store.recording, value)
1437
- });
1438
- stats.functionCallsCaptured++;
2178
+ if (recording.journal) {
2179
+ const timestampNs = process.hrtime.bigint() - recording.startedAtNs;
2180
+ const durationNs = process.hrtime.bigint() - startedAtNs;
2181
+ const returnValue = omitValues ? rasputinOmitted() : captureValue(recording, value);
2182
+ recording.journal.finishCall(KIND_EXIT, callId, timestampNs, durationNs, returnValue);
2183
+ stats.functionCallsCaptured++;
2184
+ }
2185
+ settleInFlight(recording);
1439
2186
  },
1440
2187
  (error) => {
1441
- const capturedError = serializedError(store.recording, error, callId);
1442
- store.recording.events.finishCall({
1443
- type: "function_throw",
2188
+ if (!recording.journal) {
2189
+ rememberError(error, recording);
2190
+ settleInFlight(recording);
2191
+ return;
2192
+ }
2193
+ const capturedError = serializedError(recording, error, callId);
2194
+ recording.journal.finishCall(
2195
+ KIND_THROW,
1444
2196
  callId,
1445
- timestampMs: elapsedMs(store.recording.startedAtNs),
1446
- durationMs: elapsedMs(startedAtNs),
1447
- error: capturedError.value
1448
- });
2197
+ process.hrtime.bigint() - recording.startedAtNs,
2198
+ process.hrtime.bigint() - startedAtNs,
2199
+ capturedError.value
2200
+ );
1449
2201
  stats.functionCallsCaptured++;
1450
2202
  capturedError.commit();
1451
- rememberError(error, store.recording);
2203
+ rememberError(error, recording);
2204
+ settleInFlight(recording);
1452
2205
  }
1453
2206
  )
1454
2207
  );
1455
2208
  };
1456
- const runFunction = (input, args, callback) => runPreparedFunction(preparePlan(normalizeFunctionDefinition(input)), args, callback);
1457
- const runFunctionPlans = (plans, planIndex, args, callback) => {
2209
+ const runPreparedFunction = (plan, args, callback, omitValues) => {
2210
+ const store = storage.getStore();
2211
+ if (store && !store.recording.finished && !store.recording.sawAutomatic) {
2212
+ store.recording.sawAutomatic = true;
2213
+ }
2214
+ return runPreparedFunctionFromStore(store, plan, args, callback, omitValues);
2215
+ };
2216
+ const runFunction = (input, args, callback) => {
2217
+ const store = storage.getStore();
2218
+ if (store && !store.recording.finished) store.recording.sawManual = true;
2219
+ return runPreparedFunctionFromStore(
2220
+ store,
2221
+ preparePlan(normalizeFunctionDefinition(input)),
2222
+ args,
2223
+ callback
2224
+ );
2225
+ };
2226
+ const runFunctionPlans = (plans, planIndex, args, callback, omitValues) => {
2227
+ const store = storage.getStore();
1458
2228
  const plan = preparedPlansFor(plans)[planIndex];
1459
- return plan ? runPreparedFunction(plan, args, callback) : callback();
2229
+ if (store && !store.recording.finished && !store.recording.sawAutomatic) {
2230
+ store.recording.sawAutomatic = true;
2231
+ }
2232
+ return plan ? runPreparedFunctionFromStore(store, plan, args, callback, omitValues) : callback();
1460
2233
  };
2234
+ const prepareFunctionPlans = (plans) => preparedPlansFor(plans);
1461
2235
  function trace(input, fn) {
1462
2236
  const definition = typeof input === "string" ? manualFunctionDefinition(input) : { ...input, functionId: runtimeFunctionId2(input) };
1463
2237
  const plan = preparePlan(definition);
1464
2238
  return function traced(...args) {
1465
- return runPreparedFunction(plan, args, () => fn.apply(this, args));
2239
+ const store = storage.getStore();
2240
+ if (store && !store.recording.finished) store.recording.sawManual = true;
2241
+ return runPreparedFunctionFromStore(store, plan, args, () => fn.apply(this, args));
1466
2242
  };
1467
2243
  }
2244
+ const captureDerivationRoot = (rootId, value) => {
2245
+ const store = storage.getStore();
2246
+ if (!store || store.recording.finished || store.suppressCapture) return value;
2247
+ const recording = store.recording;
2248
+ if (recording.memory.mode !== "full") return value;
2249
+ if (recording.derivationRoots.length >= maxEventsPerExecution) return value;
2250
+ const recorderStartedAtNs = process.hrtime.bigint();
2251
+ try {
2252
+ const serialized = captureValue(recording, value);
2253
+ if (recording.memory.mode !== "full") return value;
2254
+ if (!memoryBudget.reserveStorage(recording.memory, DERIVATION_ROOT_RECORD_BYTES)) {
2255
+ return value;
2256
+ }
2257
+ const completeness = capturedValueCompleteness(serialized);
2258
+ const bytes = jsonLiteralUtf8Bytes(serialized);
2259
+ recording.derivationRoots.push({
2260
+ rootId,
2261
+ callId: store.currentCallId ?? 0,
2262
+ value: serialized,
2263
+ completeness
2264
+ });
2265
+ recording.derivationRootBytes += bytes;
2266
+ stats.derivationRootsCaptured++;
2267
+ stats.derivationRootBytes += bytes;
2268
+ stats.derivationFallbackExpressions++;
2269
+ return value;
2270
+ } finally {
2271
+ addRecorderTime(recording, process.hrtime.bigint() - recorderStartedAtNs);
2272
+ }
2273
+ };
1468
2274
  return {
1469
2275
  createScope,
1470
2276
  run,
1471
2277
  getErrorState,
1472
2278
  runFunction,
1473
2279
  runFunctionPlans,
2280
+ prepareFunctionPlans,
1474
2281
  runPreparedFunction,
1475
2282
  trace,
1476
- getStats: () => ({ ...stats, ...memoryBudget.getStats() })
2283
+ captureDerivationRoot,
2284
+ getStats: () => {
2285
+ const manifest = snapshotInstrumentationManifest();
2286
+ const plans = manifest?.derivationPlans ?? [];
2287
+ return {
2288
+ ...stats,
2289
+ ...memoryBudget.getStats(),
2290
+ ...journalPool.stats(),
2291
+ derivationCandidateFunctions: plans.filter((plan) => !plan.fallbackReason).length,
2292
+ derivationFallbackFunctions: plans.filter((plan) => plan.fallbackReason).length,
2293
+ derivationFallbackReasons: { ...stats.derivationFallbackReasons },
2294
+ ...phases ? {
2295
+ phaseAlsNs: Number(phases.alsNs),
2296
+ phaseMaterializeNs: Number(phases.materializeNs)
2297
+ } : {}
2298
+ };
2299
+ }
1477
2300
  };
1478
2301
  };
1479
2302
 
1480
2303
  // src/execution-recorder/automatic-runtime.ts
1481
2304
  var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
1482
2305
  var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
2306
+ var passthroughCapture = (_rootId, value) => value;
2307
+ var passthrough = (_planIndex, _args, callback) => callback();
2308
+ var bindPassthrough = (module) => {
2309
+ module.cell.run = passthrough;
2310
+ };
2311
+ var isRegistry = (value) => Array.isArray(Reflect.get(value, "runtimes")) && Array.isArray(Reflect.get(value, "modules")) && typeof Reflect.get(value, "register") === "function";
2312
+ var registerModule = (registry, plans) => {
2313
+ const cell = { run: passthrough };
2314
+ const module = { plans, cell };
2315
+ registry.modules.push(module);
2316
+ registry.runtimes.at(-1)?.bind(module);
2317
+ return (planIndex, args, callback, omitValues) => cell.run(planIndex, args, callback, omitValues);
2318
+ };
1483
2319
  var createRegistry = () => {
1484
- const runtimes = [];
1485
- return {
1486
- __rasputinRuntimeRegistry: true,
1487
- runtimes,
2320
+ const registry = {
2321
+ runtimes: [],
2322
+ modules: [],
2323
+ captureRoot: passthroughCapture,
1488
2324
  register(plans) {
1489
- return (planIndex, args, callback) => {
1490
- const runtime = runtimes.at(-1);
1491
- return runtime ? runtime.run(plans, planIndex, args, callback) : callback();
1492
- };
2325
+ return registerModule(registry, plans);
1493
2326
  }
1494
2327
  };
2328
+ return registry;
1495
2329
  };
1496
2330
  var getAutomaticRuntimeRegistry = () => {
1497
2331
  const existing = Reflect.get(globalThis, runtimeSymbol);
1498
- if (existing && Array.isArray(existing.runtimes) && typeof existing.register === "function") {
1499
- existing.__rasputinRuntimeRegistry = true;
2332
+ if (existing && typeof existing === "object" && isRegistry(existing)) {
2333
+ if (typeof existing.captureRoot !== "function") {
2334
+ existing.captureRoot = passthroughCapture;
2335
+ }
1500
2336
  return existing;
1501
2337
  }
1502
2338
  const registry = createRegistry();
@@ -1506,12 +2342,27 @@ var getAutomaticRuntimeRegistry = () => {
1506
2342
  var installAutomaticExecutionRuntime = (execution) => {
1507
2343
  const registry = getAutomaticRuntimeRegistry();
1508
2344
  const runtime = {
1509
- run: (plans, planIndex, args, callback) => execution.runFunctionPlans(plans, planIndex, args, callback)
2345
+ bind(module) {
2346
+ const prepared = execution.prepareFunctionPlans(module.plans);
2347
+ module.cell.run = (planIndex, args, callback, omitValues) => {
2348
+ const plan = prepared[planIndex];
2349
+ return plan ? execution.runPreparedFunction(plan, args, callback, omitValues) : callback();
2350
+ };
2351
+ },
2352
+ captureRoot: (rootId, value) => execution.captureDerivationRoot(rootId, value)
1510
2353
  };
1511
2354
  registry.runtimes.push(runtime);
2355
+ registry.captureRoot = runtime.captureRoot;
2356
+ for (const module of registry.modules) runtime.bind(module);
1512
2357
  return () => {
1513
2358
  const index = registry.runtimes.lastIndexOf(runtime);
1514
2359
  if (index >= 0) registry.runtimes.splice(index, 1);
2360
+ const current = registry.runtimes.at(-1);
2361
+ registry.captureRoot = current?.captureRoot ?? passthroughCapture;
2362
+ for (const module of registry.modules) {
2363
+ if (current) current.bind(module);
2364
+ else bindPassthrough(module);
2365
+ }
1515
2366
  };
1516
2367
  };
1517
2368
 
@@ -1572,7 +2423,7 @@ import {
1572
2423
 
1573
2424
  // src/sdk-meta.ts
1574
2425
  var SDK_NAME = "@rasputin-ai/node";
1575
- var SDK_VERSION = "0.5.0-alpha.1";
2426
+ var SDK_VERSION = "0.5.0-alpha.11";
1576
2427
 
1577
2428
  // src/rasputin-init.ts
1578
2429
  var withExecution = (client, execution, installRuntime, manifest) => {