@dogpile/sdk 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dist/browser/index.js +726 -176
  3. package/dist/browser/index.js.map +1 -1
  4. package/dist/index.d.ts +3 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/providers/openai-compatible.d.ts.map +1 -1
  9. package/dist/providers/openai-compatible.js +1 -0
  10. package/dist/providers/openai-compatible.js.map +1 -1
  11. package/dist/runtime/audit.d.ts +42 -0
  12. package/dist/runtime/audit.d.ts.map +1 -0
  13. package/dist/runtime/audit.js +73 -0
  14. package/dist/runtime/audit.js.map +1 -0
  15. package/dist/runtime/broadcast.d.ts.map +1 -1
  16. package/dist/runtime/broadcast.js +39 -36
  17. package/dist/runtime/broadcast.js.map +1 -1
  18. package/dist/runtime/coordinator.d.ts +5 -0
  19. package/dist/runtime/coordinator.d.ts.map +1 -1
  20. package/dist/runtime/coordinator.js +50 -39
  21. package/dist/runtime/coordinator.js.map +1 -1
  22. package/dist/runtime/defaults.d.ts.map +1 -1
  23. package/dist/runtime/defaults.js +12 -4
  24. package/dist/runtime/defaults.js.map +1 -1
  25. package/dist/runtime/engine.d.ts +17 -4
  26. package/dist/runtime/engine.d.ts.map +1 -1
  27. package/dist/runtime/engine.js +523 -18
  28. package/dist/runtime/engine.js.map +1 -1
  29. package/dist/runtime/health.d.ts +51 -0
  30. package/dist/runtime/health.d.ts.map +1 -0
  31. package/dist/runtime/health.js +85 -0
  32. package/dist/runtime/health.js.map +1 -0
  33. package/dist/runtime/introspection.d.ts +96 -0
  34. package/dist/runtime/introspection.d.ts.map +1 -0
  35. package/dist/runtime/introspection.js +31 -0
  36. package/dist/runtime/introspection.js.map +1 -0
  37. package/dist/runtime/metrics.d.ts +44 -0
  38. package/dist/runtime/metrics.d.ts.map +1 -0
  39. package/dist/runtime/metrics.js +12 -0
  40. package/dist/runtime/metrics.js.map +1 -0
  41. package/dist/runtime/model.d.ts.map +1 -1
  42. package/dist/runtime/model.js +34 -7
  43. package/dist/runtime/model.js.map +1 -1
  44. package/dist/runtime/provenance.d.ts +25 -0
  45. package/dist/runtime/provenance.d.ts.map +1 -0
  46. package/dist/runtime/provenance.js +13 -0
  47. package/dist/runtime/provenance.js.map +1 -0
  48. package/dist/runtime/sequential.d.ts.map +1 -1
  49. package/dist/runtime/sequential.js +39 -36
  50. package/dist/runtime/sequential.js.map +1 -1
  51. package/dist/runtime/shared.d.ts.map +1 -1
  52. package/dist/runtime/shared.js +39 -36
  53. package/dist/runtime/shared.js.map +1 -1
  54. package/dist/runtime/tracing.d.ts +31 -0
  55. package/dist/runtime/tracing.d.ts.map +1 -0
  56. package/dist/runtime/tracing.js +18 -0
  57. package/dist/runtime/tracing.js.map +1 -0
  58. package/dist/types/events.d.ts +10 -4
  59. package/dist/types/events.d.ts.map +1 -1
  60. package/dist/types/replay.d.ts +2 -0
  61. package/dist/types/replay.d.ts.map +1 -1
  62. package/dist/types.d.ts +124 -1
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/types.js.map +1 -1
  65. package/package.json +39 -1
  66. package/src/index.ts +5 -0
  67. package/src/providers/openai-compatible.ts +1 -0
  68. package/src/runtime/audit.ts +121 -0
  69. package/src/runtime/broadcast.ts +40 -37
  70. package/src/runtime/coordinator.ts +54 -39
  71. package/src/runtime/defaults.ts +13 -4
  72. package/src/runtime/engine.ts +648 -18
  73. package/src/runtime/health.ts +136 -0
  74. package/src/runtime/introspection.ts +122 -0
  75. package/src/runtime/metrics.ts +45 -0
  76. package/src/runtime/model.ts +38 -6
  77. package/src/runtime/provenance.ts +43 -0
  78. package/src/runtime/sequential.ts +40 -37
  79. package/src/runtime/shared.ts +40 -37
  80. package/src/runtime/tracing.ts +35 -0
  81. package/src/types/events.ts +10 -4
  82. package/src/types/replay.ts +2 -0
  83. package/src/types.ts +132 -1
@@ -262,8 +262,8 @@ function createRunMetadata(options) {
262
262
  tier: options.tier,
263
263
  modelProviderId: options.modelProviderId,
264
264
  agentsUsed: options.agentsUsed,
265
- startedAt: firstEvent?.at ?? "",
266
- completedAt: lastEvent?.at ?? ""
265
+ startedAt: eventTimestamp$1(firstEvent) ?? "",
266
+ completedAt: eventTimestamp$1(lastEvent) ?? ""
267
267
  };
268
268
  }
