@tangle-network/agent-runtime 0.115.1 → 0.116.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 (52) hide show
  1. package/dist/{activation-Bt_XVkjK.js → activation-DuqQhee6.js} +2 -2
  2. package/dist/{activation-Bt_XVkjK.js.map → activation-DuqQhee6.js.map} +1 -1
  3. package/dist/agent.d.ts +1 -1
  4. package/dist/agent.js +2 -2
  5. package/dist/{environment-provider-Cygthiy3.d.ts → environment-provider-CWsRh6Uz.d.ts} +365 -4
  6. package/dist/environment-provider.d.ts +1 -1
  7. package/dist/{improvement-cycle-tEswzEPr.js → improvement-cycle-C1cmjvPD.js} +3 -3
  8. package/dist/{improvement-cycle-tEswzEPr.js.map → improvement-cycle-C1cmjvPD.js.map} +1 -1
  9. package/dist/{index-BUWd8QJq.d.ts → index-COumPQka.d.ts} +40 -225
  10. package/dist/{index-DZukewLl.d.ts → index-CYkDeM5L.d.ts} +3 -3
  11. package/dist/{index-K7nucOmw.d.ts → index-DcLMNnG5.d.ts} +7 -6
  12. package/dist/index.d.ts +6 -7
  13. package/dist/index.js +7 -8
  14. package/dist/index.js.map +1 -1
  15. package/dist/intelligence.d.ts +3 -3
  16. package/dist/intelligence.js +3 -3
  17. package/dist/kernel.d.ts +4 -4
  18. package/dist/kernel.js +5 -5
  19. package/dist/{knowledge-CUXQE8Sq.js → knowledge-DOzbywZT.js} +3 -3
  20. package/dist/{knowledge-CUXQE8Sq.js.map → knowledge-DOzbywZT.js.map} +1 -1
  21. package/dist/knowledge.d.ts +1 -1
  22. package/dist/knowledge.js +1 -1
  23. package/dist/{local-harness-BnB1E9nM.d.ts → local-harness-t6cDWDQ2.d.ts} +3 -105
  24. package/dist/{loop-runner-bin-BZl5vp7t.d.ts → loop-runner-bin-BFrhPLKt.d.ts} +3 -3
  25. package/dist/{loop-runner-bin-DzJz48Fb.js → loop-runner-bin-BuQjc5DR.js} +3 -3
  26. package/dist/{loop-runner-bin-DzJz48Fb.js.map → loop-runner-bin-BuQjc5DR.js.map} +1 -1
  27. package/dist/loop-runner-bin.d.ts +1 -1
  28. package/dist/loop-runner-bin.js +1 -1
  29. package/dist/mcp/bin.js +2 -2
  30. package/dist/mcp/index.d.ts +3 -2
  31. package/dist/mcp/index.js +4 -5
  32. package/dist/mcp/index.js.map +1 -1
  33. package/dist/{openai-tools-CynwZMZd.js → openai-tools-_Wyp4udO.js} +2 -2
  34. package/dist/{openai-tools-CynwZMZd.js.map → openai-tools-_Wyp4udO.js.map} +1 -1
  35. package/dist/primeintellect/index.d.ts +1 -1
  36. package/dist/{redact-BRaHUFMf.d.ts → redact-BEtQtvd6.d.ts} +2 -2
  37. package/dist/{runtime-BatQajPB.js → runtime-Ut1pkd2n.js} +4 -4
  38. package/dist/{runtime-BatQajPB.js.map → runtime-Ut1pkd2n.js.map} +1 -1
  39. package/dist/{structural-rollout-DEf37yQy.js → structural-rollout-CVY_0hJp.js} +2 -2
  40. package/dist/{structural-rollout-DEf37yQy.js.map → structural-rollout-CVY_0hJp.js.map} +1 -1
  41. package/dist/{supervise-B7TIJR3D.js → supervise-BUR9ByF7.js} +61 -22
  42. package/dist/supervise-BUR9ByF7.js.map +1 -0
  43. package/dist/{supervisor-DzQu5Ydu.js → supervisor-BBbPBXpe.js} +1004 -4
  44. package/dist/supervisor-BBbPBXpe.js.map +1 -0
  45. package/dist/testing.js +8 -8
  46. package/package.json +1 -1
  47. package/dist/otel-export-CPZTSADj.js +0 -852
  48. package/dist/otel-export-CPZTSADj.js.map +0 -1
  49. package/dist/supervise-B7TIJR3D.js.map +0 -1
  50. package/dist/supervisor-DzQu5Ydu.js.map +0 -1
  51. package/dist/trace-propagation-B-pL7xn_.js +0 -57
  52. package/dist/trace-propagation-B-pL7xn_.js.map +0 -1
@@ -2438,6 +2438,1002 @@ function defaultSleep(ms, signal) {
2438
2438
  });
2439
2439
  }
2440
2440
  //#endregion
