@tangle-network/agent-eval 0.120.2 → 0.120.3

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 (171) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/analyst/index.d.ts +3111 -0
  3. package/dist/analyst/index.js +403 -0
  4. package/dist/analyst/index.js.map +1 -0
  5. package/dist/authenticity/index.d.ts +161 -0
  6. package/dist/authenticity/index.js +215 -0
  7. package/dist/authenticity/index.js.map +1 -0
  8. package/dist/belief-state/index.d.ts +1301 -0
  9. package/dist/belief-state/index.js +2152 -0
  10. package/dist/belief-state/index.js.map +1 -0
  11. package/dist/benchmarks/index.d.ts +974 -0
  12. package/dist/benchmarks/index.js +60 -0
  13. package/dist/benchmarks/index.js.map +1 -0
  14. package/dist/builder-eval/index.d.ts +695 -0
  15. package/dist/builder-eval/index.js +366 -0
  16. package/dist/builder-eval/index.js.map +1 -0
  17. package/dist/campaign/index.d.ts +7454 -0
  18. package/dist/campaign/index.js +272 -0
  19. package/dist/campaign/index.js.map +1 -0
  20. package/dist/chunk-3CDFMEMO.js +3878 -0
  21. package/dist/chunk-3CDFMEMO.js.map +1 -0
  22. package/dist/chunk-3RF76KTD.js +84 -0
  23. package/dist/chunk-3RF76KTD.js.map +1 -0
  24. package/dist/chunk-3XH4Y2SS.js +750 -0
  25. package/dist/chunk-3XH4Y2SS.js.map +1 -0
  26. package/dist/chunk-3YYRZDON.js +45 -0
  27. package/dist/chunk-3YYRZDON.js.map +1 -0
  28. package/dist/chunk-5BYTIDZ7.js +550 -0
  29. package/dist/chunk-5BYTIDZ7.js.map +1 -0
  30. package/dist/chunk-5CVUPHJ4.js +2668 -0
  31. package/dist/chunk-5CVUPHJ4.js.map +1 -0
  32. package/dist/chunk-ARU2PZFM.js +312 -0
  33. package/dist/chunk-ARU2PZFM.js.map +1 -0
  34. package/dist/chunk-BOD4O7OF.js +40 -0
  35. package/dist/chunk-BOD4O7OF.js.map +1 -0
  36. package/dist/chunk-CVJP5TMD.js +766 -0
  37. package/dist/chunk-CVJP5TMD.js.map +1 -0
  38. package/dist/chunk-DPZAEKA6.js +880 -0
  39. package/dist/chunk-DPZAEKA6.js.map +1 -0
  40. package/dist/chunk-DTJ6QUQB.js +131 -0
  41. package/dist/chunk-DTJ6QUQB.js.map +1 -0
  42. package/dist/chunk-GGE4NNQT.js +65 -0
  43. package/dist/chunk-GGE4NNQT.js.map +1 -0
  44. package/dist/chunk-H5UD2323.js +286 -0
  45. package/dist/chunk-H5UD2323.js.map +1 -0
  46. package/dist/chunk-HHWE3POT.js +94 -0
  47. package/dist/chunk-HHWE3POT.js.map +1 -0
  48. package/dist/chunk-HKUCJ437.js +787 -0
  49. package/dist/chunk-HKUCJ437.js.map +1 -0
  50. package/dist/chunk-JHCHEVET.js +274 -0
  51. package/dist/chunk-JHCHEVET.js.map +1 -0
  52. package/dist/chunk-K4DBDHLK.js +158 -0
  53. package/dist/chunk-K4DBDHLK.js.map +1 -0
  54. package/dist/chunk-K6N6XJJX.js +306 -0
  55. package/dist/chunk-K6N6XJJX.js.map +1 -0
  56. package/dist/chunk-MA6HLL3S.js +65 -0
  57. package/dist/chunk-MA6HLL3S.js.map +1 -0
  58. package/dist/chunk-MAZ26DC7.js +99 -0
  59. package/dist/chunk-MAZ26DC7.js.map +1 -0
  60. package/dist/chunk-MOXWMGPC.js +577 -0
  61. package/dist/chunk-MOXWMGPC.js.map +1 -0
  62. package/dist/chunk-NJC7U437.js +626 -0
  63. package/dist/chunk-NJC7U437.js.map +1 -0
  64. package/dist/chunk-NMN4WGSJ.js +1030 -0
  65. package/dist/chunk-NMN4WGSJ.js.map +1 -0
  66. package/dist/chunk-NPCTHQIO.js +91 -0
  67. package/dist/chunk-NPCTHQIO.js.map +1 -0
  68. package/dist/chunk-ONWEPEDO.js +57 -0
  69. package/dist/chunk-ONWEPEDO.js.map +1 -0
  70. package/dist/chunk-OYZAPX5G.js +1526 -0
  71. package/dist/chunk-OYZAPX5G.js.map +1 -0
  72. package/dist/chunk-P5MGQ2FY.js +7958 -0
  73. package/dist/chunk-P5MGQ2FY.js.map +1 -0
  74. package/dist/chunk-PC4UYEBM.js +166 -0
  75. package/dist/chunk-PC4UYEBM.js.map +1 -0
  76. package/dist/chunk-PJQFMIOX.js +1182 -0
  77. package/dist/chunk-PJQFMIOX.js.map +1 -0
  78. package/dist/chunk-PXD6ZFNY.js +1107 -0
  79. package/dist/chunk-PXD6ZFNY.js.map +1 -0
  80. package/dist/chunk-PXE2VKMX.js +140 -0
  81. package/dist/chunk-PXE2VKMX.js.map +1 -0
  82. package/dist/chunk-PZ5AY32C.js +10 -0
  83. package/dist/chunk-PZ5AY32C.js.map +1 -0
  84. package/dist/chunk-QBRSJK47.js +622 -0
  85. package/dist/chunk-QBRSJK47.js.map +1 -0
  86. package/dist/chunk-S3UZOQ5Y.js +328 -0
  87. package/dist/chunk-S3UZOQ5Y.js.map +1 -0
  88. package/dist/chunk-SQQED7ZH.js +998 -0
  89. package/dist/chunk-SQQED7ZH.js.map +1 -0
  90. package/dist/chunk-SYV364BL.js +1266 -0
  91. package/dist/chunk-SYV364BL.js.map +1 -0
  92. package/dist/chunk-T4SQEITX.js +95 -0
  93. package/dist/chunk-T4SQEITX.js.map +1 -0
  94. package/dist/chunk-TT4KNT67.js +124 -0
  95. package/dist/chunk-TT4KNT67.js.map +1 -0
  96. package/dist/chunk-U5CHZ5M3.js +357 -0
  97. package/dist/chunk-U5CHZ5M3.js.map +1 -0
  98. package/dist/chunk-ULOKLHIQ.js +1937 -0
  99. package/dist/chunk-ULOKLHIQ.js.map +1 -0
  100. package/dist/chunk-VI2UW6B6.js +162 -0
  101. package/dist/chunk-VI2UW6B6.js.map +1 -0
  102. package/dist/chunk-VQMK5FMP.js +247 -0
  103. package/dist/chunk-VQMK5FMP.js.map +1 -0
  104. package/dist/chunk-VSMTAMNK.js +53 -0
  105. package/dist/chunk-VSMTAMNK.js.map +1 -0
  106. package/dist/chunk-VZSRQ272.js +149 -0
  107. package/dist/chunk-VZSRQ272.js.map +1 -0
  108. package/dist/chunk-WW2A73HW.js +159 -0
  109. package/dist/chunk-WW2A73HW.js.map +1 -0
  110. package/dist/chunk-X4UCIOTZ.js +136 -0
  111. package/dist/chunk-X4UCIOTZ.js.map +1 -0
  112. package/dist/chunk-XJYR7XFV.js +317 -0
  113. package/dist/chunk-XJYR7XFV.js.map +1 -0
  114. package/dist/chunk-ZET2UAYW.js +89 -0
  115. package/dist/chunk-ZET2UAYW.js.map +1 -0
  116. package/dist/chunk-ZMXDQ4K7.js +870 -0
  117. package/dist/chunk-ZMXDQ4K7.js.map +1 -0
  118. package/dist/chunk-ZZUXHH3R.js +99 -0
  119. package/dist/chunk-ZZUXHH3R.js.map +1 -0
  120. package/dist/cli.d.ts +1 -0
  121. package/dist/cli.js +112 -0
  122. package/dist/cli.js.map +1 -0
  123. package/dist/contract/index.d.ts +4972 -0
  124. package/dist/contract/index.js +1654 -0
  125. package/dist/contract/index.js.map +1 -0
  126. package/dist/control.d.ts +1013 -0
  127. package/dist/control.js +34 -0
  128. package/dist/control.js.map +1 -0
  129. package/dist/fuzz.d.ts +759 -0
  130. package/dist/fuzz.js +714 -0
  131. package/dist/fuzz.js.map +1 -0
  132. package/dist/hosted/index.d.ts +730 -0
  133. package/dist/hosted/index.js +14 -0
  134. package/dist/hosted/index.js.map +1 -0
  135. package/dist/index.d.ts +16780 -0
  136. package/dist/index.js +12168 -0
  137. package/dist/index.js.map +1 -0
  138. package/dist/matrix/index.d.ts +155 -0
  139. package/dist/matrix/index.js +8 -0
  140. package/dist/matrix/index.js.map +1 -0
  141. package/dist/meta-eval/index.d.ts +1030 -0
  142. package/dist/meta-eval/index.js +417 -0
  143. package/dist/meta-eval/index.js.map +1 -0
  144. package/dist/multishot/index.d.ts +579 -0
  145. package/dist/multishot/index.js +589 -0
  146. package/dist/multishot/index.js.map +1 -0
  147. package/dist/openapi.json +992 -0
  148. package/dist/pipelines/index.d.ts +567 -0
  149. package/dist/pipelines/index.js +515 -0
  150. package/dist/pipelines/index.js.map +1 -0
  151. package/dist/reporting.d.ts +1277 -0
  152. package/dist/reporting.js +48 -0
  153. package/dist/reporting.js.map +1 -0
  154. package/dist/rl.d.ts +4092 -0
  155. package/dist/rl.js +1724 -0
  156. package/dist/rl.js.map +1 -0
  157. package/dist/run-campaign-75RTPVV5.js +14 -0
  158. package/dist/run-campaign-75RTPVV5.js.map +1 -0
  159. package/dist/storyboard/index.d.ts +279 -0
  160. package/dist/storyboard/index.js +767 -0
  161. package/dist/storyboard/index.js.map +1 -0
  162. package/dist/trace-attributes.d.ts +52 -0
  163. package/dist/trace-attributes.js +62 -0
  164. package/dist/trace-attributes.js.map +1 -0
  165. package/dist/traces.d.ts +2343 -0
  166. package/dist/traces.js +249 -0
  167. package/dist/traces.js.map +1 -0
  168. package/dist/wire/index.d.ts +1252 -0
  169. package/dist/wire/index.js +81 -0
  170. package/dist/wire/index.js.map +1 -0
  171. package/package.json +1 -1