269
269
  function createReplayTraceRunInputs(options) {
@@ -342,7 +342,7 @@ function createReplayTraceProtocolDecision(protocol, event, eventIndex, options
342
342
  eventType: event.type,
343
343
  protocol,
344
344
  decision: options.decision ?? defaultProtocolDecision(event),
345
- at: event.at,
345
+ at: eventTimestamp$1(event),
346
346
  ...options.turn !== void 0 ? { turn: options.turn } : {},
347
347
  ...options.phase !== void 0 ? { phase: options.phase } : {},
348
348
  ...options.round !== void 0 ? { round: options.round } : {},
@@ -475,7 +475,7 @@ function createReplayTraceFinalOutput(output, event) {
475
475
  kind: "replay-trace-final-output",
476
476
  output,
477
477
  cost: emptyCost(),
478
- completedAt: event.at,
478
+ completedAt: eventTimestamp$1(event),
479
479
  transcript: {
480
480
  kind: "trace-transcript",
481
481
  entryCount: 0,
@@ -483,6 +483,11 @@ function createReplayTraceFinalOutput(output, event) {
483
483
  }
484
484
  };
485
485
  }
486
+ function eventTimestamp$1(event) {
487
+ if (event === void 0) return void 0;
488
+ if ("at" in event) return event.at;
489
+ return event.type === "model-response" ? event.completedAt : event.startedAt;
490
+ }
486
491
  function nextProviderCallId(runId, providerCalls) {
487
492
  return `${runId}:provider-call:${providerCalls.length + 1}`;
488
493
  }
@@ -509,6 +514,7 @@ function canonicalizeRunResult(result) {
509
514
  cost: canonicalizeSerializable(result.cost),
510
515
  ...result.evaluation !== void 0 ? { evaluation: canonicalizeSerializable(result.evaluation) } : {},
511
516
  eventLog,
517
+ health: canonicalizeSerializable(result.health),
512
518
  metadata: canonicalizeSerializable(result.metadata),
513
519
  output: result.output,
514
520
  ...result.quality !== void 0 ? { quality: canonicalizeSerializable(result.quality) } : {},
@@ -747,6 +753,77 @@ function canonicalizeSerializable(value) {
747
753
  return output;
748
754
  }
749
755
  //#endregion
756
+ //#region src/runtime/health.ts
757
+ /**
758
+ * Default health thresholds used for `result.health` auto-computation.
759
+ *
760
+ * Both threshold-gated anomalies (runaway-turns, budget-near-miss) are suppressed
761
+ * by default. Only threshold-free anomalies (empty-contribution) can fire on the
762
+ * auto-compute path.
763
+ */
764
+ var DEFAULT_HEALTH_THRESHOLDS = Object.freeze({});
765
+ /**
766
+ * Compute a health summary from a completed run trace.
767
+ *
768
+ * Pure function - no side effects, no I/O, no storage access. Deterministic:
769
+ * given the same trace and thresholds, always produces the same result.
770
+ *
771
+ * @param trace - Completed run trace (from RunResult.trace or a stored trace).
772
+ * @param thresholds - Optional threshold overrides. Defaults to DEFAULT_HEALTH_THRESHOLDS.
773
+ */
774
+ function computeHealth(trace, thresholds = DEFAULT_HEALTH_THRESHOLDS) {
775
+ assertFiniteNonNegativeThreshold(thresholds.runawayTurns, "runawayTurns");
776
+ assertBudgetNearMissThreshold(thresholds.budgetNearMissPct);
777
+ const turnEvents = trace.events.filter((event) => event.type === "agent-turn");
778
+ const agentIds = new Set(turnEvents.map((event) => event.agentId));
779
+ const totalTurns = turnEvents.length;
780
+ const agentCount = agentIds.size;
781
+ const maxUsd = trace.budget.caps?.maxUsd;
782
+ const finalCost = trace.finalOutput.cost.usd;
783
+ const budgetUtilizationPct = maxUsd !== void 0 ? maxUsd === 0 ? finalCost === 0 ? 0 : 100 : finalCost / maxUsd * 100 : null;
784
+ const anomalies = [];
785
+ if (thresholds.runawayTurns !== void 0) for (const agentId of agentIds) {
786
+ const count = turnEvents.filter((event) => event.agentId === agentId).length;
787
+ if (count > thresholds.runawayTurns) anomalies.push({
788
+ code: "runaway-turns",
789
+ severity: "error",
790
+ value: count,
791
+ threshold: thresholds.runawayTurns,
792
+ agentId
793
+ });
794
+ }
795
+ if (thresholds.budgetNearMissPct !== void 0 && budgetUtilizationPct !== null) {
796
+ if (budgetUtilizationPct >= thresholds.budgetNearMissPct) anomalies.push({
797
+ code: "budget-near-miss",
798
+ severity: "warning",
799
+ value: budgetUtilizationPct,
800
+ threshold: thresholds.budgetNearMissPct
801
+ });
802
+ }
803
+ for (const event of turnEvents) if (event.output.trim() === "") anomalies.push({
804
+ code: "empty-contribution",
805
+ severity: "error",
806
+ value: 0,
807
+ threshold: 0,
808
+ agentId: event.agentId
809
+ });
810
+ return {
811
+ anomalies,
812
+ stats: {
813
+ totalTurns,
814
+ agentCount,
815
+ budgetUtilizationPct
816
+ }
817
+ };
818
+ }
819
+ function assertFiniteNonNegativeThreshold(value, name) {
820
+ if (value !== void 0 && (!Number.isFinite(value) || value < 0)) throw new RangeError(`${name} must be a finite non-negative number`);
821
+ }
822
+ function assertBudgetNearMissThreshold(value) {
823
+ assertFiniteNonNegativeThreshold(value, "budgetNearMissPct");
824
+ if (value !== void 0 && value > 100) throw new RangeError("budgetNearMissPct must be between 0 and 100");
825
+ }
826
+ //#endregion
750
827
  //#region src/runtime/cancellation.ts
751
828
  /**
752
829
  * Classify an abort signal's reason into the BUDGET-01 / BUDGET-02
@@ -1034,12 +1111,25 @@ function truncate(value) {
1034
1111
  //#region src/runtime/model.ts
1035
1112
  async function generateModelTurn(options) {
1036
1113
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
1114
+ const modelId = options.model.modelId ?? options.model.id;
1115
+ const traceRequest = requestForTrace(options.request);
1037
1116
  let response;
1038
1117
  throwIfAborted(options.request.signal, options.model.id);
1118
+ options.emit({
1119
+ type: "model-request",
1120
+ runId: options.runId,
1121
+ callId: options.callId,
1122
+ providerId: options.model.id,
1123
+ modelId,
1124
+ startedAt,
1125
+ agentId: options.agent.id,
1126
+ role: options.agent.role,
1127
+ request: traceRequest
1128
+ });
1039
1129
  if (!options.model.stream) {
1040
1130
  response = await options.model.generate(options.request);
1041
1131
  throwIfAborted(options.request.signal, options.model.id);
1042
- recordProviderCall(response, startedAt, options);
1132
+ recordProviderCall(response, startedAt, modelId, traceRequest, options);
1043
1133
  return response;
1044
1134
  }
1045
1135
  let text = "";
@@ -1079,27 +1169,41 @@ async function generateModelTurn(options) {
1079
1169
  ...metadata !== void 0 ? { metadata } : {}
1080
1170
  };
1081
1171
  throwIfAborted(options.request.signal, options.model.id);
1082
- recordProviderCall(response, startedAt, options);
1172
+ recordProviderCall(response, startedAt, modelId, traceRequest, options);
1083
1173
  return response;
1084
1174
  }
1085
- function recordProviderCall(response, startedAt, options) {
1175
+ function recordProviderCall(response, startedAt, modelId, request, options) {
1176
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
1177
+ options.emit({
1178
+ type: "model-response",
1179
+ runId: options.runId,
1180
+ callId: options.callId,
1181
+ providerId: options.model.id,
1182
+ modelId,
1183
+ startedAt,
1184
+ completedAt,
1185
+ agentId: options.agent.id,
1186
+ role: options.agent.role,
1187
+ response
1188
+ });
1086
1189
  options.onProviderCall?.({
1087
1190
  kind: "replay-trace-provider-call",
1088
1191
  callId: options.callId,
1089
1192
  providerId: options.model.id,
1193
+ modelId,
1090
1194
  startedAt,
1091
- completedAt: (/* @__PURE__ */ new Date()).toISOString(),
1195
+ completedAt,
1092
1196
  agentId: options.agent.id,
1093
1197
  role: options.agent.role,
1094
- request: requestForTrace(options.request),
1198
+ request,
1095
1199
  response
1096
1200
  });
1097
1201
  }
1098
1202
  function requestForTrace(request) {
1099
1203
  return {
1100
- messages: request.messages,
1204
+ messages: request.messages.map((message) => ({ ...message })),
1101
1205
  temperature: request.temperature,
1102
- metadata: request.metadata
1206
+ metadata: JSON.parse(JSON.stringify(request.metadata))
1103
1207
  };
1104
1208
  }
1105
1209
  //#endregion
@@ -3042,44 +3146,45 @@ async function runBroadcast(options) {
3042
3146
  emit(final);
3043
3147
  recordProtocolDecision(final, { transcriptEntryCount: transcript.length });
3044
3148
  const finalEvent = events.at(-1);
3149
+ const trace = {
3150
+ schemaVersion: "1.0",
3151
+ runId,
3152
+ protocol: "broadcast",
3153
+ tier: options.tier,
3154
+ modelProviderId: options.model.id,
3155
+ agentsUsed: options.agents,
3156
+ inputs: createReplayTraceRunInputs({
3157
+ intent: options.intent,
3158
+ protocol: options.protocol,
3159
+ tier: options.tier,
3160
+ modelProviderId: options.model.id,
3161
+ agents: options.agents,
3162
+ temperature: options.temperature
3163
+ }),
3164
+ budget: createReplayTraceBudget({
3165
+ tier: options.tier,
3166
+ ...options.budget ? { caps: options.budget } : {},
3167
+ ...options.terminate ? { termination: options.terminate } : {}
3168
+ }),
3169
+ budgetStateChanges: createReplayTraceBudgetStateChanges(events),
3170
+ seed: createReplayTraceSeed(options.seed),
3171
+ protocolDecisions,
3172
+ providerCalls,
3173
+ finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? {
3174
+ type: "final",
3175
+ runId,
3176
+ at: "",
3177
+ output,
3178
+ cost: totalCost,
3179
+ transcript: createTranscriptLink(transcript)
3180
+ }),
3181
+ events,
3182
+ transcript
3183
+ };
3045
3184
  return {
3046
3185
  output,
3047
3186
  eventLog: createRunEventLog(runId, "broadcast", events),
3048
- trace: {
3049
- schemaVersion: "1.0",
3050
- runId,
3051
- protocol: "broadcast",
3052
- tier: options.tier,
3053
- modelProviderId: options.model.id,
3054
- agentsUsed: options.agents,
3055
- inputs: createReplayTraceRunInputs({
3056
- intent: options.intent,
3057
- protocol: options.protocol,
3058
- tier: options.tier,
3059
- modelProviderId: options.model.id,
3060
- agents: options.agents,
3061
- temperature: options.temperature
3062
- }),
3063
- budget: createReplayTraceBudget({
3064
- tier: options.tier,
3065
- ...options.budget ? { caps: options.budget } : {},
3066
- ...options.terminate ? { termination: options.terminate } : {}
3067
- }),
3068
- budgetStateChanges: createReplayTraceBudgetStateChanges(events),
3069
- seed: createReplayTraceSeed(options.seed),
3070
- protocolDecisions,
3071
- providerCalls,
3072
- finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? {
3073
- type: "final",
3074
- runId,
3075
- at: "",
3076
- output,
3077
- cost: totalCost,
3078
- transcript: createTranscriptLink(transcript)
3079
- }),
3080
- events,
3081
- transcript
3082
- },
3187
+ trace,
3083
3188
  transcript,