2441
+ //#region src/sanitize.ts
2442
+ /** Strip PII and large blobs from a `KnowledgeReadinessReport` for safe telemetry emission. @stable */
2443
+ function sanitizeKnowledgeReadinessReport(report, options = {}) {
2444
+ return {
2445
+ taskId: report.taskId,
2446
+ readinessScore: report.readinessScore,
2447
+ recommendedAction: report.recommendedAction,
2448
+ severity: report.severity,
2449
+ reason: report.reason,
2450
+ blockingMissingRequirements: report.blockingMissingRequirements.map((requirement) => sanitizeKnowledgeRequirement(requirement, options)),
2451
+ nonBlockingGaps: report.nonBlockingGaps.map((requirement) => sanitizeKnowledgeRequirement(requirement, options)),
2452
+ evidenceCount: report.bundle.evidenceIds.length,
2453
+ evidenceIds: options.includeEvidenceIds ? report.bundle.evidenceIds : void 0,
2454
+ missingRequirementIds: report.bundle.missing.map((requirement) => requirement.id)
2455
+ };
2456
+ }
2457
+ /** Reduce an `AgentRuntimeEvent` to a PII-safe, serializable plain object for telemetry. @stable */
2458
+ function sanitizeAgentRuntimeEvent(event, options = {}) {
2459
+ const base = {
2460
+ type: event.type,
2461
+ task: sanitizeTask(event.task, options)
2462
+ };
2463
+ if (event.type === "readiness_start" || event.type === "task_start" || event.type === "control_start") return event.type === "control_start" ? {
2464
+ ...base,
2465
+ knowledge: sanitizeKnowledgeReadinessReport(event.knowledge, options)
2466
+ } : base;
2467
+ if (event.type === "readiness_end") return {
2468
+ ...base,
2469
+ knowledge: sanitizeKnowledgeReadinessReport(event.knowledge, options)
2470
+ };
2471
+ if (event.type === "questions_start") return {
2472
+ ...base,
2473
+ questions: event.questions.map((question) => sanitizeQuestion(question, options))
2474
+ };
2475
+ if (event.type === "questions_end") return {
2476
+ ...base,
2477
+ questions: event.questions.map((question) => sanitizeQuestion(question, options)),
2478
+ userAnswers: options.includeUserAnswers ? event.userAnswers : redactRecord(event.userAnswers)
2479
+ };
2480
+ if (event.type === "acquisition_start") return {
2481
+ ...base,
2482
+ acquisitionPlans: event.acquisitionPlans.map(sanitizeAcquisitionPlan)
2483
+ };
2484
+ if (event.type === "acquisition_end") return {
2485
+ ...base,
2486
+ acquisitionPlans: event.acquisitionPlans.map(sanitizeAcquisitionPlan),
2487
+ acquiredEvidenceCount: event.acquiredEvidenceIds.length,
2488
+ acquiredEvidenceIds: options.includeEvidenceIds ? event.acquiredEvidenceIds : void 0
2489
+ };
2490
+ if (event.type === "control_step") return {
2491
+ ...base,
2492
+ step: sanitizeControlStep(event.step, options)
2493
+ };
2494
+ if (event.type === "control_end") return {
2495
+ ...base,
2496
+ control: sanitizeControlRun(event.control, options)
2497
+ };
2498
+ return {
2499
+ ...base,
2500
+ status: event.status,
2501
+ reason: event.reason
2502
+ };
2503
+ }
2504
+ /** Reduce a `RuntimeStreamEvent` to a PII-safe, serializable plain object for telemetry. @stable */
2505
+ function sanitizeRuntimeStreamEvent(event, options = {}) {
2506
+ const withTask = "task" in event && event.task ? { task: sanitizeTask(event.task, options) } : {};
2507
+ const withSession = "session" in event && event.session ? { session: sanitizeRuntimeSession(event.session, options) } : {};
2508
+ if (event.type === "readiness_end") return {
2509
+ type: event.type,
2510
+ ...withTask,
2511
+ timestamp: event.timestamp,
2512
+ decision: event.decision,
2513
+ knowledge: sanitizeKnowledgeReadinessReport(event.knowledge, options)
2514
+ };
2515
+ if (event.type === "questions_start") return {
2516
+ type: event.type,
2517
+ ...withTask,
2518
+ timestamp: event.timestamp,
2519
+ questions: event.questions.map((question) => sanitizeQuestion(question, options))
2520
+ };
2521
+ if (event.type === "questions_end") return {
2522
+ type: event.type,
2523
+ ...withTask,
2524
+ timestamp: event.timestamp,
2525
+ questions: event.questions.map((question) => sanitizeQuestion(question, options)),
2526
+ userAnswers: options.includeUserAnswers ? event.userAnswers : redactRecord(event.userAnswers)
2527
+ };
2528
+ if (event.type === "acquisition_start") return {
2529
+ type: event.type,
2530
+ ...withTask,
2531
+ timestamp: event.timestamp,
2532
+ acquisitionPlans: event.acquisitionPlans.map(sanitizeAcquisitionPlan)
2533
+ };
2534
+ if (event.type === "acquisition_end") return {
2535
+ type: event.type,
2536
+ ...withTask,
2537
+ timestamp: event.timestamp,
2538
+ acquisitionPlans: event.acquisitionPlans.map(sanitizeAcquisitionPlan),
2539
+ acquiredEvidenceCount: event.acquiredEvidenceIds.length,
2540
+ acquiredEvidenceIds: options.includeEvidenceIds ? event.acquiredEvidenceIds : void 0
2541
+ };
2542
+ if (event.type === "tool_call") return {
2543
+ type: event.type,
2544
+ ...withTask,
2545
+ ...withSession,
2546
+ timestamp: event.timestamp,
2547
+ toolName: event.toolName,
2548
+ toolCallId: event.toolCallId,
2549
+ args: options.includeControlPayloads ? event.args : void 0
2550
+ };
2551
+ if (event.type === "tool_result") return {
2552
+ type: event.type,
2553
+ ...withTask,
2554
+ ...withSession,
2555
+ timestamp: event.timestamp,
2556
+ toolName: event.toolName,
2557
+ toolCallId: event.toolCallId,
2558
+ result: options.includeControlPayloads ? event.result : void 0
2559
+ };
2560
+ if (event.type === "llm_call") return {
2561
+ type: event.type,
2562
+ ...withTask,
2563
+ ...withSession,
2564
+ timestamp: event.timestamp,
2565
+ model: event.model,
2566
+ tokensIn: event.tokensIn,
2567
+ tokensOut: event.tokensOut,
2568
+ costUsd: event.costUsd,
2569
+ latencyMs: event.latencyMs,
2570
+ finishReason: event.finishReason
2571
+ };
2572
+ if (event.type === "artifact") return {
2573
+ type: event.type,
2574
+ ...withTask,
2575
+ ...withSession,
2576
+ timestamp: event.timestamp,
2577
+ artifactId: event.artifactId,
2578
+ name: event.name,
2579
+ mimeType: event.mimeType,
2580
+ uri: options.includeEvidenceIds ? event.uri : void 0,
2581
+ content: options.includeControlPayloads ? event.content : void 0,
2582
+ metadata: options.includeMetadata ? event.metadata : void 0
2583
+ };
2584
+ if (event.type === "proposal_created") return {
2585
+ type: event.type,
2586
+ ...withTask,
2587
+ ...withSession,
2588
+ timestamp: event.timestamp,
2589
+ proposalId: event.proposalId,
2590
+ title: options.includeControlPayloads ? event.title : void 0,
2591
+ content: options.includeControlPayloads ? event.content : void 0,
2592
+ status: event.status
2593
+ };
2594
+ if (event.type === "final") {
2595
+ const sanitizedError = event.error !== void 0 ? {
2596
+ kind: event.error.kind,
2597
+ message: event.error.message,
2598
+ status: event.error.status,
2599
+ body: options.includeControlPayloads ? event.error.body : void 0
2600
+ } : void 0;
2601
+ return {
2602
+ type: event.type,
2603
+ ...withTask,
2604
+ ...withSession,
2605
+ timestamp: event.timestamp,
2606
+ status: event.status,
2607
+ reason: event.reason,
2608
+ text: options.includeControlPayloads ? event.text : void 0,
2609
+ metadata: options.includeMetadata ? event.metadata : void 0,
2610
+ ...sanitizedError !== void 0 ? { error: sanitizedError } : {}
2611
+ };
2612
+ }
2613
+ return {
2614
+ type: event.type,
2615
+ ...withTask,
2616
+ ...withSession,
2617
+ timestamp: "timestamp" in event ? event.timestamp : void 0,
2618
+ ...pickPublicStreamFields(event)
2619
+ };
2620
+ }
2621
+ function sanitizeTask(task, options) {
2622
+ return {
2623
+ id: task.id,
2624
+ intent: task.intent,
2625
+ domain: task.domain,
2626
+ inputs: options.includeInputs ? task.inputs : task.inputs ? "[redacted]" : void 0,
2627
+ requiredKnowledge: task.requiredKnowledge?.map((requirement) => sanitizeKnowledgeRequirement(requirement, options)),
2628
+ metadata: options.includeMetadata ? task.metadata : task.metadata ? "[redacted]" : void 0
2629
+ };
2630
+ }
2631
+ function sanitizeRuntimeSession(session, options) {
2632
+ return {
2633
+ id: session.id,
2634
+ backend: session.backend,
2635
+ status: session.status,
2636
+ hasResumeToken: Boolean(session.resumeToken),
2637
+ createdAt: session.createdAt,
2638
+ updatedAt: session.updatedAt,
2639
+ metadata: options.includeMetadata ? session.metadata : session.metadata ? "[redacted]" : void 0
2640
+ };
2641
+ }
2642
+ function sanitizeKnowledgeRequirement(requirement, options) {
2643
+ const includeDescription = options.includeRequirementDescriptions && requirement.sensitivity !== "secret";
2644
+ return {
2645
+ id: requirement.id,
2646
+ description: includeDescription ? requirement.description : void 0,
2647
+ requiredFor: requirement.requiredFor,
2648
+ category: requirement.category,
2649
+ acquisitionMode: requirement.acquisitionMode,
2650
+ importance: requirement.importance,
2651
+ freshness: requirement.freshness,
2652
+ sensitivity: requirement.sensitivity,
2653
+ confidenceNeeded: requirement.confidenceNeeded,
2654
+ currentConfidence: requirement.currentConfidence,
2655
+ evidenceCount: requirement.evidenceIds.length,
2656
+ evidenceIds: options.includeEvidenceIds ? requirement.evidenceIds : void 0,
2657
+ fallbackPolicy: requirement.fallbackPolicy
2658
+ };
2659
+ }
2660
+ function sanitizeQuestion(question, options) {
2661
+ return {
2662
+ id: question.id,
2663
+ question: options.includeRequirementDescriptions && question.answerType !== "credential" ? question.question : void 0,
2664
+ reason: options.includeRequirementDescriptions ? question.reason : void 0,
2665
+ requirementId: question.requirementId,
2666
+ importance: question.importance,
2667
+ answerType: question.answerType,
2668
+ impactIfUnknown: options.includeRequirementDescriptions ? question.impactIfUnknown : void 0,
2669
+ optionCount: question.options?.length ?? 0
2670
+ };
2671
+ }
2672
+ function sanitizeAcquisitionPlan(plan) {
2673
+ return {
2674
+ id: plan.id,
2675
+ requirementIds: plan.requirementIds,
2676
+ mode: plan.mode,
2677
+ priority: plan.priority,
2678
+ expectedEvidenceCount: plan.expectedEvidenceIds?.length ?? 0,
2679
+ questionCount: plan.questions?.length ?? 0
2680
+ };
2681
+ }
2682
+ function sanitizeControlStep(step, options) {
2683
+ const actionOutcome = step.actionOutcome;
2684
+ return {
2685
+ index: step.index,
2686
+ decisionType: step.decision.type,
2687
+ reason: step.decision.reason,
2688
+ action: options.includeControlPayloads && step.decision.type === "continue" ? step.decision.action : void 0,
2689
+ result: options.includeControlPayloads && actionOutcome?.ok ? actionOutcome.result : void 0,
2690
+ actionOk: actionOutcome?.ok,
2691
+ actionError: actionOutcome?.ok === false ? actionOutcome.error : void 0,
2692
+ durationMs: actionOutcome?.durationMs,
2693
+ evalsBefore: summarizeEvals(step.evalsBefore, options),
2694
+ evalsAfter: summarizeEvals(step.evalsAfter, options),
2695
+ startedAt: step.startedAt,
2696
+ endedAt: step.endedAt
2697
+ };
2698
+ }
2699
+ function sanitizeControlRun(control, options) {
2700
+ return {
2701
+ pass: control.pass,
2702
+ completed: control.completed,
2703
+ reason: control.reason,
2704
+ score: control.score,
2705
+ stepCount: control.steps.length,
2706
+ wallMs: control.wallMs,
2707
+ spentCostUsd: control.spentCostUsd,
2708
+ failureClass: control.failureClass,
2709
+ stoppedBy: control.stoppedBy,
2710
+ runId: control.runId,
2711
+ runtimeErrorCount: control.runtimeErrors.length,
2712
+ finalEvals: summarizeEvals(control.finalEvals, options)
2713
+ };
2714
+ }
2715
+ function summarizeEvals(evals, options) {
2716
+ return evals.map((evalResult) => ({
2717
+ id: evalResult.id,
2718
+ passed: evalResult.passed,
2719
+ score: evalResult.score,
2720
+ severity: evalResult.severity,
2721
+ objective: evalResult.objective,
2722
+ detail: options.includeEvalDetails ? evalResult.detail : void 0,
2723
+ evidence: options.includeEvalDetails ? evalResult.evidence : void 0
2724
+ }));
2725
+ }
2726
+ function redactRecord(record) {
2727
+ return Object.fromEntries(Object.keys(record).map((key) => [key, "[redacted]"]));
2728
+ }
2729
+ function pickPublicStreamFields(event) {
2730
+ if (event.type === "session_created" || event.type === "session_resumed") return {};
2731
+ if (event.type === "backend_start" || event.type === "backend_end") return { backend: event.backend };
2732
+ if (event.type === "backend_error") {
2733
+ const sanitizedError = event.error !== void 0 ? {
2734
+ kind: event.error.kind,
2735
+ status: event.error.status
2736
+ } : void 0;
2737
+ return {
2738
+ backend: event.backend,
2739
+ message: event.message,
2740
+ recoverable: event.recoverable,
2741
+ ...sanitizedError !== void 0 ? { error: sanitizedError } : {}
2742
+ };
2743
+ }
2744
+ if (event.type === "task_end") return {
2745
+ status: event.status,
2746
+ reason: event.reason
2747
+ };
2748
+ if (event.type === "text_delta" || event.type === "reasoning_delta") return { text: event.text };
2749
+ return {};
2750
+ }
2751
+ /** Build an in-memory collector that sanitizes and accumulates `AgentRuntimeEvent`s for inspection. @stable */
2752
+ function createRuntimeEventCollector(options = {}) {
2753
+ const events = [];
2754
+ return {
2755
+ events,
2756
+ onEvent: (event) => {
2757
+ events.push(sanitizeAgentRuntimeEvent(event, options));
2758
+ }
2759
+ };
2760
+ }
2761
+ /**
2762
+ *
2763
+ * Streaming-event counterpart of `createRuntimeEventCollector`. Pass each
2764
+ * event yielded by `runAgentTaskStream` through `onEvent` and read the
2765
+ * sanitized copies off `events`; the same `RuntimeTelemetryOptions` redaction
2766
+ * flags apply. Kept distinct from `createRuntimeEventCollector` because the
2767
+ * stream and non-stream event shapes overlap on `type` literals — dispatching
2768
+ * on `type` alone would misroute events.
2769
+ *
2770
+ * @stable
2771
+ */
2772
+ function createRuntimeStreamEventCollector(options = {}) {
2773
+ const events = [];
2774
+ const eventCountsByType = {};
2775
+ let firstSessionId;
2776
+ let finalStatus;
2777
+ let finalReason;
2778
+ let finalText = "";
2779
+ return {
2780
+ events,
2781
+ onEvent: (event) => {
2782
+ events.push(sanitizeRuntimeStreamEvent(event, options));
2783
+ eventCountsByType[event.type] = (eventCountsByType[event.type] ?? 0) + 1;
2784
+ if (event.type === "text_delta") finalText += event.text;
2785
+ if (!firstSessionId && (event.type === "session_created" || event.type === "session_resumed")) firstSessionId = event.session.id;
2786
+ if (event.type === "final") {
2787
+ finalStatus = event.status;
2788
+ finalReason = event.reason;
2789
+ }
2790
+ },
2791
+ summary() {
2792
+ return {
2793
+ eventCount: events.length,
2794
+ eventCountsByType: { ...eventCountsByType },
2795
+ firstSessionId,
2796
+ finalStatus,
2797
+ finalReason,
2798
+ finalText
2799
+ };
2800
+ }
2801
+ };
2802
+ }
2803
+ //#endregion
2804
+ //#region src/otel-export.ts
2805
+ /**
2806
+ * OTEL span exporter — streams LoopTraceEvents to an OTLP/HTTP collector.
2807
+ *
2808
+ * Reads OTEL_EXPORTER_OTLP_ENDPOINT + OTEL_EXPORTER_OTLP_HEADERS from env
2809
+ * when no explicit config is given. Keeps the runtime dep-free from
2810
+ * @opentelemetry/sdk-trace-base — minimal OTLP/JSON serializer.
2811
+ *
2812
+ * The exporter accepts both raw OtelSpan objects and LoopTraceEvents
2813
+ * (which get converted to OTLP spans automatically).
2814
+ */
2815
+ const SCOPE = {
2816
+ name: "@tangle-network/agent-runtime",
2817
+ version: "0.83.0"
2818
+ };
2819
+ /**
2820
+ * Current (non-deprecated) OpenTelemetry GenAI semantic-convention keys.
2821
+ * Registry: https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/
2822
+ * NB: `gen_ai.system` / `gen_ai.usage.prompt_tokens` / `completion_tokens` are
2823
+ * DEPRECATED — do not emit them. We use `provider.name` + `input/output_tokens`.
2824
+ */
2825
+ const GEN_AI = {
2826
+ operation: "gen_ai.operation.name",
2827
+ agentName: "gen_ai.agent.name",
2828
+ conversationId: "gen_ai.conversation.id",
2829
+ inputTokens: "gen_ai.usage.input_tokens",
2830
+ outputTokens: "gen_ai.usage.output_tokens"
2831
+ };
2832
+ /**
2833
+ * Create an OTEL exporter. Returns undefined when no endpoint is configured.
2834
+ */
2835
+ function createOtelExporter(config) {
2836
+ const resolvedEndpoint = config?.endpoint ?? (typeof process !== "undefined" ? process.env.OTEL_EXPORTER_OTLP_ENDPOINT : void 0);
2837
+ if (!resolvedEndpoint) return void 0;
2838
+ const endpoint = resolvedEndpoint;
2839
+ const headers = config?.headers ?? parseHeadersFromEnv();
2840
+ const batchSize = config?.batchSize ?? 64;
2841
+ const flushIntervalMs = config?.flushIntervalMs ?? 5e3;
2842
+ const serviceName = config?.serviceName ?? "agent-runtime";
2843
+ const resourceAttrs = config?.resourceAttributes ?? {};
2844
+ const pending = [];
2845
+ let timer;
2846
+ let stopped = false;
2847
+ const exporter = {
2848
+ exportSpan(span) {
2849
+ if (stopped) return;
2850
+ pending.push(span);
2851
+ if (pending.length >= batchSize) doFlush();
2852
+ },
2853
+ async flush() {
2854
+ await doFlush();
2855
+ },
2856
+ async shutdown() {
2857
+ stopped = true;
2858
+ if (timer !== void 0) {
2859
+ clearInterval(timer);
2860
+ timer = void 0;
2861
+ }
2862
+ await doFlush();
2863
+ }
2864
+ };
2865
+ timer = setInterval(() => {
2866
+ if (pending.length > 0) doFlush();
2867
+ }, flushIntervalMs);
2868
+ if (typeof timer === "object" && "unref" in timer) timer.unref();
2869
+ async function doFlush() {
2870
+ if (pending.length === 0) return;
2871
+ const batch = pending.splice(0);
2872
+ const body = { resourceSpans: [{
2873
+ resource: { attributes: toOtelAttributes({
2874
+ "service.name": serviceName,
2875
+ ...resourceAttrs
2876
+ }) },
2877
+ scopeSpans: [{
2878
+ scope: SCOPE,
2879
+ spans: batch
2880
+ }]
2881
+ }] };
2882
+ const url = `${endpoint.replace(/\/+$/, "")}/v1/traces`;
2883
+ try {
2884
+ await fetch(url, {
2885
+ method: "POST",
2886
+ headers: {
2887
+ "content-type": "application/json",
2888
+ ...headers
2889
+ },
2890
+ body: JSON.stringify(body)
2891
+ });
2892
+ } catch {}
2893
+ }
2894
+ return exporter;
2895
+ }
2896
+ /**
2897
+ * Convert a LoopTraceEvent into an OtelSpan for export.
2898
+ */
2899
+ function loopEventToOtelSpan(event, traceId, parentSpanId) {
2900
+ const spanId = generateSpanId();
2901
+ const attrs = {
2902
+ "loop.event_kind": event.kind,
2903
+ "loop.run_id": event.runId
2904
+ };
2905
+ for (const [k, v] of Object.entries(event.payload)) if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") attrs[`loop.${k}`] = v;
2906
+ const ts = msToNs(event.timestamp);
2907
+ return {
2908
+ traceId: padTraceId(traceId),
2909
+ spanId,
2910
+ parentSpanId: parentSpanId ? padSpanId(parentSpanId) : void 0,
2911
+ name: event.kind,
2912
+ kind: 1,
2913
+ startTimeUnixNano: ts,
2914
+ endTimeUnixNano: ts,
2915
+ attributes: toOtelAttributes(attrs),
2916
+ status: { code: 1 }
2917
+ };
2918
+ }
2919
+ /**
2920
+ * Build a single flat OtelSpan whose attribute keys are emitted VERBATIM — no
2921
+ * `loop.` namespace. Use for non-loop spans (e.g. the intelligence per-run
2922
+ * span) whose keys ARE the downstream contract (`gen_ai.request.model`,
2923
+ * `tangle.sessionId`) and are exact-matched by readers. `loopEventToOtelSpan`
2924
+ * namespaces payload keys because loop payload fields are free-form; do not
2925
+ * reuse it for spans with contract keys.
2926
+ */
2927
+ function flatOtelSpan(name, attributes, traceId, timestampMs, parentSpanId, endTimestampMs = timestampMs) {
2928
+ const start = msToNs(timestampMs);
2929
+ const end = msToNs(Math.max(timestampMs, endTimestampMs));
2930
+ return {
2931
+ traceId: padTraceId(traceId),
2932
+ spanId: generateSpanId(),
2933
+ parentSpanId: parentSpanId ? padSpanId(parentSpanId) : void 0,
2934
+ name,
2935
+ kind: 1,
2936
+ startTimeUnixNano: start,
2937
+ endTimeUnixNano: end,
2938
+ attributes: toOtelAttributes(attributes),
2939
+ status: { code: 1 }
2940
+ };
2941
+ }
2942
+ function eventTimestampMs(event) {
2943
+ if ("timestamp" in event && typeof event.timestamp === "string") {
2944
+ const parsed = Date.parse(event.timestamp);
2945
+ if (Number.isFinite(parsed)) return parsed;
2946
+ }
2947
+ return Date.now();
2948
+ }
2949
+ function serialized(value) {
2950
+ try {
2951
+ return JSON.stringify(value) ?? String(value);
2952
+ } catch {
2953
+ return String(value);
2954
+ }
2955
+ }
2956
+ function mcpIdentity(toolName) {
2957
+ if (!toolName.startsWith("mcp__")) return {};
2958
+ const [, server, ...toolParts] = toolName.split("__");
2959
+ return {
2960
+ ...server ? { server } : {},
2961
+ ...toolParts.length > 0 ? { tool: toolParts.join("__") } : {}
2962
+ };
2963
+ }
2964
+ /** Convert normalized runtime events into lossless, redacted child spans. */
2965
+ function buildRuntimeEventOtelSpans(events, traceId, parentSpanId, options = {}) {
2966
+ return events.map((event) => {
2967
+ const sanitized = sanitizeRuntimeStreamEvent(event, options);
2968
+ const safe = options.redact ? options.redact(sanitized) : sanitized;
2969
+ const record = safe && typeof safe === "object" && !Array.isArray(safe) ? safe : { value: safe };
2970
+ const attrs = {
2971
+ "tangle.runtime.event_type": event.type,
2972
+ "tangle.runtime.event": serialized(record)
2973
+ };
2974
+ let name = `tangle.runtime.${event.type}`;
2975
+ if (event.type === "tool_call" || event.type === "tool_result") {
2976
+ name = `agent.${event.type}`;
2977
+ attrs["tool.name"] = event.toolName;
2978
+ if (event.toolCallId) attrs["tool.call_id"] = event.toolCallId;
2979
+ const mcp = mcpIdentity(event.toolName);
2980
+ if (mcp.server) attrs["mcp.server"] = mcp.server;
2981
+ if (mcp.tool) attrs["mcp.tool.name"] = mcp.tool;
2982
+ const payload = event.type === "tool_call" ? record.args : record.result;
2983
+ if (payload !== void 0) attrs[event.type === "tool_call" ? "tool.input" : "tool.output"] = serialized(payload);
2984
+ } else if (event.type === "llm_call") {
2985
+ name = "gen_ai.client.inference";
2986
+ attrs["gen_ai.request.model"] = event.model;
2987
+ if (event.tokensIn !== void 0) attrs["gen_ai.usage.input_tokens"] = event.tokensIn;
2988
+ if (event.tokensOut !== void 0) attrs["gen_ai.usage.output_tokens"] = event.tokensOut;
2989
+ if (event.costUsd !== void 0) attrs["tangle.cost.usd"] = event.costUsd;
2990
+ if (event.latencyMs !== void 0) attrs["tangle.latency_ms"] = event.latencyMs;
2991
+ if (event.finishReason !== void 0) attrs["gen_ai.response.finish_reasons"] = event.finishReason;
2992
+ } else if (event.type === "backend_error") {
2993
+ attrs["error.type"] = event.error?.kind ?? "backend";
2994
+ attrs["error.message"] = event.message;
2995
+ } else if (event.type === "final") {
2996
+ attrs["tangle.outcome.status"] = event.status;
2997
+ attrs["tangle.outcome.reason"] = event.reason;
2998
+ if (event.error) {
2999
+ attrs["error.type"] = event.error.kind;
3000
+ attrs["error.message"] = event.error.message;
3001
+ }
3002
+ }
3003
+ const startMs = eventTimestampMs(event);
3004
+ const endMs = event.type === "llm_call" && event.latencyMs !== void 0 && Number.isFinite(event.latencyMs) ? startMs + event.latencyMs : startMs;
3005
+ const span = flatOtelSpan(name, attrs, traceId, startMs, parentSpanId, endMs);
3006
+ if (event.type === "backend_error" || event.type === "final" && event.status !== "completed") span.status = {
3007
+ code: 2,
3008
+ message: attrs["error.message"]?.toString() ?? event.type
3009
+ };
3010
+ return span;
3011
+ });
3012
+ }
3013
+ /**
3014
+ * Build a nested, real-duration OTLP span tree for ONE loop run from its full
3015
+ * ordered `LoopTraceEvent` stream. Unlike `loopEventToOtelSpan` (one flat,
3016
+ * zero-duration span per event), this reconstructs the topology hierarchy a
3017
+ * GenAI trace viewer renders natively:
3018
+ *
3019
+ * loop (invoke_workflow)
3020
+ * └─ loop.round[k] (invoke_workflow) ← tangle.loop.move.{kind,width,rationale}
3021
+ * ├─ loop.iteration[i] (invoke_agent) ← gen_ai.agent.name + usage + verdict + placement
3022
+ * └─ …
3023
+ *
3024
+ * Attributes follow the current GenAI semconv (`gen_ai.*`) where they apply and
3025
+ * a namespaced `tangle.loop.*` / `tangle.cost.usd` extension for topology /
3026
+ * verdict / placement / cost (not yet standardized). Pure: feed it a buffered
3027
+ * per-runId event array (e.g. flushed on `loop.ended`) and export the result.
3028
+ */
3029
+ function buildLoopOtelSpans(events, traceId, rootParentSpanId) {
3030
+ const tid = padTraceId(traceId);
3031
+ return buildLoopSpanNodes(events).map((node) => ({
3032
+ traceId: tid,
3033
+ spanId: node.spanId,
3034
+ parentSpanId: node.parentSpanId ? padSpanId(node.parentSpanId) : rootParentSpanId ? padSpanId(rootParentSpanId) : void 0,
3035
+ name: node.name,
3036
+ kind: 1,
3037
+ startTimeUnixNano: msToNs(node.startMs),
3038
+ endTimeUnixNano: msToNs(node.endMs),
3039
+ attributes: toOtelAttributes(node.attrs),
3040
+ status: { code: node.error ? 2 : 1 }
3041
+ }));
3042
+ }
3043
+ /**
3044
+ * Sink-neutral core behind {@link buildLoopOtelSpans}: reconstruct the
3045
+ * loop → round → branch span tree from one run's ordered `LoopTraceEvent`
3046
+ * stream. Consumed by the OTEL mapper above and by the MCP delegation
3047
+ * journal's compact trace tee — one topology reconstruction, two sinks.
3048
+ * Tolerates partial streams (a run that never reached `loop.ended` closes
3049
+ * at the last observed event's timestamp).
3050
+ */
3051
+ function buildLoopSpanNodes(events) {
3052
+ if (events.length === 0) return [];
3053
+ const out = [];
3054
+ const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
3055
+ const str = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
3056
+ const rec = (v) => v && typeof v === "object" ? v : {};
3057
+ const started = events.find((e) => e.kind === "loop.started");
3058
+ const ended = events.find((e) => e.kind === "loop.ended");
3059
+ const runId = events[0]?.runId ?? "";
3060
+ const rootStart = started?.timestamp ?? events[0].timestamp;
3061
+ const rootEnd = ended?.timestamp ?? events[events.length - 1].timestamp;
3062
+ const rootId = generateSpanId();
3063
+ const make = (spanId, parentSpanId, name, kind, startMs, endMs, attrs, error = false) => ({
3064
+ spanId,
3065
+ parentSpanId,
3066
+ name,
3067
+ kind,
3068
+ startMs,
3069
+ endMs,
3070
+ attrs,
3071
+ error
3072
+ });
3073
+ const sp = rec(started?.payload);
3074
+ const rootAttrs = {
3075
+ [GEN_AI.operation]: "invoke_workflow",
3076
+ [GEN_AI.conversationId]: runId,
3077
+ "tangle.run.id": runId,
3078
+ "tangle.loop.driver": str(sp.driver) ?? "driver"
3079
+ };
3080
+ if (Array.isArray(sp.agentRunNames) && sp.agentRunNames.length > 0) {
3081
+ rootAttrs["tangle.loop.agents"] = sp.agentRunNames.map(String).join(",");
3082
+ rootAttrs["tangle.subject.key"] = String(sp.agentRunNames[0]);
3083
+ }
3084
+ if (ended) {
3085
+ const ep = rec(ended.payload);
3086
+ const win = num(ep.winnerIterationIndex);
3087
+ if (win !== void 0) rootAttrs["tangle.loop.winner.iteration_index"] = win;
3088
+ const cost = num(ep.totalCostUsd);
3089
+ if (cost !== void 0) rootAttrs["tangle.cost.usd"] = cost;
3090
+ const dur = num(ep.durationMs);
3091
+ if (dur !== void 0) rootAttrs["tangle.loop.duration_ms"] = dur;
3092
+ const iters = num(ep.iterations);
3093
+ if (iters !== void 0) rootAttrs["tangle.loop.iterations"] = iters;
3094
+ }
3095
+ out.push(make(rootId, void 0, "loop", "loop", rootStart, rootEnd, rootAttrs));
3096
+ const iterStartTs = /* @__PURE__ */ new Map();
3097
+ const placementByIdx = /* @__PURE__ */ new Map();
3098
+ let currentRoundId;
3099
+ let pendingRound;
3100
+ const flushRound = (endMs) => {
3101
+ if (!pendingRound) return;
3102
+ out.push(make(pendingRound.id, rootId, "loop.round", "round", pendingRound.start, endMs, pendingRound.attrs));
3103
+ pendingRound = void 0;
3104
+ };
3105
+ for (const e of events) {
3106
+ const p = rec(e.payload);
3107
+ switch (e.kind) {
3108
+ case "loop.plan": {
3109
+ flushRound(e.timestamp);
3110
+ const id = generateSpanId();
3111
+ const roundIdx = num(p.roundIndex) ?? 0;
3112
+ const attrs = {
3113
+ [GEN_AI.operation]: "invoke_workflow",
3114
+ "tangle.loop.round.index": roundIdx,
3115
+ "tangle.loop.move.kind": str(p.moveKind) ?? "unknown",
3116
+ "tangle.loop.move.round": roundIdx,
3117
+ "tangle.loop.move.width": num(p.plannedCount) ?? 0
3118
+ };
3119
+ const r = str(p.rationale);
3120
+ if (r) attrs["tangle.loop.move.rationale"] = r;
3121
+ const parent = num(p.parentIndex);
3122
+ if (parent !== void 0) attrs["tangle.loop.move.parent_index"] = parent;
3123
+ if (Array.isArray(p.childIndices) && p.childIndices.length > 0) attrs["tangle.loop.move.child_indices"] = p.childIndices.map(String).join(",");
3124
+ pendingRound = {
3125
+ id,
3126
+ start: e.timestamp,
3127
+ attrs
3128
+ };
3129
+ currentRoundId = id;
3130
+ break;
3131
+ }
3132
+ case "loop.iteration.started": {
3133
+ const idx = num(p.iterationIndex);
3134
+ if (idx !== void 0) iterStartTs.set(idx, e.timestamp);
3135
+ break;
3136
+ }
3137
+ case "loop.iteration.dispatch": {
3138
+ const idx = num(p.iterationIndex);
3139
+ if (idx === void 0) break;
3140
+ const place = {};
3141
+ const kind = str(p.placement);
3142
+ if (kind) place["tangle.loop.placement.kind"] = kind;
3143
+ const sid = str(p.sandboxId);
3144
+ if (sid) place["tangle.sandbox.id"] = sid;
3145
+ const fid = str(p.fleetId);
3146
+ if (fid) place["tangle.fleet.id"] = fid;
3147
+ const mid = str(p.machineId);
3148
+ if (mid) place["tangle.machine.id"] = mid;
3149
+ placementByIdx.set(idx, place);
3150
+ break;
3151
+ }
3152
+ case "loop.iteration.ended": {
3153
+ const idx = num(p.iterationIndex) ?? 0;
3154
+ const start = iterStartTs.get(idx) ?? e.timestamp;
3155
+ const err = str(p.error);
3156
+ const attrs = {
3157
+ [GEN_AI.operation]: "invoke_agent",
3158
+ "tangle.loop.iteration.index": idx
3159
+ };
3160
+ const agent = str(p.agentRunName);
3161
+ if (agent) attrs[GEN_AI.agentName] = agent;
3162
+ const tu = rec(p.tokenUsage);
3163
+ const inTok = num(tu.input);
3164
+ if (inTok !== void 0) attrs[GEN_AI.inputTokens] = inTok;
3165
+ const outTok = num(tu.output);
3166
+ if (outTok !== void 0) attrs[GEN_AI.outputTokens] = outTok;
3167
+ const cost = num(p.costUsd);
3168
+ if (cost !== void 0) attrs["tangle.cost.usd"] = cost;
3169
+ const verdict = rec(p.verdict);
3170
+ if (typeof verdict.valid === "boolean") attrs["tangle.loop.verdict.valid"] = verdict.valid;
3171
+ const score = num(verdict.score);
3172
+ if (score !== void 0) attrs["tangle.loop.verdict.score"] = score;
3173
+ if (err) attrs["tangle.loop.error"] = err;
3174
+ const gid = num(p.groupId);
3175
+ if (gid !== void 0) attrs["tangle.loop.iteration.group_id"] = gid;
3176
+ const par = num(p.parentIndex);
3177
+ if (par !== void 0) attrs["tangle.loop.iteration.parent_index"] = par;
3178
+ const dur = num(p.durationMs);
3179
+ if (dur !== void 0) attrs["tangle.loop.iteration.duration_ms"] = dur;
3180
+ const preview = str(p.outputPreview);
3181
+ if (preview) attrs["tangle.loop.iteration.output_preview"] = preview;
3182
+ Object.assign(attrs, placementByIdx.get(idx) ?? {});
3183
+ out.push(make(generateSpanId(), currentRoundId ?? rootId, "loop.iteration", "branch", start, e.timestamp, attrs, err !== void 0));
3184
+ break;
3185
+ }
3186
+ case "loop.decision":
3187
+ if (pendingRound) {
3188
+ const dec = str(p.decision);
3189
+ if (dec) pendingRound.attrs["tangle.loop.decision"] = dec;
3190
+ flushRound(e.timestamp);
3191
+ }
3192
+ currentRoundId = void 0;
3193
+ break;
3194
+ }
3195
+ }
3196
+ flushRound(rootEnd);
3197
+ return out;
3198
+ }
3199
+ function parseHeadersFromEnv() {
3200
+ if (typeof process === "undefined") return {};
3201
+ const raw = process.env.OTEL_EXPORTER_OTLP_HEADERS;
3202
+ if (!raw) return {};
3203
+ const out = {};
3204
+ for (const pair of raw.split(",")) {
3205
+ const eq = pair.indexOf("=");
3206
+ if (eq < 0) continue;
3207
+ const key = pair.slice(0, eq).trim();
3208
+ const value = pair.slice(eq + 1).trim();
3209
+ if (key) out[key] = value;
3210
+ }
3211
+ return out;
3212
+ }
3213
+ /**
3214
+ * Convert a flat record into the OTLP attribute list. Non-finite numbers are DROPPED (an OTLP
3215
+ * `doubleValue` of `NaN`/`Infinity` is not representable), integers ride as `intValue`. Exported so
3216
+ * a producer that mints its own `OtelSpan` (the supervisor span recorder) builds attributes exactly
3217
+ * the way every span in this file does, rather than re-deriving the encoding.
3218
+ */
3219
+ function toOtelAttributes(record) {
3220
+ return Object.entries(record).flatMap(([key, value]) => {
3221
+ if (typeof value === "number" && !Number.isFinite(value)) return [];
3222
+ return [{
3223
+ key,
3224
+ value: typeof value === "number" ? Number.isInteger(value) ? { intValue: value.toString() } : { doubleValue: value } : typeof value === "boolean" ? { boolValue: value } : { stringValue: value }
3225
+ }];
3226
+ });
3227
+ }
3228
+ function msToNs(ms) {
3229
+ return (BigInt(Math.floor(Number.isFinite(ms) ? ms : Date.now())) * 1000000n).toString();
3230
+ }
3231
+ function padSpanId(id) {
3232
+ return id.replace(/-/g, "").slice(0, 16).padEnd(16, "0");
3233
+ }
3234
+ function padTraceId(id) {
3235
+ return id.replace(/-/g, "").slice(0, 32).padEnd(32, "0");
3236
+ }
3237
+ /** Mint a fresh 16-hex-character OTLP span id. Exported so a producer that must know a span's id
3238
+ * BEFORE the span closes (a node opened at spawn and parented by its children) uses this one
3239
+ * generator instead of a second copy of it. */
3240
+ function generateSpanId() {
3241
+ const bytes = /* @__PURE__ */ new Uint8Array(8);
3242
+ if (typeof globalThis.crypto?.getRandomValues === "function") globalThis.crypto.getRandomValues(bytes);
3243
+ else for (let i = 0; i < 8; i++) bytes[i] = Math.floor(Math.random() * 256);
3244
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
3245
+ }
3246
+ /** Wire version the eval-runs ingest enforces (X-Tangle-Wire-Version + body). */
3247
+ const INTELLIGENCE_WIRE_VERSION = "2026-05-26.v1";
3248
+ const DEFAULT_INTELLIGENCE_BASE = "https://intelligence.tangle.tools";
3249
+ /**
3250
+ * Ship self-improvement eval-run events to Tangle Intelligence. Unlike the
3251
+ * best-effort span exporter, this RESOLVES with the ingest verdict (accepted /
3252
+ * rejected per event) so a consumer's loop can assert its provenance landed.
3253
+ * Throws only on a missing key or network failure.
3254
+ */
3255
+ async function exportEvalRuns(events, config) {
3256
+ if (events.length === 0) return {
3257
+ ok: true,
3258
+ status: 0,
3259
+ accepted: 0,
3260
+ rejected: []
3261
+ };
3262
+ const apiKey = config?.apiKey ?? (typeof process !== "undefined" ? process.env.TANGLE_API_KEY : void 0);
3263
+ if (!apiKey) throw new Error("exportEvalRuns: apiKey required (pass config.apiKey or set TANGLE_API_KEY)");
3264
+ const url = `${(config?.base ?? (typeof process !== "undefined" ? process.env.TANGLE_INTELLIGENCE_URL : void 0) ?? DEFAULT_INTELLIGENCE_BASE).replace(/\/+$/, "")}/v1/ingest/eval-runs`;
3265
+ const res = await fetch(url, {
3266
+ method: "POST",
3267
+ headers: {
3268
+ "content-type": "application/json",
3269
+ authorization: `Bearer ${apiKey}`,
3270
+ "X-Tangle-Wire-Version": INTELLIGENCE_WIRE_VERSION,
3271
+ ...config?.idempotencyKey ? { "Idempotency-Key": config.idempotencyKey } : {}
3272
+ },
3273
+ body: JSON.stringify({
3274
+ wireVersion: INTELLIGENCE_WIRE_VERSION,
3275
+ events
3276
+ })
3277
+ });
3278
+ let parsed = {};
3279
+ try {
3280
+ parsed = await res.json();
3281
+ } catch {}
3282
+ return {
3283
+ ok: res.ok,
3284
+ status: res.status,
3285
+ accepted: parsed.accepted ?? (res.ok ? events.length : 0),
3286
+ rejected: parsed.rejected ?? []
3287
+ };
3288
+ }
3289
+ //#endregion
3290
+ //#region src/mcp/trace-propagation.ts
3291
+ /**
3292
+ * Read trace context from the process environment.
3293
+ * Returns a context with inherited ids or a freshly generated root.
3294
+ */
3295
+ function readTraceContextFromEnv() {
3296
+ return {
3297
+ traceId: process.env.TRACE_ID || generateTraceId(),
3298
+ parentSpanId: process.env.PARENT_SPAN_ID || void 0
3299
+ };
3300
+ }
3301
+ /**
3302
+ * Create a LoopTraceEmitter that:
3303
+ * 1. Parents all spans under the inherited PARENT_SPAN_ID.
3304
+ * 2. Exports spans to OTEL when OTEL_EXPORTER_OTLP_ENDPOINT is set.
3305
+ *
3306
+ * Returns both the emitter and the optional exporter handle for shutdown.
3307
+ */
3308
+ function createPropagatingTraceEmitter(ctx) {
3309
+ const exporter = createOtelExporter();
3310
+ const buffers = /* @__PURE__ */ new Map();
3311
+ return {
3312
+ emitter: { emit(event) {
3313
+ if (!exporter) return;
3314
+ const buf = buffers.get(event.runId);
3315
+ if (buf) buf.push(event);
3316
+ else buffers.set(event.runId, [event]);
3317
+ if (event.kind === "loop.ended") {
3318
+ const events = buffers.get(event.runId) ?? [event];
3319
+ buffers.delete(event.runId);
3320
+ for (const span of buildLoopOtelSpans(events, ctx.traceId, ctx.parentSpanId)) exporter.exportSpan(span);
3321
+ }
3322
+ } },
3323
+ exporter,
3324
+ context: ctx
3325
+ };
3326
+ }
3327
+ /**
3328
+ * Build env vars to pass to a child MCP subprocess so it inherits the
3329
+ * current trace context.
3330
+ */
3331
+ function traceContextToEnv(ctx) {
3332
+ const env = { TRACE_ID: ctx.traceId };
3333
+ if (ctx.parentSpanId) env.PARENT_SPAN_ID = ctx.parentSpanId;
3334
+ return env;
3335
+ }
3336
+ function generateTraceId() {
3337
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
3338
+ if (typeof globalThis.crypto?.getRandomValues === "function") globalThis.crypto.getRandomValues(bytes);
3339
+ else for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);
3340
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
3341
+ }
3342
+ //#endregion
3343
+ //#region src/runtime/supervise/worker-trace.ts
3344
+ /**
3345
+ * Trace context handed DOWN to a spawned worker, so one supervised tree spans machines.
3346
+ *
3347
+ * WHY. `otel-spans.ts` makes the supervisor's own tree readable by any trace viewer, but it stops at
3348
+ * the process boundary: a worker running on a remote sandbox or in a child process opens its OWN
3349
+ * trace root, so the viewer shows two unrelated trees for one run. A worker that inherits the
3350
+ * parent's trace id and the spawning node's span id emits spans that JOIN the parent's trace, and
3351
+ * the viewer assembles the whole cross-machine tree with no viewer change.
3352
+ *
3353
+ * NO NEW FORMAT. The wire is the env-var convention this package already ships and already reads:
3354
+ * `TRACE_ID` / `PARENT_SPAN_ID`, written by {@link traceContextToEnv} and read back by
3355
+ * `readTraceContextFromEnv()` (`src/mcp/trace-propagation.ts`). Nothing here invents a second
3356
+ * encoding; W3C `traceparent` would be the standard alternative, but adopting it alongside this one
3357
+ * would fork the contract, and the reader for THIS one is already in the package.
3358
+ *
3359
+ * WHICH SPAN. The stamped `PARENT_SPAN_ID` is the span of the node that DID the spawning — the
3360
+ * scope's own `parentId` — not the span of the worker's own node. At depth 0 that is the run's root
3361
+ * span; inside a driver's nested scope it is that driver child's span, so a depth-2 worker joins
3362
+ * under the middle node rather than the root. That is also the only span guaranteed to exist at the
3363
+ * moment the child's `ExecutorContext` is built (the worker's own node span is opened by the
3364
+ * `agent.spawn` hook that fires just after), so the wire needs no ordering assumption.
3365
+ *
3366
+ * OFF WHEN TRACING IS OFF. The context reaches an executor only through the `ExecutorContext` seam
3367
+ * this module names, and that seam is seeded only when a run configured `SuperviseOptions.otel` AND
3368
+ * an exporter resolved. With tracing off there is no seam, {@link workerTraceEnv} returns an empty
3369
+ * record, and every spawn environment is byte-identical to what it was before this module existed.
3370
+ *
3371
+ * TWO SEEDING SITES, because there are two ways a worker's `ExecutorContext` gets built. `Scope`
3372
+ * seeds it per child for anything the executor registry resolves — that is the path that knows the
3373
+ * spawning node and reaches arbitrary depth. `workerFromBackend` (what `supervise({ backend })`
3374
+ * uses) instead builds its leaf executor eagerly and hands it back as a bring-your-own `Executor`,
3375
+ * which the registry resolves WITHOUT consulting the scope's context at all; `supervise()` therefore
3376
+ * passes the seam to `workerFromBackend` itself, resolved against the run root, which is the node
3377
+ * that spawns every front-door worker. Miss that second site and the front door looks wired and
3378
+ * stamps nothing.
3379
+ *
3380
+ * PRECEDENCE, from lowest to highest:
3381
+ * 1. the supervisor process's own `process.env` (ambient inheritance),
3382
+ * 2. the trace context stamped here,
3383
+ * 3. the caller's own seam env (`PiSeam.env`, `CliSeam.env`).
3384
+ * A caller who sets `TRACE_ID` / `PARENT_SPAN_ID` on a seam wins — theirs is a deliberate
3385
+ * declaration about the worker. Ambient `process.env` does NOT win: when the supervisor process was
3386
+ * itself launched as someone's worker, its inherited ids describe the SUPERVISOR's place in an
3387
+ * outer trace, and reusing them for a child would file the child's spans under the wrong parent.
3388
+ * The supported way to join that outer trace is `SupervisorSpanOptions.traceId` / `parentSpanId`
3389
+ * (feed them from `readTraceContextFromEnv()`), which makes the recorder's own ids the outer ones
3390
+ * and so makes every stamped child inherit them too.
3391
+ *
3392
+ * WHICH BACKENDS PROPAGATE. Only a backend with a real environment channel to the worker can carry
3393
+ * this, and the ones that cannot say so here rather than dropping it silently:
3394
+ * - `pi` YES — `PiSeam.env` → the `pi --mode rpc` subprocess.
3395
+ * - `cli` YES — `CliSeam.env` → the spawned subprocess.
3396
+ * - `sandbox` YES — `CreateSandboxOptions.env` on the box the worker runs in (single-shot
3397
+ * and steerable). This is the cross-MACHINE case the feature exists for.
3398
+ * - `router`,
3399
+ * `router-tools` NO — a direct model HTTP call. There is no worker process to inherit anything.
3400
+ * - `bridge`,
3401
+ * `cli-worktree` NO — the work is dispatched over cli-bridge HTTP or through a local harness
3402
+ * transport that exposes no environment channel. Wiring these means adding that
3403
+ * channel to the transport first; until then they are honestly unpropagated.
3404
+ * - `provider` NO — `AgentEnvironmentProvider` has no environment field on its port.
3405
+ */
3406
+ /**
3407
+ * Seam key the `Scope` seeds a {@link TraceContext} under on each child's `ExecutorContext.seams`.
3408
+ * Single-sourced here so the scope and every backend agree on it without a circular import — the
3409
+ * same arrangement `nestedScopeSeamKey` uses.
3410
+ */
3411
+ const workerTraceSeamKey = "worker-trace";
3412
+ /**
3413
+ * Read the inherited trace context off an `ExecutorContext`, or `undefined` when the run records no
3414
+ * spans. Fails CLOSED on a malformed seam value (returns `undefined`) rather than stamping a
3415
+ * half-formed id that would produce an unjoinable orphan span downstream.
3416
+ */
3417
+ function readWorkerTraceContext(ctx) {
3418
+ const seam = ctx.seams[workerTraceSeamKey];
3419
+ if (seam === void 0 || seam === null || typeof seam !== "object") return void 0;
3420
+ const { traceId, parentSpanId } = seam;
3421
+ if (typeof traceId !== "string" || traceId.length === 0) return void 0;
3422
+ return {
3423
+ traceId,
3424
+ ...typeof parentSpanId === "string" && parentSpanId.length > 0 ? { parentSpanId } : {}
3425
+ };
3426
+ }
3427
+ /**
3428
+ * The `TRACE_ID` / `PARENT_SPAN_ID` pair to merge into a worker's environment — EMPTY when the run
3429
+ * records no spans, which is what keeps the untraced path byte-identical. Merge it BELOW the
3430
+ * caller's own seam env so a deliberately-set id wins (see the precedence note above).
3431
+ */
3432
+ function workerTraceEnv(ctx) {
3433
+ const traceContext = readWorkerTraceContext(ctx);
3434
+ return traceContext ? traceContextToEnv(traceContext) : {};
3435
+ }
3436
+ //#endregion
2441
3437
  //#region src/runtime/supervise/scope.ts