@@ -0,0 +1,2668 @@
1
+ import {
2
+ callLlm,
3
+ costReceiptFromLlm,
4
+ costReceiptFromLlmError,
5
+ maximumChargeForLlmRequest
6
+ } from "./chunk-NJC7U437.js";
7
+ import {
8
+ CostLedger
9
+ } from "./chunk-ZMXDQ4K7.js";
10
+ import {
11
+ buildTraceAnalystTools,
12
+ runTraceAnalysisLoop
13
+ } from "./chunk-PXD6ZFNY.js";
14
+ import {
15
+ validateAgentProfileCell
16
+ } from "./chunk-XJYR7XFV.js";
17
+ import {
18
+ canonicalize
19
+ } from "./chunk-VSMTAMNK.js";
20
+ import {
21
+ ValidationError
22
+ } from "./chunk-ONWEPEDO.js";
23
+
24
+ // src/analyst/ax-service.ts
25
+ import { ai } from "@ax-llm/ax";
26
+ var configuredModels = /* @__PURE__ */ new WeakMap();
27
+ function createAnalystAi(config) {
28
+ const model = config.model.trim();
29
+ if (!model) throw new TypeError("createAnalystAi: model must be a non-empty string");
30
+ const args = {
31
+ name: config.provider ?? "openai",
32
+ apiKey: config.apiKey,
33
+ ...config.baseUrl ? { apiURL: config.baseUrl } : {},
34
+ ...config.headers ? { headers: config.headers } : {},
35
+ config: { model }
36
+ };
37
+ const service = ai(args);
38
+ configuredModels.set(service, model);
39
+ return service;
40
+ }
41
+ function getConfiguredAnalystModel(service) {
42
+ return configuredModels.get(service);
43
+ }
44
+ function resolveAnalystModel(service, override) {
45
+ if (override !== void 0) {
46
+ const model2 = override.trim();
47
+ if (!model2) throw new TypeError("createTraceAnalystKind: model must be a non-empty string");
48
+ return model2;
49
+ }
50
+ const model = getConfiguredAnalystModel(service)?.trim();
51
+ if (!model) {
52
+ throw new TypeError(
53
+ "createTraceAnalystKind: model is required for Ax services not created by createAnalystAi()"
54
+ );
55
+ }
56
+ return model;
57
+ }
58
+
59
+ // src/analyst/types.ts
60
+ import { createHash } from "crypto";
61
+ function computeFindingId(input) {
62
+ const basis = JSON.stringify({
63
+ a: input.analyst_id,
64
+ r: input.area,
65
+ s: input.subject ?? "",
66
+ c: normalizeClaim(input.id_basis ?? input.claim)
67
+ });
68
+ return `f_${createHash("sha256").update(basis).digest("hex").slice(0, 20)}`;
69
+ }
70
+ function normalizeClaim(c) {
71
+ return c.toLowerCase().replace(/\s+/g, " ").replace(/[.!?;:,]+$/g, "").trim();
72
+ }
73
+ function makeFinding(init) {
74
+ const { id_basis, produced_at, ...rest } = init;
75
+ return {
76
+ schema_version: "1.0.0",
77
+ finding_id: computeFindingId({
78
+ analyst_id: rest.analyst_id,
79
+ area: rest.area,
80
+ subject: rest.subject,
81
+ claim: rest.claim,
82
+ id_basis
83
+ }),
84
+ produced_at: produced_at ?? (/* @__PURE__ */ new Date()).toISOString(),
85
+ ...rest
86
+ };
87
+ }
88
+
89
+ // src/analyst/finding-subject.ts
90
+ import { z } from "zod";
91
+ var FINDING_SUBJECT_KINDS = [
92
+ "knowledge.wiki",
93
+ "knowledge.claim",
94
+ "knowledge.raw",
95
+ "knowledge.stale",
96
+ "system-prompt",
97
+ "skill",
98
+ "tool-doc",
99
+ "new-tool",
100
+ "mcp",
101
+ "hook",
102
+ "subagent",
103
+ "workflow",
104
+ "rollout-policy",
105
+ "agent-profile",
106
+ "code",
107
+ "rag",
108
+ "memory",
109
+ "scaffolding",
110
+ "output-schema",
111
+ "websearch.outdated",
112
+ "prior-run-summary",
113
+ "cluster"
114
+ ];
115
+ function parseFindingSubject(raw) {
116
+ if (raw === null || raw === void 0) return null;
117
+ const trimmed = raw.trim();
118
+ if (trimmed.length === 0) return null;
119
+ const wiki = trimmed.match(
120
+ /^agent-knowledge:wiki:([a-z0-9][a-z0-9-]*)(?:#([a-z0-9][a-z0-9-]*))?$/
121
+ );
122
+ if (wiki)
123
+ return { kind: "knowledge.wiki", slug: wiki[1], ...wiki[2] ? { heading: wiki[2] } : {} };
124
+ const claim = trimmed.match(/^agent-knowledge:claim:(.+)$/);
125
+ if (claim && claim[1].trim().length > 0)
126
+ return { kind: "knowledge.claim", topic: claim[1].trim() };
127
+ const raw_ = trimmed.match(/^agent-knowledge:raw:(.+)$/);
128
+ if (raw_ && raw_[1].trim().length > 0)
129
+ return { kind: "knowledge.raw", sourceId: raw_[1].trim() };
130
+ const stale = trimmed.match(/^agent-knowledge:stale:([a-z0-9][a-z0-9-]*)$/);
131
+ if (stale) return { kind: "knowledge.stale", slug: stale[1] };
132
+ const sp = trimmed.match(/^system-prompt:(.+)$/);
133
+ if (sp && sp[1].trim().length > 0) return { kind: "system-prompt", section: sp[1].trim() };
134
+ const skill = trimmed.match(/^skill:([a-z0-9][a-z0-9_.-]*)$/);
135
+ if (skill) return { kind: "skill", name: skill[1] };
136
+ const tdAspect = trimmed.match(/^tool-doc:([a-z0-9][a-z0-9_-]*):(.+)$/);
137
+ if (tdAspect && tdAspect[2].trim().length > 0) {
138
+ return { kind: "tool-doc", tool: tdAspect[1], aspect: tdAspect[2].trim() };
139
+ }
140
+ const td = trimmed.match(/^tool-doc:([a-z0-9][a-z0-9_-]*)$/);
141
+ if (td) return { kind: "tool-doc", tool: td[1] };
142
+ const nt = trimmed.match(/^new-tool:([a-z0-9][a-z0-9_-]*)$/);
143
+ if (nt) return { kind: "new-tool", name: nt[1] };
144
+ const mcp = trimmed.match(/^mcp:([a-z0-9][a-z0-9_.-]*)(?::([a-z0-9][a-z0-9_.-]*))?$/);
145
+ if (mcp) {
146
+ return { kind: "mcp", server: mcp[1], ...mcp[2] ? { tool: mcp[2] } : {} };
147
+ }
148
+ const hook = trimmed.match(/^hook:([a-z0-9][a-z0-9_.-]*)$/);
149
+ if (hook) return { kind: "hook", name: hook[1] };
150
+ const subagent = trimmed.match(/^subagent:([a-z0-9][a-z0-9_.-]*)$/);
151
+ if (subagent) return { kind: "subagent", name: subagent[1] };
152
+ const workflow = trimmed.match(/^workflow:([a-z0-9][a-z0-9_.-]*)$/);
153
+ if (workflow) return { kind: "workflow", name: workflow[1] };
154
+ const rolloutPolicy = trimmed.match(/^rollout-policy:(.+)$/);
155
+ if (rolloutPolicy && rolloutPolicy[1].trim().length > 0) {
156
+ return { kind: "rollout-policy", field: rolloutPolicy[1].trim() };
157
+ }
158
+ const agentProfile = trimmed.match(/^agent-profile:(.+)$/);
159
+ if (agentProfile && agentProfile[1].trim().length > 0) {
160
+ return { kind: "agent-profile", field: agentProfile[1].trim() };
161
+ }
162
+ const code = trimmed.match(/^code:(.+)$/);
163
+ if (code && code[1].trim().length > 0) return { kind: "code", path: code[1].trim() };
164
+ const rag = trimmed.match(/^rag:([a-z0-9][a-z0-9_-]*):(.+)$/);
165
+ if (rag && rag[2].trim().length > 0) {
166
+ return { kind: "rag", corpus: rag[1], docId: rag[2].trim() };
167
+ }
168
+ const mem = trimmed.match(/^memory:(.+)$/);
169
+ if (mem && mem[1].trim().length > 0) return { kind: "memory", key: mem[1].trim() };
170
+ const sc = trimmed.match(/^scaffolding:(.+)$/);
171
+ if (sc && sc[1].trim().length > 0) return { kind: "scaffolding", concern: sc[1].trim() };
172
+ const os = trimmed.match(/^output-schema:(.+)$/);
173
+ if (os && os[1].trim().length > 0) return { kind: "output-schema", field: os[1].trim() };
174
+ const ws = trimmed.match(/^websearch:outdated:(.+)$/);
175
+ if (ws && ws[1].trim().length > 0) return { kind: "websearch.outdated", topic: ws[1].trim() };
176
+ const prs = trimmed.match(/^prior-run-summary:(.+)$/);
177
+ if (prs && prs[1].trim().length > 0) return { kind: "prior-run-summary", topic: prs[1].trim() };
178
+ if (/^[a-z0-9][a-z0-9._-]*$/.test(trimmed) && trimmed.length <= 80) {
179
+ return { kind: "cluster", label: trimmed };
180
+ }
181
+ return null;
182
+ }
183
+ function renderFindingSubject(s) {
184
+ switch (s.kind) {
185
+ case "knowledge.wiki":
186
+ return s.heading ? `agent-knowledge:wiki:${s.slug}#${s.heading}` : `agent-knowledge:wiki:${s.slug}`;
187
+ case "knowledge.claim":
188
+ return `agent-knowledge:claim:${s.topic}`;
189
+ case "knowledge.raw":
190
+ return `agent-knowledge:raw:${s.sourceId}`;
191
+ case "knowledge.stale":
192
+ return `agent-knowledge:stale:${s.slug}`;
193
+ case "system-prompt":
194
+ return `system-prompt:${s.section}`;
195
+ case "skill":
196
+ return `skill:${s.name}`;
197
+ case "tool-doc":
198
+ return s.aspect ? `tool-doc:${s.tool}:${s.aspect}` : `tool-doc:${s.tool}`;
199
+ case "new-tool":
200
+ return `new-tool:${s.name}`;
201
+ case "mcp":
202
+ return s.tool ? `mcp:${s.server}:${s.tool}` : `mcp:${s.server}`;
203
+ case "hook":
204
+ return `hook:${s.name}`;
205
+ case "subagent":
206
+ return `subagent:${s.name}`;
207
+ case "workflow":
208
+ return `workflow:${s.name}`;
209
+ case "rollout-policy":
210
+ return `rollout-policy:${s.field}`;
211
+ case "agent-profile":
212
+ return `agent-profile:${s.field}`;
213
+ case "code":
214
+ return `code:${s.path}`;
215
+ case "rag":
216
+ return `rag:${s.corpus}:${s.docId}`;
217
+ case "memory":
218
+ return `memory:${s.key}`;
219
+ case "scaffolding":
220
+ return `scaffolding:${s.concern}`;
221
+ case "output-schema":
222
+ return `output-schema:${s.field}`;
223
+ case "websearch.outdated":
224
+ return `websearch:outdated:${s.topic}`;
225
+ case "prior-run-summary":
226
+ return `prior-run-summary:${s.topic}`;
227
+ case "cluster":
228
+ return s.label;
229
+ }
230
+ }
231
+ var FINDING_SUBJECT_SYNTAX = {
232
+ "knowledge.wiki": "agent-knowledge:wiki:<slug>[#<heading>]",
233
+ "knowledge.claim": "agent-knowledge:claim:<topic>",
234
+ "knowledge.raw": "agent-knowledge:raw:<source-id>",
235
+ "knowledge.stale": "agent-knowledge:stale:<slug>",
236
+ "system-prompt": "system-prompt:<section>",
237
+ skill: "skill:<name>",
238
+ "tool-doc": "tool-doc:<tool>[:<aspect>]",
239
+ "new-tool": "new-tool:<name>",
240
+ mcp: "mcp:<server>[:<tool>]",
241
+ hook: "hook:<name>",
242
+ subagent: "subagent:<name>",
243
+ workflow: "workflow:<name>",
244
+ "rollout-policy": "rollout-policy:<field>",
245
+ "agent-profile": "agent-profile:<field>",
246
+ code: "code:<path>",
247
+ rag: "rag:<corpus>:<doc-id>",
248
+ memory: "memory:<key>",
249
+ scaffolding: "scaffolding:<concern>",
250
+ "output-schema": "output-schema:<field>",
251
+ "websearch.outdated": "websearch:outdated:<topic>",
252
+ "prior-run-summary": "prior-run-summary:<topic>",
253
+ cluster: "<lowercase-cluster-label>"
254
+ };
255
+ var FINDING_SUBJECT_PURPOSE = {
256
+ "knowledge.wiki": "create or update a wiki page",
257
+ "knowledge.claim": "draft a claim or relation",
258
+ "knowledge.raw": "curate a raw source",
259
+ "knowledge.stale": "mark a stale page",
260
+ "system-prompt": "revise a system-prompt section",
261
+ skill: "create or revise a skill",
262
+ "tool-doc": "revise a tool contract",
263
+ "new-tool": "propose a new tool",
264
+ mcp: "revise an MCP server or tool",
265
+ hook: "revise a lifecycle hook",
266
+ subagent: "revise a delegated agent",
267
+ workflow: "revise an orchestration workflow",
268
+ "rollout-policy": "revise budget, sampling, or stop policy",
269
+ "agent-profile": "revise another AgentProfile field",
270
+ code: "revise an implementation path",
271
+ rag: "ingest or correct a RAG document",
272
+ memory: "invalidate or set memory",
273
+ scaffolding: "revise preconditions, retries, or verification",
274
+ "output-schema": "constrain the output shape",
275
+ "websearch.outdated": "identify a stale web result",
276
+ "prior-run-summary": "identify a stale prior-run summary",
277
+ cluster: "name one failure cluster"
278
+ };
279
+ function renderFindingSubjectGrammar(kinds) {
280
+ return [
281
+ "Subjects MUST match one of these forms \u2014 anything else is rejected at parse time:",
282
+ ...kinds.map((kind) => ` ${FINDING_SUBJECT_SYNTAX[kind]} \u2014 ${FINDING_SUBJECT_PURPOSE[kind]}`),
283
+ "Runtime ids are lowercase [a-z0-9_.-]+. Topics, keys, paths, and sections are free-form and trimmed."
284
+ ].join("\n");
285
+ }
286
+ var FINDING_SUBJECT_GRAMMAR_PROMPT = renderFindingSubjectGrammar(FINDING_SUBJECT_KINDS);
287
+ var KIND_EXPECTED_SUBJECTS = {
288
+ "failure-mode": ["cluster"],
289
+ "knowledge-gap": [
290
+ "knowledge.wiki",
291
+ "knowledge.claim",
292
+ "knowledge.raw",
293
+ "knowledge.stale",
294
+ "tool-doc",
295
+ "system-prompt",
296
+ "skill",
297
+ "mcp",
298
+ "subagent",
299
+ "workflow",
300
+ "memory",
301
+ "websearch.outdated",
302
+ "prior-run-summary"
303
+ ],
304
+ "knowledge-poisoning": [
305
+ "knowledge.wiki",
306
+ "knowledge.claim",
307
+ "knowledge.raw",
308
+ "tool-doc",
309
+ "system-prompt",
310
+ "skill",
311
+ "mcp",
312
+ "hook",
313
+ "memory",
314
+ "websearch.outdated",
315
+ "prior-run-summary"
316
+ ],
317
+ improvement: [
318
+ "system-prompt",
319
+ "skill",
320
+ "tool-doc",
321
+ "new-tool",
322
+ "mcp",
323
+ "hook",
324
+ "subagent",
325
+ "workflow",
326
+ "rollout-policy",
327
+ "agent-profile",
328
+ "code",
329
+ "rag",
330
+ "memory",
331
+ "scaffolding",
332
+ "output-schema",
333
+ "knowledge.wiki",
334
+ "knowledge.claim"
335
+ ]
336
+ };
337
+ function findingSubjectGrammarPromptFor(kindId) {
338
+ const kinds = KIND_EXPECTED_SUBJECTS[kindId];
339
+ if (!kinds) throw new Error(`unknown analyst kind: ${kindId}`);
340
+ return renderFindingSubjectGrammar(kinds);
341
+ }
342
+ var FindingSubjectStringSchema = z.string().refine((s) => parseFindingSubject(s) !== null, {
343
+ message: "subject does not match the finding-subject grammar"
344
+ });
345
+
346
+ // src/analyst/parse-tolerant.ts
347
+ function stripCodeFences(text) {
348
+ const t = text.trim();
349
+ const fence = /^```[a-zA-Z0-9]*\s*\n?([\s\S]*?)\n?```$/;
350
+ const m = t.match(fence);
351
+ return m ? m[1].trim() : t;
352
+ }
353
+ function dropTrailingCommas(s) {
354
+ return s.replace(/,(\s*[}\]])/g, "$1");
355
+ }
356
+ function coerceJson(text) {
357
+ const candidate = dropTrailingCommas(stripCodeFences(text));
358
+ try {
359
+ return JSON.parse(candidate);
360
+ } catch {
361
+ return void 0;
362
+ }
363
+ }
364
+ function coerceToFindingRows(raw) {
365
+ let value = raw;
366
+ if (typeof value === "string") {
367
+ const parsed = coerceJson(value);
368
+ if (parsed === void 0) return [];
369
+ value = parsed;
370
+ }
371
+ if (Array.isArray(value)) return value;
372
+ if (value && typeof value === "object") {
373
+ const inner = value.findings;
374
+ if (Array.isArray(inner)) return inner;
375
+ return [value];
376
+ }
377
+ return [];
378
+ }
379
+
380
+ // src/analyst/finding-signature.ts
381
+ import { z as z2 } from "zod";
382
+ var ANALYST_SEVERITIES = ["critical", "high", "medium", "low", "info"];
383
+ var RawAnalystEvidenceSchema = z2.object({
384
+ uri: z2.string().trim().min(1).max(2e3),
385
+ excerpt: z2.string().max(2e3).optional()
386
+ }).strict();
387
+ var RawAnalystFindingBaseShape = {
388
+ severity: z2.enum(ANALYST_SEVERITIES),
389
+ claim: z2.string().min(1).max(2e3),
390
+ subject: z2.string().max(400).refine((subject) => parseFindingSubject(subject) !== null, {
391
+ message: "subject does not match the finding-subject grammar"
392
+ }).optional(),
393
+ confidence: z2.number().min(0).max(1),
394
+ rationale: z2.string().max(4e3).optional(),
395
+ recommended_action: z2.string().max(2e3).optional()
396
+ };
397
+ var RawAnalystFindingSchema = z2.object({
398
+ ...RawAnalystFindingBaseShape,
399
+ evidence_uri: z2.string().min(1).max(2e3).refine((uri) => uri.trim().length > 0, { message: "evidence_uri must not be blank" }),
400
+ evidence_excerpt: z2.string().max(2e3).optional()
401
+ }).strict();
402
+ var CanonicalRawAnalystFindingObjectSchema = z2.object({
403
+ ...RawAnalystFindingBaseShape,
404
+ evidence: z2.array(RawAnalystEvidenceSchema).min(1)
405
+ }).strict();
406
+ var CanonicalRawAnalystFindingSchema = z2.preprocess(
407
+ normalizeLegacySingleCitation,
408
+ CanonicalRawAnalystFindingObjectSchema
409
+ );
410
+ var RAW_FINDING_SCHEMA_PROMPT = `Each finding MUST be a strict JSON object with:
411
+ - severity: "critical" | "high" | "medium" | "low" | "info"
412
+ - claim: one-sentence statement (max 2000 chars)
413
+ - subject?: one exact subject form listed by this kind; omit rather than guess
414
+ - evidence: REQUIRED non-empty array of {"uri": string, "excerpt"?: string}. Use real identifiers with span://, event://, artifact://, metric://, or finding://. Include a short exact quote in excerpt when available. If nothing is citable, do not emit the finding.
415
+ - confidence: number 0..1 (0.9+ exact evidence; 0.6-0.8 inferred pattern; <0.5 speculative)
416
+ - rationale?: one or two reasoning sentences
417
+ - recommended_action?: concrete imperative change; omit for descriptive findings
418
+
419
+ Unknown fields are rejected. Do not emit area; the factory assigns it. Emit [] when there are no findings. Never fabricate evidence.`;
420
+ function evidenceRefsFromRawFinding(finding) {
421
+ return finding.evidence.map(({ uri, excerpt }) => ({
422
+ kind: evidenceKindFromUri(uri),
423
+ uri,
424
+ excerpt
425
+ }));
426
+ }
427
+ function parseRawFinding(row, log) {
428
+ return parseFindingWithSchema(RawAnalystFindingSchema, row, log);
429
+ }
430
+ function parseCanonicalRawFinding(row, log) {
431
+ return parseFindingWithSchema(CanonicalRawAnalystFindingSchema, row, log);
432
+ }
433
+ function parseFindingWithSchema(schema, row, log) {
434
+ const result = schema.safeParse(row);
435
+ if (result.success) return result.data;
436
+ if (typeof row === "string") {
437
+ const coerced = coerceJson(row);
438
+ if (coerced !== void 0) {
439
+ const retry = schema.safeParse(coerced);
440
+ if (retry.success) return retry.data;
441
+ }
442
+ }
443
+ log?.("finding rejected: schema failure", {
444
+ issues: result.error.issues.map((i) => ({
445
+ path: i.path.join("."),
446
+ code: i.code,
447
+ message: i.message
448
+ }))
449
+ });
450
+ return null;
451
+ }
452
+ function normalizeLegacySingleCitation(value) {
453
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return value;
454
+ const row = value;
455
+ if ("evidence" in row) {
456
+ if (!("evidence_uri" in row) || !Array.isArray(row.evidence)) return value;
457
+ const { evidence, evidence_uri: uri2, evidence_excerpt: excerpt2, ...rest2 } = row;
458
+ let alreadyPresent = false;
459
+ const mergedEvidence = evidence.map((citation) => {
460
+ if (citation === null || typeof citation !== "object" || Array.isArray(citation) || citation.uri !== uri2) {
461
+ return citation;
462
+ }
463
+ alreadyPresent = true;
464
+ const current = citation;
465
+ return excerpt2 === void 0 || current.excerpt !== void 0 ? citation : { ...current, excerpt: excerpt2 };
466
+ });
467
+ return {
468
+ ...rest2,
469
+ evidence: alreadyPresent ? mergedEvidence : [...evidence, { uri: uri2, ...excerpt2 === void 0 ? {} : { excerpt: excerpt2 } }]
470
+ };
471
+ }
472
+ if (!("evidence_uri" in row)) return value;
473
+ const { evidence_uri: uri, evidence_excerpt: excerpt, ...rest } = row;
474
+ return {
475
+ ...rest,
476
+ evidence: [{ uri, ...excerpt === void 0 ? {} : { excerpt } }]
477
+ };
478
+ }
479
+ function toLegacyRawAnalystFinding(finding) {
480
+ const primaryEvidence = finding.evidence[0];
481
+ if (!primaryEvidence) {
482
+ throw new TypeError("Canonical raw analyst findings require at least one evidence citation");
483
+ }
484
+ const { evidence: _evidence, ...rest } = finding;
485
+ return {
486
+ ...rest,
487
+ evidence_uri: primaryEvidence.uri,
488
+ ...primaryEvidence.excerpt === void 0 ? {} : { evidence_excerpt: primaryEvidence.excerpt }
489
+ };
490
+ }
491
+ function applyLegacyRawFindingCallback(finding, callback, log) {
492
+ const callbackResult = callback(toLegacyRawAnalystFinding(finding));
493
+ if (callbackResult === null) return null;
494
+ const parsed = parseCanonicalRawFinding(callbackResult, log);
495
+ if (!parsed) return null;
496
+ const primaryEvidence = parsed.evidence[0];
497
+ if (!primaryEvidence) {
498
+ throw new TypeError("Canonical raw analyst findings require at least one evidence citation");
499
+ }
500
+ const remainingEvidence = finding.evidence.slice(1).filter((citation) => citation.uri !== primaryEvidence.uri);
501
+ return {
502
+ ...parsed,
503
+ evidence: [primaryEvidence, ...remainingEvidence]
504
+ };
505
+ }
506
+ function evidenceKindFromUri(uri) {
507
+ if (uri.startsWith("span://")) return "span";
508
+ if (uri.startsWith("event://")) return "event";
509
+ if (uri.startsWith("finding://")) return "finding";
510
+ if (uri.startsWith("metric://")) return "metric";
511
+ return "artifact";
512
+ }
513
+
514
+ // src/analyst/structure-findings.ts
515
+ var SYSTEM = [
516
+ "You convert a free-form trace-analysis report into a STRICT JSON array of findings.",
517
+ "Output ONLY the JSON array \u2014 no prose, no code fences.",
518
+ RAW_FINDING_SCHEMA_PROMPT,
519
+ "Omit subject when the report does not contain an exact valid locus.",
520
+ "If the report asserts NO problems, output exactly []."
521
+ ].join(" ");
522
+ function buildRows(raw, opts) {
523
+ const rows = coerceToFindingRows(raw);
524
+ const out = [];
525
+ for (const row of rows) {
526
+ const parsed = parseCanonicalRawFinding(row);
527
+ if (!parsed) continue;
528
+ const callbackProcessed = opts.processRow ? applyLegacyRawFindingCallback(parsed, opts.processRow) : parsed;
529
+ if (!callbackProcessed) continue;
530
+ const processed = opts.processCanonicalRow ? opts.processCanonicalRow(callbackProcessed) : callbackProcessed;
531
+ if (!processed) continue;
532
+ out.push(
533
+ makeFinding({
534
+ analyst_id: opts.analystId,
535
+ area: opts.area,
536
+ subject: processed.subject,
537
+ claim: processed.claim,
538
+ rationale: processed.rationale,
539
+ severity: processed.severity,
540
+ confidence: processed.confidence,
541
+ evidence_refs: evidenceRefsFromRawFinding(processed),
542
+ recommended_action: processed.recommended_action,
543
+ ...opts.findingMetadata ? { metadata: { ...opts.findingMetadata } } : {}
544
+ })
545
+ );
546
+ }
547
+ return out;
548
+ }
549
+ async function structureFindings(opts) {
550
+ const maxReasks = opts.maxReasks ?? 1;
551
+ if (!Number.isSafeInteger(maxReasks) || maxReasks < 0) {
552
+ throw new RangeError("structureFindings: maxReasks must be a non-negative safe integer");
553
+ }
554
+ const llm = { baseUrl: opts.baseUrl, apiKey: opts.apiKey, fetch: opts.fetchImpl };
555
+ const costLedger = opts.costLedger ?? new CostLedger();
556
+ let user = `TRACE-ANALYSIS REPORT:
557
+ ${opts.report}
558
+
559
+ Return the findings JSON array.`;
560
+ for (let attempt = 0; attempt <= maxReasks; attempt++) {
561
+ const request = {
562
+ model: opts.model,
563
+ messages: [
564
+ { role: "system", content: SYSTEM },
565
+ { role: "user", content: user }
566
+ ],
567
+ maxTokens: opts.maxTokens ?? 2e3
568
+ };
569
+ const paid = await costLedger.runPaidCall({
570
+ channel: "analyst",
571
+ phase: opts.costPhase ?? "analyst.structure-findings",
572
+ actor: "structure-findings",
573
+ model: opts.model,
574
+ signal: opts.signal,
575
+ maximumCharge: maximumChargeForLlmRequest(request, llm),
576
+ tags: { ...opts.costTags, analystId: opts.analystId, attempt: String(attempt) },
577
+ execute: (signal, callId) => callLlm(request, { ...llm, signal, idempotencyKey: callId }),
578
+ receipt: costReceiptFromLlm,
579
+ receiptFromError: costReceiptFromLlmError
580
+ });
581
+ if (!paid.succeeded) throw paid.error;
582
+ const res = paid.value;
583
+ const text = res.content.trim();
584
+ const findings = buildRows(text, opts);
585
+ if (findings.length > 0) return { findings, outcome: "ok" };
586
+ if (opts.report.trim().length < 200) return { findings: [], outcome: "ok" };
587
+ user = `${user}
588
+
589
+ That produced no valid findings. The report DOES describe issues \u2014 re-extract them as the strict JSON array described in the system prompt. Output ONLY the array.`;
590
+ }
591
+ return { findings: [], outcome: "extraction_failed" };
592
+ }
593
+
594
+ // src/analyst/ax-cost-service.ts
595
+ function meterAxChatService(ai2, options) {
596
+ assertPositiveInteger(options.maxOutputTokens, "maxOutputTokens");
597
+ const source = ai2;
598
+ if (typeof source.chat !== "function") {
599
+ throw new TypeError("meterAxChatService: Ax service must implement chat()");
600
+ }
601
+ const providerChat = source.chat.bind(ai2);
602
+ const chat = async (request, callOptions = {}) => {
603
+ const boundedRequest = boundOutputTokens(request, options.maxOutputTokens);
604
+ const model = modelName(boundedRequest.model) || options.defaultModel || "";
605
+ const canTurnOffThinking = canDisableThinking(ai2, model);
606
+ const combined = combineSignals(options.signal, callOptions.abortSignal);
607
+ try {
608
+ const paid = await options.ledger.runPaidCall({
609
+ channel: "analyst",
610
+ phase: options.phase ?? "analyst.ax.chat",
611
+ actor: options.actor,
612
+ model,
613
+ tags: options.tags,
614
+ signal: combined.signal,
615
+ maximumCharge: maximumChargeForAxChatRequest(boundedRequest, model),
616
+ execute: async (executionSignal) => {
617
+ const providerOptions = {
618
+ ...callOptions,
619
+ abortSignal: executionSignal,
620
+ retry: { ...callOptions.retry, maxRetries: 0 },
621
+ stream: false,
622
+ showThoughts: false
623
+ };
624
+ if (canTurnOffThinking) providerOptions.thinkingTokenBudget = "none";
625
+ else Reflect.deleteProperty(providerOptions, "thinkingTokenBudget");
626
+ const response = await providerChat(boundedRequest, providerOptions);
627
+ if (response instanceof ReadableStream) {
628
+ throw new Error("meterAxChatService: provider returned a stream after stream:false");
629
+ }
630
+ return response;
631
+ },
632
+ receipt: (response) => costReceiptFromAxResponse(response, model)
633
+ });
634
+ if (!paid.succeeded) throw paid.error;
635
+ return paid.value;
636
+ } finally {
637
+ combined.dispose();
638
+ }
639
+ };
640
+ return new Proxy(ai2, {
641
+ get(target, property) {
642
+ if (property === "chat") return chat;
643
+ const value = Reflect.get(target, property, target);
644
+ return typeof value === "function" ? value.bind(target) : value;
645
+ }
646
+ });
647
+ }
648
+ function maximumChargeForAxChatRequest(request, defaultModel) {
649
+ const model = modelName(request.model) || defaultModel || "";
650
+ const maxTokens = request.modelConfig?.maxTokens;
651
+ const completions = request.modelConfig?.n;
652
+ if (!model || maxTokens === void 0 || completions === void 0) return void 0;
653
+ assertPositiveInteger(maxTokens, "request.modelConfig.maxTokens");
654
+ assertPositiveInteger(completions, "request.modelConfig.n");
655
+ const maximumOutputTokens = maxTokens * completions;
656
+ assertPositiveInteger(maximumOutputTokens, "maximum output tokens");
657
+ if (containsUnboundedOrCacheableContent(request)) return void 0;
658
+ let inputTokens;
659
+ try {
660
+ const pricedRequest = request.model === void 0 ? { ...request, model } : request;
661
+ inputTokens = new TextEncoder().encode(JSON.stringify(pricedRequest)).byteLength * completions;
662
+ } catch {
663
+ return void 0;
664
+ }
665
+ assertPositiveInteger(inputTokens, "maximum input tokens");
666
+ return { model, inputTokens, outputTokens: maximumOutputTokens };
667
+ }
668
+ function boundOutputTokens(request, limit) {
669
+ const requested = request.modelConfig?.maxTokens;
670
+ if (requested !== void 0) {
671
+ assertPositiveInteger(requested, "request.modelConfig.maxTokens");
672
+ }
673
+ const completions = request.modelConfig?.n ?? 1;
674
+ assertPositiveInteger(completions, "request.modelConfig.n");
675
+ const maxTokens = requested === void 0 ? limit : Math.min(requested, limit);
676
+ return {
677
+ ...request,
678
+ modelConfig: { ...request.modelConfig, maxTokens, n: completions }
679
+ };
680
+ }
681
+ function costReceiptFromAxResponse(response, fallbackModel) {
682
+ const usage = response.modelUsage;
683
+ const tokens = usage?.tokens;
684
+ const model = usage?.model || fallbackModel;
685
+ if (!tokens || !validUsage(tokens.promptTokens) || !validUsage(tokens.completionTokens) || !validUsage(tokens.totalTokens) || tokens.totalTokens < tokens.promptTokens + tokens.completionTokens || !validOptionalUsage(tokens.cacheReadTokens) || !validOptionalUsage(tokens.cacheCreationTokens) || !validOptionalUsage(tokens.reasoningTokens) || !validOptionalUsage(tokens.thoughtsTokens)) {
686
+ return {
687
+ model,
688
+ inputTokens: 0,
689
+ outputTokens: 0,
690
+ usageUnknown: true
691
+ };
692
+ }
693
+ const cacheReadTokens = validUsage(tokens.cacheReadTokens) ? tokens.cacheReadTokens : 0;
694
+ const cacheCreationTokens = validUsage(tokens.cacheCreationTokens) ? tokens.cacheCreationTokens : 0;
695
+ const totalCacheTokens = cacheReadTokens + cacheCreationTokens;
696
+ const thoughtsTokens = validUsage(tokens.thoughtsTokens) ? tokens.thoughtsTokens : 0;
697
+ const reasoningTokens = Math.max(
698
+ validUsage(tokens.reasoningTokens) ? tokens.reasoningTokens : 0,
699
+ thoughtsTokens
700
+ );
701
+ const separateReasoningTokens = reasoningTokens > tokens.completionTokens ? reasoningTokens : 0;
702
+ const additionalOutputTokens = Math.max(thoughtsTokens, separateReasoningTokens);
703
+ const outputTokens = tokens.completionTokens + additionalOutputTokens;
704
+ const directTotal = tokens.promptTokens + tokens.completionTokens;
705
+ const validTotals = /* @__PURE__ */ new Set([
706
+ directTotal,
707
+ directTotal + totalCacheTokens,
708
+ directTotal + additionalOutputTokens,
709
+ directTotal + totalCacheTokens + additionalOutputTokens
710
+ ]);
711
+ if (!validTotals.has(tokens.totalTokens)) {
712
+ return {
713
+ model,
714
+ inputTokens: 0,
715
+ outputTokens: 0,
716
+ usageUnknown: true
717
+ };
718
+ }
719
+ return {
720
+ model,
721
+ inputTokens: tokens.promptTokens,
722
+ outputTokens,
723
+ ...reasoningTokens > 0 ? { reasoningTokens } : {},
724
+ ...cacheReadTokens > 0 ? { cachedTokens: cacheReadTokens } : {},
725
+ ...cacheCreationTokens > 0 ? { cacheWriteTokens: cacheCreationTokens } : {}
726
+ };
727
+ }
728
+ function combineSignals(first, second) {
729
+ if (!first) return { signal: second, dispose: () => {
730
+ } };
731
+ if (!second || first === second) return { signal: first, dispose: () => {
732
+ } };
733
+ if (typeof AbortSignal.any === "function") {
734
+ return { signal: AbortSignal.any([first, second]), dispose: () => {
735
+ } };
736
+ }
737
+ const controller = new AbortController();
738
+ const dispose = () => {
739
+ first.removeEventListener("abort", abortFromFirst);
740
+ second.removeEventListener("abort", abortFromSecond);
741
+ };
742
+ const abortFrom = (source) => {
743
+ if (!controller.signal.aborted) controller.abort(source.reason);
744
+ dispose();
745
+ };
746
+ const abortFromFirst = () => abortFrom(first);
747
+ const abortFromSecond = () => abortFrom(second);
748
+ if (first.aborted) abortFrom(first);
749
+ else if (second.aborted) abortFrom(second);
750
+ else {
751
+ first.addEventListener("abort", abortFromFirst, { once: true });
752
+ second.addEventListener("abort", abortFromSecond, { once: true });
753
+ }
754
+ return { signal: controller.signal, dispose };
755
+ }
756
+ function containsUnboundedOrCacheableContent(request) {
757
+ if (request.functions?.some((fn) => fn.cache === true)) return true;
758
+ return request.chatPrompt.some((message) => {
759
+ if (message.cache === true) return true;
760
+ if (!("content" in message) || !Array.isArray(message.content)) return false;
761
+ return message.content.some((part) => part.cache === true || part.type !== "text");
762
+ });
763
+ }
764
+ function modelName(value) {
765
+ return typeof value === "string" ? value : "";
766
+ }
767
+ function canDisableThinking(ai2, model) {
768
+ const namedAi = ai2;
769
+ const serviceName = typeof namedAi.getName === "function" ? namedAi.getName() : "";
770
+ const modelId = model.slice(model.lastIndexOf("/") + 1);
771
+ return serviceName !== "GoogleGeminiAI" || !/^gemini-3(?:[.-]|$)/i.test(modelId);
772
+ }
773
+ function validUsage(value) {
774
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
775
+ }
776
+ function validOptionalUsage(value) {
777
+ return value === void 0 || validUsage(value);
778
+ }
779
+ function assertPositiveInteger(value, field) {
780
+ if (!Number.isSafeInteger(value) || value <= 0) {
781
+ throw new RangeError(`meterAxChatService: ${field} must be a positive integer`);
782
+ }
783
+ }
784
+
785
+ // src/analyst/usage-receipt.ts
786
+ var DEFAULT_USAGE_SETTLEMENT_TIMEOUT_MS = 5e3;
787
+ function usageReceiptFromCostLedger(ledger, filter = "analyst") {
788
+ const resolvedFilter = typeof filter === "string" ? { channel: filter } : filter;
789
+ const summary = ledger.summary(resolvedFilter);
790
+ const receipts = ledger.list(resolvedFilter);
791
+ const hasReasoningUsage = receipts.some((receipt) => receipt.reasoningTokens !== void 0);
792
+ const hasCacheWriteUsage = receipts.some((receipt) => receipt.cacheWriteTokens !== void 0);
793
+ const costUncaptured = summary.pendingCalls > 0 || receipts.some((receipt) => receipt.costUnknown);
794
+ const cost = costUncaptured ? { kind: "uncaptured", usd: null } : receipts.every((receipt) => receipt.actualCostUsd !== void 0) ? { kind: "observed", usd: summary.totalCostUsd } : { kind: "estimated", usd: summary.totalCostUsd };
795
+ return {
796
+ calls: summary.totalCalls + summary.pendingCalls,
797
+ tokens: summary.usageComplete ? {
798
+ input: summary.inputTokens,
799
+ output: summary.outputTokens,
800
+ ...hasReasoningUsage ? { reasoning: summary.reasoningTokens ?? 0 } : {},
801
+ ...summary.cachedTokens > 0 ? { cached: summary.cachedTokens } : {},
802
+ ...hasCacheWriteUsage ? { cacheWrite: summary.cacheWriteTokens ?? 0 } : {}
803
+ } : null,
804
+ cost,
805
+ ...cost.kind === "uncaptured" ? { knownCostUsd: summary.totalCostUsd } : {}
806
+ };
807
+ }
808
+ async function settleUsageReceiptFromCostLedger(ledger, options = {}) {
809
+ const { timeoutMs: requestedTimeoutMs, ...requestedFilter } = options;
810
+ const filter = {
811
+ channel: requestedFilter.channel ?? "analyst",
812
+ ...requestedFilter.phase === void 0 ? {} : { phase: requestedFilter.phase },
813
+ ...requestedFilter.tags === void 0 ? {} : { tags: requestedFilter.tags }
814
+ };
815
+ const timeoutMs = validateUsageSettlementTimeout(requestedTimeoutMs);
816
+ const initial = ledger.summary(filter);
817
+ const waitResult = initial.pendingCalls === 0 ? true : ledger.waitForIdle ? await ledger.waitForIdle({ timeoutMs }) : false;
818
+ const pendingCalls = ledger.summary(filter).pendingCalls;
819
+ return {
820
+ settled: waitResult && pendingCalls === 0,
821
+ pendingCalls,
822
+ receipt: usageReceiptFromCostLedger(ledger, filter)
823
+ };
824
+ }
825
+ function validateUsageSettlementTimeout(timeoutMs) {
826
+ const resolved = timeoutMs ?? DEFAULT_USAGE_SETTLEMENT_TIMEOUT_MS;
827
+ if (!Number.isSafeInteger(resolved) || resolved < 0 || resolved > 2147483647) {
828
+ throw new TypeError(
829
+ "settlementTimeoutMs must be a non-negative safe integer no greater than 2147483647"
830
+ );
831
+ }
832
+ return resolved;
833
+ }
834
+
835
+ // src/analyst/kind-factory.ts
836
+ function createTraceAnalystKind(spec, opts) {
837
+ rejectRemovedKindOptions(spec);
838
+ const version = opts.versionSuffix ? `${spec.version}+${opts.versionSuffix}` : spec.version;
839
+ const model = resolveAnalystModel(opts.ai, opts.model);
840
+ const minimumEvidenceCitations = spec.minimumEvidenceCitations ?? 1;
841
+ if (!Number.isInteger(minimumEvidenceCitations) || minimumEvidenceCitations < 1) {
842
+ throw new TypeError("minimumEvidenceCitations must be a positive integer");
843
+ }
844
+ const settlementTimeoutMs = validateUsageSettlementTimeout(opts.settlementTimeoutMs);
845
+ return {
846
+ id: spec.id,
847
+ description: spec.description,
848
+ inputKind: "trace-store",
849
+ cost: { ...spec.cost, settlement_timeout_ms: settlementTimeoutMs },
850
+ version,
851
+ async analyze(store, ctx) {
852
+ const maxOutputTokens = spec.maxOutputTokens ?? 4096;
853
+ const costLedger = ctx.costLedger ?? new CostLedger(ctx.budgetUsd);
854
+ const costTags = {
855
+ ...ctx.tags ?? {},
856
+ analystId: spec.id,
857
+ ...ctx.correlationId ? { analystRunId: ctx.correlationId } : {}
858
+ };
859
+ const meteredAi = meterAxChatService(opts.ai, {
860
+ ledger: costLedger,
861
+ actor: spec.id,
862
+ maxOutputTokens,
863
+ defaultModel: model,
864
+ phase: ctx.costPhase,
865
+ signal: ctx.signal,
866
+ tags: costTags
867
+ });
868
+ try {
869
+ const tools = spec.buildTools(store);
870
+ const maxSubqueries = spec.subqueries?.maxCalls ?? 0;
871
+ const maxParallel = spec.subqueries?.maxParallel ?? 2;
872
+ const priorContext = renderPriorFindings(ctx.priorFindings);
873
+ const upstreamContext = renderUpstreamFindings(ctx.upstreamFindings);
874
+ const actorDescription = spec.actorDescription.trim() + priorContext + upstreamContext + "\n\n" + RAW_FINDING_SCHEMA_PROMPT + (minimumEvidenceCitations > 1 ? `
875
+
876
+ This kind requires at least ${minimumEvidenceCitations} evidence citations per finding; rows with fewer are rejected.` : "") + "\n\nFirst write `report`: a concise free-form prose diagnosis of what the traces show \u2014 what succeeded, what was suboptimal or failed \u2014 with concrete trace ids and numbers. THEN return the structured `findings` array (it MAY be empty when there is nothing to report).";
877
+ ctx.log?.(`analyst.kind ${spec.id} forward`, {
878
+ max_subqueries: maxSubqueries,
879
+ tool_count: tools.length,
880
+ tags: ctx.tags
881
+ });
882
+ const completed = await runTraceAnalysisLoop({
883
+ id: spec.id,
884
+ description: spec.description,
885
+ prompt: actorDescription,
886
+ question: deriveQuestion(ctx, spec),
887
+ ai: meteredAi,
888
+ model,
889
+ tools,
890
+ findingType: "object",
891
+ maxSubqueries,
892
+ maxParallelSubqueries: maxParallel,
893
+ maxTurns: spec.maxTurns ?? 12,
894
+ maxRuntimeChars: spec.maxRuntimeChars ?? 6e3,
895
+ ...ctx.signal ? { signal: ctx.signal } : {}
896
+ });
897
+ const { report, findings: submittedFindings } = completed;
898
+ const expectedSubjects = KIND_EXPECTED_SUBJECTS[spec.id];
899
+ const out = [];
900
+ const rawRows = submittedFindings;
901
+ let rejectedWrongKind = 0;
902
+ let rejectedInsufficientEvidence = 0;
903
+ const processRow = (parsed) => {
904
+ const postProcessed = spec.postProcess ? applyLegacyRawFindingCallback(
905
+ parsed,
906
+ (row) => spec.postProcess?.(row, ctx) ?? null,
907
+ ctx.log
908
+ ) : parsed;
909
+ if (!postProcessed) return null;
910
+ if (expectedSubjects && postProcessed.subject !== void 0) {
911
+ const parsedSubject = parseFindingSubject(postProcessed.subject);
912
+ if (parsedSubject === null) {
913
+ ctx.log?.("finding rejected: subject failed to parse", {
914
+ kind: spec.id,
915
+ subject: postProcessed.subject
916
+ });
917
+ rejectedWrongKind += 1;
918
+ return null;
919
+ }
920
+ if (!expectedSubjects.includes(parsedSubject.kind)) {
921
+ ctx.log?.("finding rejected: subject variant not allowed for this kind", {
922
+ kind: spec.id,
923
+ subject_kind: parsedSubject.kind,
924
+ subject: postProcessed.subject,
925
+ allowed: expectedSubjects
926
+ });
927
+ rejectedWrongKind += 1;
928
+ return null;
929
+ }
930
+ }
931
+ const distinctEvidenceCitations = new Set(
932
+ postProcessed.evidence.map((citation) => citation.uri.trim())
933
+ ).size;
934
+ if (distinctEvidenceCitations < minimumEvidenceCitations) {
935
+ ctx.log?.("finding rejected: insufficient evidence citations", {
936
+ kind: spec.id,
937
+ required: minimumEvidenceCitations,
938
+ received: postProcessed.evidence.length,
939
+ distinct: distinctEvidenceCitations
940
+ });
941
+ rejectedInsufficientEvidence += 1;
942
+ return null;
943
+ }
944
+ return postProcessed;
945
+ };
946
+ for (const row of rawRows) {
947
+ const parsed = parseCanonicalRawFinding(row, ctx.log);
948
+ if (!parsed) continue;
949
+ const postProcessed = processRow(parsed);
950
+ if (!postProcessed) continue;
951
+ out.push(toAnalystFinding(spec, version, postProcessed));
952
+ }
953
+ ctx.log?.(`analyst.kind ${spec.id} done`, {
954
+ emitted: rawRows.length,
955
+ accepted: out.length,
956
+ rejected_wrong_subject: rejectedWrongKind,
957
+ rejected_insufficient_evidence: rejectedInsufficientEvidence
958
+ });
959
+ if (out.length === 0 && report.trim().length >= 200) {
960
+ if (opts.recovery) {
961
+ const wrongKindBefore = rejectedWrongKind;
962
+ const insufficientEvidenceBefore = rejectedInsufficientEvidence;
963
+ const recovered = await structureFindings({
964
+ report,
965
+ analystId: spec.id,
966
+ area: spec.area,
967
+ model: opts.recovery.model ?? model,
968
+ baseUrl: opts.recovery.baseUrl,
969
+ apiKey: opts.recovery.apiKey,
970
+ fetchImpl: opts.recovery.fetchImpl,
971
+ costLedger,
972
+ costPhase: ctx.costPhase,
973
+ costTags,
974
+ signal: ctx.signal,
975
+ maxTokens: Math.min(maxOutputTokens, 2e3),
976
+ processCanonicalRow: processRow,
977
+ findingMetadata: { kind_version: version }
978
+ });
979
+ out.push(...recovered.findings);
980
+ ctx.log?.(`analyst.kind ${spec.id} recovery`, {
981
+ outcome: recovered.outcome,
982
+ recovered: recovered.findings.length,
983
+ rejected_wrong_subject: rejectedWrongKind - wrongKindBefore,
984
+ rejected_insufficient_evidence: rejectedInsufficientEvidence - insufficientEvidenceBefore
985
+ });
986
+ }
987
+ if (out.length === 0) {
988
+ const fallback = processRow({
989
+ claim: "Analyst produced a diagnosis but no structured findings \u2014 see report.",
990
+ rationale: report.slice(0, 1500),
991
+ severity: "info",
992
+ confidence: 0.3,
993
+ evidence: [{ uri: "report://summary", excerpt: report.slice(0, 2e3) }]
994
+ });
995
+ if (fallback) {
996
+ out.push(toAnalystFinding(spec, version, fallback, { outcome: "extraction_failed" }));
997
+ } else {
998
+ throw new Error(
999
+ `Trace analyst '${spec.id}' produced a substantive report, but no finding satisfied its acceptance rules`
1000
+ );
1001
+ }
1002
+ }
1003
+ }
1004
+ return out;
1005
+ } finally {
1006
+ const usage = await settleUsageReceiptFromCostLedger(costLedger, {
1007
+ tags: {
1008
+ analystId: spec.id,
1009
+ ...ctx.correlationId ? { analystRunId: ctx.correlationId } : {}
1010
+ },
1011
+ timeoutMs: settlementTimeoutMs
1012
+ });
1013
+ if (!usage.settled) {
1014
+ ctx.log?.(`analyst.kind ${spec.id} provider settlement timed out`, {
1015
+ pending_calls: usage.pendingCalls,
1016
+ timeout_ms: settlementTimeoutMs
1017
+ });
1018
+ }
1019
+ ctx.recordUsage?.(usage.receipt);
1020
+ }
1021
+ }
1022
+ };
1023
+ }
1024
+ function rejectRemovedKindOptions(spec) {
1025
+ const supplied = spec;
1026
+ const migrations = [
1027
+ ["recursion", "subqueries"],
1028
+ ["responderDescription", "actorDescription"],
1029
+ ["maxDepth", "subqueries"],
1030
+ ["maxParallelSubagents", "subqueries.maxParallel"],
1031
+ ["subagentDescription", "actorDescription"]
1032
+ ];
1033
+ for (const [removed, replacement] of migrations) {
1034
+ if (removed in supplied) {
1035
+ throw new TypeError(
1036
+ `createTraceAnalystKind: '${removed}' is unsupported; use '${replacement}'`
1037
+ );
1038
+ }
1039
+ }
1040
+ }
1041
+ function deriveQuestion(ctx, spec) {
1042
+ const focus = ctx.tags?.focus?.trim();
1043
+ const task = `Analyze this trace dataset with the available tools and report ${spec.area} findings. ${spec.description}`;
1044
+ return focus ? `${task} Focus: ${focus}.` : task;
1045
+ }
1046
+ function toAnalystFinding(spec, version, raw, metadata = {}) {
1047
+ return makeFinding({
1048
+ analyst_id: spec.id,
1049
+ area: spec.area,
1050
+ subject: raw.subject,
1051
+ claim: raw.claim,
1052
+ rationale: raw.rationale,
1053
+ severity: raw.severity,
1054
+ confidence: raw.confidence,
1055
+ evidence_refs: evidenceRefsFromRawFinding(raw),
1056
+ recommended_action: raw.recommended_action,
1057
+ metadata: { kind_version: version, ...metadata }
1058
+ });
1059
+ }
1060
+ function renderPriorFindings(prior) {
1061
+ if (!prior || prior.length === 0) return "";
1062
+ const MAX_ROWS = 40;
1063
+ const rows = prior.slice(0, MAX_ROWS).map((f) => {
1064
+ const subject = f.subject ? ` [${f.subject}]` : "";
1065
+ return ` - id=${f.finding_id} ${f.severity}${subject} ${truncateForContext(f.claim, 160)}`;
1066
+ });
1067
+ const overflow = prior.length > MAX_ROWS ? `
1068
+ ... +${prior.length - MAX_ROWS} more prior findings (older history truncated)` : "";
1069
+ return [
1070
+ "",
1071
+ "",
1072
+ "PRIOR FINDINGS (from a previous run on related data):",
1073
+ "When the work you do now matches a row below, REUSE the `finding_id` (pass it as `id_basis`) so the cross-run diff stays stable.",
1074
+ "A finding that reappears with no remediation evidence SHOULD raise its `confidence` and may justify a higher `severity`.",
1075
+ ...rows,
1076
+ overflow
1077
+ ].filter(Boolean).join("\n");
1078
+ }
1079
+ function renderUpstreamFindings(upstream) {
1080
+ if (!upstream || upstream.length === 0) return "";
1081
+ const MAX_ROWS = 40;
1082
+ const rows = upstream.slice(0, MAX_ROWS).map((finding) => {
1083
+ const subject = finding.subject ? ` [${finding.subject}]` : "";
1084
+ const action = finding.recommended_action ? ` action=${truncateForContext(finding.recommended_action, 120)}` : "";
1085
+ const evidence = finding.evidence_refs[0] ? ` evidence=${truncateForContext(finding.evidence_refs[0].uri, 120)}` : "";
1086
+ return ` - id=${finding.finding_id} source=${finding.analyst_id} ${finding.severity}${subject} claim=${truncateForContext(finding.claim, 160)}${action}${evidence}`;
1087
+ });
1088
+ const overflow = upstream.length > MAX_ROWS ? `
1089
+ ... +${upstream.length - MAX_ROWS} more upstream findings (truncated)` : "";
1090
+ return [
1091
+ "",
1092
+ "",
1093
+ "UPSTREAM FINDINGS (produced earlier in this same registry run):",
1094
+ "Use these as intermediate evidence. Build on them instead of repeating the same diagnosis, and cite a dependency with `finding://<id>`.",
1095
+ ...rows,
1096
+ overflow
1097
+ ].filter(Boolean).join("\n");
1098
+ }
1099
+ function truncateForContext(s, max) {
1100
+ if (s.length <= max) return s;
1101
+ return `${s.slice(0, max - 1).trimEnd()}\u2026`;
1102
+ }
1103
+
1104
+ // src/analyst/tool-groups.ts
1105
+ var TOOL_NAMES_BY_GROUP = {
1106
+ all: /* @__PURE__ */ new Set(),
1107
+ discovery: /* @__PURE__ */ new Set(["getDatasetOverview", "queryTraces", "countTraces"]),
1108
+ discoveryAndRead: /* @__PURE__ */ new Set([
1109
+ "getDatasetOverview",
1110
+ "queryTraces",
1111
+ "countTraces",
1112
+ "viewTrace",
1113
+ "viewSpans"
1114
+ ]),
1115
+ discoveryAndSearch: /* @__PURE__ */ new Set([
1116
+ "getDatasetOverview",
1117
+ "queryTraces",
1118
+ "countTraces",
1119
+ "searchTrace",
1120
+ "searchSpan"
1121
+ ]),
1122
+ targeted: /* @__PURE__ */ new Set(["getDatasetOverview", "queryTraces", "viewSpans", "searchSpan"])
1123
+ };
1124
+ function buildTraceToolsForGroup(group, store) {
1125
+ const all = buildTraceAnalystTools({ store });
1126
+ if (group === "all") return all;
1127
+ const allow = TOOL_NAMES_BY_GROUP[group];
1128
+ if (!allow) throw new Error(`unknown trace tool group: ${group}`);
1129
+ return all.filter((tool) => allow.has(tool.name));
1130
+ }
1131
+
1132
+ // src/analyst/kinds/failure-mode.ts
1133
+ var subjectGrammar = findingSubjectGrammarPromptFor("failure-mode");
1134
+ var ACTOR_PROMPT = `You are a failure-mode classifier for an OTLP trace dataset. Your job is to identify the **distinct ways agents failed** in this dataset, not to grade individual runs.
1135
+
1136
+ ${subjectGrammar}
1137
+
1138
+ DISCOVERY \u2192 CLUSTER \u2192 CITE protocol:
1139
+
1140
+ 1. Call \`traces.getDatasetOverview({})\` first. Use \`has_errors\`, \`models\`, \`agent_names\`, \`tools\`, and \`sample_trace_ids\` to size the failure surface.
1141
+ 2. Use \`traces.queryTraces({ filters: { has_errors: true }, limit })\` to pull error-bearing traces. Combine with \`traces.countTraces\` to see what fraction of the dataset failed.
1142
+ 3. For each candidate failure cluster, use \`traces.searchTrace\` with regex like \`STATUS_CODE_ERROR\`, \`MaxTurnsExceeded\`, \`assertion\`, \`unauthorized\`, \`timeout\`, \`429\`, \`5\\d\\d\`, the agent's specific error strings, or the names of its tools. Pull one or two representative traces per cluster, **not all** of them.
1143
+ 4. **Cluster, do not enumerate.** Two failures with the same root cause should be ONE finding citing both traces, not two findings. The point of this analyst is to compress N runs into K modes.
1144
+ 5. For each defensible cluster, emit ONE finding. Use a lowercase cluster label matching the subject grammar ("tool-call-loop", "auth-revoked-mid-run", ...). Rate it critical when it blocks the run, high when the run finishes degraded, and medium when it slows convergence. Cite representative spans and include exact error, payload, or contradictory-output quotes. Use confidence 0.85+ when multiple traces show the same shape, 0.6-0.8 for a single-trace inference, and <0.5 for speculation. Keep the imperative fix idea short; the improvement analyst expands it.
1145
+
1146
+ If the dataset has no failures, return an empty findings array \u2014 do NOT pad with low-confidence speculation.
1147
+
1148
+ **Use subqueries over loaded evidence.** After the first scan, load representative span excerpts for each candidate cluster. Then send one bounded \`llmQuery\` per cluster in one batch, including the exact excerpts and asking it to classify the root cause. Subqueries cannot call trace tools. Merge or split clusters yourself from their classifications and the cited source evidence.
1149
+
1150
+ OBSERVABILITY rules:
1151
+ - Each non-final turn must emit at least one \`console.log\` for evidence.
1152
+ - Reuse runtime variables across turns; don't recompute.`;
1153
+ var FAILURE_MODE_KIND_SPEC = {
1154
+ id: "failure-mode",
1155
+ description: "Clusters trace-dataset failures into distinct failure modes with cited evidence and a short recommended action.",
1156
+ area: "failure-mode",
1157
+ version: "1.2.0",
1158
+ actorDescription: ACTOR_PROMPT,
1159
+ buildTools: (store) => buildTraceToolsForGroup("all", store),
1160
+ subqueries: { maxCalls: 8, maxParallel: 4 },
1161
+ maxTurns: 24,
1162
+ cost: { kind: "llm" }
1163
+ };
1164
+
1165
+ // src/analyst/kinds/improvement.ts
1166
+ var subjectGrammar2 = findingSubjectGrammarPromptFor("improvement");
1167
+ var ACTOR_PROMPT2 = `You are a self-improvement analyst. Your job is to propose **concrete, locus-named edits** the agent's runtime should adopt to fix the failure modes, knowledge gaps, and poisonings present in this dataset.
1168
+
1169
+ Upstream analysts have already classified the problems. Your job is to convert each problem into a *change to make* and grade its expected leverage. Each finding is one proposed edit.
1170
+
1171
+ ${subjectGrammar2}
1172
+
1173
+ DISCOVERY \u2192 CANDIDATE-FIXES \u2192 COMPETE \u2192 CITE protocol:
1174
+
1175
+ 1. \`traces.getDatasetOverview({})\` first. Note the agents, tools, and any system-prompt fingerprints (look for the prompt text echoed in early spans).
1176
+ 2. For each high-severity failure pattern, generate 2-3 candidate fixes. Real candidate axes:
1177
+ - **System-prompt edit** \u2014 add an instruction, remove a misleading one, restructure precedence
1178
+ - **Tool description edit** \u2014 rewrite a tool's description so the agent picks it correctly / passes valid args
1179
+ - **New tool** \u2014 add a tool the agent kept emulating in code
1180
+ - **RAG ingestion** \u2014 add a document or correct a stale one
1181
+ - **Memory invalidation** \u2014 clear cached prior-run decisions that no longer apply
1182
+ - **Scaffolding** \u2014 add a precondition check, a retry policy, a turn budget, a verification step
1183
+ - **Output schema** \u2014 narrow the agent's output to forbid the failure shape
1184
+ - **Skill / MCP / hook / subagent** \u2014 change the reusable profile component responsible for the behavior
1185
+ - **Workflow / rollout policy** \u2014 change orchestration, budget, sampling, or stopping behavior
1186
+ - **Code** \u2014 change an implementation path when profile edits cannot repair the behavior
1187
+ 3. **Compare candidate fixes with bounded subqueries.** Load the representative failure excerpts, then send one \`llmQuery\` per candidate-fix axis the same evidence. Ask for likely effect, side effects, and implementation scope. Subqueries cannot call trace tools; trace ids alone are insufficient context.
1188
+ 4. After the comparisons return, **pick the winning candidate per cluster** based on expected effect and risk, then emit ONE finding. Keep the alternatives and rejection reasons in the rationale so the recommendation is auditable.
1189
+ 5. **Cross-reference upstream findings.** Cite prior failure-mode or knowledge-gap findings as \`finding://<prior-finding-id>\`. This builds the dependency graph that lets the dashboard show "fix #X resolves failure modes A, B, C."
1190
+
1191
+ For each winning recommendation, emit ONE finding. Use one exact locus from the subject grammar and state the edit in one sentence. Match leverage to the source failure's severity; use medium for quality-of-life changes and info for cleanup with no behavioral effect. Cite the targeted \`finding://<id>\` when available and the most representative span when useful. Quote the problem being fixed. Use confidence 0.85+ for a mechanical fix to a well-evidenced failure, 0.6-0.8 when judgment is required, and <0.5 for speculation. Explain in at most two sentences why this candidate beat its alternatives. The recommended action must be the literal diff, quoted replacement, tool description, or setting change.
1192
+
1193
+ If no upstream failure findings exist in this run, derive your own from the trace dataset using the failure-mode protocol inline (\`searchTrace\` for STATUS_CODE_ERROR / MaxTurnsExceeded / etc.). But prefer to consume upstream findings when present \u2014 the kinds are designed to chain.
1194
+
1195
+ Do NOT propose a fix you cannot defend with evidence. "Tighten the prompt" is not a finding; "Add 'When the user asks for X, always Y' to the system prompt section "request-classification"" is.
1196
+
1197
+ OBSERVABILITY rules:
1198
+ - Each non-final turn must emit at least one \`console.log\` for evidence.`;
1199
+ var IMPROVEMENT_KIND_SPEC = {
1200
+ id: "improvement",
1201
+ description: "Converts upstream failure / gap / poisoning findings into concrete locus-named edits (prompt, tool-doc, RAG, scaffolding) with leverage grades.",
1202
+ area: "improvement",
1203
+ version: "1.2.0",
1204
+ actorDescription: ACTOR_PROMPT2,
1205
+ buildTools: (store) => buildTraceToolsForGroup("all", store),
1206
+ subqueries: { maxCalls: 8, maxParallel: 4 },
1207
+ maxTurns: 30,
1208
+ maxRuntimeChars: 12e3,
1209
+ cost: { kind: "llm" }
1210
+ };
1211
+
1212
+ // src/analyst/kinds/knowledge-gap.ts
1213
+ var subjectGrammar3 = findingSubjectGrammarPromptFor("knowledge-gap");
1214
+ var ACTOR_PROMPT3 = `You are a knowledge-gap analyst for an OTLP trace dataset. Your job is to identify the **specific pieces of information the agent lacked, or that were stale**, that caused poor decisions.
1215
+
1216
+ The agent under analysis maintains a curated knowledge base via \`@tangle-network/agent-knowledge\` \u2014 a wiki of \`KnowledgePage\`s with raw source anchors, claims, and relations. The primary expected store of agent-knowable facts IS that wiki. A "knowledge gap" is anything the agent had to discover or guess at run-time that the wiki should have held \u2014 or an outdated/contradictory fact the agent picked up from a non-wiki source.
1217
+
1218
+ ${subjectGrammar3}
1219
+
1220
+ DISCOVERY \u2192 ATTRIBUTE-TO-LAYER \u2192 CITE protocol:
1221
+
1222
+ 1. \`traces.getDatasetOverview({})\` first. Note which agents, tools, and models appear.
1223
+ 2. Pull traces where the agent shows gap signals. The strongest signals are:
1224
+ - Self-correction turns ("I assumed X but\u2026", "let me re-check", "actually,")
1225
+ - Clarifying-question turns where the agent asked the user something the runtime should have surfaced
1226
+ - Repeated retrieval / lookup calls for the same artifact with slightly varied queries
1227
+ - Tool errors that name a missing argument or unknown resource
1228
+ - Web-search calls returning pages dated before a known cutoff for content that changes (versioned APIs, schemas, policies)
1229
+ - Agent quoting a tool's docs / system prompt incorrectly because the actual text was insufficient
1230
+ - Fabricated identifiers that don't appear in dataset \`sample_trace_ids\`
1231
+ Use \`traces.searchTrace\` with patterns like \`I (don.?t|do not) know\`, \`assumed\`, \`unclear\`, \`could you (clarify|tell me|provide)\`, \`not found\`, \`undefined\`, \`unknown\`, \`null\`, dates older than the analysis window, or the agent's specific clarification phrases.
1232
+ 3. For each gap, identify the **layer of the runtime that should have prevented it** and use its exact locus from the subject grammar above.
1233
+ 4. For each defensible gap, emit ONE finding. Use an exact locus from the subject grammar and name the missing or stale knowledge (for example, "wiki has no page on invoice line-item shape; agent re-derived it from raw spans"). Rate it high when it caused failure or a clarifying question, medium for unnecessary turns, and low for minor inefficiency. Cite the span where the question, correction, retrieval miss, or stale result surfaced and quote it exactly. Use confidence 0.85+ when the agent articulated the gap and 0.6-0.8 when inferred. Recommend a concrete wiki edit for an agent-knowledge locus or a prompt/tool-description edit otherwise.
1234
+
1235
+ **Compare layers over loaded evidence.** After the first scan, load the exact excerpts behind candidates across \`agent-knowledge:*\`, \`websearch:outdated\`, \`tool-doc:*\`, \`system-prompt:*\`, and \`memory:*\`. Use one bounded \`llmQuery\` per layer to classify those excerpts. Subqueries cannot call trace tools. Merge their classifications into the final finding set only when the source excerpts support them.
1236
+
1237
+ Do NOT report a gap that the agent later recovered from cleanly within the same turn \u2014 that's resilience, not a gap. Cite the *non-recovery* version when both exist.
1238
+
1239
+ OBSERVABILITY rules:
1240
+ - Each non-final turn must emit at least one \`console.log\` for evidence.`;
1241
+ var KNOWLEDGE_GAP_KIND_SPEC = {
1242
+ id: "knowledge-gap",
1243
+ description: "Identifies missing or stale pieces of knowledge \u2014 primarily against the agent-knowledge wiki \u2014 and attributes each to the runtime layer (wiki page, claim, raw source, websearch, tool-doc, system-prompt, memory) that should have held it.",
1244
+ area: "knowledge-gap",
1245
+ version: "1.2.0",
1246
+ actorDescription: ACTOR_PROMPT3,
1247
+ buildTools: (store) => buildTraceToolsForGroup("discoveryAndSearch", store),
1248
+ subqueries: { maxCalls: 5, maxParallel: 4 },
1249
+ maxTurns: 18,
1250
+ cost: { kind: "llm" }
1251
+ };
1252
+
1253
+ // src/analyst/kinds/knowledge-poisoning.ts
1254
+ var subjectGrammar4 = findingSubjectGrammarPromptFor("knowledge-poisoning");
1255
+ var ACTOR_PROMPT4 = `You are a knowledge-poisoning analyst for an OTLP trace dataset. Your job is to identify cases where the agent **confidently used wrong information** \u2014 not where it lacked information (that's the knowledge-gap analyst).
1256
+
1257
+ ${subjectGrammar4}
1258
+
1259
+ DISCOVERY \u2192 DUAL-VERIFY \u2192 CITE protocol:
1260
+
1261
+ 1. \`traces.getDatasetOverview({})\` first. Identify the agents, models, and tools.
1262
+ 2. Pull traces where the agent's confident action was later contradicted. Strongest signals:
1263
+ - Agent stated a fact in one span; a later span surfaced contradictory evidence; the agent then proceeded anyway or fabricated reconciliation.
1264
+ - Tool call with stale arguments (an id that no longer exists, an API shape that changed).
1265
+ - Agent cited an \`agent-knowledge\` wiki page or claim whose content contradicts the trace's own evidence \u2014 the wiki itself drifted.
1266
+ - Web-search result the agent cited that returned an outdated page; agent treated it as canonical.
1267
+ - System-prompt instruction the agent followed that ground-truth evidence in the trace contradicts (e.g. prompt says "use endpoint A"; tool reply says "endpoint A deprecated, use B").
1268
+ - Repeated wrong-shape parsing despite the tool's actual output proving the shape.
1269
+ 3. Use \`traces.searchTrace\` with regex on phrases like \`actually\`, \`turns out\`, \`previously assumed\`, \`old version\`, \`deprecated\`, \`updated to\`, \`now uses\`, or specific entity names you suspect have changed.
1270
+ 4. For each candidate poisoning, **DUAL-VERIFY**:
1271
+ - Confirm the agent actually acted on the false belief (cite the span where it did)
1272
+ - Confirm the belief is actually false in this trace's own evidence (cite the span that contradicts it)
1273
+ Only emit a finding when both halves are nailed down. If you can only nail one, drop it \u2014 single-evidence poisoning findings are too speculative to be useful.
1274
+
1275
+ **Independently assess both halves.** Load the action excerpt and contradicting excerpt yourself, then send bounded \`llmQuery\` calls the exact evidence for "did the agent act?" and "does the trace contradict the belief?" Subqueries cannot call trace tools. Accept a poisoning only when both assessments and the source excerpts support it.
1276
+
1277
+ For each confirmed poisoning, emit ONE finding. Use the source of the false belief as the exact subject. State "agent believed X (from source S); trace evidence shows X is false." Rate it critical for a wrong user-visible action, high when caught internally after significant waste, and medium for inefficiency. Cite BOTH the action span and the contradicting span with exact quotes. Use confidence 0.85+ when both halves have exact quotes and 0.6-0.8 when one half is inferred. Recommend the literal source correction: update the wiki claim, invalidate and re-curate the raw source, or replace the stale prompt/tool instruction.
1278
+
1279
+ Do NOT report a finding if the agent caught and corrected the false belief in the same turn \u2014 that's the system working. Reserve poisoning for cases where the false belief shaped downstream action.
1280
+
1281
+ OBSERVABILITY rules:
1282
+ - Each non-final turn must emit at least one \`console.log\` for evidence.`;
1283
+ var KNOWLEDGE_POISONING_KIND_SPEC = {
1284
+ id: "knowledge-poisoning",
1285
+ description: "Identifies confident-but-wrong actions caused by stale memory, contradicting RAG, deprecated tool docs, or outdated system-prompt instructions.",
1286
+ area: "knowledge-poisoning",
1287
+ version: "1.2.0",
1288
+ actorDescription: ACTOR_PROMPT4,
1289
+ buildTools: (store) => buildTraceToolsForGroup("all", store),
1290
+ subqueries: { maxCalls: 8, maxParallel: 4 },
1291
+ maxTurns: 20,
1292
+ minimumEvidenceCitations: 2,
1293
+ cost: { kind: "llm" }
1294
+ };
1295
+
1296
+ // src/analyst/kinds/index.ts
1297
+ var DEFAULT_TRACE_ANALYST_KINDS = [
1298
+ FAILURE_MODE_KIND_SPEC,
1299
+ KNOWLEDGE_GAP_KIND_SPEC,
1300
+ KNOWLEDGE_POISONING_KIND_SPEC,
1301
+ IMPROVEMENT_KIND_SPEC
1302
+ ];
1303
+
1304
+ // src/analyst/registry.ts
1305
+ import { randomUUID } from "crypto";
1306
+ var AnalystRegistry = class {
1307
+ analysts = /* @__PURE__ */ new Map();
1308
+ options;
1309
+ constructor(options = {}) {
1310
+ this.options = options;
1311
+ }
1312
+ register(analyst) {
1313
+ if (!analyst.id) throw new Error("AnalystRegistry.register: analyst.id is required");
1314
+ if (this.analysts.has(analyst.id)) {
1315
+ throw new Error(`AnalystRegistry.register: duplicate analyst id "${analyst.id}"`);
1316
+ }
1317
+ if (!analyst.version) {
1318
+ throw new Error(`AnalystRegistry.register: analyst "${analyst.id}" must declare a version`);
1319
+ }
1320
+ if (analyst.cost.kind === "deterministic" && analyst.cost.settlement_timeout_ms !== void 0) {
1321
+ throw new TypeError(
1322
+ `AnalystRegistry.register: deterministic analyst "${analyst.id}" cannot declare settlement_timeout_ms`
1323
+ );
1324
+ }
1325
+ if (analyst.cost.settlement_timeout_ms !== void 0) {
1326
+ validateUsageSettlementTimeout(analyst.cost.settlement_timeout_ms);
1327
+ }
1328
+ this.analysts.set(analyst.id, analyst);
1329
+ }
1330
+ list() {
1331
+ return Array.from(this.analysts.values()).map((a) => ({
1332
+ id: a.id,
1333
+ description: a.description,
1334
+ version: a.version,
1335
+ cost: a.cost
1336
+ }));
1337
+ }
1338
+ async run(runId, inputs, runOpts = {}) {
1339
+ for await (const ev of this.runStream(runId, inputs, runOpts)) {
1340
+ if (ev.type === "run-completed") return ev.result;
1341
+ }
1342
+ throw new Error("AnalystRegistry.run: stream completed without run-completed event");
1343
+ }
1344
+ /**
1345
+ * Streaming counterpart to `run()`. Emits `AnalystRunEvent` values
1346
+ * in real time — `run-started`, then per-analyst `skipped` /
1347
+ * `started` / `completed`, then a terminal `run-completed` whose
1348
+ * payload is the full `AnalystRunResult`. UIs use this to render
1349
+ * progress; persistence consumers use `run()` and read the result.
1350
+ *
1351
+ * Hooks (`onBeforeAnalyze` / `onAfterAnalyze` / `onError` /
1352
+ * `onComplete`) fire as before — streaming is additive, not a hook
1353
+ * replacement.
1354
+ */
1355
+ async *runStream(runId, inputs, runOpts = {}) {
1356
+ const correlationId = `ar_${randomUUID().slice(0, 12)}`;
1357
+ const log = this.options.log ?? (() => {
1358
+ });
1359
+ const hooks = this.options.hooks ?? {};
1360
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
1361
+ const started = Date.now();
1362
+ const timeoutMs = validateTimeout(runOpts.timeoutMs);
1363
+ const deadlineMs = timeoutMs === void 0 ? void 0 : started + timeoutMs;
1364
+ const timeoutSignal = timeoutMs === void 0 ? void 0 : AbortSignal.timeout(timeoutMs);
1365
+ const runSignal = combineAbortSignals(runOpts.signal, timeoutSignal);
1366
+ const selected = this.selectAnalysts(runOpts);
1367
+ const budget = runOpts.budget ?? this.options.defaultBudget;
1368
+ validateBudgetPolicy(budget);
1369
+ yield {
1370
+ type: "run-started",
1371
+ run_id: runId,
1372
+ correlation_id: correlationId,
1373
+ started_at: startedAt,
1374
+ analyst_ids: selected.map((a) => a.id)
1375
+ };
1376
+ const summaries = [];
1377
+ const allFindings = [];
1378
+ let totalCost = 0;
1379
+ let remainingUsd = budget?.totalUsd;
1380
+ const runnableAnalysts = selected.filter((a) => this.routeInput(a, inputs).kind !== "missing");
1381
+ const runnableCount = runnableAnalysts.length;
1382
+ const weights = budget?.weights;
1383
+ const totalWeight = weights && budget?.totalUsd != null && !budget.allocate && runnableCount > 0 ? runnableAnalysts.reduce((sum, analyst) => sum + analystWeight(weights, analyst.id), 0) : void 0;
1384
+ if (totalWeight === 0) {
1385
+ throw new Error("BudgetPolicy.weights must allocate positive weight to a runnable analyst");
1386
+ }
1387
+ for (const analyst of selected) {
1388
+ const t0 = Date.now();
1389
+ if (runSignal?.aborted) {
1390
+ const summary2 = abortedBeforeStartSummary(analyst, runSignal);
1391
+ summaries.push(summary2);
1392
+ log(`[analyst] skip ${analyst.id} \u2014 run aborted`, { runId, reason: summary2.reason });
1393
+ yield { type: "analyst-skipped", summary: summary2 };
1394
+ continue;
1395
+ }
1396
+ const input = this.routeInput(analyst, inputs);
1397
+ if (input.kind === "missing") {
1398
+ const summary2 = {
1399
+ analyst_id: analyst.id,
1400
+ status: "skipped",
1401
+ reason: `missing input of kind '${analyst.inputKind}'`,
1402
+ findings_count: 0,
1403
+ latency_ms: 0,
1404
+ cost_usd: 0,
1405
+ usage: zeroUsage()
1406
+ };
1407
+ summaries.push(summary2);
1408
+ log(`[analyst] skip ${analyst.id} \u2014 missing input`, { runId, kind: analyst.inputKind });
1409
+ await waitForHook(
1410
+ hooks.onAfterAnalyze ? () => hooks.onAfterAnalyze?.({ analyst, summary: summary2, findings: [], runId }) : void 0,
1411
+ runSignal
1412
+ );
1413
+ yield { type: "analyst-skipped", summary: summary2 };
1414
+ continue;
1415
+ }
1416
+ const perBudget = allocateBudget(budget, {
1417
+ analyst,
1418
+ remainingUsd,
1419
+ runningCount: runnableCount,
1420
+ totalWeight
1421
+ });
1422
+ const usageReceipts = [];
1423
+ const ctx = {
1424
+ runId,
1425
+ correlationId,
1426
+ deadlineMs,
1427
+ budgetUsd: perBudget,
1428
+ costLedger: runOpts.costLedger,
1429
+ costPhase: runOpts.costPhase,
1430
+ chat: this.options.chat,
1431
+ tags: runOpts.tags,
1432
+ log: (msg, fields) => log(`[${analyst.id}] ${msg}`, { runId, correlationId, ...fields }),
1433
+ signal: runSignal,
1434
+ priorFindings: selectPriorFindings(runOpts.priorFindings, analyst.id),
1435
+ upstreamFindings: runOpts.chainFindings && allFindings.length > 0 ? [...allFindings] : void 0,
1436
+ recordUsage: (receipt) => {
1437
+ assertValidUsageReceipt(receipt);
1438
+ usageReceipts.push(receipt);
1439
+ }
1440
+ };
1441
+ await waitForHook(
1442
+ hooks.onBeforeAnalyze ? () => hooks.onBeforeAnalyze?.({ analyst, ctx, runId }) : void 0,
1443
+ runSignal
1444
+ );
1445
+ if (runSignal?.aborted) {
1446
+ const summary2 = abortedBeforeStartSummary(analyst, runSignal, Date.now() - t0);
1447
+ summaries.push(summary2);
1448
+ log(`[analyst] skip ${analyst.id} \u2014 run aborted`, { runId, reason: summary2.reason });
1449
+ yield { type: "analyst-skipped", summary: summary2 };
1450
+ continue;
1451
+ }
1452
+ const effectiveBudget = validateEffectiveBudget(ctx.budgetUsd, remainingUsd, analyst.id);
1453
+ yield {
1454
+ type: "analyst-started",
1455
+ analyst_id: analyst.id,
1456
+ started_at: new Date(t0).toISOString()
1457
+ };
1458
+ let findings;
1459
+ let summary;
1460
+ try {
1461
+ if (runSignal?.aborted) throw abortReason(runSignal);
1462
+ findings = await waitForOperation(
1463
+ analyst.analyze(input.value, ctx),
1464
+ runSignal,
1465
+ analystAbortGraceMs(analyst)
1466
+ );
1467
+ const latency = Date.now() - t0;
1468
+ const usage = resolveUsage(analyst, findings, usageReceipts);
1469
+ const cost = knownCostUsd(usage);
1470
+ totalCost += cost;
1471
+ if (typeof remainingUsd === "number") {
1472
+ remainingUsd = Math.max(0, remainingUsd - budgetDebit(usage, effectiveBudget));
1473
+ }
1474
+ allFindings.push(...findings);
1475
+ summary = {
1476
+ analyst_id: analyst.id,
1477
+ status: "ok",
1478
+ findings_count: findings.length,
1479
+ latency_ms: latency,
1480
+ cost_usd: cost,
1481
+ usage
1482
+ };
1483
+ summaries.push(summary);
1484
+ log(`[analyst] ok ${analyst.id}`, {
1485
+ runId,
1486
+ findings: findings.length,
1487
+ latency_ms: latency,
1488
+ cost_usd: cost,
1489
+ cost_kind: usage.cost.kind,
1490
+ input_tokens: usage.tokens?.input ?? null,
1491
+ output_tokens: usage.tokens?.output ?? null
1492
+ });
1493
+ if (effectiveBudget !== void 0 && usage.cost.kind === "uncaptured") {
1494
+ log(`[analyst] WARN ${analyst.id} \u2014 USD cost uncaptured; budget not reconciled`, {
1495
+ runId,
1496
+ budget_usd: effectiveBudget,
1497
+ cost_captured: false
1498
+ });
1499
+ }
1500
+ } catch (err) {
1501
+ const latency = Date.now() - t0;
1502
+ const e = err instanceof Error ? err : new Error(String(err));
1503
+ const hookFindings = runSignal?.aborted ? [] : await hooks.onError?.({ analyst, error: e, runId }) ?? [];
1504
+ if (hookFindings.length) allFindings.push(...hookFindings);
1505
+ const usage = resolveUsage(analyst, hookFindings, usageReceipts);
1506
+ const cost = knownCostUsd(usage);
1507
+ totalCost += cost;
1508
+ if (typeof remainingUsd === "number") {
1509
+ remainingUsd = Math.max(0, remainingUsd - budgetDebit(usage, effectiveBudget));
1510
+ }
1511
+ const summary2 = {
1512
+ analyst_id: analyst.id,
1513
+ status: "failed",
1514
+ findings_count: hookFindings.length,
1515
+ latency_ms: latency,
1516
+ cost_usd: cost,
1517
+ usage,
1518
+ error: { class: e.constructor.name, message: e.message }
1519
+ };
1520
+ summaries.push(summary2);
1521
+ log(`[analyst] FAIL ${analyst.id}`, {
1522
+ runId,
1523
+ error_class: e.constructor.name,
1524
+ error: e.message,
1525
+ cost_usd: cost,
1526
+ cost_kind: usage.cost.kind
1527
+ });
1528
+ if (effectiveBudget !== void 0 && usage.cost.kind === "uncaptured") {
1529
+ log(`[analyst] WARN ${analyst.id} \u2014 USD cost uncaptured; budget not reconciled`, {
1530
+ runId,
1531
+ budget_usd: effectiveBudget,
1532
+ cost_captured: false
1533
+ });
1534
+ }
1535
+ await waitForHook(
1536
+ hooks.onAfterAnalyze ? () => hooks.onAfterAnalyze?.({ analyst, summary: summary2, findings: hookFindings, runId }) : void 0,
1537
+ runSignal
1538
+ );
1539
+ yield { type: "analyst-completed", summary: summary2, findings: hookFindings };
1540
+ continue;
1541
+ }
1542
+ await waitForHook(
1543
+ hooks.onAfterAnalyze ? () => hooks.onAfterAnalyze?.({ analyst, summary, findings, runId }) : void 0,
1544
+ runSignal
1545
+ );
1546
+ yield { type: "analyst-completed", summary, findings };
1547
+ }
1548
+ const result = {
1549
+ run_id: runId,
1550
+ correlation_id: correlationId,
1551
+ started_at: startedAt,
1552
+ ended_at: (/* @__PURE__ */ new Date()).toISOString(),
1553
+ findings: allFindings,
1554
+ per_analyst: summaries,
1555
+ total_cost_usd: totalCost,
1556
+ total_cost_provenance: aggregateCostProvenance(
1557
+ summaries.map((summary) => summary.usage?.cost ?? { kind: "uncaptured", usd: null })
1558
+ )
1559
+ };
1560
+ await waitForHook(
1561
+ hooks.onComplete ? () => hooks.onComplete?.({ result }) : void 0,
1562
+ runSignal
1563
+ );
1564
+ yield { type: "run-completed", result };
1565
+ }
1566
+ selectAnalysts(opts) {
1567
+ let candidates = Array.from(this.analysts.values());
1568
+ if (opts.only?.length) {
1569
+ const only = new Set(opts.only);
1570
+ candidates = candidates.filter((a) => only.has(a.id));
1571
+ }
1572
+ if (opts.skip?.length) {
1573
+ const skip = new Set(opts.skip);
1574
+ candidates = candidates.filter((a) => !skip.has(a.id));
1575
+ }
1576
+ return candidates;
1577
+ }
1578
+ routeInput(analyst, inputs) {
1579
+ switch (analyst.inputKind) {
1580
+ case "trace-store":
1581
+ return inputs.traceStore ? { kind: "present", value: inputs.traceStore } : { kind: "missing" };
1582
+ case "artifact-dir":
1583
+ return inputs.artifactDir ? { kind: "present", value: inputs.artifactDir } : { kind: "missing" };
1584
+ case "run-record":
1585
+ return inputs.runRecord ? { kind: "present", value: inputs.runRecord } : { kind: "missing" };
1586
+ case "judge-input":
1587
+ return inputs.judgeInput ? { kind: "present", value: inputs.judgeInput } : { kind: "missing" };
1588
+ case "custom": {
1589
+ const v = inputs.custom?.[analyst.id];
1590
+ return v !== void 0 ? { kind: "present", value: v } : { kind: "missing" };
1591
+ }
1592
+ }
1593
+ }
1594
+ };
1595
+ function validateTimeout(timeoutMs) {
1596
+ if (timeoutMs === void 0) return void 0;
1597
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647) {
1598
+ throw new TypeError(
1599
+ "RegistryRunOpts.timeoutMs must be a positive safe integer no greater than 2147483647"
1600
+ );
1601
+ }
1602
+ return timeoutMs;
1603
+ }
1604
+ function combineAbortSignals(caller, timeout) {
1605
+ if (!caller) return timeout;
1606
+ if (!timeout) return caller;
1607
+ return AbortSignal.any([caller, timeout]);
1608
+ }
1609
+ async function waitForOperation(operation, signal, abortGraceMs) {
1610
+ if (!signal) return operation;
1611
+ if (signal.aborted) {
1612
+ void operation.catch(() => {
1613
+ });
1614
+ throw abortReason(signal);
1615
+ }
1616
+ return new Promise((resolve, reject) => {
1617
+ let settlementTimer;
1618
+ const cleanup = () => {
1619
+ signal.removeEventListener("abort", onAbort);
1620
+ if (settlementTimer) clearTimeout(settlementTimer);
1621
+ };
1622
+ const onAbort = () => {
1623
+ if (abortGraceMs === 0) {
1624
+ cleanup();
1625
+ reject(abortReason(signal));
1626
+ return;
1627
+ }
1628
+ settlementTimer = setTimeout(() => {
1629
+ cleanup();
1630
+ reject(abortReason(signal));
1631
+ }, abortGraceMs);
1632
+ };
1633
+ signal.addEventListener("abort", onAbort, { once: true });
1634
+ operation.then(
1635
+ (value) => {
1636
+ cleanup();
1637
+ if (signal.aborted) reject(abortReason(signal));
1638
+ else resolve(value);
1639
+ },
1640
+ (error) => {
1641
+ cleanup();
1642
+ reject(signal.aborted ? abortReason(signal) : error);
1643
+ }
1644
+ );
1645
+ });
1646
+ }
1647
+ async function waitForHook(operation, signal) {
1648
+ if (operation === void 0 || signal?.aborted) return void 0;
1649
+ try {
1650
+ return await waitForOperation(
1651
+ Promise.resolve().then(() => {
1652
+ if (signal?.aborted) throw abortReason(signal);
1653
+ return operation();
1654
+ }),
1655
+ signal,
1656
+ 0
1657
+ );
1658
+ } catch (error) {
1659
+ if (signal?.aborted) return void 0;
1660
+ throw error;
1661
+ }
1662
+ }
1663
+ function analystAbortGraceMs(analyst) {
1664
+ if (analyst.cost.kind === "deterministic") return 0;
1665
+ const settlementMs = validateUsageSettlementTimeout(analyst.cost.settlement_timeout_ms);
1666
+ if (settlementMs === 0) return 0;
1667
+ return Math.min(settlementMs + 100, 2147483647);
1668
+ }
1669
+ function abortedBeforeStartSummary(analyst, signal, latencyMs = 0) {
1670
+ const reason = abortReason(signal);
1671
+ return {
1672
+ analyst_id: analyst.id,
1673
+ status: "skipped",
1674
+ reason: `${reason.name}: ${reason.message}`,
1675
+ findings_count: 0,
1676
+ latency_ms: latencyMs,
1677
+ cost_usd: 0,
1678
+ usage: zeroUsage()
1679
+ };
1680
+ }
1681
+ function abortReason(signal) {
1682
+ return signal.reason instanceof Error ? signal.reason : new DOMException("The operation was aborted", "AbortError");
1683
+ }
1684
+ function allocateBudget(policy, args) {
1685
+ if (!policy) return void 0;
1686
+ if (policy.allocate) {
1687
+ const allocated2 = policy.allocate({
1688
+ analyst: args.analyst,
1689
+ totalUsd: policy.totalUsd,
1690
+ remainingUsd: args.remainingUsd,
1691
+ runningCount: args.runningCount
1692
+ });
1693
+ if (allocated2 === void 0) {
1694
+ if (policy.totalUsd !== void 0) {
1695
+ throw new Error(
1696
+ `BudgetPolicy.allocate('${args.analyst.id}') cannot return undefined when totalUsd is set`
1697
+ );
1698
+ }
1699
+ return void 0;
1700
+ }
1701
+ assertBudgetAmount(allocated2, `BudgetPolicy.allocate('${args.analyst.id}')`);
1702
+ return args.remainingUsd === void 0 ? allocated2 : Math.min(allocated2, args.remainingUsd);
1703
+ }
1704
+ if (policy.totalUsd == null) return void 0;
1705
+ const allocated = policy.weights ? policy.totalUsd * analystWeight(policy.weights, args.analyst.id) / args.totalWeight : policy.totalUsd / Math.max(1, args.runningCount);
1706
+ return args.remainingUsd === void 0 ? allocated : Math.min(allocated, args.remainingUsd);
1707
+ }
1708
+ function validateBudgetPolicy(policy) {
1709
+ if (!policy) return;
1710
+ if (policy.totalUsd !== void 0) assertBudgetAmount(policy.totalUsd, "BudgetPolicy.totalUsd");
1711
+ for (const [analystId, weight] of Object.entries(policy.weights ?? {})) {
1712
+ assertBudgetAmount(weight, `BudgetPolicy.weights['${analystId}']`);
1713
+ }
1714
+ }
1715
+ function assertBudgetAmount(value, field) {
1716
+ if (!Number.isFinite(value) || value < 0) {
1717
+ throw new Error(`${field} must be a non-negative finite number`);
1718
+ }
1719
+ }
1720
+ function validateEffectiveBudget(budgetUsd, remainingUsd, analystId) {
1721
+ if (budgetUsd !== void 0) {
1722
+ assertBudgetAmount(budgetUsd, `AnalystContext.budgetUsd for '${analystId}'`);
1723
+ }
1724
+ if (remainingUsd === void 0) return budgetUsd;
1725
+ if (budgetUsd === void 0) {
1726
+ throw new Error(
1727
+ `AnalystContext.budgetUsd for '${analystId}' cannot be removed while an overall budget remains`
1728
+ );
1729
+ }
1730
+ if (budgetUsd > remainingUsd) {
1731
+ throw new Error(
1732
+ `AnalystContext.budgetUsd for '${analystId}' (${budgetUsd}) exceeds the remaining overall budget (${remainingUsd})`
1733
+ );
1734
+ }
1735
+ return budgetUsd;
1736
+ }
1737
+ function analystWeight(weights, analystId) {
1738
+ const weight = weights[analystId] ?? 1;
1739
+ assertBudgetAmount(weight, `BudgetPolicy.weights['${analystId}']`);
1740
+ return weight;
1741
+ }
1742
+ function zeroUsage() {
1743
+ return {
1744
+ calls: 0,
1745
+ tokens: { input: 0, output: 0 },
1746
+ cost: { kind: "observed", usd: 0 }
1747
+ };
1748
+ }
1749
+ function resolveUsage(analyst, findings, receipts) {
1750
+ const legacyCost = sumFindingCost(findings);
1751
+ if (receipts.length > 0) {
1752
+ const merged = mergeUsageReceipts(receipts);
1753
+ return merged.cost.kind === "uncaptured" && legacyCost.captured ? { ...merged, knownCostUsd: Math.max(merged.knownCostUsd ?? 0, legacyCost.usd) } : merged;
1754
+ }
1755
+ if (legacyCost.captured) {
1756
+ return {
1757
+ calls: null,
1758
+ tokens: null,
1759
+ cost: { kind: "observed", usd: legacyCost.usd }
1760
+ };
1761
+ }
1762
+ if (analyst.cost.kind === "deterministic") return zeroUsage();
1763
+ return { calls: null, tokens: null, cost: { kind: "uncaptured", usd: null } };
1764
+ }
1765
+ function mergeUsageReceipts(receipts) {
1766
+ const calls = receipts.every((receipt) => receipt.calls !== null) ? receipts.reduce((sum, receipt) => sum + (receipt.calls ?? 0), 0) : null;
1767
+ const tokens = receipts.every((receipt) => receipt.tokens !== null) ? receipts.reduce(
1768
+ (sum, receipt) => ({
1769
+ input: sum.input + (receipt.tokens?.input ?? 0),
1770
+ output: sum.output + (receipt.tokens?.output ?? 0),
1771
+ ...sum.reasoning !== void 0 || receipt.tokens?.reasoning !== void 0 ? { reasoning: (sum.reasoning ?? 0) + (receipt.tokens?.reasoning ?? 0) } : {},
1772
+ ...sum.cached !== void 0 || receipt.tokens?.cached !== void 0 ? { cached: (sum.cached ?? 0) + (receipt.tokens?.cached ?? 0) } : {},
1773
+ ...sum.cacheWrite !== void 0 || receipt.tokens?.cacheWrite !== void 0 ? { cacheWrite: (sum.cacheWrite ?? 0) + (receipt.tokens?.cacheWrite ?? 0) } : {}
1774
+ }),
1775
+ { input: 0, output: 0 }
1776
+ ) : null;
1777
+ const cost = aggregateCostProvenance(receipts.map((receipt) => receipt.cost));
1778
+ return {
1779
+ calls,
1780
+ tokens,
1781
+ cost,
1782
+ ...cost.kind === "uncaptured" ? {
1783
+ knownCostUsd: receipts.reduce((sum, receipt) => sum + knownCostUsd(receipt), 0)
1784
+ } : {}
1785
+ };
1786
+ }
1787
+ function knownCostUsd(receipt) {
1788
+ return receipt.cost.kind === "uncaptured" ? receipt.knownCostUsd ?? 0 : receipt.cost.usd;
1789
+ }
1790
+ function budgetDebit(receipt, allocatedUsd) {
1791
+ const known = knownCostUsd(receipt);
1792
+ return receipt.cost.kind === "uncaptured" && allocatedUsd !== void 0 ? Math.max(known, allocatedUsd) : known;
1793
+ }
1794
+ function aggregateCostProvenance(costs) {
1795
+ if (costs.some((cost) => cost.kind === "uncaptured")) {
1796
+ return { kind: "uncaptured", usd: null };
1797
+ }
1798
+ const usd = costs.reduce((sum, cost) => sum + (cost.usd ?? 0), 0);
1799
+ return costs.some((cost) => cost.kind === "estimated") ? { kind: "estimated", usd } : { kind: "observed", usd };
1800
+ }
1801
+ function assertValidUsageReceipt(receipt) {
1802
+ if (receipt.calls !== null && (!Number.isInteger(receipt.calls) || receipt.calls < 0)) {
1803
+ throw new Error("AnalystContext.recordUsage: calls must be a non-negative integer or null");
1804
+ }
1805
+ if (receipt.tokens) {
1806
+ assertNonNegativeFinite(receipt.tokens.input, "tokens.input");
1807
+ assertNonNegativeFinite(receipt.tokens.output, "tokens.output");
1808
+ if (receipt.tokens.reasoning !== void 0) {
1809
+ assertNonNegativeFinite(receipt.tokens.reasoning, "tokens.reasoning");
1810
+ if (receipt.tokens.reasoning > receipt.tokens.output) {
1811
+ throw new Error(
1812
+ "AnalystContext.recordUsage: tokens.reasoning must not exceed tokens.output"
1813
+ );
1814
+ }
1815
+ }
1816
+ if (receipt.tokens.cached !== void 0) {
1817
+ assertNonNegativeFinite(receipt.tokens.cached, "tokens.cached");
1818
+ }
1819
+ if (receipt.tokens.cacheWrite !== void 0) {
1820
+ assertNonNegativeFinite(receipt.tokens.cacheWrite, "tokens.cacheWrite");
1821
+ }
1822
+ }
1823
+ if (receipt.cost.kind !== "uncaptured") {
1824
+ assertNonNegativeFinite(receipt.cost.usd, "cost.usd");
1825
+ } else if (receipt.cost.usd !== null) {
1826
+ throw new Error("AnalystContext.recordUsage: uncaptured cost.usd must be null");
1827
+ }
1828
+ if (receipt.knownCostUsd !== void 0) {
1829
+ assertNonNegativeFinite(receipt.knownCostUsd, "knownCostUsd");
1830
+ }
1831
+ }
1832
+ function assertNonNegativeFinite(value, field) {
1833
+ if (!Number.isFinite(value) || value < 0) {
1834
+ throw new Error(`AnalystContext.recordUsage: ${field} must be a non-negative finite number`);
1835
+ }
1836
+ }
1837
+ function sumFindingCost(findings) {
1838
+ let sum = 0;
1839
+ let captured = false;
1840
+ for (const f of findings) {
1841
+ const c = f.metadata?.cost_usd;
1842
+ if (c === void 0) continue;
1843
+ if (typeof c !== "number" || !Number.isFinite(c) || c < 0) {
1844
+ throw new Error(
1845
+ `Analyst finding '${f.finding_id}' metadata.cost_usd must be a non-negative finite number`
1846
+ );
1847
+ }
1848
+ sum += c;
1849
+ captured = true;
1850
+ }
1851
+ return { usd: sum, captured };
1852
+ }
1853
+ function selectPriorFindings(source, analystId) {
1854
+ if (!source) return void 0;
1855
+ if (Array.isArray(source)) {
1856
+ const own2 = source.filter((f) => f.analyst_id === analystId);
1857
+ return own2.length > 0 ? own2 : void 0;
1858
+ }
1859
+ const record = source;
1860
+ const own = record[analystId] ?? [];
1861
+ const wildcard = record["*"] ?? [];
1862
+ const merged = [...own, ...wildcard];
1863
+ return merged.length > 0 ? merged : void 0;
1864
+ }
1865
+
1866
+ // src/analyst/steer-firewall.ts
1867
+ var OBSERVABLE_KINDS = /* @__PURE__ */ new Set([
1868
+ "span",
1869
+ "event",
1870
+ "artifact"
1871
+ ]);
1872
+ function isTraceObservable(finding) {
1873
+ return finding.evidence_refs.some((ref) => OBSERVABLE_KINDS.has(ref.kind));
1874
+ }
1875
+ function isJudgeVerdict(finding) {
1876
+ return finding.derived_from_judge === true;
1877
+ }
1878
+ function assertNoJudgeVerdict(findings, context = "steer") {
1879
+ const leaks = findings.filter(isJudgeVerdict);
1880
+ if (leaks.length > 0) {
1881
+ throw new Error(
1882
+ `${context}: a judge verdict cannot be admitted as steering input \u2014 that is the held-out judge leaking into the loop. Offending judge-derived findings: [${leaks.map((f) => f.finding_id).join(", ")}]. Steering consumes observations of behavior, never acceptance verdicts.`
1883
+ );
1884
+ }
1885
+ return findings;
1886
+ }
1887
+
1888
+ // src/analyst/policy-edit.ts
1889
+ import { createHash as createHash2 } from "crypto";
1890
+ var POLICY_EDIT_AXES = [
1891
+ "carrier",
1892
+ "representation",
1893
+ "budget",
1894
+ "sampling",
1895
+ "output_contract",
1896
+ "tool_contract",
1897
+ "routing",
1898
+ "memory",
1899
+ "agent_profile",
1900
+ "deployment_target"
1901
+ ];
1902
+ var POLICY_EDIT_TARGET_SURFACES = [
1903
+ "prompt",
1904
+ "tool-contract",
1905
+ "runtime-config",
1906
+ "memory",
1907
+ "agent-profile",
1908
+ "code",
1909
+ "deployment"
1910
+ ];
1911
+ var POLICY_EDIT_CANDIDATE_RECORD_SCHEMA = "tangle.policy-edit-candidate.v1";
1912
+ var PolicyEditValidationError = class extends ValidationError {
1913
+ path;
1914
+ constructor(message, path = "") {
1915
+ super(path ? `${message} (at ${path})` : message);
1916
+ this.path = path;
1917
+ }
1918
+ };
1919
+ var DEFAULT_MIN_SCORE = 0.7;
1920
+ var DEFAULT_MIN_EXPECTED_GAIN = 0.01;
1921
+ var POLICY_EDIT_ID = /^policy-edit:sha256:[0-9a-f]{64}$/;
1922
+ function makePolicyEdit(init) {
1923
+ const normalized = normalizePolicyEdit({
1924
+ schemaVersion: "policy-edit/v1",
1925
+ ...init,
1926
+ source: normalizeSource(init.source)
1927
+ });
1928
+ const edit = {
1929
+ ...normalized,
1930
+ editId: init.editId ?? computePolicyEditId(normalized)
1931
+ };
1932
+ return validatePolicyEdit(edit);
1933
+ }
1934
+ function computePolicyEditId(edit) {
1935
+ const { editId: _editId, schemaVersion, ...material } = edit;
1936
+ void _editId;
1937
+ const canonical = JSON.stringify(canonicalize({ schemaVersion, ...material }));
1938
+ return `policy-edit:sha256:${createHash2("sha256").update(canonical).digest("hex")}`;
1939
+ }
1940
+ function validatePolicyEdit(input) {
1941
+ if (input === null || typeof input !== "object") {
1942
+ throw new PolicyEditValidationError("expected object");
1943
+ }
1944
+ const obj = input;
1945
+ expectLiteral(obj.schemaVersion, "policy-edit/v1", "schemaVersion");
1946
+ expectString(obj.editId, "editId");
1947
+ if (!POLICY_EDIT_ID.test(obj.editId)) {
1948
+ throw new PolicyEditValidationError(
1949
+ "editId must match policy-edit:sha256:<64 lowercase hex chars>",
1950
+ "editId"
1951
+ );
1952
+ }
1953
+ expectOneOf(obj.axis, POLICY_EDIT_AXES, "axis");
1954
+ validateTarget(obj.target);
1955
+ validateChange(obj.change);
1956
+ expectString(obj.claim, "claim");
1957
+ validateExpectedGain(obj.expectedGain);
1958
+ expectConfidence(obj.confidence, "confidence");
1959
+ expectOneOf(obj.risk, ["low", "medium", "high", "unknown"], "risk");
1960
+ validateSource(obj.source);
1961
+ if (obj.rationale !== void 0) expectString(obj.rationale, "rationale");
1962
+ if (obj.validationPlan !== void 0) expectString(obj.validationPlan, "validationPlan");
1963
+ if (obj.metadata !== void 0 && (obj.metadata === null || typeof obj.metadata !== "object")) {
1964
+ throw new PolicyEditValidationError("expected object", "metadata");
1965
+ }
1966
+ const expectedId = computePolicyEditId(obj);
1967
+ if (obj.editId !== expectedId) {
1968
+ throw new PolicyEditValidationError("editId does not match policy edit content", "editId");
1969
+ }
1970
+ return obj;
1971
+ }
1972
+ function makePolicyEditCandidateRecord(edit) {
1973
+ return validatePolicyEditCandidateRecord({
1974
+ schema: POLICY_EDIT_CANDIDATE_RECORD_SCHEMA,
1975
+ policyEdit: edit
1976
+ });
1977
+ }
1978
+ function validatePolicyEditCandidateRecord(input) {
1979
+ if (input === null || typeof input !== "object" || Array.isArray(input)) {
1980
+ throw new PolicyEditValidationError("expected object", "candidateRecord");
1981
+ }
1982
+ const obj = input;
1983
+ const keys = Object.keys(obj).sort();
1984
+ if (keys.length !== 2 || keys[0] !== "policyEdit" || keys[1] !== "schema") {
1985
+ throw new PolicyEditValidationError("expected exactly schema and policyEdit", "candidateRecord");
1986
+ }
1987
+ expectLiteral(obj.schema, POLICY_EDIT_CANDIDATE_RECORD_SCHEMA, "candidateRecord.schema");
1988
+ const policyEdit = validatePolicyEdit(obj.policyEdit);
1989
+ assertJsonSafe(policyEdit, "candidateRecord.policyEdit");
1990
+ const snapshot = JSON.parse(JSON.stringify(policyEdit));
1991
+ return {
1992
+ schema: POLICY_EDIT_CANDIDATE_RECORD_SCHEMA,
1993
+ policyEdit: validatePolicyEdit(snapshot)
1994
+ };
1995
+ }
1996
+ function isPolicyEdit(input) {
1997
+ try {
1998
+ validatePolicyEdit(input);
1999
+ return true;
2000
+ } catch {
2001
+ return false;
2002
+ }
2003
+ }
2004
+ function policyEditsFromFindings(findings, opts = {}) {
2005
+ assertNoJudgeVerdict(findings, "policyEditsFromFindings");
2006
+ const edits = [];
2007
+ for (const finding of findings) {
2008
+ const edit = policyEditFromFinding(finding, opts);
2009
+ if (edit) edits.push(edit);
2010
+ }
2011
+ return edits;
2012
+ }
2013
+ function policyEditFromFinding(finding, opts = {}) {
2014
+ assertNoJudgeVerdict([finding], "policyEditFromFinding");
2015
+ if (!finding.recommended_action?.trim()) return null;
2016
+ const expectedGain = resolveExpectedGain(finding, opts);
2017
+ if (!expectedGain) return null;
2018
+ const routed = routeFindingSubject(finding.subject, opts);
2019
+ const risk = resolveRisk(finding, opts);
2020
+ return makePolicyEdit({
2021
+ axis: routed.axis,
2022
+ target: routed.target,
2023
+ change: { kind: "text", mode: "append", value: finding.recommended_action.trim() },
2024
+ claim: finding.claim,
2025
+ rationale: finding.rationale,
2026
+ expectedGain,
2027
+ confidence: finding.confidence,
2028
+ risk,
2029
+ validationPlan: finding.validation_plan,
2030
+ source: {
2031
+ findingIds: [finding.finding_id],
2032
+ analystIds: [finding.analyst_id],
2033
+ evidenceRefs: finding.evidence_refs,
2034
+ derivedFromJudge: finding.derived_from_judge
2035
+ }
2036
+ });
2037
+ }
2038
+ function scorePolicyEditReadiness(edit, opts = {}) {
2039
+ validatePolicyEdit(edit);
2040
+ const minExpectedGain = opts.minExpectedGain ?? DEFAULT_MIN_EXPECTED_GAIN;
2041
+ const evidenceScore = Math.min(1, edit.source.evidenceRefs.length / 2);
2042
+ const confidenceScore = clamp01(edit.confidence);
2043
+ const gainScore = clamp01(
2044
+ Math.abs(edit.expectedGain.amount) / Math.max(minExpectedGain * 5, 1e-3)
2045
+ );
2046
+ const targetScore = targetSpecificityScore(edit);
2047
+ const riskPenalty = edit.risk === "high" && opts.allowHighRisk !== true ? 0.35 : edit.risk === "unknown" ? 0.2 : 0;
2048
+ return clamp01(
2049
+ 0.3 * evidenceScore + 0.25 * confidenceScore + 0.25 * gainScore + 0.2 * targetScore - riskPenalty
2050
+ );
2051
+ }
2052
+ function admitPolicyEdit(edit, opts = {}) {
2053
+ const validated = validatePolicyEdit(edit);
2054
+ const score = scorePolicyEditReadiness(validated, opts);
2055
+ const reasons = [];
2056
+ const minExpectedGain = opts.minExpectedGain ?? DEFAULT_MIN_EXPECTED_GAIN;
2057
+ const requireEvidence = opts.requireEvidence ?? true;
2058
+ if (validated.source.derivedFromJudge) {
2059
+ reasons.push("source is judge-derived; judge verdicts cannot steer policy edits");
2060
+ }
2061
+ if (requireEvidence && validated.source.evidenceRefs.length === 0) {
2062
+ reasons.push("missing evidence refs");
2063
+ }
2064
+ if (Math.abs(validated.expectedGain.amount) < minExpectedGain) {
2065
+ reasons.push(`expected gain below ${minExpectedGain}`);
2066
+ }
2067
+ if (validated.risk === "high" && opts.allowHighRisk !== true) {
2068
+ reasons.push("high-risk edit requires explicit allowHighRisk");
2069
+ }
2070
+ if (score < (opts.minScore ?? DEFAULT_MIN_SCORE)) {
2071
+ reasons.push(
2072
+ `readiness score ${score.toFixed(3)} below ${(opts.minScore ?? DEFAULT_MIN_SCORE).toFixed(3)}`
2073
+ );
2074
+ }
2075
+ return {
2076
+ edit: validated,
2077
+ decision: reasons.length === 0 ? "admit" : "reject",
2078
+ score,
2079
+ reasons
2080
+ };
2081
+ }
2082
+ function applyPolicyEditToSurface(surface, edit) {
2083
+ const validated = validatePolicyEdit(edit);
2084
+ if (validated.change.kind === "text") return applyTextChange(surface, validated.change);
2085
+ return applyJsonChange(surface, validated.change);
2086
+ }
2087
+ function routeFindingSubject(subject, opts) {
2088
+ const parsed = parseFindingSubject(subject);
2089
+ if (!parsed) {
2090
+ return {
2091
+ axis: opts.defaultAxis ?? "representation",
2092
+ target: { surface: opts.defaultTargetSurface ?? "prompt" }
2093
+ };
2094
+ }
2095
+ return routeParsedSubject(parsed);
2096
+ }
2097
+ function routeParsedSubject(subject) {
2098
+ switch (subject.kind) {
2099
+ case "system-prompt":
2100
+ return {
2101
+ axis: "representation",
2102
+ target: { surface: "prompt", path: `system-prompt:${subject.section}` }
2103
+ };
2104
+ case "skill":
2105
+ return {
2106
+ axis: "agent_profile",
2107
+ target: { surface: "agent-profile", path: `skill:${subject.name}` }
2108
+ };
2109
+ case "tool-doc":
2110
+ return {
2111
+ axis: "tool_contract",
2112
+ target: {
2113
+ surface: "tool-contract",
2114
+ path: subject.aspect ? `tool-doc:${subject.tool}:${subject.aspect}` : `tool-doc:${subject.tool}`
2115
+ }
2116
+ };
2117
+ case "new-tool":
2118
+ return {
2119
+ axis: "tool_contract",
2120
+ target: { surface: "tool-contract", path: `new-tool:${subject.name}` }
2121
+ };
2122
+ case "mcp":
2123
+ return {
2124
+ axis: "tool_contract",
2125
+ target: {
2126
+ surface: "agent-profile",
2127
+ path: subject.tool ? `mcp:${subject.server}:${subject.tool}` : `mcp:${subject.server}`
2128
+ }
2129
+ };
2130
+ case "hook":
2131
+ return {
2132
+ axis: "agent_profile",
2133
+ target: { surface: "agent-profile", path: `hook:${subject.name}` }
2134
+ };
2135
+ case "subagent":
2136
+ return {
2137
+ axis: "routing",
2138
+ target: { surface: "agent-profile", path: `subagent:${subject.name}` }
2139
+ };
2140
+ case "workflow":
2141
+ return {
2142
+ axis: "routing",
2143
+ target: { surface: "runtime-config", path: `workflow:${subject.name}` }
2144
+ };
2145
+ case "rollout-policy":
2146
+ return {
2147
+ axis: rolloutPolicyAxis(subject.field),
2148
+ target: { surface: "runtime-config", path: `rollout-policy:${subject.field}` }
2149
+ };
2150
+ case "agent-profile":
2151
+ return {
2152
+ axis: "agent_profile",
2153
+ target: { surface: "agent-profile", path: `agent-profile:${subject.field}` }
2154
+ };
2155
+ case "code":
2156
+ return {
2157
+ axis: "representation",
2158
+ target: { surface: "code", path: `code:${subject.path}` }
2159
+ };
2160
+ case "rag":
2161
+ return {
2162
+ axis: "memory",
2163
+ target: { surface: "memory", path: `rag:${subject.corpus}:${subject.docId}` }
2164
+ };
2165
+ case "memory":
2166
+ return { axis: "memory", target: { surface: "memory", path: `memory:${subject.key}` } };
2167
+ case "scaffolding":
2168
+ return {
2169
+ axis: "routing",
2170
+ target: { surface: "runtime-config", path: `scaffolding:${subject.concern}` }
2171
+ };
2172
+ case "output-schema":
2173
+ return {
2174
+ axis: "output_contract",
2175
+ target: { surface: "runtime-config", path: `output-schema:${subject.field}` }
2176
+ };
2177
+ case "knowledge.wiki":
2178
+ return {
2179
+ axis: "memory",
2180
+ target: {
2181
+ surface: "memory",
2182
+ path: `agent-knowledge:wiki:${subject.slug}${subject.heading ? `#${subject.heading}` : ""}`
2183
+ }
2184
+ };
2185
+ case "knowledge.claim":
2186
+ return {
2187
+ axis: "memory",
2188
+ target: { surface: "memory", path: `agent-knowledge:claim:${subject.topic}` }
2189
+ };
2190
+ case "knowledge.raw":
2191
+ return {
2192
+ axis: "memory",
2193
+ target: { surface: "memory", path: `agent-knowledge:raw:${subject.sourceId}` }
2194
+ };
2195
+ case "knowledge.stale":
2196
+ return {
2197
+ axis: "memory",
2198
+ target: { surface: "memory", path: `agent-knowledge:stale:${subject.slug}` }
2199
+ };
2200
+ case "websearch.outdated":
2201
+ return {
2202
+ axis: "memory",
2203
+ target: { surface: "memory", path: `websearch:outdated:${subject.topic}` }
2204
+ };
2205
+ case "prior-run-summary":
2206
+ return {
2207
+ axis: "memory",
2208
+ target: { surface: "memory", path: `prior-run-summary:${subject.topic}` }
2209
+ };
2210
+ case "cluster":
2211
+ return { axis: "representation", target: { surface: "prompt", path: subject.label } };
2212
+ }
2213
+ }
2214
+ function rolloutPolicyAxis(field) {
2215
+ const normalized = field.toLowerCase();
2216
+ if (/budget|max(?:imum)?[-_. ]?(?:turns?|tokens?|cost)|timeout|deadline/.test(normalized)) {
2217
+ return "budget";
2218
+ }
2219
+ if (/temperature|top[-_. ]?p|sampling|seed|shots?|parallel|concurrency/.test(normalized)) {
2220
+ return "sampling";
2221
+ }
2222
+ if (/output|schema|format/.test(normalized)) return "output_contract";
2223
+ return "routing";
2224
+ }
2225
+ function resolveExpectedGain(finding, opts) {
2226
+ if (typeof opts.expectedGain === "function") return opts.expectedGain(finding) ?? null;
2227
+ if (opts.expectedGain) return opts.expectedGain;
2228
+ return readExpectedGainFromMetadata(finding.metadata);
2229
+ }
2230
+ function readExpectedGainFromMetadata(metadata) {
2231
+ const raw = readPolicyEditMetadata(metadata)?.expectedGain ?? readPolicyEditMetadata(metadata)?.expected_gain;
2232
+ if (!raw || typeof raw !== "object") return null;
2233
+ const obj = raw;
2234
+ if (typeof obj.metric !== "string" || obj.direction !== "increase" && obj.direction !== "decrease" || typeof obj.amount !== "number") {
2235
+ return null;
2236
+ }
2237
+ const out = {
2238
+ metric: obj.metric,
2239
+ direction: obj.direction,
2240
+ amount: obj.amount
2241
+ };
2242
+ if (obj.unit === "absolute" || obj.unit === "relative" || obj.unit === "percent" || obj.unit === "score") {
2243
+ out.unit = obj.unit;
2244
+ }
2245
+ if (typeof obj.rationale === "string") out.rationale = obj.rationale;
2246
+ return out;
2247
+ }
2248
+ function readPolicyEditMetadata(metadata) {
2249
+ const raw = metadata?.policyEdit ?? metadata?.policy_edit;
2250
+ return raw && typeof raw === "object" ? raw : null;
2251
+ }
2252
+ function resolveRisk(finding, opts) {
2253
+ if (typeof opts.risk === "function") return opts.risk(finding);
2254
+ if (opts.risk) return opts.risk;
2255
+ const raw = readPolicyEditMetadata(finding.metadata)?.risk;
2256
+ if (raw === "low" || raw === "medium" || raw === "high" || raw === "unknown") return raw;
2257
+ if (finding.severity === "critical" || finding.severity === "high") return "medium";
2258
+ return "low";
2259
+ }
2260
+ function applyTextChange(surface, change) {
2261
+ if (typeof surface !== "string") {
2262
+ throw new PolicyEditValidationError("text policy edits require a string surface", "change");
2263
+ }
2264
+ if (change.mode === "append") {
2265
+ if (hasExactTextBlock(surface, change.value)) return surface;
2266
+ return `${surface.trimEnd()}
2267
+
2268
+ ${change.value}`.trimStart();
2269
+ }
2270
+ if (change.mode === "prepend") {
2271
+ if (hasExactTextBlock(surface, change.value)) return surface;
2272
+ return `${change.value}
2273
+
2274
+ ${surface.trimStart()}`.trimEnd();
2275
+ }
2276
+ const find = expectNonEmpty(change.find, "change.find");
2277
+ if (!surface.includes(find)) {
2278
+ throw new PolicyEditValidationError("replace target not found in surface", "change.find");
2279
+ }
2280
+ return surface.replace(find, change.value);
2281
+ }
2282
+ function applyJsonChange(surface, change) {
2283
+ const root = parseJsonSurface(surface);
2284
+ const path = splitPath(change.path);
2285
+ if (change.mode === "remove") return setJsonAtPath(root, path, void 0, "remove");
2286
+ if (change.mode === "set") return setJsonAtPath(root, path, change.value ?? null, "set");
2287
+ const prior = readJsonAtPath(root, path);
2288
+ const merged = prior && typeof prior === "object" && !Array.isArray(prior) && change.value && typeof change.value === "object" && !Array.isArray(change.value) ? { ...prior, ...change.value } : change.value ?? null;
2289
+ return setJsonAtPath(root, path, merged, "set");
2290
+ }
2291
+ function parseJsonSurface(surface) {
2292
+ if (typeof surface === "string") {
2293
+ try {
2294
+ return JSON.parse(surface);
2295
+ } catch {
2296
+ throw new PolicyEditValidationError(
2297
+ "json policy edits require a JSON string surface",
2298
+ "change"
2299
+ );
2300
+ }
2301
+ }
2302
+ assertJson(surface, "surface");
2303
+ return surface;
2304
+ }
2305
+ function readJsonAtPath(root, path) {
2306
+ let cursor = root;
2307
+ for (const part of path) {
2308
+ if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) return void 0;
2309
+ cursor = cursor[part];
2310
+ }
2311
+ return cursor;
2312
+ }
2313
+ function setJsonAtPath(root, path, value, mode) {
2314
+ if (path.length === 0) {
2315
+ if (mode === "remove") return null;
2316
+ return value ?? null;
2317
+ }
2318
+ if (root === null || typeof root !== "object" || Array.isArray(root)) {
2319
+ throw new PolicyEditValidationError("json edit root must be an object", "change.path");
2320
+ }
2321
+ const out = { ...root };
2322
+ let cursor = out;
2323
+ for (let i = 0; i < path.length - 1; i++) {
2324
+ const key = path[i];
2325
+ const existing = cursor[key];
2326
+ if (mode === "remove" && (!existing || typeof existing !== "object" || Array.isArray(existing))) {
2327
+ return out;
2328
+ }
2329
+ const next = existing && typeof existing === "object" && !Array.isArray(existing) ? { ...existing } : {};
2330
+ cursor[key] = next;
2331
+ cursor = next;
2332
+ }
2333
+ const leaf = path[path.length - 1];
2334
+ if (mode === "remove") delete cursor[leaf];
2335
+ else cursor[leaf] = value ?? null;
2336
+ return out;
2337
+ }
2338
+ function normalizePolicyEdit(input) {
2339
+ const out = {
2340
+ schemaVersion: "policy-edit/v1",
2341
+ axis: input.axis,
2342
+ target: normalizeTarget(input.target),
2343
+ change: normalizeChange(input.change),
2344
+ claim: input.claim.trim(),
2345
+ expectedGain: normalizeExpectedGain(input.expectedGain),
2346
+ confidence: input.confidence,
2347
+ risk: input.risk,
2348
+ source: normalizeSource(input.source)
2349
+ };
2350
+ if (input.rationale?.trim()) out.rationale = input.rationale.trim();
2351
+ if (input.validationPlan?.trim()) out.validationPlan = input.validationPlan.trim();
2352
+ if (input.metadata) out.metadata = input.metadata;
2353
+ return out;
2354
+ }
2355
+ function assertJsonSafe(value, path, ancestors = /* @__PURE__ */ new WeakSet()) {
2356
+ if (value === null || typeof value === "string" || typeof value === "boolean") return;
2357
+ if (typeof value === "number") {
2358
+ if (Number.isFinite(value)) return;
2359
+ throw new PolicyEditValidationError("expected finite JSON number", path);
2360
+ }
2361
+ if (typeof value !== "object") {
2362
+ throw new PolicyEditValidationError("expected JSON-safe value", path);
2363
+ }
2364
+ if (ancestors.has(value)) {
2365
+ throw new PolicyEditValidationError("cyclic value is not JSON-safe", path);
2366
+ }
2367
+ ancestors.add(value);
2368
+ if (Array.isArray(value)) {
2369
+ for (let i = 0; i < value.length; i++) {
2370
+ if (!(i in value)) {
2371
+ throw new PolicyEditValidationError("sparse array is not JSON-safe", `${path}.${i}`);
2372
+ }
2373
+ assertJsonSafe(value[i], `${path}.${i}`, ancestors);
2374
+ }
2375
+ } else {
2376
+ const prototype = Object.getPrototypeOf(value);
2377
+ if (prototype !== Object.prototype && prototype !== null) {
2378
+ throw new PolicyEditValidationError("expected plain JSON object", path);
2379
+ }
2380
+ if (Object.getOwnPropertySymbols(value).length > 0) {
2381
+ throw new PolicyEditValidationError("symbol keys are not JSON-safe", path);
2382
+ }
2383
+ for (const [key, child] of Object.entries(value)) {
2384
+ assertJsonSafe(child, `${path}.${key}`, ancestors);
2385
+ }
2386
+ }
2387
+ ancestors.delete(value);
2388
+ }
2389
+ function normalizeTarget(target) {
2390
+ const out = { surface: target.surface };
2391
+ if (target.path?.trim()) out.path = target.path.trim();
2392
+ if (target.agentProfileCell)
2393
+ out.agentProfileCell = validateAgentProfileCell(target.agentProfileCell);
2394
+ if (target.label?.trim()) out.label = target.label.trim();
2395
+ return out;
2396
+ }
2397
+ function normalizeChange(change) {
2398
+ if (change.kind === "text") {
2399
+ const out2 = {
2400
+ kind: "text",
2401
+ mode: change.mode,
2402
+ value: change.value.trim()
2403
+ };
2404
+ if (change.find?.trim()) out2.find = change.find.trim();
2405
+ return out2;
2406
+ }
2407
+ const out = {
2408
+ kind: "json",
2409
+ mode: change.mode,
2410
+ path: change.path.trim()
2411
+ };
2412
+ if (change.value !== void 0) out.value = change.value;
2413
+ return out;
2414
+ }
2415
+ function normalizeExpectedGain(gain) {
2416
+ const out = {
2417
+ metric: gain.metric.trim(),
2418
+ direction: gain.direction,
2419
+ amount: gain.amount
2420
+ };
2421
+ if (gain.unit) out.unit = gain.unit;
2422
+ if (gain.rationale?.trim()) out.rationale = gain.rationale.trim();
2423
+ return out;
2424
+ }
2425
+ function normalizeSource(source) {
2426
+ const out = {
2427
+ findingIds: uniqueSorted(source.findingIds.map((s) => s.trim()).filter(Boolean)),
2428
+ analystIds: uniqueSorted(source.analystIds.map((s) => s.trim()).filter(Boolean)),
2429
+ evidenceRefs: source.evidenceRefs
2430
+ };
2431
+ if (source.derivedFromJudge) out.derivedFromJudge = true;
2432
+ return out;
2433
+ }
2434
+ function validateTarget(target) {
2435
+ if (!target || typeof target !== "object")
2436
+ throw new PolicyEditValidationError("expected object", "target");
2437
+ const obj = target;
2438
+ expectOneOf(obj.surface, POLICY_EDIT_TARGET_SURFACES, "target.surface");
2439
+ if (obj.path !== void 0) expectString(obj.path, "target.path");
2440
+ if (obj.label !== void 0) expectString(obj.label, "target.label");
2441
+ if (obj.agentProfileCell !== void 0) validateAgentProfileCell(obj.agentProfileCell);
2442
+ }
2443
+ function validateChange(change) {
2444
+ if (!change || typeof change !== "object")
2445
+ throw new PolicyEditValidationError("expected object", "change");
2446
+ const obj = change;
2447
+ if (obj.kind !== "text" && obj.kind !== "json") {
2448
+ throw new PolicyEditValidationError("kind must be text or json", "change.kind");
2449
+ }
2450
+ if (obj.kind === "text") {
2451
+ expectOneOf(obj.mode, ["append", "prepend", "replace"], "change.mode");
2452
+ expectString(obj.value, "change.value");
2453
+ if (obj.mode === "replace") expectString(obj.find, "change.find");
2454
+ return;
2455
+ }
2456
+ expectOneOf(obj.mode, ["set", "merge", "remove"], "change.mode");
2457
+ expectString(obj.path, "change.path");
2458
+ if (obj.value !== void 0) assertJson(obj.value, "change.value");
2459
+ }
2460
+ function validateExpectedGain(gain) {
2461
+ if (!gain || typeof gain !== "object")
2462
+ throw new PolicyEditValidationError("expected object", "expectedGain");
2463
+ const obj = gain;
2464
+ expectString(obj.metric, "expectedGain.metric");
2465
+ expectOneOf(obj.direction, ["increase", "decrease"], "expectedGain.direction");
2466
+ if (!Number.isFinite(obj.amount) || obj.amount <= 0) {
2467
+ throw new PolicyEditValidationError(
2468
+ "amount must be a positive finite number",
2469
+ "expectedGain.amount"
2470
+ );
2471
+ }
2472
+ if (obj.unit !== void 0) {
2473
+ expectOneOf(
2474
+ obj.unit,
2475
+ ["absolute", "relative", "percent", "score"],
2476
+ "expectedGain.unit"
2477
+ );
2478
+ }
2479
+ if (obj.rationale !== void 0) expectString(obj.rationale, "expectedGain.rationale");
2480
+ }
2481
+ function validateSource(source) {
2482
+ if (!source || typeof source !== "object")
2483
+ throw new PolicyEditValidationError("expected object", "source");
2484
+ const obj = source;
2485
+ expectNonEmptyStringArray(obj.findingIds, "source.findingIds");
2486
+ expectNonEmptyStringArray(obj.analystIds, "source.analystIds");
2487
+ if (!Array.isArray(obj.evidenceRefs)) {
2488
+ throw new PolicyEditValidationError("expected array", "source.evidenceRefs");
2489
+ }
2490
+ for (const [i, ref] of obj.evidenceRefs.entries())
2491
+ validateEvidenceRef(ref, `source.evidenceRefs.${i}`);
2492
+ if (obj.derivedFromJudge !== void 0 && typeof obj.derivedFromJudge !== "boolean") {
2493
+ throw new PolicyEditValidationError("expected boolean", "source.derivedFromJudge");
2494
+ }
2495
+ }
2496
+ function validateEvidenceRef(ref, path) {
2497
+ if (!ref || typeof ref !== "object") throw new PolicyEditValidationError("expected object", path);
2498
+ const obj = ref;
2499
+ expectOneOf(obj.kind, ["span", "event", "artifact", "finding", "metric"], `${path}.kind`);
2500
+ expectString(obj.uri, `${path}.uri`);
2501
+ if (obj.excerpt !== void 0) expectString(obj.excerpt, `${path}.excerpt`);
2502
+ }
2503
+ function assertJson(value, path) {
2504
+ if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) {
2505
+ return;
2506
+ }
2507
+ if (Array.isArray(value)) {
2508
+ for (const [i, item] of value.entries()) assertJson(item, `${path}.${i}`);
2509
+ return;
2510
+ }
2511
+ if (typeof value === "object") {
2512
+ for (const [key, item] of Object.entries(value)) {
2513
+ if (!key) throw new PolicyEditValidationError("empty object key", path);
2514
+ assertJson(item, `${path}.${key}`);
2515
+ }
2516
+ return;
2517
+ }
2518
+ throw new PolicyEditValidationError("expected JSON-compatible value", path);
2519
+ }
2520
+ function targetSpecificityScore(edit) {
2521
+ let score = 0.4;
2522
+ if (edit.target.path) score += 0.25;
2523
+ if (edit.target.agentProfileCell) score += 0.15;
2524
+ if (edit.change.kind === "json" || edit.change.mode === "replace") score += 0.2;
2525
+ else if (edit.change.value.length > 0) score += 0.1;
2526
+ return clamp01(score);
2527
+ }
2528
+ function splitPath(path) {
2529
+ const parts = path.split(".").map((p) => p.trim()).filter(Boolean);
2530
+ if (parts.length === 0)
2531
+ throw new PolicyEditValidationError("path must not be empty", "change.path");
2532
+ return parts;
2533
+ }
2534
+ function expectLiteral(value, expected, path) {
2535
+ if (value !== expected) throw new PolicyEditValidationError(`expected ${expected}`, path);
2536
+ }
2537
+ function expectString(value, path) {
2538
+ if (typeof value !== "string" || value.trim().length === 0) {
2539
+ throw new PolicyEditValidationError("expected non-empty string", path);
2540
+ }
2541
+ }
2542
+ function expectNonEmpty(value, path) {
2543
+ expectString(value, path);
2544
+ return value;
2545
+ }
2546
+ function expectConfidence(value, path) {
2547
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) {
2548
+ throw new PolicyEditValidationError("expected finite number in [0,1]", path);
2549
+ }
2550
+ }
2551
+ function hasExactTextBlock(surface, value) {
2552
+ const needle = normalizeTextBlock(value);
2553
+ const normalizedSurface = surface.replace(/\r\n/g, "\n");
2554
+ return [...normalizedSurface.split(/\n{2,}/), ...normalizedSurface.split("\n")].some(
2555
+ (block) => normalizeTextBlock(block) === needle
2556
+ );
2557
+ }
2558
+ function normalizeTextBlock(value) {
2559
+ return value.replace(/\r\n/g, "\n").trim();
2560
+ }
2561
+ function expectOneOf(value, allowed, path) {
2562
+ if (typeof value !== "string" || !allowed.includes(value)) {
2563
+ throw new PolicyEditValidationError(`expected one of ${allowed.join(", ")}`, path);
2564
+ }
2565
+ }
2566
+ function expectStringArray(value, path) {
2567
+ if (!Array.isArray(value)) throw new PolicyEditValidationError("expected array", path);
2568
+ for (const [i, item] of value.entries()) expectString(item, `${path}.${i}`);
2569
+ }
2570
+ function expectNonEmptyStringArray(value, path) {
2571
+ expectStringArray(value, path);
2572
+ if (value.length === 0) throw new PolicyEditValidationError("expected non-empty array", path);
2573
+ }
2574
+ function uniqueSorted(values) {
2575
+ return [...new Set(values)].sort();
2576
+ }
2577
+ function clamp01(n) {
2578
+ if (!Number.isFinite(n)) return 0;
2579
+ if (n < 0) return 0;
2580
+ if (n > 1) return 1;
2581
+ return n;
2582
+ }
2583
+
2584
+ // src/run-score.ts
2585
+ var DEFAULT_RUN_SCORE_WEIGHTS = {
2586
+ success: 4,
2587
+ goalProgress: 2,
2588
+ repoGroundedness: 1.5,
2589
+ driftPenalty: -1.5,
2590
+ toolUseQuality: 1,
2591
+ patchQuality: 1.25,
2592
+ testReality: 1.5,
2593
+ finalGate: 3,
2594
+ reviewerBlockers: -2,
2595
+ costUsd: -0.2,
2596
+ wallSeconds: -0.1
2597
+ };
2598
+ function aggregateRunScore(score, weights = {}) {
2599
+ const w = { ...DEFAULT_RUN_SCORE_WEIGHTS, ...weights };
2600
+ return w.success * clamp012(score.success) + w.goalProgress * clamp012(score.goalProgress) + w.repoGroundedness * clamp012(score.repoGroundedness) + w.driftPenalty * clamp012(score.driftPenalty) + w.toolUseQuality * clamp012(score.toolUseQuality) + w.patchQuality * clamp012(score.patchQuality) + w.testReality * clamp012(score.testReality) + w.finalGate * clamp012(score.finalGate) + w.reviewerBlockers * clamp012(score.reviewerBlockers) + w.costUsd * Math.max(0, finiteOrZero(score.costUsd)) + w.wallSeconds * Math.max(0, finiteOrZero(score.wallSeconds) / 60);
2601
+ }
2602
+ function clamp012(value) {
2603
+ if (!Number.isFinite(value)) return 0;
2604
+ return Math.max(0, Math.min(1, value));
2605
+ }
2606
+ function finiteOrZero(value) {
2607
+ return Number.isFinite(value) ? value : 0;
2608
+ }
2609
+
2610
+ export {
2611
+ createAnalystAi,
2612
+ computeFindingId,
2613
+ makeFinding,
2614
+ FINDING_SUBJECT_KINDS,
2615
+ parseFindingSubject,
2616
+ renderFindingSubject,
2617
+ FINDING_SUBJECT_SYNTAX,
2618
+ FINDING_SUBJECT_GRAMMAR_PROMPT,
2619
+ KIND_EXPECTED_SUBJECTS,
2620
+ findingSubjectGrammarPromptFor,
2621
+ FindingSubjectStringSchema,
2622
+ stripCodeFences,
2623
+ coerceJson,
2624
+ coerceToFindingRows,
2625
+ ANALYST_SEVERITIES,
2626
+ RawAnalystEvidenceSchema,
2627
+ RawAnalystFindingSchema,
2628
+ CanonicalRawAnalystFindingSchema,
2629
+ RAW_FINDING_SCHEMA_PROMPT,
2630
+ evidenceRefsFromRawFinding,
2631
+ parseRawFinding,
2632
+ parseCanonicalRawFinding,
2633
+ structureFindings,
2634
+ settleUsageReceiptFromCostLedger,
2635
+ validateUsageSettlementTimeout,
2636
+ createTraceAnalystKind,
2637
+ renderPriorFindings,
2638
+ renderUpstreamFindings,
2639
+ buildTraceToolsForGroup,
2640
+ FAILURE_MODE_KIND_SPEC,
2641
+ IMPROVEMENT_KIND_SPEC,
2642
+ KNOWLEDGE_GAP_KIND_SPEC,
2643
+ KNOWLEDGE_POISONING_KIND_SPEC,
2644
+ DEFAULT_TRACE_ANALYST_KINDS,
2645
+ AnalystRegistry,
2646
+ isTraceObservable,
2647
+ isJudgeVerdict,
2648
+ assertNoJudgeVerdict,
2649
+ POLICY_EDIT_AXES,
2650
+ POLICY_EDIT_TARGET_SURFACES,
2651
+ POLICY_EDIT_CANDIDATE_RECORD_SCHEMA,
2652
+ PolicyEditValidationError,
2653
+ makePolicyEdit,
2654
+ computePolicyEditId,
2655
+ validatePolicyEdit,
2656
+ makePolicyEditCandidateRecord,
2657
+ validatePolicyEditCandidateRecord,
2658
+ isPolicyEdit,
2659
+ policyEditsFromFindings,
2660
+ policyEditFromFinding,
2661
+ scorePolicyEditReadiness,
2662
+ admitPolicyEdit,
2663
+ applyPolicyEditToSurface,
2664
+ DEFAULT_RUN_SCORE_WEIGHTS,
2665
+ aggregateRunScore,
2666
+ clamp012 as clamp01
2667
+ };
2668
+ //# sourceMappingURL=chunk-5CVUPHJ4.js.map