@tangle-network/agent-runtime 0.104.0 → 0.106.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +176 -42
- package/dist/activation-CRjVfOiy.js +203 -0
- package/dist/activation-CRjVfOiy.js.map +1 -0
- package/dist/activation-DyWB0K6E.d.ts +98 -0
- package/dist/agent.d.ts +216 -234
- package/dist/agent.js +615 -547
- package/dist/agent.js.map +1 -1
- package/dist/analyst-loop-DGdtVENg.js +517 -0
- package/dist/analyst-loop-DGdtVENg.js.map +1 -0
- package/dist/analyst-loop.d.ts +10 -43
- package/dist/analyst-loop.js +2 -11
- package/dist/backends-Dy8NujB4.js +713 -0
- package/dist/backends-Dy8NujB4.js.map +1 -0
- package/dist/candidate-execution/index.d.ts +4 -192
- package/dist/candidate-execution/index.js +5 -66
- package/dist/candidate-execution-DqMbv1rU.js +488 -0
- package/dist/candidate-execution-DqMbv1rU.js.map +1 -0
- package/dist/coder-yhVWbdWc.js +40 -0
- package/dist/coder-yhVWbdWc.js.map +1 -0
- package/dist/conversation-XucG_K3l.js +1265 -0
- package/dist/conversation-XucG_K3l.js.map +1 -0
- package/dist/conversation.d.ts +2 -804
- package/dist/conversation.js +2 -55
- package/dist/environment-provider-CfLUmKmf.js +1168 -0
- package/dist/environment-provider-CfLUmKmf.js.map +1 -0
- package/dist/environment-provider-IUGU3epE.d.ts +964 -0
- package/dist/environment-provider.d.ts +2 -78
- package/dist/environment-provider.js +2 -18
- package/dist/errors-DEAvWQPy.js +111 -0
- package/dist/errors-DEAvWQPy.js.map +1 -0
- package/dist/improvement-adapter-Cr5TBSji.d.ts +166 -0
- package/dist/improvement-cycle-C095kS_g.js +2624 -0
- package/dist/improvement-cycle-C095kS_g.js.map +1 -0
- package/dist/index-BGdxAwfO.d.ts +838 -0
- package/dist/index-C-FYUuFG.d.ts +193 -0
- package/dist/index-CbNM40xG.d.ts +140 -0
- package/dist/index-DruSKs3Z.d.ts +7224 -0
- package/dist/index-Efjb3nrQ.d.ts +671 -0
- package/dist/index.d.ts +14 -1134
- package/dist/index.js +2134 -1972
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +776 -888
- package/dist/intelligence.js +1466 -1410
- package/dist/intelligence.js.map +1 -1
- package/dist/{kb-gate-CwHO0vz6.d.ts → kb-gate-C8z2juK8.d.ts} +33 -31
- package/dist/kb-gate-DpaSwXVx.js +80 -0
- package/dist/kb-gate-DpaSwXVx.js.map +1 -0
- package/dist/knowledge-Ku-mPP4f.js +433 -0
- package/dist/knowledge-Ku-mPP4f.js.map +1 -0
- package/dist/knowledge.d.ts +2 -150
- package/dist/knowledge.js +2 -40
- package/dist/local-harness-Dh8PJ0ot.d.ts +455 -0
- package/dist/loop-runner-bin-BXsFEUcf.d.ts +162 -0
- package/dist/loop-runner-bin-BwjQ1NfH.js +268 -0
- package/dist/loop-runner-bin-BwjQ1NfH.js.map +1 -0
- package/dist/loop-runner-bin.d.ts +2 -17
- package/dist/loop-runner-bin.js +2 -24
- package/dist/loops.d.ts +5 -4164
- package/dist/loops.js +8 -419
- package/dist/mcp/bin.d.ts +1 -1
- package/dist/mcp/bin.js +116 -146
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +295 -437
- package/dist/mcp/index.js +1242 -1177
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/memory-bin.d.ts +1 -1
- package/dist/mcp/memory-bin.js +37 -16
- package/dist/mcp/memory-bin.js.map +1 -1
- package/dist/memory-server-DL6cE2Ag.js +234 -0
- package/dist/memory-server-DL6cE2Ag.js.map +1 -0
- package/dist/model-resolution-Btd9iIKV.js +98 -0
- package/dist/model-resolution-Btd9iIKV.js.map +1 -0
- package/dist/openai-tools-B68JaOCx.d.ts +24 -0
- package/dist/openai-tools-CKLy1C7M.js +44 -0
- package/dist/openai-tools-CKLy1C7M.js.map +1 -0
- package/dist/otel-export-D34IQW1C.js +843 -0
- package/dist/otel-export-D34IQW1C.js.map +1 -0
- package/dist/platform.d.ts +188 -185
- package/dist/platform.js +185 -218
- package/dist/platform.js.map +1 -1
- package/dist/prepare-B53sPV7M.js +4292 -0
- package/dist/prepare-B53sPV7M.js.map +1 -0
- package/dist/primeintellect/index.d.ts +150 -147
- package/dist/primeintellect/index.js +626 -1004
- package/dist/primeintellect/index.js.map +1 -1
- package/dist/profiles.d.ts +296 -490
- package/dist/profiles.js +722 -749
- package/dist/profiles.js.map +1 -1
- package/dist/protected-model-port-BLPH5VVv.js +722 -0
- package/dist/protected-model-port-BLPH5VVv.js.map +1 -0
- package/dist/protected-model-port-BP6Z4eau.d.ts +283 -0
- package/dist/protected-redaction--F3v1oo8.js +200 -0
- package/dist/protected-redaction--F3v1oo8.js.map +1 -0
- package/dist/redact-D-u-rrcn.js +82 -0
- package/dist/redact-D-u-rrcn.js.map +1 -0
- package/dist/redact-kbIOjfjM.d.ts +452 -0
- package/dist/researcher-CoVqNhfI.js +433 -0
- package/dist/researcher-CoVqNhfI.js.map +1 -0
- package/dist/runtime-BG0BziaP.js +5408 -0
- package/dist/runtime-BG0BziaP.js.map +1 -0
- package/dist/sandbox-events-DeI5xX8P.js +316 -0
- package/dist/sandbox-events-DeI5xX8P.js.map +1 -0
- package/dist/spawn-journal-CwPvKUTa.js +456 -0
- package/dist/spawn-journal-CwPvKUTa.js.map +1 -0
- package/dist/structural-rollout-IXUEplky.js +1445 -0
- package/dist/structural-rollout-IXUEplky.js.map +1 -0
- package/dist/substrate-B0TYNrXn.js +27 -0
- package/dist/substrate-B0TYNrXn.js.map +1 -0
- package/dist/{substrate-Dyiqmd3o.d.ts → substrate-BcnuSHXm.d.ts} +57 -54
- package/dist/supervise-BiRutHS9.js +7321 -0
- package/dist/supervise-BiRutHS9.js.map +1 -0
- package/dist/supervisor-DTKhF-RV.js +3826 -0
- package/dist/supervisor-DTKhF-RV.js.map +1 -0
- package/dist/testing.d.ts +4 -3
- package/dist/testing.js +2478 -2637
- package/dist/testing.js.map +1 -1
- package/dist/tool-server-RcWgLIsL.js +141 -0
- package/dist/tool-server-RcWgLIsL.js.map +1 -0
- package/dist/trace-propagation-CJJC7SVB.js +57 -0
- package/dist/trace-propagation-CJJC7SVB.js.map +1 -0
- package/dist/types-BaIpb0XT.d.ts +492 -0
- package/dist/types-BevOjfTY.d.ts +765 -0
- package/dist/types-C9j4qg6l.d.ts +500 -0
- package/dist/types-Cja5OTGh.d.ts +190 -0
- package/dist/util-MVgdwuIS.js +145 -0
- package/dist/util-MVgdwuIS.js.map +1 -0
- package/dist/workspace-archive-BosQiotA.js +575 -0
- package/dist/workspace-archive-BosQiotA.js.map +1 -0
- package/package.json +43 -55
- package/skills/build-with-agent-runtime/SKILL.md +16 -9
- package/skills/loop-writer/SKILL.md +1 -1
- package/dist/activation-Cm-9MMX1.d.ts +0 -69
- package/dist/analyst-loop.js.map +0 -1
- package/dist/backends-BO4Jqoe2.d.ts +0 -134
- package/dist/candidate-execution/index.js.map +0 -1
- package/dist/chunk-2KGAN2HM.js +0 -1256
- package/dist/chunk-2KGAN2HM.js.map +0 -1
- package/dist/chunk-2QJSNOGK.js +0 -48
- package/dist/chunk-2QJSNOGK.js.map +0 -1
- package/dist/chunk-2UABGGUB.js +0 -474
- package/dist/chunk-2UABGGUB.js.map +0 -1
- package/dist/chunk-2VZYRB55.js +0 -834
- package/dist/chunk-2VZYRB55.js.map +0 -1
- package/dist/chunk-2X65HYMI.js +0 -3526
- package/dist/chunk-2X65HYMI.js.map +0 -1
- package/dist/chunk-3MDZX7YU.js +0 -125
- package/dist/chunk-3MDZX7YU.js.map +0 -1
- package/dist/chunk-3PPVCS75.js +0 -199
- package/dist/chunk-3PPVCS75.js.map +0 -1
- package/dist/chunk-5HNDYWUT.js +0 -6424
- package/dist/chunk-5HNDYWUT.js.map +0 -1
- package/dist/chunk-5IBQAPVB.js +0 -29
- package/dist/chunk-5IBQAPVB.js.map +0 -1
- package/dist/chunk-5QOB7H74.js +0 -387
- package/dist/chunk-5QOB7H74.js.map +0 -1
- package/dist/chunk-ATDFUJYI.js +0 -4728
- package/dist/chunk-ATDFUJYI.js.map +0 -1
- package/dist/chunk-B7K7V22Y.js +0 -699
- package/dist/chunk-B7K7V22Y.js.map +0 -1
- package/dist/chunk-DPEUKJRO.js +0 -27
- package/dist/chunk-DPEUKJRO.js.map +0 -1
- package/dist/chunk-FD2MBMOH.js +0 -494
- package/dist/chunk-FD2MBMOH.js.map +0 -1
- package/dist/chunk-FJI3EXU3.js +0 -2402
- package/dist/chunk-FJI3EXU3.js.map +0 -1
- package/dist/chunk-HAO4CXHP.js +0 -200
- package/dist/chunk-HAO4CXHP.js.map +0 -1
- package/dist/chunk-ISPWRSEC.js +0 -408
- package/dist/chunk-ISPWRSEC.js.map +0 -1
- package/dist/chunk-KRBFHMV6.js +0 -1228
- package/dist/chunk-KRBFHMV6.js.map +0 -1
- package/dist/chunk-L5DST3QC.js +0 -351
- package/dist/chunk-L5DST3QC.js.map +0 -1
- package/dist/chunk-M22PL4GH.js +0 -171
- package/dist/chunk-M22PL4GH.js.map +0 -1
- package/dist/chunk-NURIQBJQ.js +0 -72
- package/dist/chunk-NURIQBJQ.js.map +0 -1
- package/dist/chunk-PZZKQVQV.js +0 -52
- package/dist/chunk-PZZKQVQV.js.map +0 -1
- package/dist/chunk-Q2JSAVQ3.js +0 -671
- package/dist/chunk-Q2JSAVQ3.js.map +0 -1
- package/dist/chunk-SBTWKPVR.js +0 -5576
- package/dist/chunk-SBTWKPVR.js.map +0 -1
- package/dist/chunk-SGKPNBXE.js +0 -60
- package/dist/chunk-SGKPNBXE.js.map +0 -1
- package/dist/chunk-SNSMRT6H.js +0 -680
- package/dist/chunk-SNSMRT6H.js.map +0 -1
- package/dist/chunk-STJEHM5F.js +0 -1338
- package/dist/chunk-STJEHM5F.js.map +0 -1
- package/dist/chunk-UPWGXKXB.js +0 -254
- package/dist/chunk-UPWGXKXB.js.map +0 -1
- package/dist/chunk-YEJR7IXO.js +0 -68
- package/dist/chunk-YEJR7IXO.js.map +0 -1
- package/dist/chunk-ZXICDSAK.js +0 -907
- package/dist/chunk-ZXICDSAK.js.map +0 -1
- package/dist/completion-gate-Bv47cOnn.d.ts +0 -256
- package/dist/conversation.js.map +0 -1
- package/dist/coordination-CxtzMxSM.d.ts +0 -2570
- package/dist/environment-provider.js.map +0 -1
- package/dist/improve--2A0gK1-.d.ts +0 -440
- package/dist/improvement-adapter-HAZz-7vK.d.ts +0 -203
- package/dist/knowledge.js.map +0 -1
- package/dist/local-harness-CtK9dSny.d.ts +0 -175
- package/dist/loop-runner-bin-DUOGtvOl.d.ts +0 -207
- package/dist/loop-runner-bin.js.map +0 -1
- package/dist/loops.js.map +0 -1
- package/dist/openai-tools-fnj6SRVg.d.ts +0 -46
- package/dist/protected-model-port-DzkUY3HQ.d.ts +0 -271
- package/dist/sanitize-DEbPNtyI.d.ts +0 -328
- package/dist/structural-rollout-C4mabf87.d.ts +0 -446
- package/dist/supervise-7q9F5brx.d.ts +0 -379
- package/dist/types-BCoemcXU.d.ts +0 -802
- package/dist/types-BwoZWq-i.d.ts +0 -510
- package/dist/types-Cbp72qyu.d.ts +0 -491
- package/dist/types-CmYCMbFT.d.ts +0 -203
- package/dist/types-CvNwMzQt.d.ts +0 -957
- package/dist/worktree-fanout-WsOzt8qu.d.ts +0 -999
|
@@ -0,0 +1,843 @@
|
|
|
1
|
+
//#region src/sanitize.ts
|
|
2
|
+
/** Strip PII and large blobs from a `KnowledgeReadinessReport` for safe telemetry emission. @stable */
|
|
3
|
+
function sanitizeKnowledgeReadinessReport(report, options = {}) {
|
|
4
|
+
return {
|
|
5
|
+
taskId: report.taskId,
|
|
6
|
+
readinessScore: report.readinessScore,
|
|
7
|
+
recommendedAction: report.recommendedAction,
|
|
8
|
+
severity: report.severity,
|
|
9
|
+
reason: report.reason,
|
|
10
|
+
blockingMissingRequirements: report.blockingMissingRequirements.map((requirement) => sanitizeKnowledgeRequirement(requirement, options)),
|
|
11
|
+
nonBlockingGaps: report.nonBlockingGaps.map((requirement) => sanitizeKnowledgeRequirement(requirement, options)),
|
|
12
|
+
evidenceCount: report.bundle.evidenceIds.length,
|
|
13
|
+
evidenceIds: options.includeEvidenceIds ? report.bundle.evidenceIds : void 0,
|
|
14
|
+
missingRequirementIds: report.bundle.missing.map((requirement) => requirement.id)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** Reduce an `AgentRuntimeEvent` to a PII-safe, serializable plain object for telemetry. @stable */
|
|
18
|
+
function sanitizeAgentRuntimeEvent(event, options = {}) {
|
|
19
|
+
const base = {
|
|
20
|
+
type: event.type,
|
|
21
|
+
task: sanitizeTask(event.task, options)
|
|
22
|
+
};
|
|
23
|
+
if (event.type === "readiness_start" || event.type === "task_start" || event.type === "control_start") return event.type === "control_start" ? {
|
|
24
|
+
...base,
|
|
25
|
+
knowledge: sanitizeKnowledgeReadinessReport(event.knowledge, options)
|
|
26
|
+
} : base;
|
|
27
|
+
if (event.type === "readiness_end") return {
|
|
28
|
+
...base,
|
|
29
|
+
knowledge: sanitizeKnowledgeReadinessReport(event.knowledge, options)
|
|
30
|
+
};
|
|
31
|
+
if (event.type === "questions_start") return {
|
|
32
|
+
...base,
|
|
33
|
+
questions: event.questions.map((question) => sanitizeQuestion(question, options))
|
|
34
|
+
};
|
|
35
|
+
if (event.type === "questions_end") return {
|
|
36
|
+
...base,
|
|
37
|
+
questions: event.questions.map((question) => sanitizeQuestion(question, options)),
|
|
38
|
+
userAnswers: options.includeUserAnswers ? event.userAnswers : redactRecord(event.userAnswers)
|
|
39
|
+
};
|
|
40
|
+
if (event.type === "acquisition_start") return {
|
|
41
|
+
...base,
|
|
42
|
+
acquisitionPlans: event.acquisitionPlans.map(sanitizeAcquisitionPlan)
|
|
43
|
+
};
|
|
44
|
+
if (event.type === "acquisition_end") return {
|
|
45
|
+
...base,
|
|
46
|
+
acquisitionPlans: event.acquisitionPlans.map(sanitizeAcquisitionPlan),
|
|
47
|
+
acquiredEvidenceCount: event.acquiredEvidenceIds.length,
|
|
48
|
+
acquiredEvidenceIds: options.includeEvidenceIds ? event.acquiredEvidenceIds : void 0
|
|
49
|
+
};
|
|
50
|
+
if (event.type === "control_step") return {
|
|
51
|
+
...base,
|
|
52
|
+
step: sanitizeControlStep(event.step, options)
|
|
53
|
+
};
|
|
54
|
+
if (event.type === "control_end") return {
|
|
55
|
+
...base,
|
|
56
|
+
control: sanitizeControlRun(event.control, options)
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
...base,
|
|
60
|
+
status: event.status,
|
|
61
|
+
reason: event.reason
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** Reduce a `RuntimeStreamEvent` to a PII-safe, serializable plain object for telemetry. @stable */
|
|
65
|
+
function sanitizeRuntimeStreamEvent(event, options = {}) {
|
|
66
|
+
const withTask = "task" in event && event.task ? { task: sanitizeTask(event.task, options) } : {};
|
|
67
|
+
const withSession = "session" in event && event.session ? { session: sanitizeRuntimeSession(event.session, options) } : {};
|
|
68
|
+
if (event.type === "readiness_end") return {
|
|
69
|
+
type: event.type,
|
|
70
|
+
...withTask,
|
|
71
|
+
timestamp: event.timestamp,
|
|
72
|
+
decision: event.decision,
|
|
73
|
+
knowledge: sanitizeKnowledgeReadinessReport(event.knowledge, options)
|
|
74
|
+
};
|
|
75
|
+
if (event.type === "questions_start") return {
|
|
76
|
+
type: event.type,
|
|
77
|
+
...withTask,
|
|
78
|
+
timestamp: event.timestamp,
|
|
79
|
+
questions: event.questions.map((question) => sanitizeQuestion(question, options))
|
|
80
|
+
};
|
|
81
|
+
if (event.type === "questions_end") return {
|
|
82
|
+
type: event.type,
|
|
83
|
+
...withTask,
|
|
84
|
+
timestamp: event.timestamp,
|
|
85
|
+
questions: event.questions.map((question) => sanitizeQuestion(question, options)),
|
|
86
|
+
userAnswers: options.includeUserAnswers ? event.userAnswers : redactRecord(event.userAnswers)
|
|
87
|
+
};
|
|
88
|
+
if (event.type === "acquisition_start") return {
|
|
89
|
+
type: event.type,
|
|
90
|
+
...withTask,
|
|
91
|
+
timestamp: event.timestamp,
|
|
92
|
+
acquisitionPlans: event.acquisitionPlans.map(sanitizeAcquisitionPlan)
|
|
93
|
+
};
|
|
94
|
+
if (event.type === "acquisition_end") return {
|
|
95
|
+
type: event.type,
|
|
96
|
+
...withTask,
|
|
97
|
+
timestamp: event.timestamp,
|
|
98
|
+
acquisitionPlans: event.acquisitionPlans.map(sanitizeAcquisitionPlan),
|
|
99
|
+
acquiredEvidenceCount: event.acquiredEvidenceIds.length,
|
|
100
|
+
acquiredEvidenceIds: options.includeEvidenceIds ? event.acquiredEvidenceIds : void 0
|
|
101
|
+
};
|
|
102
|
+
if (event.type === "tool_call") return {
|
|
103
|
+
type: event.type,
|
|
104
|
+
...withTask,
|
|
105
|
+
...withSession,
|
|
106
|
+
timestamp: event.timestamp,
|
|
107
|
+
toolName: event.toolName,
|
|
108
|
+
toolCallId: event.toolCallId,
|
|
109
|
+
args: options.includeControlPayloads ? event.args : void 0
|
|
110
|
+
};
|
|
111
|
+
if (event.type === "tool_result") return {
|
|
112
|
+
type: event.type,
|
|
113
|
+
...withTask,
|
|
114
|
+
...withSession,
|
|
115
|
+
timestamp: event.timestamp,
|
|
116
|
+
toolName: event.toolName,
|
|
117
|
+
toolCallId: event.toolCallId,
|
|
118
|
+
result: options.includeControlPayloads ? event.result : void 0
|
|
119
|
+
};
|
|
120
|
+
if (event.type === "llm_call") return {
|
|
121
|
+
type: event.type,
|
|
122
|
+
...withTask,
|
|
123
|
+
...withSession,
|
|
124
|
+
timestamp: event.timestamp,
|
|
125
|
+
model: event.model,
|
|
126
|
+
tokensIn: event.tokensIn,
|
|
127
|
+
tokensOut: event.tokensOut,
|
|
128
|
+
costUsd: event.costUsd,
|
|
129
|
+
latencyMs: event.latencyMs,
|
|
130
|
+
finishReason: event.finishReason
|
|
131
|
+
};
|
|
132
|
+
if (event.type === "artifact") return {
|
|
133
|
+
type: event.type,
|
|
134
|
+
...withTask,
|
|
135
|
+
...withSession,
|
|
136
|
+
timestamp: event.timestamp,
|
|
137
|
+
artifactId: event.artifactId,
|
|
138
|
+
name: event.name,
|
|
139
|
+
mimeType: event.mimeType,
|
|
140
|
+
uri: options.includeEvidenceIds ? event.uri : void 0,
|
|
141
|
+
content: options.includeControlPayloads ? event.content : void 0,
|
|
142
|
+
metadata: options.includeMetadata ? event.metadata : void 0
|
|
143
|
+
};
|
|
144
|
+
if (event.type === "proposal_created") return {
|
|
145
|
+
type: event.type,
|
|
146
|
+
...withTask,
|
|
147
|
+
...withSession,
|
|
148
|
+
timestamp: event.timestamp,
|
|
149
|
+
proposalId: event.proposalId,
|
|
150
|
+
title: options.includeControlPayloads ? event.title : void 0,
|
|
151
|
+
content: options.includeControlPayloads ? event.content : void 0,
|
|
152
|
+
status: event.status
|
|
153
|
+
};
|
|
154
|
+
if (event.type === "final") {
|
|
155
|
+
const sanitizedError = event.error !== void 0 ? {
|
|
156
|
+
kind: event.error.kind,
|
|
157
|
+
message: event.error.message,
|
|
158
|
+
status: event.error.status,
|
|
159
|
+
body: options.includeControlPayloads ? event.error.body : void 0
|
|
160
|
+
} : void 0;
|
|
161
|
+
return {
|
|
162
|
+
type: event.type,
|
|
163
|
+
...withTask,
|
|
164
|
+
...withSession,
|
|
165
|
+
timestamp: event.timestamp,
|
|
166
|
+
status: event.status,
|
|
167
|
+
reason: event.reason,
|
|
168
|
+
text: options.includeControlPayloads ? event.text : void 0,
|
|
169
|
+
metadata: options.includeMetadata ? event.metadata : void 0,
|
|
170
|
+
...sanitizedError !== void 0 ? { error: sanitizedError } : {}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
type: event.type,
|
|
175
|
+
...withTask,
|
|
176
|
+
...withSession,
|
|
177
|
+
timestamp: "timestamp" in event ? event.timestamp : void 0,
|
|
178
|
+
...pickPublicStreamFields(event)
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function sanitizeTask(task, options) {
|
|
182
|
+
return {
|
|
183
|
+
id: task.id,
|
|
184
|
+
intent: task.intent,
|
|
185
|
+
domain: task.domain,
|
|
186
|
+
inputs: options.includeInputs ? task.inputs : task.inputs ? "[redacted]" : void 0,
|
|
187
|
+
requiredKnowledge: task.requiredKnowledge?.map((requirement) => sanitizeKnowledgeRequirement(requirement, options)),
|
|
188
|
+
metadata: options.includeMetadata ? task.metadata : task.metadata ? "[redacted]" : void 0
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function sanitizeRuntimeSession(session, options) {
|
|
192
|
+
return {
|
|
193
|
+
id: session.id,
|
|
194
|
+
backend: session.backend,
|
|
195
|
+
status: session.status,
|
|
196
|
+
hasResumeToken: Boolean(session.resumeToken),
|
|
197
|
+
createdAt: session.createdAt,
|
|
198
|
+
updatedAt: session.updatedAt,
|
|
199
|
+
metadata: options.includeMetadata ? session.metadata : session.metadata ? "[redacted]" : void 0
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function sanitizeKnowledgeRequirement(requirement, options) {
|
|
203
|
+
const includeDescription = options.includeRequirementDescriptions && requirement.sensitivity !== "secret";
|
|
204
|
+
return {
|
|
205
|
+
id: requirement.id,
|
|
206
|
+
description: includeDescription ? requirement.description : void 0,
|
|
207
|
+
requiredFor: requirement.requiredFor,
|
|
208
|
+
category: requirement.category,
|
|
209
|
+
acquisitionMode: requirement.acquisitionMode,
|
|
210
|
+
importance: requirement.importance,
|
|
211
|
+
freshness: requirement.freshness,
|
|
212
|
+
sensitivity: requirement.sensitivity,
|
|
213
|
+
confidenceNeeded: requirement.confidenceNeeded,
|
|
214
|
+
currentConfidence: requirement.currentConfidence,
|
|
215
|
+
evidenceCount: requirement.evidenceIds.length,
|
|
216
|
+
evidenceIds: options.includeEvidenceIds ? requirement.evidenceIds : void 0,
|
|
217
|
+
fallbackPolicy: requirement.fallbackPolicy
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function sanitizeQuestion(question, options) {
|
|
221
|
+
return {
|
|
222
|
+
id: question.id,
|
|
223
|
+
question: options.includeRequirementDescriptions && question.answerType !== "credential" ? question.question : void 0,
|
|
224
|
+
reason: options.includeRequirementDescriptions ? question.reason : void 0,
|
|
225
|
+
requirementId: question.requirementId,
|
|
226
|
+
importance: question.importance,
|
|
227
|
+
answerType: question.answerType,
|
|
228
|
+
impactIfUnknown: options.includeRequirementDescriptions ? question.impactIfUnknown : void 0,
|
|
229
|
+
optionCount: question.options?.length ?? 0
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function sanitizeAcquisitionPlan(plan) {
|
|
233
|
+
return {
|
|
234
|
+
id: plan.id,
|
|
235
|
+
requirementIds: plan.requirementIds,
|
|
236
|
+
mode: plan.mode,
|
|
237
|
+
priority: plan.priority,
|
|
238
|
+
expectedEvidenceCount: plan.expectedEvidenceIds?.length ?? 0,
|
|
239
|
+
questionCount: plan.questions?.length ?? 0
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function sanitizeControlStep(step, options) {
|
|
243
|
+
const actionOutcome = step.actionOutcome;
|
|
244
|
+
return {
|
|
245
|
+
index: step.index,
|
|
246
|
+
decisionType: step.decision.type,
|
|
247
|
+
reason: step.decision.reason,
|
|
248
|
+
action: options.includeControlPayloads && step.decision.type === "continue" ? step.decision.action : void 0,
|
|
249
|
+
result: options.includeControlPayloads && actionOutcome?.ok ? actionOutcome.result : void 0,
|
|
250
|
+
actionOk: actionOutcome?.ok,
|
|
251
|
+
actionError: actionOutcome?.ok === false ? actionOutcome.error : void 0,
|
|
252
|
+
durationMs: actionOutcome?.durationMs,
|
|
253
|
+
evalsBefore: summarizeEvals(step.evalsBefore, options),
|
|
254
|
+
evalsAfter: summarizeEvals(step.evalsAfter, options),
|
|
255
|
+
startedAt: step.startedAt,
|
|
256
|
+
endedAt: step.endedAt
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function sanitizeControlRun(control, options) {
|
|
260
|
+
return {
|
|
261
|
+
pass: control.pass,
|
|
262
|
+
completed: control.completed,
|
|
263
|
+
reason: control.reason,
|
|
264
|
+
score: control.score,
|
|
265
|
+
stepCount: control.steps.length,
|
|
266
|
+
wallMs: control.wallMs,
|
|
267
|
+
spentCostUsd: control.spentCostUsd,
|
|
268
|
+
failureClass: control.failureClass,
|
|
269
|
+
stoppedBy: control.stoppedBy,
|
|
270
|
+
runId: control.runId,
|
|
271
|
+
runtimeErrorCount: control.runtimeErrors.length,
|
|
272
|
+
finalEvals: summarizeEvals(control.finalEvals, options)
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function summarizeEvals(evals, options) {
|
|
276
|
+
return evals.map((evalResult) => ({
|
|
277
|
+
id: evalResult.id,
|
|
278
|
+
passed: evalResult.passed,
|
|
279
|
+
score: evalResult.score,
|
|
280
|
+
severity: evalResult.severity,
|
|
281
|
+
objective: evalResult.objective,
|
|
282
|
+
detail: options.includeEvalDetails ? evalResult.detail : void 0,
|
|
283
|
+
evidence: options.includeEvalDetails ? evalResult.evidence : void 0
|
|
284
|
+
}));
|
|
285
|
+
}
|
|
286
|
+
function redactRecord(record) {
|
|
287
|
+
return Object.fromEntries(Object.keys(record).map((key) => [key, "[redacted]"]));
|
|
288
|
+
}
|
|
289
|
+
function pickPublicStreamFields(event) {
|
|
290
|
+
if (event.type === "session_created" || event.type === "session_resumed") return {};
|
|
291
|
+
if (event.type === "backend_start" || event.type === "backend_end") return { backend: event.backend };
|
|
292
|
+
if (event.type === "backend_error") {
|
|
293
|
+
const sanitizedError = event.error !== void 0 ? {
|
|
294
|
+
kind: event.error.kind,
|
|
295
|
+
status: event.error.status
|
|
296
|
+
} : void 0;
|
|
297
|
+
return {
|
|
298
|
+
backend: event.backend,
|
|
299
|
+
message: event.message,
|
|
300
|
+
recoverable: event.recoverable,
|
|
301
|
+
...sanitizedError !== void 0 ? { error: sanitizedError } : {}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
if (event.type === "task_end") return {
|
|
305
|
+
status: event.status,
|
|
306
|
+
reason: event.reason
|
|
307
|
+
};
|
|
308
|
+
if (event.type === "text_delta" || event.type === "reasoning_delta") return { text: event.text };
|
|
309
|
+
return {};
|
|
310
|
+
}
|
|
311
|
+
/** Build an in-memory collector that sanitizes and accumulates `AgentRuntimeEvent`s for inspection. @stable */
|
|
312
|
+
function createRuntimeEventCollector(options = {}) {
|
|
313
|
+
const events = [];
|
|
314
|
+
return {
|
|
315
|
+
events,
|
|
316
|
+
onEvent: (event) => {
|
|
317
|
+
events.push(sanitizeAgentRuntimeEvent(event, options));
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
*
|
|
323
|
+
* Streaming-event counterpart of `createRuntimeEventCollector`. Pass each
|
|
324
|
+
* event yielded by `runAgentTaskStream` through `onEvent` and read the
|
|
325
|
+
* sanitized copies off `events`; the same `RuntimeTelemetryOptions` redaction
|
|
326
|
+
* flags apply. Kept distinct from `createRuntimeEventCollector` because the
|
|
327
|
+
* stream and non-stream event shapes overlap on `type` literals — dispatching
|
|
328
|
+
* on `type` alone would misroute events.
|
|
329
|
+
*
|
|
330
|
+
* @stable
|
|
331
|
+
*/
|
|
332
|
+
function createRuntimeStreamEventCollector(options = {}) {
|
|
333
|
+
const events = [];
|
|
334
|
+
const eventCountsByType = {};
|
|
335
|
+
let firstSessionId;
|
|
336
|
+
let finalStatus;
|
|
337
|
+
let finalReason;
|
|
338
|
+
let finalText = "";
|
|
339
|
+
return {
|
|
340
|
+
events,
|
|
341
|
+
onEvent: (event) => {
|
|
342
|
+
events.push(sanitizeRuntimeStreamEvent(event, options));
|
|
343
|
+
eventCountsByType[event.type] = (eventCountsByType[event.type] ?? 0) + 1;
|
|
344
|
+
if (event.type === "text_delta") finalText += event.text;
|
|
345
|
+
if (!firstSessionId && (event.type === "session_created" || event.type === "session_resumed")) firstSessionId = event.session.id;
|
|
346
|
+
if (event.type === "final") {
|
|
347
|
+
finalStatus = event.status;
|
|
348
|
+
finalReason = event.reason;
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
summary() {
|
|
352
|
+
return {
|
|
353
|
+
eventCount: events.length,
|
|
354
|
+
eventCountsByType: { ...eventCountsByType },
|
|
355
|
+
firstSessionId,
|
|
356
|
+
finalStatus,
|
|
357
|
+
finalReason,
|
|
358
|
+
finalText
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
//#endregion
|
|
364
|
+
//#region src/otel-export.ts
|
|
365
|
+
/**
|
|
366
|
+
* OTEL span exporter — streams LoopTraceEvents to an OTLP/HTTP collector.
|
|
367
|
+
*
|
|
368
|
+
* Reads OTEL_EXPORTER_OTLP_ENDPOINT + OTEL_EXPORTER_OTLP_HEADERS from env
|
|
369
|
+
* when no explicit config is given. Keeps the runtime dep-free from
|
|
370
|
+
* @opentelemetry/sdk-trace-base — minimal OTLP/JSON serializer.
|
|
371
|
+
*
|
|
372
|
+
* The exporter accepts both raw OtelSpan objects and LoopTraceEvents
|
|
373
|
+
* (which get converted to OTLP spans automatically).
|
|
374
|
+
*/
|
|
375
|
+
const SCOPE = {
|
|
376
|
+
name: "@tangle-network/agent-runtime",
|
|
377
|
+
version: "0.83.0"
|
|
378
|
+
};
|
|
379
|
+
/**
|
|
380
|
+
* Current (non-deprecated) OpenTelemetry GenAI semantic-convention keys.
|
|
381
|
+
* Registry: https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/
|
|
382
|
+
* NB: `gen_ai.system` / `gen_ai.usage.prompt_tokens` / `completion_tokens` are
|
|
383
|
+
* DEPRECATED — do not emit them. We use `provider.name` + `input/output_tokens`.
|
|
384
|
+
*/
|
|
385
|
+
const GEN_AI = {
|
|
386
|
+
operation: "gen_ai.operation.name",
|
|
387
|
+
agentName: "gen_ai.agent.name",
|
|
388
|
+
conversationId: "gen_ai.conversation.id",
|
|
389
|
+
inputTokens: "gen_ai.usage.input_tokens",
|
|
390
|
+
outputTokens: "gen_ai.usage.output_tokens"
|
|
391
|
+
};
|
|
392
|
+
/**
|
|
393
|
+
* Create an OTEL exporter. Returns undefined when no endpoint is configured.
|
|
394
|
+
*/
|
|
395
|
+
function createOtelExporter(config) {
|
|
396
|
+
const resolvedEndpoint = config?.endpoint ?? (typeof process !== "undefined" ? process.env.OTEL_EXPORTER_OTLP_ENDPOINT : void 0);
|
|
397
|
+
if (!resolvedEndpoint) return void 0;
|
|
398
|
+
const endpoint = resolvedEndpoint;
|
|
399
|
+
const headers = config?.headers ?? parseHeadersFromEnv();
|
|
400
|
+
const batchSize = config?.batchSize ?? 64;
|
|
401
|
+
const flushIntervalMs = config?.flushIntervalMs ?? 5e3;
|
|
402
|
+
const serviceName = config?.serviceName ?? "agent-runtime";
|
|
403
|
+
const resourceAttrs = config?.resourceAttributes ?? {};
|
|
404
|
+
const pending = [];
|
|
405
|
+
let timer;
|
|
406
|
+
let stopped = false;
|
|
407
|
+
const exporter = {
|
|
408
|
+
exportSpan(span) {
|
|
409
|
+
if (stopped) return;
|
|
410
|
+
pending.push(span);
|
|
411
|
+
if (pending.length >= batchSize) doFlush();
|
|
412
|
+
},
|
|
413
|
+
async flush() {
|
|
414
|
+
await doFlush();
|
|
415
|
+
},
|
|
416
|
+
async shutdown() {
|
|
417
|
+
stopped = true;
|
|
418
|
+
if (timer !== void 0) {
|
|
419
|
+
clearInterval(timer);
|
|
420
|
+
timer = void 0;
|
|
421
|
+
}
|
|
422
|
+
await doFlush();
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
timer = setInterval(() => {
|
|
426
|
+
if (pending.length > 0) doFlush();
|
|
427
|
+
}, flushIntervalMs);
|
|
428
|
+
if (typeof timer === "object" && "unref" in timer) timer.unref();
|
|
429
|
+
async function doFlush() {
|
|
430
|
+
if (pending.length === 0) return;
|
|
431
|
+
const batch = pending.splice(0);
|
|
432
|
+
const body = { resourceSpans: [{
|
|
433
|
+
resource: { attributes: toAttributes({
|
|
434
|
+
"service.name": serviceName,
|
|
435
|
+
...resourceAttrs
|
|
436
|
+
}) },
|
|
437
|
+
scopeSpans: [{
|
|
438
|
+
scope: SCOPE,
|
|
439
|
+
spans: batch
|
|
440
|
+
}]
|
|
441
|
+
}] };
|
|
442
|
+
const url = `${endpoint.replace(/\/+$/, "")}/v1/traces`;
|
|
443
|
+
try {
|
|
444
|
+
await fetch(url, {
|
|
445
|
+
method: "POST",
|
|
446
|
+
headers: {
|
|
447
|
+
"content-type": "application/json",
|
|
448
|
+
...headers
|
|
449
|
+
},
|
|
450
|
+
body: JSON.stringify(body)
|
|
451
|
+
});
|
|
452
|
+
} catch {}
|
|
453
|
+
}
|
|
454
|
+
return exporter;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Convert a LoopTraceEvent into an OtelSpan for export.
|
|
458
|
+
*/
|
|
459
|
+
function loopEventToOtelSpan(event, traceId, parentSpanId) {
|
|
460
|
+
const spanId = generateSpanId();
|
|
461
|
+
const attrs = {
|
|
462
|
+
"loop.event_kind": event.kind,
|
|
463
|
+
"loop.run_id": event.runId
|
|
464
|
+
};
|
|
465
|
+
for (const [k, v] of Object.entries(event.payload)) if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") attrs[`loop.${k}`] = v;
|
|
466
|
+
const ts = msToNs(event.timestamp);
|
|
467
|
+
return {
|
|
468
|
+
traceId: padTraceId(traceId),
|
|
469
|
+
spanId,
|
|
470
|
+
parentSpanId: parentSpanId ? padSpanId(parentSpanId) : void 0,
|
|
471
|
+
name: event.kind,
|
|
472
|
+
kind: 1,
|
|
473
|
+
startTimeUnixNano: ts,
|
|
474
|
+
endTimeUnixNano: ts,
|
|
475
|
+
attributes: toAttributes(attrs),
|
|
476
|
+
status: { code: 1 }
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Build a single flat OtelSpan whose attribute keys are emitted VERBATIM — no
|
|
481
|
+
* `loop.` namespace. Use for non-loop spans (e.g. the intelligence per-run
|
|
482
|
+
* span) whose keys ARE the downstream contract (`gen_ai.request.model`,
|
|
483
|
+
* `tangle.sessionId`) and are exact-matched by readers. `loopEventToOtelSpan`
|
|
484
|
+
* namespaces payload keys because loop payload fields are free-form; do not
|
|
485
|
+
* reuse it for spans with contract keys.
|
|
486
|
+
*/
|
|
487
|
+
function flatOtelSpan(name, attributes, traceId, timestampMs, parentSpanId, endTimestampMs = timestampMs) {
|
|
488
|
+
const start = msToNs(timestampMs);
|
|
489
|
+
const end = msToNs(Math.max(timestampMs, endTimestampMs));
|
|
490
|
+
return {
|
|
491
|
+
traceId: padTraceId(traceId),
|
|
492
|
+
spanId: generateSpanId(),
|
|
493
|
+
parentSpanId: parentSpanId ? padSpanId(parentSpanId) : void 0,
|
|
494
|
+
name,
|
|
495
|
+
kind: 1,
|
|
496
|
+
startTimeUnixNano: start,
|
|
497
|
+
endTimeUnixNano: end,
|
|
498
|
+
attributes: toAttributes(attributes),
|
|
499
|
+
status: { code: 1 }
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function eventTimestampMs(event) {
|
|
503
|
+
if ("timestamp" in event && typeof event.timestamp === "string") {
|
|
504
|
+
const parsed = Date.parse(event.timestamp);
|
|
505
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
506
|
+
}
|
|
507
|
+
return Date.now();
|
|
508
|
+
}
|
|
509
|
+
function serialized(value) {
|
|
510
|
+
try {
|
|
511
|
+
return JSON.stringify(value) ?? String(value);
|
|
512
|
+
} catch {
|
|
513
|
+
return String(value);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function mcpIdentity(toolName) {
|
|
517
|
+
if (!toolName.startsWith("mcp__")) return {};
|
|
518
|
+
const [, server, ...toolParts] = toolName.split("__");
|
|
519
|
+
return {
|
|
520
|
+
...server ? { server } : {},
|
|
521
|
+
...toolParts.length > 0 ? { tool: toolParts.join("__") } : {}
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
/** Convert normalized runtime events into lossless, redacted child spans. */
|
|
525
|
+
function buildRuntimeEventOtelSpans(events, traceId, parentSpanId, options = {}) {
|
|
526
|
+
return events.map((event) => {
|
|
527
|
+
const sanitized = sanitizeRuntimeStreamEvent(event, options);
|
|
528
|
+
const safe = options.redact ? options.redact(sanitized) : sanitized;
|
|
529
|
+
const record = safe && typeof safe === "object" && !Array.isArray(safe) ? safe : { value: safe };
|
|
530
|
+
const attrs = {
|
|
531
|
+
"tangle.runtime.event_type": event.type,
|
|
532
|
+
"tangle.runtime.event": serialized(record)
|
|
533
|
+
};
|
|
534
|
+
let name = `tangle.runtime.${event.type}`;
|
|
535
|
+
if (event.type === "tool_call" || event.type === "tool_result") {
|
|
536
|
+
name = `agent.${event.type}`;
|
|
537
|
+
attrs["tool.name"] = event.toolName;
|
|
538
|
+
if (event.toolCallId) attrs["tool.call_id"] = event.toolCallId;
|
|
539
|
+
const mcp = mcpIdentity(event.toolName);
|
|
540
|
+
if (mcp.server) attrs["mcp.server"] = mcp.server;
|
|
541
|
+
if (mcp.tool) attrs["mcp.tool.name"] = mcp.tool;
|
|
542
|
+
const payload = event.type === "tool_call" ? record.args : record.result;
|
|
543
|
+
if (payload !== void 0) attrs[event.type === "tool_call" ? "tool.input" : "tool.output"] = serialized(payload);
|
|
544
|
+
} else if (event.type === "llm_call") {
|
|
545
|
+
name = "gen_ai.client.inference";
|
|
546
|
+
attrs["gen_ai.request.model"] = event.model;
|
|
547
|
+
if (event.tokensIn !== void 0) attrs["gen_ai.usage.input_tokens"] = event.tokensIn;
|
|
548
|
+
if (event.tokensOut !== void 0) attrs["gen_ai.usage.output_tokens"] = event.tokensOut;
|
|
549
|
+
if (event.costUsd !== void 0) attrs["tangle.cost.usd"] = event.costUsd;
|
|
550
|
+
if (event.latencyMs !== void 0) attrs["tangle.latency_ms"] = event.latencyMs;
|
|
551
|
+
if (event.finishReason !== void 0) attrs["gen_ai.response.finish_reasons"] = event.finishReason;
|
|
552
|
+
} else if (event.type === "backend_error") {
|
|
553
|
+
attrs["error.type"] = event.error?.kind ?? "backend";
|
|
554
|
+
attrs["error.message"] = event.message;
|
|
555
|
+
} else if (event.type === "final") {
|
|
556
|
+
attrs["tangle.outcome.status"] = event.status;
|
|
557
|
+
attrs["tangle.outcome.reason"] = event.reason;
|
|
558
|
+
if (event.error) {
|
|
559
|
+
attrs["error.type"] = event.error.kind;
|
|
560
|
+
attrs["error.message"] = event.error.message;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
const startMs = eventTimestampMs(event);
|
|
564
|
+
const endMs = event.type === "llm_call" && event.latencyMs !== void 0 && Number.isFinite(event.latencyMs) ? startMs + event.latencyMs : startMs;
|
|
565
|
+
const span = flatOtelSpan(name, attrs, traceId, startMs, parentSpanId, endMs);
|
|
566
|
+
if (event.type === "backend_error" || event.type === "final" && event.status !== "completed") span.status = {
|
|
567
|
+
code: 2,
|
|
568
|
+
message: attrs["error.message"]?.toString() ?? event.type
|
|
569
|
+
};
|
|
570
|
+
return span;
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Build a nested, real-duration OTLP span tree for ONE loop run from its full
|
|
575
|
+
* ordered `LoopTraceEvent` stream. Unlike `loopEventToOtelSpan` (one flat,
|
|
576
|
+
* zero-duration span per event), this reconstructs the topology hierarchy a
|
|
577
|
+
* GenAI trace viewer renders natively:
|
|
578
|
+
*
|
|
579
|
+
* loop (invoke_workflow)
|
|
580
|
+
* └─ loop.round[k] (invoke_workflow) ← tangle.loop.move.{kind,width,rationale}
|
|
581
|
+
* ├─ loop.iteration[i] (invoke_agent) ← gen_ai.agent.name + usage + verdict + placement
|
|
582
|
+
* └─ …
|
|
583
|
+
*
|
|
584
|
+
* Attributes follow the current GenAI semconv (`gen_ai.*`) where they apply and
|
|
585
|
+
* a namespaced `tangle.loop.*` / `tangle.cost.usd` extension for topology /
|
|
586
|
+
* verdict / placement / cost (not yet standardized). Pure: feed it a buffered
|
|
587
|
+
* per-runId event array (e.g. flushed on `loop.ended`) and export the result.
|
|
588
|
+
*/
|
|
589
|
+
function buildLoopOtelSpans(events, traceId, rootParentSpanId) {
|
|
590
|
+
const tid = padTraceId(traceId);
|
|
591
|
+
return buildLoopSpanNodes(events).map((node) => ({
|
|
592
|
+
traceId: tid,
|
|
593
|
+
spanId: node.spanId,
|
|
594
|
+
parentSpanId: node.parentSpanId ? padSpanId(node.parentSpanId) : rootParentSpanId ? padSpanId(rootParentSpanId) : void 0,
|
|
595
|
+
name: node.name,
|
|
596
|
+
kind: 1,
|
|
597
|
+
startTimeUnixNano: msToNs(node.startMs),
|
|
598
|
+
endTimeUnixNano: msToNs(node.endMs),
|
|
599
|
+
attributes: toAttributes(node.attrs),
|
|
600
|
+
status: { code: node.error ? 2 : 1 }
|
|
601
|
+
}));
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Sink-neutral core behind {@link buildLoopOtelSpans}: reconstruct the
|
|
605
|
+
* loop → round → branch span tree from one run's ordered `LoopTraceEvent`
|
|
606
|
+
* stream. Consumed by the OTEL mapper above and by the MCP delegation
|
|
607
|
+
* journal's compact trace tee — one topology reconstruction, two sinks.
|
|
608
|
+
* Tolerates partial streams (a run that never reached `loop.ended` closes
|
|
609
|
+
* at the last observed event's timestamp).
|
|
610
|
+
*/
|
|
611
|
+
function buildLoopSpanNodes(events) {
|
|
612
|
+
if (events.length === 0) return [];
|
|
613
|
+
const out = [];
|
|
614
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
615
|
+
const str = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
|
|
616
|
+
const rec = (v) => v && typeof v === "object" ? v : {};
|
|
617
|
+
const started = events.find((e) => e.kind === "loop.started");
|
|
618
|
+
const ended = events.find((e) => e.kind === "loop.ended");
|
|
619
|
+
const runId = events[0]?.runId ?? "";
|
|
620
|
+
const rootStart = started?.timestamp ?? events[0].timestamp;
|
|
621
|
+
const rootEnd = ended?.timestamp ?? events[events.length - 1].timestamp;
|
|
622
|
+
const rootId = generateSpanId();
|
|
623
|
+
const make = (spanId, parentSpanId, name, kind, startMs, endMs, attrs, error = false) => ({
|
|
624
|
+
spanId,
|
|
625
|
+
parentSpanId,
|
|
626
|
+
name,
|
|
627
|
+
kind,
|
|
628
|
+
startMs,
|
|
629
|
+
endMs,
|
|
630
|
+
attrs,
|
|
631
|
+
error
|
|
632
|
+
});
|
|
633
|
+
const sp = rec(started?.payload);
|
|
634
|
+
const rootAttrs = {
|
|
635
|
+
[GEN_AI.operation]: "invoke_workflow",
|
|
636
|
+
[GEN_AI.conversationId]: runId,
|
|
637
|
+
"tangle.run.id": runId,
|
|
638
|
+
"tangle.loop.driver": str(sp.driver) ?? "driver"
|
|
639
|
+
};
|
|
640
|
+
if (Array.isArray(sp.agentRunNames) && sp.agentRunNames.length > 0) {
|
|
641
|
+
rootAttrs["tangle.loop.agents"] = sp.agentRunNames.map(String).join(",");
|
|
642
|
+
rootAttrs["tangle.subject.key"] = String(sp.agentRunNames[0]);
|
|
643
|
+
}
|
|
644
|
+
if (ended) {
|
|
645
|
+
const ep = rec(ended.payload);
|
|
646
|
+
const win = num(ep.winnerIterationIndex);
|
|
647
|
+
if (win !== void 0) rootAttrs["tangle.loop.winner.iteration_index"] = win;
|
|
648
|
+
const cost = num(ep.totalCostUsd);
|
|
649
|
+
if (cost !== void 0) rootAttrs["tangle.cost.usd"] = cost;
|
|
650
|
+
const dur = num(ep.durationMs);
|
|
651
|
+
if (dur !== void 0) rootAttrs["tangle.loop.duration_ms"] = dur;
|
|
652
|
+
const iters = num(ep.iterations);
|
|
653
|
+
if (iters !== void 0) rootAttrs["tangle.loop.iterations"] = iters;
|
|
654
|
+
}
|
|
655
|
+
out.push(make(rootId, void 0, "loop", "loop", rootStart, rootEnd, rootAttrs));
|
|
656
|
+
const iterStartTs = /* @__PURE__ */ new Map();
|
|
657
|
+
const placementByIdx = /* @__PURE__ */ new Map();
|
|
658
|
+
let currentRoundId;
|
|
659
|
+
let pendingRound;
|
|
660
|
+
const flushRound = (endMs) => {
|
|
661
|
+
if (!pendingRound) return;
|
|
662
|
+
out.push(make(pendingRound.id, rootId, "loop.round", "round", pendingRound.start, endMs, pendingRound.attrs));
|
|
663
|
+
pendingRound = void 0;
|
|
664
|
+
};
|
|
665
|
+
for (const e of events) {
|
|
666
|
+
const p = rec(e.payload);
|
|
667
|
+
switch (e.kind) {
|
|
668
|
+
case "loop.plan": {
|
|
669
|
+
flushRound(e.timestamp);
|
|
670
|
+
const id = generateSpanId();
|
|
671
|
+
const roundIdx = num(p.roundIndex) ?? 0;
|
|
672
|
+
const attrs = {
|
|
673
|
+
[GEN_AI.operation]: "invoke_workflow",
|
|
674
|
+
"tangle.loop.round.index": roundIdx,
|
|
675
|
+
"tangle.loop.move.kind": str(p.moveKind) ?? "unknown",
|
|
676
|
+
"tangle.loop.move.round": roundIdx,
|
|
677
|
+
"tangle.loop.move.width": num(p.plannedCount) ?? 0
|
|
678
|
+
};
|
|
679
|
+
const r = str(p.rationale);
|
|
680
|
+
if (r) attrs["tangle.loop.move.rationale"] = r;
|
|
681
|
+
const parent = num(p.parentIndex);
|
|
682
|
+
if (parent !== void 0) attrs["tangle.loop.move.parent_index"] = parent;
|
|
683
|
+
if (Array.isArray(p.childIndices) && p.childIndices.length > 0) attrs["tangle.loop.move.child_indices"] = p.childIndices.map(String).join(",");
|
|
684
|
+
pendingRound = {
|
|
685
|
+
id,
|
|
686
|
+
start: e.timestamp,
|
|
687
|
+
attrs
|
|
688
|
+
};
|
|
689
|
+
currentRoundId = id;
|
|
690
|
+
break;
|
|
691
|
+
}
|
|
692
|
+
case "loop.iteration.started": {
|
|
693
|
+
const idx = num(p.iterationIndex);
|
|
694
|
+
if (idx !== void 0) iterStartTs.set(idx, e.timestamp);
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
case "loop.iteration.dispatch": {
|
|
698
|
+
const idx = num(p.iterationIndex);
|
|
699
|
+
if (idx === void 0) break;
|
|
700
|
+
const place = {};
|
|
701
|
+
const kind = str(p.placement);
|
|
702
|
+
if (kind) place["tangle.loop.placement.kind"] = kind;
|
|
703
|
+
const sid = str(p.sandboxId);
|
|
704
|
+
if (sid) place["tangle.sandbox.id"] = sid;
|
|
705
|
+
const fid = str(p.fleetId);
|
|
706
|
+
if (fid) place["tangle.fleet.id"] = fid;
|
|
707
|
+
const mid = str(p.machineId);
|
|
708
|
+
if (mid) place["tangle.machine.id"] = mid;
|
|
709
|
+
placementByIdx.set(idx, place);
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
case "loop.iteration.ended": {
|
|
713
|
+
const idx = num(p.iterationIndex) ?? 0;
|
|
714
|
+
const start = iterStartTs.get(idx) ?? e.timestamp;
|
|
715
|
+
const err = str(p.error);
|
|
716
|
+
const attrs = {
|
|
717
|
+
[GEN_AI.operation]: "invoke_agent",
|
|
718
|
+
"tangle.loop.iteration.index": idx
|
|
719
|
+
};
|
|
720
|
+
const agent = str(p.agentRunName);
|
|
721
|
+
if (agent) attrs[GEN_AI.agentName] = agent;
|
|
722
|
+
const tu = rec(p.tokenUsage);
|
|
723
|
+
const inTok = num(tu.input);
|
|
724
|
+
if (inTok !== void 0) attrs[GEN_AI.inputTokens] = inTok;
|
|
725
|
+
const outTok = num(tu.output);
|
|
726
|
+
if (outTok !== void 0) attrs[GEN_AI.outputTokens] = outTok;
|
|
727
|
+
const cost = num(p.costUsd);
|
|
728
|
+
if (cost !== void 0) attrs["tangle.cost.usd"] = cost;
|
|
729
|
+
const verdict = rec(p.verdict);
|
|
730
|
+
if (typeof verdict.valid === "boolean") attrs["tangle.loop.verdict.valid"] = verdict.valid;
|
|
731
|
+
const score = num(verdict.score);
|
|
732
|
+
if (score !== void 0) attrs["tangle.loop.verdict.score"] = score;
|
|
733
|
+
if (err) attrs["tangle.loop.error"] = err;
|
|
734
|
+
const gid = num(p.groupId);
|
|
735
|
+
if (gid !== void 0) attrs["tangle.loop.iteration.group_id"] = gid;
|
|
736
|
+
const par = num(p.parentIndex);
|
|
737
|
+
if (par !== void 0) attrs["tangle.loop.iteration.parent_index"] = par;
|
|
738
|
+
const dur = num(p.durationMs);
|
|
739
|
+
if (dur !== void 0) attrs["tangle.loop.iteration.duration_ms"] = dur;
|
|
740
|
+
const preview = str(p.outputPreview);
|
|
741
|
+
if (preview) attrs["tangle.loop.iteration.output_preview"] = preview;
|
|
742
|
+
Object.assign(attrs, placementByIdx.get(idx) ?? {});
|
|
743
|
+
out.push(make(generateSpanId(), currentRoundId ?? rootId, "loop.iteration", "branch", start, e.timestamp, attrs, err !== void 0));
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
746
|
+
case "loop.decision":
|
|
747
|
+
if (pendingRound) {
|
|
748
|
+
const dec = str(p.decision);
|
|
749
|
+
if (dec) pendingRound.attrs["tangle.loop.decision"] = dec;
|
|
750
|
+
flushRound(e.timestamp);
|
|
751
|
+
}
|
|
752
|
+
currentRoundId = void 0;
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
flushRound(rootEnd);
|
|
757
|
+
return out;
|
|
758
|
+
}
|
|
759
|
+
function parseHeadersFromEnv() {
|
|
760
|
+
if (typeof process === "undefined") return {};
|
|
761
|
+
const raw = process.env.OTEL_EXPORTER_OTLP_HEADERS;
|
|
762
|
+
if (!raw) return {};
|
|
763
|
+
const out = {};
|
|
764
|
+
for (const pair of raw.split(",")) {
|
|
765
|
+
const eq = pair.indexOf("=");
|
|
766
|
+
if (eq < 0) continue;
|
|
767
|
+
const key = pair.slice(0, eq).trim();
|
|
768
|
+
const value = pair.slice(eq + 1).trim();
|
|
769
|
+
if (key) out[key] = value;
|
|
770
|
+
}
|
|
771
|
+
return out;
|
|
772
|
+
}
|
|
773
|
+
function toAttributes(record) {
|
|
774
|
+
return Object.entries(record).flatMap(([key, value]) => {
|
|
775
|
+
if (typeof value === "number" && !Number.isFinite(value)) return [];
|
|
776
|
+
return [{
|
|
777
|
+
key,
|
|
778
|
+
value: typeof value === "number" ? Number.isInteger(value) ? { intValue: value.toString() } : { doubleValue: value } : typeof value === "boolean" ? { boolValue: value } : { stringValue: value }
|
|
779
|
+
}];
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
function msToNs(ms) {
|
|
783
|
+
return (BigInt(Math.floor(Number.isFinite(ms) ? ms : Date.now())) * 1000000n).toString();
|
|
784
|
+
}
|
|
785
|
+
function padSpanId(id) {
|
|
786
|
+
return id.replace(/-/g, "").slice(0, 16).padEnd(16, "0");
|
|
787
|
+
}
|
|
788
|
+
function padTraceId(id) {
|
|
789
|
+
return id.replace(/-/g, "").slice(0, 32).padEnd(32, "0");
|
|
790
|
+
}
|
|
791
|
+
function generateSpanId() {
|
|
792
|
+
const bytes = /* @__PURE__ */ new Uint8Array(8);
|
|
793
|
+
if (typeof globalThis.crypto?.getRandomValues === "function") globalThis.crypto.getRandomValues(bytes);
|
|
794
|
+
else for (let i = 0; i < 8; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
795
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
796
|
+
}
|
|
797
|
+
/** Wire version the eval-runs ingest enforces (X-Tangle-Wire-Version + body). */
|
|
798
|
+
const INTELLIGENCE_WIRE_VERSION = "2026-05-26.v1";
|
|
799
|
+
const DEFAULT_INTELLIGENCE_BASE = "https://intelligence.tangle.tools";
|
|
800
|
+
/**
|
|
801
|
+
* Ship self-improvement eval-run events to Tangle Intelligence. Unlike the
|
|
802
|
+
* best-effort span exporter, this RESOLVES with the ingest verdict (accepted /
|
|
803
|
+
* rejected per event) so a consumer's loop can assert its provenance landed.
|
|
804
|
+
* Throws only on a missing key or network failure.
|
|
805
|
+
*/
|
|
806
|
+
async function exportEvalRuns(events, config) {
|
|
807
|
+
if (events.length === 0) return {
|
|
808
|
+
ok: true,
|
|
809
|
+
status: 0,
|
|
810
|
+
accepted: 0,
|
|
811
|
+
rejected: []
|
|
812
|
+
};
|
|
813
|
+
const apiKey = config?.apiKey ?? (typeof process !== "undefined" ? process.env.TANGLE_API_KEY : void 0);
|
|
814
|
+
if (!apiKey) throw new Error("exportEvalRuns: apiKey required (pass config.apiKey or set TANGLE_API_KEY)");
|
|
815
|
+
const url = `${(config?.base ?? (typeof process !== "undefined" ? process.env.TANGLE_INTELLIGENCE_URL : void 0) ?? DEFAULT_INTELLIGENCE_BASE).replace(/\/+$/, "")}/v1/ingest/eval-runs`;
|
|
816
|
+
const res = await fetch(url, {
|
|
817
|
+
method: "POST",
|
|
818
|
+
headers: {
|
|
819
|
+
"content-type": "application/json",
|
|
820
|
+
authorization: `Bearer ${apiKey}`,
|
|
821
|
+
"X-Tangle-Wire-Version": INTELLIGENCE_WIRE_VERSION,
|
|
822
|
+
...config?.idempotencyKey ? { "Idempotency-Key": config.idempotencyKey } : {}
|
|
823
|
+
},
|
|
824
|
+
body: JSON.stringify({
|
|
825
|
+
wireVersion: INTELLIGENCE_WIRE_VERSION,
|
|
826
|
+
events
|
|
827
|
+
})
|
|
828
|
+
});
|
|
829
|
+
let parsed = {};
|
|
830
|
+
try {
|
|
831
|
+
parsed = await res.json();
|
|
832
|
+
} catch {}
|
|
833
|
+
return {
|
|
834
|
+
ok: res.ok,
|
|
835
|
+
status: res.status,
|
|
836
|
+
accepted: parsed.accepted ?? (res.ok ? events.length : 0),
|
|
837
|
+
rejected: parsed.rejected ?? []
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
//#endregion
|
|
841
|
+
export { createOtelExporter as a, loopEventToOtelSpan as c, sanitizeAgentRuntimeEvent as d, sanitizeKnowledgeReadinessReport as f, buildRuntimeEventOtelSpans as i, createRuntimeEventCollector as l, buildLoopOtelSpans as n, exportEvalRuns as o, sanitizeRuntimeStreamEvent as p, buildLoopSpanNodes as r, flatOtelSpan as s, INTELLIGENCE_WIRE_VERSION as t, createRuntimeStreamEventCollector as u };
|
|
842
|
+
|
|
843
|
+
//# sourceMappingURL=otel-export-D34IQW1C.js.map
|