2442
3438
  /**
2443
3439
  *
@@ -2490,7 +3486,8 @@ function makeNestedScopeSeam(args, childNodeId) {
2490
3486
  ...args.maxDepth !== void 0 ? { maxDepth: args.maxDepth } : {},
2491
3487
  signal,
2492
3488
  ...args.now ? { now: args.now } : {},
2493
- ...args.hooks ? { hooks: args.hooks } : {}
3489
+ ...args.hooks ? { hooks: args.hooks } : {},
3490
+ ...args.workerTrace ? { workerTrace: args.workerTrace } : {}
2494
3491
  });
2495
3492
  }
2496
3493
  };
@@ -2579,11 +3576,13 @@ function createScope(args) {
2579
3576
  const cascadeAbort = () => childAbort.abort();
2580
3577
  if (args.signal.aborted) childAbort.abort();
2581
3578
  else args.signal.addEventListener("abort", cascadeAbort, { once: true });
3579
+ const workerTrace = args.workerTrace?.(args.parentId);
2582
3580
  const ctx = {
2583
3581
  signal: childAbort.signal,
2584
3582
  seams: {
2585
3583
  ...args.seams,
2586
- [nestedScopeSeamKey]: makeNestedScopeSeam(args, id)
3584
+ [nestedScopeSeamKey]: makeNestedScopeSeam(args, id),
3585
+ ...workerTrace ? { [workerTraceSeamKey]: workerTrace } : {}
2587
3586
  }
2588
3587
  };
2589
3588
  const executor = resolved.value(spec, ctx);
@@ -4028,6 +5027,7 @@ function createSupervisor() {
4028
5027
  now,
4029
5028
  hooks: opts.hooks,
4030
5029
  ...opts.probes ? { probes: opts.probes } : {},
5030
+ ...opts.workerTrace ? { workerTrace: opts.workerTrace } : {},
4031
5031
  ...resumeFrom ? { resumeFrom } : {}
4032
5032
  });
4033
5033
  const openScope = scope;
@@ -4273,6 +5273,6 @@ function isNonEmptySpend(s) {
4273
5273
  return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0 || s.tokensKnown === false || s.usdKnown === false;
4274
5274
  }
4275
5275
  //#endregion
4276
- export { captureWorktreeDiff as A, routerChatWithUsage as C, runSettledCommand as D, runBrainLoop as E, runLocalHarness as F, CodexExecutionDiagnosticError as I, removeWorktree as M, harnessInvocation as N, runWorktreeChecks as O, parseCodexTokenUsage as P, routerChatWithTools as S, streamRouterChatWithTools as T, waitUntil as _, runFinalizer as a, readWorkerProgress as b, spendFromUsageEvents as c, settledToIteration as d, createWaitProbes as f, validateWaitSpec as g, timerAt as h, pickBestDelivered as i, createWorktree as j, runWorktreeHarness as k, withDriverExecutor as l, pollFor as m, bestDelivered as n, runTree as o, isWaitOutcome as p, collectDelivered as r, createBudgetPool as s, createSupervisor as t, createScope as u, DEFAULT_STALL_AFTER_MS as v, routerToolLoop as w, routerBrain as x, createActivityLog as y };
5276
+ export { removeWorktree as $, sanitizeAgentRuntimeEvent as A, createActivityLog as B, exportEvalRuns as C, toOtelAttributes as D, loopEventToOtelSpan as E, pollFor as F, routerToolLoop as G, routerBrain as H, timerAt as I, runSettledCommand as J, streamRouterChatWithTools as K, validateWaitSpec as L, sanitizeRuntimeStreamEvent as M, createWaitProbes as N, createRuntimeEventCollector as O, isWaitOutcome as P, createWorktree as Q, waitUntil as R, createOtelExporter as S, generateSpanId as T, routerChatWithTools as U, readWorkerProgress as V, routerChatWithUsage as W, runWorktreeHarness as X, runWorktreeChecks as Y, captureWorktreeDiff as Z, traceContextToEnv as _, runFinalizer as a, buildLoopSpanNodes as b, spendFromUsageEvents as c, settledToIteration as d, harnessInvocation as et, readWorkerTraceContext as f, readTraceContextFromEnv as g, createPropagatingTraceEmitter as h, pickBestDelivered as i, sanitizeKnowledgeReadinessReport as j, createRuntimeStreamEventCollector as k, withDriverExecutor as l, workerTraceSeamKey as m, bestDelivered as n, runLocalHarness as nt, runTree as o, workerTraceEnv as p, runBrainLoop as q, collectDelivered as r, CodexExecutionDiagnosticError as rt, createBudgetPool as s, createSupervisor as t, parseCodexTokenUsage as tt, createScope as u, INTELLIGENCE_WIRE_VERSION as v, flatOtelSpan as w, buildRuntimeEventOtelSpans as x, buildLoopOtelSpans as y, DEFAULT_STALL_AFTER_MS as z };
4277
5277
 
4278
- //# sourceMappingURL=supervisor-DzQu5Ydu.js.map
5278
+ //# sourceMappingURL=supervisor-BBbPBXpe.js.map