3084
3189
  usage: createRunUsage(totalCost),
3085
3190
  metadata: createRunMetadata({
@@ -3097,7 +3202,8 @@ async function runBroadcast(options) {
3097
3202
  cost: totalCost,
3098
3203
  events
3099
3204
  }),
3100
- cost: totalCost
3205
+ cost: totalCost,
3206
+ health: computeHealth(trace, DEFAULT_HEALTH_THRESHOLDS)
3101
3207
  };
3102
3208
  function stopIfNeeded() {
3103
3209
  throwIfAborted(options.signal, options.model.id);
@@ -3645,45 +3751,46 @@ async function runCoordinator(options) {
3645
3751
  emit(final);
3646
3752
  recordProtocolDecision(final, { transcriptEntryCount: transcript.length });
3647
3753
  const finalEvent = events.at(-1);
3754
+ const trace = {
3755
+ schemaVersion: "1.0",
3756
+ runId,
3757
+ protocol: "coordinator",
3758
+ tier: options.tier,
3759
+ modelProviderId: options.model.id,
3760
+ agentsUsed: activeAgents,
3761
+ inputs: createReplayTraceRunInputs({
3762
+ intent: options.intent,
3763
+ protocol: options.protocol,
3764
+ tier: options.tier,
3765
+ modelProviderId: options.model.id,
3766
+ agents: activeAgents,
3767
+ temperature: options.temperature
3768
+ }),
3769
+ budget: createReplayTraceBudget({
3770
+ tier: options.tier,
3771
+ ...options.budget ? { caps: options.budget } : {},
3772
+ ...options.terminate ? { termination: options.terminate } : {}
3773
+ }),
3774
+ budgetStateChanges: createReplayTraceBudgetStateChanges(events),
3775
+ seed: createReplayTraceSeed(options.seed),
3776
+ protocolDecisions,
3777
+ providerCalls,
3778
+ finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? {
3779
+ type: "final",
3780
+ runId,
3781
+ at: "",
3782
+ output,
3783
+ cost: totalCost,
3784
+ transcript: createTranscriptLink(transcript)
3785
+ }),
3786
+ ...triggeringFailureForAbortMode !== void 0 ? { triggeringFailureForAbortMode } : {},
3787
+ events,
3788
+ transcript
3789
+ };
3648
3790
  return {
3649
3791
  output,
3650
3792
  eventLog: createRunEventLog(runId, "coordinator", events),
3651
- trace: {
3652
- schemaVersion: "1.0",
3653
- runId,
3654
- protocol: "coordinator",
3655
- tier: options.tier,
3656
- modelProviderId: options.model.id,
3657
- agentsUsed: activeAgents,
3658
- inputs: createReplayTraceRunInputs({
3659
- intent: options.intent,
3660
- protocol: options.protocol,
3661
- tier: options.tier,
3662
- modelProviderId: options.model.id,
3663
- agents: activeAgents,
3664
- temperature: options.temperature
3665
- }),
3666
- budget: createReplayTraceBudget({
3667
- tier: options.tier,
3668
- ...options.budget ? { caps: options.budget } : {},
3669
- ...options.terminate ? { termination: options.terminate } : {}
3670
- }),
3671
- budgetStateChanges: createReplayTraceBudgetStateChanges(events),
3672
- seed: createReplayTraceSeed(options.seed),
3673
- protocolDecisions,
3674
- providerCalls,
3675
- finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? {
3676
- type: "final",
3677
- runId,
3678
- at: "",
3679
- output,
3680
- cost: totalCost,
3681
- transcript: createTranscriptLink(transcript)
3682
- }),
3683
- ...triggeringFailureForAbortMode !== void 0 ? { triggeringFailureForAbortMode } : {},
3684
- events,
3685
- transcript
3686
- },
3793
+ trace,
3687
3794
  transcript,
3688
3795
  usage: createRunUsage(totalCost),
3689
3796
  metadata: createRunMetadata({
@@ -3701,7 +3808,8 @@ async function runCoordinator(options) {
3701
3808
  cost: totalCost,
3702
3809
  events
3703
3810
  }),
3704
- cost: totalCost
3811
+ cost: totalCost,
3812
+ health: computeHealth(trace, DEFAULT_HEALTH_THRESHOLDS)
3705
3813
  };
3706
3814
  function stopIfNeeded() {
3707
3815
  throwIfAborted(options.signal, options.model.id);
@@ -4063,6 +4171,7 @@ async function dispatchDelegate(input) {
4063
4171
  input.dispatchedChild.controller.abort(childDeadlineReason);
4064
4172
  }, childTimeoutMs) : void 0;
4065
4173
  const childOptions = {
4174
+ runId: childRunId,
4066
4175
  intent: decision.intent,
4067
4176
  protocol: decision.protocol,
4068
4177
  tier: options.tier,
@@ -4214,11 +4323,16 @@ async function dispatchDelegate(input) {
4214
4323
  function renderSubRunResult(childRunId, subResult) {
4215
4324
  const turns = subResult.transcript.length;
4216
4325
  const costUsd = subResult.cost.usd ?? 0;
4217
- const startedAt = subResult.trace.events[0]?.at;
4218
- const endedAt = subResult.trace.events.at(-1)?.at;
4326
+ const startedAt = eventTimestamp(subResult.trace.events[0]);
4327
+ const endedAt = eventTimestamp(subResult.trace.events.at(-1));
4219
4328
  const durationMs = startedAt && endedAt ? Math.max(0, Date.parse(endedAt) - Date.parse(startedAt)) : 0;
4220
4329
  return [`[sub-run ${childRunId}]: ${subResult.output}`, `[sub-run ${childRunId} stats]: turns=${turns} costUsd=${costUsd} durationMs=${durationMs}`].join("\n");
4221
4330
  }
4331
+ function eventTimestamp(event) {
4332
+ if (event === void 0) return void 0;
4333
+ if ("at" in event) return event.at;
4334
+ return event.type === "model-response" ? event.completedAt : event.startedAt;
4335
+ }
4222
4336
  /**
4223
4337
  * Build a JSON-serializable {@link Trace} for `sub-run-failed.partialTrace`
4224
4338
  * from a buffered tee of child emits. Keeps `runProtocol`'s error contract
@@ -4482,44 +4596,45 @@ async function runSequential(options) {
4482
4596
  emit(final);
4483
4597
  recordProtocolDecision(final, { transcriptEntryCount: transcript.length });
4484
4598
  const finalEvent = events.at(-1);
4599
+ const trace = {
4600
+ schemaVersion: "1.0",
4601
+ runId,
4602
+ protocol: "sequential",
4603
+ tier: options.tier,
4604
+ modelProviderId: options.model.id,
4605
+ agentsUsed: activeAgents,
4606
+ inputs: createReplayTraceRunInputs({
4607
+ intent: options.intent,
4608
+ protocol: options.protocol,
4609
+ tier: options.tier,
4610
+ modelProviderId: options.model.id,
4611
+ agents: activeAgents,
4612
+ temperature: options.temperature
4613
+ }),
4614
+ budget: createReplayTraceBudget({
4615
+ tier: options.tier,
4616
+ ...options.budget ? { caps: options.budget } : {},
4617
+ ...options.terminate ? { termination: options.terminate } : {}
4618
+ }),
4619
+ budgetStateChanges: createReplayTraceBudgetStateChanges(events),
4620
+ seed: createReplayTraceSeed(options.seed),
4621
+ protocolDecisions,
4622
+ providerCalls,
4623
+ finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? events[0] ?? {
4624
+ type: "final",
4625
+ runId,
4626
+ at: "",
4627
+ output,
4628
+ cost: totalCost,
4629
+ transcript: createTranscriptLink(transcript)
4630
+ }),
4631
+ events,
4632
+ transcript
4633
+ };
4485
4634
  return {
4486
4635
  output,
4487
4636
  eventLog: createRunEventLog(runId, "sequential", events),
4488
- trace: {
4489
- schemaVersion: "1.0",
4490
- runId,
4491
- protocol: "sequential",
4492
- tier: options.tier,
4493
- modelProviderId: options.model.id,
4494
- agentsUsed: activeAgents,
4495
- inputs: createReplayTraceRunInputs({
4496
- intent: options.intent,
4497
- protocol: options.protocol,
4498
- tier: options.tier,
4499
- modelProviderId: options.model.id,
4500
- agents: activeAgents,
4501
- temperature: options.temperature
4502
- }),
4503
- budget: createReplayTraceBudget({
4504
- tier: options.tier,
4505
- ...options.budget ? { caps: options.budget } : {},
4506
- ...options.terminate ? { termination: options.terminate } : {}
4507
- }),
4508
- budgetStateChanges: createReplayTraceBudgetStateChanges(events),
4509
- seed: createReplayTraceSeed(options.seed),
4510
- protocolDecisions,
4511
- providerCalls,
4512
- finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? events[0] ?? {
4513
- type: "final",
4514
- runId,
4515
- at: "",
4516
- output,
4517
- cost: totalCost,
4518
- transcript: createTranscriptLink(transcript)
4519
- }),
4520
- events,
4521
- transcript
4522
- },
4637
+ trace,
4523
4638
  transcript,
4524
4639
  usage: createRunUsage(totalCost),
4525
4640
  metadata: createRunMetadata({
@@ -4537,7 +4652,8 @@ async function runSequential(options) {
4537
4652
  cost: totalCost,
4538
4653
  events
4539
4654
  }),
4540
- cost: totalCost
4655
+ cost: totalCost,
4656
+ health: computeHealth(trace, DEFAULT_HEALTH_THRESHOLDS)
4541
4657
  };
4542
4658
  function stopIfNeeded() {
4543
4659
  throwIfAborted(options.signal, options.model.id);
@@ -4756,44 +4872,45 @@ async function runShared(options) {
4756
4872
  emit(final);
4757
4873
  recordProtocolDecision(final, { transcriptEntryCount: transcript.length });
4758
4874
  const finalEvent = events.at(-1);
4875
+ const trace = {
4876
+ schemaVersion: "1.0",
4877
+ runId,
4878
+ protocol: "shared",
4879
+ tier: options.tier,
4880
+ modelProviderId: options.model.id,
4881
+ agentsUsed: activeAgents,
4882
+ inputs: createReplayTraceRunInputs({
4883
+ intent: options.intent,
4884
+ protocol: options.protocol,
4885
+ tier: options.tier,
4886
+ modelProviderId: options.model.id,
4887
+ agents: activeAgents,
4888
+ temperature: options.temperature
4889
+ }),
4890
+ budget: createReplayTraceBudget({
4891
+ tier: options.tier,
4892
+ ...options.budget ? { caps: options.budget } : {},
4893
+ ...options.terminate ? { termination: options.terminate } : {}
4894
+ }),
4895
+ budgetStateChanges: createReplayTraceBudgetStateChanges(events),
4896
+ seed: createReplayTraceSeed(options.seed),
4897
+ protocolDecisions,
4898
+ providerCalls,
4899
+ finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? {
4900
+ type: "final",
4901
+ runId,
4902
+ at: "",
4903
+ output,
4904
+ cost: totalCost,
4905
+ transcript: createTranscriptLink(transcript)
4906
+ }),
4907
+ events,
4908
+ transcript
4909
+ };
4759
4910
  return {
4760
4911
  output,
4761
4912
  eventLog: createRunEventLog(runId, "shared", events),
4762
- trace: {
4763
- schemaVersion: "1.0",
4764
- runId,
4765
- protocol: "shared",
4766
- tier: options.tier,
4767
- modelProviderId: options.model.id,
4768
- agentsUsed: activeAgents,
4769
- inputs: createReplayTraceRunInputs({
4770
- intent: options.intent,
4771
- protocol: options.protocol,
4772
- tier: options.tier,
4773
- modelProviderId: options.model.id,
4774
- agents: activeAgents,
4775
- temperature: options.temperature
4776
- }),
4777
- budget: createReplayTraceBudget({
4778
- tier: options.tier,
4779
- ...options.budget ? { caps: options.budget } : {},
4780
- ...options.terminate ? { termination: options.terminate } : {}
4781
- }),
4782
- budgetStateChanges: createReplayTraceBudgetStateChanges(events),
4783
- seed: createReplayTraceSeed(options.seed),
4784
- protocolDecisions,
4785
- providerCalls,
4786
- finalOutput: createReplayTraceFinalOutput(output, finalEvent ?? {
4787
- type: "final",
4788
- runId,
4789
- at: "",
4790
- output,
4791
- cost: totalCost,
4792
- transcript: createTranscriptLink(transcript)
4793
- }),
4794
- events,
4795
- transcript
4796
- },
4913
+ trace,
4797
4914
  transcript,
4798
4915
  usage: createRunUsage(totalCost),
4799
4916
  metadata: createRunMetadata({
@@ -4811,7 +4928,8 @@ async function runShared(options) {
4811
4928
  cost: totalCost,
4812
4929
  events
4813
4930
  }),
4814
- cost: totalCost
4931
+ cost: totalCost,
4932
+ health: computeHealth(trace, DEFAULT_HEALTH_THRESHOLDS)
4815
4933
  };
4816
4934
  function stopIfNeeded() {
4817
4935
  throwIfAborted(options.signal, options.model.id);
@@ -4867,6 +4985,14 @@ function responseCost(response) {
4867
4985
  };
4868
4986
  }
4869
4987
  //#endregion
4988
+ //#region src/runtime/tracing.ts
4989
+ var DOGPILE_SPAN_NAMES = {
4990
+ RUN: "dogpile.run",
4991
+ SUB_RUN: "dogpile.sub-run",
4992
+ AGENT_TURN: "dogpile.agent-turn",
4993
+ MODEL_CALL: "dogpile.model-call"
4994
+ };
4995
+ //#endregion
4870
4996
  //#region src/runtime/engine.ts
4871
4997
  var DEFAULT_MAX_DEPTH = 4;
4872
4998
  var DEFAULT_MAX_CONCURRENT_CHILDREN = 4;
@@ -4918,6 +5044,9 @@ function createEngine(options) {
4918
5044
  ...terminate ? { terminate } : {},
4919
5045
  ...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {},
4920
5046
  ...options.evaluate ? { evaluate: options.evaluate } : {},
5047
+ ...options.tracer ? { tracer: options.tracer } : {},
5048
+ ...options.metricsHook ? { metricsHook: options.metricsHook } : {},
5049
+ ...options.logger ? { logger: options.logger } : {},
4921
5050
  currentDepth: 0,
4922
5051
  effectiveMaxDepth,
4923
5052
  effectiveMaxConcurrentChildren,
@@ -5019,6 +5148,9 @@ function createEngine(options) {
5019
5148
  onChildFailure,
5020
5149
  ...streamParentDeadlineMs !== void 0 ? { parentDeadlineMs: streamParentDeadlineMs } : {},
5021
5150
  ...options.defaultSubRunTimeoutMs !== void 0 ? { defaultSubRunTimeoutMs: options.defaultSubRunTimeoutMs } : {},
5151
+ ...options.tracer ? { tracer: options.tracer } : {},
5152
+ ...options.metricsHook ? { metricsHook: options.metricsHook } : {},
5153
+ ...options.logger ? { logger: options.logger } : {},
5022
5154
  streamEvents: true,
5023
5155
  emit(event) {
5024
5156
  if (status !== "running") return;
@@ -5279,6 +5411,329 @@ function dogpileErrorStreamDetail(error) {
5279
5411
  if (error.detail !== void 0) for (const [key, value] of Object.entries(error.detail)) detail[key] = value;
5280
5412
  return detail;
5281
5413
  }
5414
+ function openRunTracing(options) {
5415
+ if (!options.tracer) return;
5416
+ const runSpan = options.tracer.startSpan(DOGPILE_SPAN_NAMES.RUN, {
5417
+ ...options.parentSpan ? { parent: options.parentSpan } : {},
5418
+ attributes: {
5419
+ "dogpile.run.protocol": options.protocolKind,
5420
+ "dogpile.run.tier": String(options.tier),
5421
+ "dogpile.run.intent": options.intent.slice(0, 200)
5422
+ }
5423
+ });
5424
+ return {
5425
+ tracer: options.tracer,
5426
+ runSpan,
5427
+ subRunSpans: /* @__PURE__ */ new Map(),
5428
+ agentTurnSpans: /* @__PURE__ */ new Map(),
5429
+ modelCallSpans: /* @__PURE__ */ new Map(),
5430
+ pendingModelRequests: /* @__PURE__ */ new Map(),
5431
+ agentTurnCounters: /* @__PURE__ */ new Map(),
5432
+ turnAccumByAgent: /* @__PURE__ */ new Map(),
5433
+ agentIds: /* @__PURE__ */ new Set(),
5434
+ turnCount: 0,
5435
+ lastCost: emptyCost()
5436
+ };
5437
+ }
5438
+ function openRunMetrics(options) {
5439
+ if (!options.metricsHook) return;
5440
+ return {
5441
+ metricsHook: options.metricsHook,
5442
+ logger: options.logger,
5443
+ startedAtMs: Date.now(),
5444
+ subRunStartTimes: /* @__PURE__ */ new Map(),
5445
+ totalCost: emptyCost(),
5446
+ nestedCost: emptyCost(),
5447
+ turns: 0
5448
+ };
5449
+ }
5450
+ function routeMetricsError(err, logger) {
5451
+ const msg = err instanceof Error ? err.message : String(err);
5452
+ try {
5453
+ if (logger !== void 0) logger.error("dogpile:metricsHook threw", { error: msg });
5454
+ else console.error("dogpile:metricsHook threw", { error: msg });
5455
+ } catch {}
5456
+ }
5457
+ function fireHook(callback, snapshot, logger) {
5458
+ if (!callback) return;
5459
+ try {
5460
+ const result = callback(snapshot);
5461
+ if (result && typeof result.catch === "function") result.catch((err) => {
5462
+ routeMetricsError(err, logger);
5463
+ });
5464
+ } catch (err) {
5465
+ routeMetricsError(err, logger);
5466
+ }
5467
+ }
5468
+ function buildRunSnapshot(result, startedAtMs) {
5469
+ const nestedCosts = nestedSubRunCosts(result);
5470
+ const outcome = result.trace.events.find((event) => event.type === "budget-stop") !== void 0 ? "budget-stopped" : "completed";
5471
+ const totalInputTokens = result.cost.inputTokens;
5472
+ const totalOutputTokens = result.cost.outputTokens;
5473
+ const totalCostUsd = result.cost.usd;
5474
+ return {
5475
+ outcome,
5476
+ inputTokens: totalInputTokens - nestedCosts.reduce((sum, cost) => sum + cost.inputTokens, 0),
5477
+ outputTokens: totalOutputTokens - nestedCosts.reduce((sum, cost) => sum + cost.outputTokens, 0),
5478
+ costUsd: totalCostUsd - nestedCosts.reduce((sum, cost) => sum + cost.usd, 0),
5479
+ totalInputTokens,
5480
+ totalOutputTokens,
5481
+ totalCostUsd,
5482
+ turns: result.trace.events.filter((event) => event.type === "agent-turn").length,
5483
+ durationMs: Date.now() - startedAtMs
5484
+ };
5485
+ }
5486
+ function buildSubRunSnapshot(subResult, durationMs) {
5487
+ const nestedCosts = nestedSubRunCosts(subResult);
5488
+ const outcome = subResult.trace.events.find((event) => event.type === "budget-stop") !== void 0 ? "budget-stopped" : "completed";
5489
+ const totalInputTokens = subResult.cost.inputTokens;
5490
+ const totalOutputTokens = subResult.cost.outputTokens;
5491
+ const totalCostUsd = subResult.cost.usd;
5492
+ return {
5493
+ outcome,
5494
+ inputTokens: totalInputTokens - nestedCosts.reduce((sum, cost) => sum + cost.inputTokens, 0),
5495
+ outputTokens: totalOutputTokens - nestedCosts.reduce((sum, cost) => sum + cost.outputTokens, 0),
5496
+ costUsd: totalCostUsd - nestedCosts.reduce((sum, cost) => sum + cost.usd, 0),
5497
+ totalInputTokens,
5498
+ totalOutputTokens,
5499
+ totalCostUsd,
5500
+ turns: subResult.trace.events.filter((event) => event.type === "agent-turn").length,
5501
+ durationMs
5502
+ };
5503
+ }
5504
+ function nestedSubRunCosts(result) {
5505
+ return result.trace.events.flatMap((event) => {
5506
+ if (event.type === "sub-run-completed") return [event.subResult.cost];
5507
+ if (event.type === "sub-run-failed") return [event.partialCost];
5508
+ return [];
5509
+ });
5510
+ }
5511
+ function subtractCost(total, nested) {
5512
+ return {
5513
+ usd: total.usd - nested.usd,
5514
+ inputTokens: total.inputTokens - nested.inputTokens,
5515
+ outputTokens: total.outputTokens - nested.outputTokens,
5516
+ totalTokens: total.totalTokens - nested.totalTokens
5517
+ };
5518
+ }
5519
+ function handleMetricsEvent(state, event) {
5520
+ if (event.parentRunIds !== void 0) return;
5521
+ switch (event.type) {
5522
+ case "agent-turn":
5523
+ state.totalCost = event.cost;
5524
+ state.turns += 1;
5525
+ break;
5526
+ case "broadcast":
5527
+ case "budget-stop":
5528
+ case "final":
5529
+ state.totalCost = event.cost;
5530
+ break;
5531
+ case "sub-run-started":
5532
+ state.subRunStartTimes.set(event.childRunId, Date.now());
5533
+ break;
5534
+ case "sub-run-completed": {
5535
+ state.totalCost = addCost(state.totalCost, event.subResult.cost);
5536
+ state.nestedCost = addCost(state.nestedCost, event.subResult.cost);
5537
+ const startMs = state.subRunStartTimes.get(event.childRunId);
5538
+ const durationMs = startMs !== void 0 ? Date.now() - startMs : 0;
5539
+ state.subRunStartTimes.delete(event.childRunId);
5540
+ const snapshot = buildSubRunSnapshot(event.subResult, durationMs);
5541
+ fireHook(state.metricsHook.onSubRunComplete, snapshot, state.logger);
5542
+ break;
5543
+ }
5544
+ case "sub-run-failed":
5545
+ state.totalCost = addCost(state.totalCost, event.partialCost);
5546
+ state.nestedCost = addCost(state.nestedCost, event.partialCost);
5547
+ state.subRunStartTimes.delete(event.childRunId);
5548
+ break;
5549
+ default: break;
5550
+ }
5551
+ }
5552
+ function closeRunMetrics(state, result) {
5553
+ if (result !== void 0) {
5554
+ const snapshot = buildRunSnapshot(result, state.startedAtMs);
5555
+ fireHook(state.metricsHook.onRunComplete, snapshot, state.logger);
5556
+ return;
5557
+ }
5558
+ const ownCost = subtractCost(state.totalCost, state.nestedCost);
5559
+ const snapshot = {
5560
+ outcome: "aborted",
5561
+ inputTokens: ownCost.inputTokens,
5562
+ outputTokens: ownCost.outputTokens,
5563
+ costUsd: ownCost.usd,
5564
+ totalInputTokens: state.totalCost.inputTokens,
5565
+ totalOutputTokens: state.totalCost.outputTokens,
5566
+ totalCostUsd: state.totalCost.usd,
5567
+ turns: state.turns,
5568
+ durationMs: Date.now() - state.startedAtMs
5569
+ };
5570
+ fireHook(state.metricsHook.onRunComplete, snapshot, state.logger);
5571
+ }
5572
+ function handleTracingEvent(state, event) {
5573
+ if (event.parentRunIds !== void 0) return;
5574
+ if (state.runId === void 0) {
5575
+ state.runId = event.runId;
5576
+ state.runSpan.setAttribute("dogpile.run.id", event.runId);
5577
+ }
5578
+ switch (event.type) {
5579
+ case "model-request": {
5580
+ state.pendingModelRequests.set(event.callId, event);
5581
+ state.agentIds.add(event.agentId);
5582
+ if (!state.agentTurnSpans.has(event.agentId)) {
5583
+ const turnNumber = (state.agentTurnCounters.get(event.agentId) ?? 0) + 1;
5584
+ state.agentTurnCounters.set(event.agentId, turnNumber);
5585
+ const turnParent = state.subRunSpans.get(event.runId) ?? state.runSpan;
5586
+ const turnSpan = state.tracer.startSpan(DOGPILE_SPAN_NAMES.AGENT_TURN, {
5587
+ parent: turnParent,
5588
+ attributes: {
5589
+ "dogpile.agent.id": event.agentId,
5590
+ "dogpile.agent.role": event.role,
5591
+ "dogpile.turn.number": turnNumber,
5592
+ "dogpile.model.id": event.modelId
5593
+ }
5594
+ });
5595
+ state.agentTurnSpans.set(event.agentId, turnSpan);
5596
+ }
5597
+ const callParent = state.agentTurnSpans.get(event.agentId) ?? state.subRunSpans.get(event.runId) ?? state.runSpan;
5598
+ const callSpan = state.tracer.startSpan(DOGPILE_SPAN_NAMES.MODEL_CALL, {
5599
+ parent: callParent,
5600
+ attributes: {
5601
+ "dogpile.model.id": event.modelId,
5602
+ "dogpile.call.id": event.callId,
5603
+ "dogpile.provider.id": event.providerId
5604
+ }
5605
+ });
5606
+ state.modelCallSpans.set(event.callId, callSpan);
5607
+ break;
5608
+ }
5609
+ case "model-response": {
5610
+ const span = state.modelCallSpans.get(event.callId);
5611
+ if (span) {
5612
+ const inputTokens = event.response.usage?.inputTokens ?? 0;
5613
+ const outputTokens = event.response.usage?.outputTokens ?? 0;
5614
+ const responseCost = {
5615
+ usd: event.response.costUsd ?? 0,
5616
+ inputTokens,
5617
+ outputTokens,
5618
+ totalTokens: event.response.usage?.totalTokens ?? inputTokens + outputTokens
5619
+ };
5620
+ span.setAttribute("dogpile.model.input_tokens", inputTokens);
5621
+ span.setAttribute("dogpile.model.output_tokens", outputTokens);
5622
+ if (event.response.costUsd !== void 0) span.setAttribute("dogpile.model.cost_usd", event.response.costUsd);
5623
+ span.setStatus("ok");
5624
+ span.end();
5625
+ state.modelCallSpans.delete(event.callId);
5626
+ const accum = state.turnAccumByAgent.get(event.agentId) ?? {
5627
+ inputTokens: 0,
5628
+ outputTokens: 0,
5629
+ costUsd: 0
5630
+ };
5631
+ accum.inputTokens += inputTokens;
5632
+ accum.outputTokens += outputTokens;
5633
+ accum.costUsd += responseCost.usd;
5634
+ state.turnAccumByAgent.set(event.agentId, accum);
5635
+ state.lastCost = addCost(state.lastCost, responseCost);
5636
+ }
5637
+ state.pendingModelRequests.delete(event.callId);
5638
+ break;
5639
+ }
5640
+ case "agent-turn": {
5641
+ state.agentIds.add(event.agentId);
5642
+ state.turnCount += 1;
5643
+ state.lastCost = event.cost;
5644
+ const turnSpan = state.agentTurnSpans.get(event.agentId);
5645
+ if (turnSpan) {
5646
+ turnSpan.setAttribute("dogpile.agent.role", event.role);
5647
+ const accum = state.turnAccumByAgent.get(event.agentId);
5648
+ turnSpan.setAttribute("dogpile.turn.cost_usd", accum?.costUsd ?? 0);
5649
+ turnSpan.setAttribute("dogpile.turn.input_tokens", accum?.inputTokens ?? 0);
5650
+ turnSpan.setAttribute("dogpile.turn.output_tokens", accum?.outputTokens ?? 0);
5651
+ turnSpan.setStatus("ok");
5652
+ turnSpan.end();
5653
+ state.agentTurnSpans.delete(event.agentId);
5654
+ }
5655
+ state.turnAccumByAgent.delete(event.agentId);
5656
+ break;
5657
+ }
5658
+ case "broadcast":
5659
+ case "budget-stop":
5660
+ case "final":
5661
+ state.lastCost = event.cost;
5662
+ break;
5663
+ case "sub-run-started": {
5664
+ const span = state.tracer.startSpan(DOGPILE_SPAN_NAMES.SUB_RUN, {
5665
+ parent: state.runSpan,
5666
+ attributes: {
5667
+ "dogpile.sub_run.child_run_id": event.childRunId,
5668
+ "dogpile.sub_run.parent_run_id": event.parentRunId,
5669
+ "dogpile.sub_run.depth": event.depth
5670
+ }
5671
+ });
5672
+ state.subRunSpans.set(event.childRunId, span);
5673
+ break;
5674
+ }
5675
+ case "sub-run-completed": {
5676
+ const span = state.subRunSpans.get(event.childRunId);
5677
+ if (span) {
5678
+ span.setStatus("ok");
5679
+ span.end();
5680
+ state.subRunSpans.delete(event.childRunId);
5681
+ }
5682
+ break;
5683
+ }
5684
+ case "sub-run-failed": {
5685
+ const span = state.subRunSpans.get(event.childRunId);
5686
+ if (span) {
5687
+ span.setStatus("error", event.error.message);
5688
+ span.end();
5689
+ state.subRunSpans.delete(event.childRunId);
5690
+ }
5691
+ break;
5692
+ }
5693
+ default: break;
5694
+ }
5695
+ }
5696
+ function closeRunTracing(state, result, error) {
5697
+ if (error !== void 0) {
5698
+ if (state.runId !== void 0) state.runSpan.setAttribute("dogpile.run.id", state.runId);
5699
+ state.runSpan.setAttribute("dogpile.run.agent_count", state.agentIds.size);
5700
+ state.runSpan.setAttribute("dogpile.run.turn_count", state.turnCount);
5701
+ state.runSpan.setAttribute("dogpile.run.cost_usd", state.lastCost.usd);
5702
+ state.runSpan.setAttribute("dogpile.run.input_tokens", state.lastCost.inputTokens);
5703
+ state.runSpan.setAttribute("dogpile.run.output_tokens", state.lastCost.outputTokens);
5704
+ state.runSpan.setAttribute("dogpile.run.outcome", "aborted");
5705
+ state.runSpan.setStatus("error", error instanceof Error ? error.message : String(error));
5706
+ closeOpenTracingSpans(state);
5707
+ state.runSpan.end();
5708
+ return;
5709
+ }
5710
+ if (result === void 0) {
5711
+ closeOpenTracingSpans(state);
5712
+ state.runSpan.end();
5713
+ return;
5714
+ }
5715
+ const terminationReason = result.trace.events.find((event) => event.type === "budget-stop")?.reason;
5716
+ const outcome = terminationReason !== void 0 ? "budget-stopped" : "completed";
5717
+ state.runSpan.setAttribute("dogpile.run.id", result.trace.runId);
5718
+ state.runSpan.setAttribute("dogpile.run.agent_count", result.trace.agentsUsed.length);
5719
+ state.runSpan.setAttribute("dogpile.run.turn_count", result.trace.events.filter((event) => event.type === "agent-turn").length);
5720
+ state.runSpan.setAttribute("dogpile.run.cost_usd", result.cost.usd);
5721
+ state.runSpan.setAttribute("dogpile.run.input_tokens", result.cost.inputTokens);
5722
+ state.runSpan.setAttribute("dogpile.run.output_tokens", result.cost.outputTokens);
5723
+ state.runSpan.setAttribute("dogpile.run.outcome", outcome);
5724
+ if (terminationReason !== void 0) state.runSpan.setAttribute("dogpile.run.termination_reason", terminationReason);
5725
+ state.runSpan.setStatus("ok");
5726
+ closeOpenTracingSpans(state);
5727
+ state.runSpan.end();
5728
+ }
5729
+ function closeOpenTracingSpans(state) {
5730
+ for (const span of state.modelCallSpans.values()) span.end();
5731
+ state.modelCallSpans.clear();
5732
+ for (const span of state.agentTurnSpans.values()) span.end();
5733
+ state.agentTurnSpans.clear();
5734
+ for (const span of state.subRunSpans.values()) span.end();
5735
+ state.subRunSpans.clear();
5736
+ }
5282
5737
  async function runNonStreamingProtocol(options) {
5283
5738
  const failureInstancesByChildRunId = /* @__PURE__ */ new Map();
5284
5739
  const abortLifecycle = createNonStreamingAbortLifecycle({
@@ -5314,7 +5769,8 @@ async function runNonStreamingProtocol(options) {
5314
5769
  events
5315
5770
  }),
5316
5771
  eventLog: createRunEventLog(trace.runId, trace.protocol, events),
5317
- trace
5772
+ trace,
5773
+ health: computeHealth(trace, DEFAULT_HEALTH_THRESHOLDS)
5318
5774
  };
5319
5775
  const terminalThrow = resolveRuntimeTerminalThrow(runResult.trace, failureInstancesByChildRunId);
5320
5776
  if (terminalThrow) throw terminalThrow;
@@ -5352,7 +5808,39 @@ function finalEventWithEvaluation(event, evaluation) {
5352
5808
  evaluation
5353
5809
  };
5354
5810
  }
5355
- function runProtocol(options) {
5811
+ async function runProtocol(options) {
5812
+ const tracing = openRunTracing({
5813
+ ...options.tracer ? { tracer: options.tracer } : {},
5814
+ ...options.parentSpan ? { parentSpan: options.parentSpan } : {},
5815
+ intent: options.intent,
5816
+ protocolKind: options.protocol.kind,
5817
+ tier: options.tier
5818
+ });
5819
+ const metrics = openRunMetrics({
5820
+ ...options.metricsHook ? { metricsHook: options.metricsHook } : {},
5821
+ ...options.logger ? { logger: options.logger } : {}
5822
+ });
5823
+ const emitForProtocol = tracing || metrics || options.emit ? (event) => {
5824
+ if (tracing) handleTracingEvent(tracing, event);
5825
+ if (metrics) handleMetricsEvent(metrics, event);
5826
+ options.emit?.(event);
5827
+ } : void 0;
5828
+ const protocolOptions = tracing ? {
5829
+ ...options,
5830
+ subRunSpansByChildId: tracing.subRunSpans
5831
+ } : options;
5832
+ try {
5833
+ const result = await runProtocolInner(protocolOptions, emitForProtocol);
5834
+ if (tracing) closeRunTracing(tracing, result);
5835
+ if (metrics && (options.currentDepth === 0 || options.currentDepth === void 0)) closeRunMetrics(metrics, result);
5836
+ return result;
5837
+ } catch (error) {
5838
+ if (tracing) closeRunTracing(tracing, void 0, error);
5839
+ if (metrics && (options.currentDepth === 0 || options.currentDepth === void 0)) closeRunMetrics(metrics, void 0);
5840
+ throw error;
5841
+ }
5842
+ }
5843
+ function runProtocolInner(options, emitForProtocol) {
5356
5844
  switch (options.protocol.kind) {
5357
5845
  case "sequential": return runSequential({
5358
5846
  intent: options.intent,
@@ -5367,7 +5855,7 @@ function runProtocol(options) {
5367
5855
  ...options.signal !== void 0 ? { signal: options.signal } : {},
5368
5856
  ...options.terminate ? { terminate: options.terminate } : {},
5369
5857
  ...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {},
5370
- ...options.emit ? { emit: options.emit } : {}
5858
+ ...emitForProtocol ? { emit: emitForProtocol } : {}
5371
5859
  });
5372
5860
  case "broadcast": return runBroadcast({
5373
5861
  intent: options.intent,
@@ -5382,7 +5870,7 @@ function runProtocol(options) {
5382
5870
  ...options.signal !== void 0 ? { signal: options.signal } : {},
5383
5871
  ...options.terminate ? { terminate: options.terminate } : {},
5384
5872
  ...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {},
5385
- ...options.emit ? { emit: options.emit } : {}
5873
+ ...emitForProtocol ? { emit: emitForProtocol } : {}
5386
5874
  });
5387
5875
  case "coordinator": return runCoordinator({
5388
5876
  intent: options.intent,
@@ -5397,7 +5885,7 @@ function runProtocol(options) {
5397
5885
  ...options.signal !== void 0 ? { signal: options.signal } : {},
5398
5886
  ...options.terminate ? { terminate: options.terminate } : {},
5399
5887
  ...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {},
5400
- ...options.emit ? { emit: options.emit } : {},
5888
+ ...emitForProtocol ? { emit: emitForProtocol } : {},
5401
5889
  ...options.streamEvents !== void 0 ? { streamEvents: options.streamEvents } : {},
5402
5890
  currentDepth: options.currentDepth ?? 0,
5403
5891
  effectiveMaxDepth: options.effectiveMaxDepth ?? Infinity,
@@ -5407,10 +5895,17 @@ function runProtocol(options) {
5407
5895
  ...options.defaultSubRunTimeoutMs !== void 0 ? { defaultSubRunTimeoutMs: options.defaultSubRunTimeoutMs } : {},
5408
5896
  ...options.registerAbortDrain !== void 0 ? { registerAbortDrain: options.registerAbortDrain } : {},
5409
5897
  ...options.failureInstancesByChildRunId !== void 0 ? { failureInstancesByChildRunId: options.failureInstancesByChildRunId } : {},
5410
- runProtocol: (childInput) => runProtocol({
5411
- ...childInput,
5412
- protocol: normalizeProtocol(childInput.protocol)
5413
- })
5898
+ runProtocol: (childInput) => {
5899
+ const { runId: childRunId, ...childProtocolInput } = childInput;
5900
+ const childParent = options.subRunSpansByChildId?.get(childRunId) ?? options.parentSpan;
5901
+ return runProtocol({
5902
+ ...childProtocolInput,
5903
+ protocol: normalizeProtocol(childProtocolInput.protocol),
5904
+ ...options.tracer ? { tracer: options.tracer } : {},
5905
+ ...childParent ? { parentSpan: childParent } : {},
5906
+ ...options.logger ? { logger: options.logger } : {}
5907
+ });
5908
+ }
5414
5909
  });
5415
5910
  case "shared": return runShared({
5416
5911
  intent: options.intent,
@@ -5425,7 +5920,7 @@ function runProtocol(options) {
5425
5920
  ...options.signal !== void 0 ? { signal: options.signal } : {},
5426
5921
  ...options.terminate ? { terminate: options.terminate } : {},
5427
5922
  ...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {},
5428
- ...options.emit ? { emit: options.emit } : {}
5923
+ ...emitForProtocol ? { emit: emitForProtocol } : {}
5429
5924
  });
5430
5925
  }
5431
5926
  }
@@ -5473,6 +5968,12 @@ function stream(options) {
5473
5968
  * the ergonomic {@link RunResult} wrapper from the JSON-serializable
5474
5969
  * {@link Trace} returned by a previous `run()`, `stream()`, or
5475
5970
  * `Dogpile.pile()` call.
5971
+ *
5972
+ * Tracing and metrics: replay is intentionally tracing-free and metrics-free.
5973
+ * Even when an engine instance has been configured with a `tracer` or
5974
+ * `metricsHook` on its `EngineOptions`, calling this function emits no spans
5975
+ * or callbacks — replaying historical events with current timestamps would
5976
+ * confuse observability backends. See `docs/developer-usage.md`.
5476
5977
  */
5477
5978
  function replay(trace) {
5478
5979
  const cost = trace.finalOutput.cost;
@@ -5482,7 +5983,7 @@ function replay(trace) {
5482
5983
  if (replayThrow) throw replayThrow;
5483
5984
  const baseResult = {
5484
5985
  output: trace.finalOutput.output,
5485
- eventLog: createRunEventLog(trace.runId, trace.protocol, trace.events),
5986
+ eventLog: createRunEventLog(trace.runId, trace.protocol, synthesizeProviderEvents(trace, trace.providerCalls)),
5486
5987
  trace,
5487
5988
  transcript: trace.transcript,
5488
5989
  usage: createRunUsage(cost),
@@ -5495,7 +5996,8 @@ function replay(trace) {
5495
5996
  events: trace.events
5496
5997
  }),
5497
5998
  accounting,
5498
- cost
5999
+ cost,
6000
+ health: computeHealth(trace, DEFAULT_HEALTH_THRESHOLDS)
5499
6001
  };
5500
6002
  if (lastEvent?.type !== "final") return baseResult;
5501
6003
  return {
@@ -5504,6 +6006,46 @@ function replay(trace) {
5504
6006
  ...lastEvent.evaluation !== void 0 ? { evaluation: lastEvent.evaluation } : {}
5505
6007
  };
5506
6008
  }
6009
+ function synthesizeProviderEvents(trace, providerCalls) {
6010
+ if (trace.events.some((event) => event.type === "model-request" || event.type === "model-response")) return trace.events;
6011
+ const baseEvents = trace.events.filter((event) => event.type !== "model-request" && event.type !== "model-response");
6012
+ const result = [];
6013
+ let turnCount = 0;
6014
+ for (const event of baseEvents) {
6015
+ if (event.type === "agent-turn") {
6016
+ const call = providerCalls[turnCount];
6017
+ if (call !== void 0) {
6018
+ const modelId = typeof call.modelId === "string" && call.modelId.length > 0 ? call.modelId : call.providerId;
6019
+ result.push({
6020
+ type: "model-request",
6021
+ runId: trace.runId,
6022
+ callId: call.callId,
6023
+ providerId: call.providerId,
6024
+ modelId,
6025
+ startedAt: call.startedAt,
6026
+ agentId: call.agentId,
6027
+ role: call.role,
6028
+ request: call.request
6029
+ });
6030
+ result.push({
6031
+ type: "model-response",
6032
+ runId: trace.runId,
6033
+ callId: call.callId,
6034
+ providerId: call.providerId,
6035
+ modelId,
6036
+ startedAt: call.startedAt,
6037
+ completedAt: call.completedAt,
6038
+ agentId: call.agentId,
6039
+ role: call.role,
6040
+ response: call.response
6041
+ });
6042
+ }
6043
+ turnCount += 1;
6044
+ }
6045
+ result.push(event);
6046
+ }
6047
+ return result;
6048
+ }
5507
6049
  function resolveRuntimeTerminalThrow(trace, failureInstancesByChildRunId) {
5508
6050
  if (trace.triggeringFailureForAbortMode !== void 0) return failureInstancesByChildRunId.get(trace.triggeringFailureForAbortMode.childRunId) ?? null;
5509
6051
  const finalEvent = trace.events.at(-1);
@@ -5566,10 +6108,17 @@ function dogpileErrorFromSerializedPayload(input) {
5566
6108
  * Replay a saved completed trace as a stream without invoking a model provider.
5567
6109
  *
5568
6110
  * @remarks
5569
- * This is the streaming counterpart to {@link replay}. It yields the exact
5570
- * saved {@link Trace.events} in order and resolves {@link StreamHandle.result}
5571
- * to the rehydrated {@link RunResult}. Since all data comes from the trace,
5572
- * replay remains storage-free and provider-free.
6111
+ * This is the streaming counterpart to {@link replay}. It yields the same
6112
+ * event sequence exposed by the replayed result event log, including legacy
6113
+ * provenance synthesis when a saved trace predates model request/response
6114
+ * events. Since all data comes from the trace, replay remains storage-free and
6115
+ * provider-free.
6116
+ *
6117
+ * Tracing and metrics: replayStream is intentionally tracing-free and
6118
+ * metrics-free. Even when an engine instance has been configured with a
6119
+ * `tracer` or `metricsHook` on its `EngineOptions`, calling this function
6120
+ * emits no spans or callbacks — replaying historical events with current
6121
+ * timestamps would confuse observability backends. See `docs/developer-usage.md`.
5573
6122
  */
5574
6123
  function replayStream(trace) {
5575
6124
  const result = Promise.resolve(replay(trace));
@@ -5605,7 +6154,7 @@ function replayStream(trace) {
5605
6154
  }
5606
6155
  function replayStreamEvents(trace, parentRunIds = []) {
5607
6156
  const events = [];
5608
- for (const event of trace.events) {
6157
+ for (const event of synthesizeProviderEvents(trace, trace.providerCalls)) {
5609
6158
  if (event.type === "sub-run-completed") events.push(...replayStreamEvents(event.subResult.trace, [...parentRunIds, trace.runId]));
5610
6159
  events.push(wrapReplayStreamEvent(event, parentRunIds));
5611
6160
  }
@@ -5714,6 +6263,7 @@ function createOpenAICompatibleProvider(options) {
5714
6263
  });
5715
6264
  return {
5716
6265
  id: providerId,
6266
+ modelId: options.model,
5717
6267
  metadata: { locality: resolvedLocality },
5718
6268
  async generate(request) {
5719
6269
  let response;
@@ -6278,6 +6828,6 @@ function defaultSleep(ms, signal) {
6278
6828
  });
6279
6829
  }
6280
6830
  //#endregion
6281
- export { DEFAULT_RETRYABLE_DOGPILE_CODES, Dogpile, DogpileError, budget, builtInDogpileToolIdentity, builtInDogpileToolInputSchema, builtInDogpileToolPermissions, combineTerminationDecisions, consoleLogger, convergence, createCodeExecToolAdapter, createEngine, createOpenAICompatibleProvider, createRuntimeToolExecutor, createWebSearchToolAdapter, evaluateBudget, evaluateConvergence, evaluateFirstOf, evaluateJudge, evaluateTermination, evaluateTerminationStop, firstOf, judge, loggerFromEvents, noopLogger, normalizeBuiltInDogpileTool, normalizeBuiltInDogpileTools, normalizeRuntimeToolAdapterError, replay, replayStream, run, runtimeToolAvailability, runtimeToolManifest, stream, validateBuiltInDogpileToolInput, withRetry };
6831
+ export { DEFAULT_RETRYABLE_DOGPILE_CODES, DOGPILE_SPAN_NAMES, Dogpile, DogpileError, budget, builtInDogpileToolIdentity, builtInDogpileToolInputSchema, builtInDogpileToolPermissions, combineTerminationDecisions, consoleLogger, convergence, createCodeExecToolAdapter, createEngine, createOpenAICompatibleProvider, createRuntimeToolExecutor, createWebSearchToolAdapter, evaluateBudget, evaluateConvergence, evaluateFirstOf, evaluateJudge, evaluateTermination, evaluateTerminationStop, firstOf, judge, loggerFromEvents, noopLogger, normalizeBuiltInDogpileTool, normalizeBuiltInDogpileTools, normalizeRuntimeToolAdapterError, replay, replayStream, run, runtimeToolAvailability, runtimeToolManifest, stream, validateBuiltInDogpileToolInput, withRetry };
6282
6832
 
6283
6833
  //# sourceMappingURL=index.js.map