@tangle-network/agent-eval 0.86.0 → 0.90.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/dist/adapters/http.d.ts +3 -3
  3. package/dist/adapters/langchain.d.ts +3 -3
  4. package/dist/adapters/otel.d.ts +6 -6
  5. package/dist/adversarial-DIVcDoI_.d.ts +88 -0
  6. package/dist/analyst/index.d.ts +11 -10
  7. package/dist/analyst/index.js +13 -8
  8. package/dist/analyst/index.js.map +1 -1
  9. package/dist/analyze-runs-DwCEkpO_.d.ts +81 -0
  10. package/dist/belief-state/index.d.ts +4 -4
  11. package/dist/belief-state/index.js +1 -1
  12. package/dist/benchmarks/index.d.ts +3 -3
  13. package/dist/campaign/index.d.ts +165 -18
  14. package/dist/campaign/index.js +289 -14
  15. package/dist/campaign/index.js.map +1 -1
  16. package/dist/chunk-45EEMHTC.js +35 -0
  17. package/dist/chunk-45EEMHTC.js.map +1 -0
  18. package/dist/{chunk-FZWAFVAA.js → chunk-4FBZZIYD.js} +2 -2
  19. package/dist/{chunk-YV7J7X5N.js → chunk-5HRORJQY.js} +22 -12
  20. package/dist/chunk-5HRORJQY.js.map +1 -0
  21. package/dist/{chunk-OTYQPHPL.js → chunk-6SOJM3VR.js} +5 -5
  22. package/dist/chunk-BOD4O7OF.js +40 -0
  23. package/dist/chunk-BOD4O7OF.js.map +1 -0
  24. package/dist/{chunk-Z7VFTS2J.js → chunk-CY6U5S3X.js} +2 -2
  25. package/dist/{chunk-VIDQF3F5.js → chunk-D3V5B42D.js} +5 -34
  26. package/dist/chunk-D3V5B42D.js.map +1 -0
  27. package/dist/{chunk-YGYXHNAQ.js → chunk-FIUKOSWI.js} +21 -8
  28. package/dist/chunk-FIUKOSWI.js.map +1 -0
  29. package/dist/{chunk-WJL2NJXN.js → chunk-GSH6QNNS.js} +2 -2
  30. package/dist/{chunk-RBNA5AZT.js → chunk-L3JOU6XM.js} +2 -2
  31. package/dist/{chunk-IDVBLYCY.js → chunk-LMZQ2Z4U.js} +56 -2
  32. package/dist/{chunk-IDVBLYCY.js.map → chunk-LMZQ2Z4U.js.map} +1 -1
  33. package/dist/{chunk-VUINJM5M.js → chunk-QAY5UIJO.js} +2 -193
  34. package/dist/chunk-QAY5UIJO.js.map +1 -0
  35. package/dist/{chunk-P2J6SOXT.js → chunk-QG2OVF2D.js} +5 -3
  36. package/dist/{chunk-P2J6SOXT.js.map → chunk-QG2OVF2D.js.map} +1 -1
  37. package/dist/chunk-REVYNR6C.js +100 -0
  38. package/dist/chunk-REVYNR6C.js.map +1 -0
  39. package/dist/chunk-STGVSCDH.js +202 -0
  40. package/dist/chunk-STGVSCDH.js.map +1 -0
  41. package/dist/{chunk-ZZ2HOPME.js → chunk-TWS7AZEY.js} +2 -2
  42. package/dist/chunk-UHMJT4T7.js +200 -0
  43. package/dist/chunk-UHMJT4T7.js.map +1 -0
  44. package/dist/chunk-UMMZHCPB.js +190 -0
  45. package/dist/chunk-UMMZHCPB.js.map +1 -0
  46. package/dist/chunk-VZSRQ272.js +149 -0
  47. package/dist/chunk-VZSRQ272.js.map +1 -0
  48. package/dist/{chunk-L5G7OUKD.js → chunk-XY4DDNEG.js} +8 -190
  49. package/dist/chunk-XY4DDNEG.js.map +1 -0
  50. package/dist/chunk-Y47J2LJ3.js +859 -0
  51. package/dist/chunk-Y47J2LJ3.js.map +1 -0
  52. package/dist/{chunk-BABOZOSN.js → chunk-ZFIBGEOL.js} +3 -3
  53. package/dist/chunk-ZFIBGEOL.js.map +1 -0
  54. package/dist/{code-agent-session-BRXmavYv.d.ts → code-agent-session-BO8nCnv3.d.ts} +1 -1
  55. package/dist/contract/index.d.ts +24 -95
  56. package/dist/contract/index.js +25 -764
  57. package/dist/contract/index.js.map +1 -1
  58. package/dist/{control-GeE8OhpN.d.ts → control-_Qb7skHX.d.ts} +2 -2
  59. package/dist/control.d.ts +5 -5
  60. package/dist/corpus-BoR-041R.d.ts +560 -0
  61. package/dist/cost-ledger-DuSqlw5B.d.ts +113 -0
  62. package/dist/counterfactual-Dwibr5IW.d.ts +85 -0
  63. package/dist/{dataset-B2kL-fSM.d.ts → dataset-BbGkaN2I.d.ts} +1 -1
  64. package/dist/{registry-DrEQ3Luj.d.ts → default-registry-zoGHUQEH.d.ts} +29 -2
  65. package/dist/diagnose.d.ts +251 -0
  66. package/dist/diagnose.js +381 -0
  67. package/dist/diagnose.js.map +1 -0
  68. package/dist/{errors-Dwqw-T_m.d.ts → errors-CzMUYo7b.d.ts} +1 -1
  69. package/dist/{feedback-trajectory-B3rErRsh.d.ts → feedback-trajectory-D9OVLrg9.d.ts} +1 -1
  70. package/dist/fuzz.d.ts +484 -0
  71. package/dist/fuzz.js +613 -0
  72. package/dist/fuzz.js.map +1 -0
  73. package/dist/governance/index.d.ts +4 -4
  74. package/dist/hosted/index.d.ts +6 -6
  75. package/dist/{index-DE3RXAXD.d.ts → index-Bx3gZ8xl.d.ts} +1 -1
  76. package/dist/index.d.ts +718 -455
  77. package/dist/index.js +1604 -793
  78. package/dist/index.js.map +1 -1
  79. package/dist/{insight-report-3ADTfClO.d.ts → insight-report-BBwvOh6x.d.ts} +2 -2
  80. package/dist/{integrity-CJzrpUua.d.ts → integrity-VJ9A7aST.d.ts} +1 -1
  81. package/dist/{judge-calibration-DilmB3Ml.d.ts → judge-calibration-0p2QcWNE.d.ts} +1 -1
  82. package/dist/{kind-factory-CVecZZG_.d.ts → kind-factory-5b7xXXOr.d.ts} +2 -2
  83. package/dist/{llm-client-CuUg2Mn3.d.ts → llm-client-BeEcAokY.d.ts} +1 -1
  84. package/dist/matrix/index.d.ts +2 -2
  85. package/dist/meta-eval/index.d.ts +177 -3
  86. package/dist/meta-eval/index.js +260 -1
  87. package/dist/meta-eval/index.js.map +1 -1
  88. package/dist/{multi-layer-verifier-DlWCXuxL.d.ts → multi-layer-verifier-DUZXrPDA.d.ts} +7 -1
  89. package/dist/multishot/index.d.ts +25 -11
  90. package/dist/multishot/index.js +36 -7
  91. package/dist/multishot/index.js.map +1 -1
  92. package/dist/openapi.json +1 -1
  93. package/dist/perf/index.d.ts +123 -0
  94. package/dist/perf/index.js +18 -0
  95. package/dist/pipelines/index.js +2 -2
  96. package/dist/{agent-profile-D0PBIWlV.d.ts → pre-registration-DELOEJ8v.d.ts} +144 -4
  97. package/dist/{provenance-DPpNIOJD.d.ts → provenance-LnqRT0sS.d.ts} +5 -5
  98. package/dist/{red-team-DW9Ca_tj.d.ts → red-team-BXHil6c8.d.ts} +1 -1
  99. package/dist/{release-report-hlNtD12q.d.ts → release-report-euXIV_Sk.d.ts} +3 -3
  100. package/dist/reporting.d.ts +8 -8
  101. package/dist/reporting.js +3 -3
  102. package/dist/{researcher-BLPHBbNV.d.ts → researcher-DE6Gpnb4.d.ts} +4 -4
  103. package/dist/rl.d.ts +194 -656
  104. package/dist/rl.js +231 -149
  105. package/dist/rl.js.map +1 -1
  106. package/dist/{rubric-predictive-validity-CnEl9Jc8.d.ts → rubric-predictive-validity-Cy_W-hWZ.d.ts} +1 -1
  107. package/dist/{run-campaign-4Y5V5CN3.js → run-campaign-RDGAM5KJ.js} +3 -3
  108. package/dist/run-campaign-RDGAM5KJ.js.map +1 -0
  109. package/dist/{run-improvement-loop-CNqQckTj.d.ts → run-improvement-loop-5z_l5zDz.d.ts} +2 -2
  110. package/dist/{run-record-De9VarXR.d.ts → run-record-e7vj1uZQ.d.ts} +1 -1
  111. package/dist/{runtime-trajectory-BLRiaifm.d.ts → runtime-trajectory-BDgfGZSr.d.ts} +1 -1
  112. package/dist/{semantic-concept-judge-DIEgr_6v.d.ts → semantic-concept-judge-Dn8Z6KEG.d.ts} +5 -31
  113. package/dist/series-convergence-D5OWMBg6.d.ts +33 -0
  114. package/dist/{statistics-CnC1FMbx.d.ts → statistics-C7PozGrZ.d.ts} +71 -2
  115. package/dist/{summary-report-Db0dDSWP.d.ts → summary-report-DGmUucwQ.d.ts} +1 -1
  116. package/dist/traces.d.ts +3 -3
  117. package/dist/traces.js +8 -6
  118. package/dist/{types-Cu3u_x59.d.ts → types-2VVIL04s.d.ts} +2 -2
  119. package/dist/{types-D7lLRYe9.d.ts → types-BU-7W85F.d.ts} +21 -1
  120. package/dist/{types-CqPax19X.d.ts → types-mn5Aqk7x.d.ts} +1 -1
  121. package/dist/{verdict-CeEgtjyI.d.ts → verdict-C9MlYujm.d.ts} +3 -0
  122. package/dist/wire/index.d.ts +3 -3
  123. package/dist/workflow/index.d.ts +12 -11
  124. package/dist/workflow/index.js +1 -1
  125. package/package.json +16 -1
  126. package/dist/chunk-BABOZOSN.js.map +0 -1
  127. package/dist/chunk-L5G7OUKD.js.map +0 -1
  128. package/dist/chunk-SHTXZ4O2.js +0 -113
  129. package/dist/chunk-SHTXZ4O2.js.map +0 -1
  130. package/dist/chunk-VIDQF3F5.js.map +0 -1
  131. package/dist/chunk-VUINJM5M.js.map +0 -1
  132. package/dist/chunk-YGYXHNAQ.js.map +0 -1
  133. package/dist/chunk-YV7J7X5N.js.map +0 -1
  134. /package/dist/{chunk-FZWAFVAA.js.map → chunk-4FBZZIYD.js.map} +0 -0
  135. /package/dist/{chunk-OTYQPHPL.js.map → chunk-6SOJM3VR.js.map} +0 -0
  136. /package/dist/{chunk-Z7VFTS2J.js.map → chunk-CY6U5S3X.js.map} +0 -0
  137. /package/dist/{chunk-WJL2NJXN.js.map → chunk-GSH6QNNS.js.map} +0 -0
  138. /package/dist/{chunk-RBNA5AZT.js.map → chunk-L3JOU6XM.js.map} +0 -0
  139. /package/dist/{chunk-ZZ2HOPME.js.map → chunk-TWS7AZEY.js.map} +0 -0
  140. /package/dist/{run-campaign-4Y5V5CN3.js.map → perf/index.js.map} +0 -0
@@ -0,0 +1,190 @@
1
+ import {
2
+ AnalystRegistry,
3
+ DEFAULT_TRACE_ANALYST_KINDS,
4
+ createTraceAnalystKind
5
+ } from "./chunk-D3V5B42D.js";
6
+ import {
7
+ makeFinding
8
+ } from "./chunk-45EEMHTC.js";
9
+
10
+ // src/trace-analyst/behavioral-metrics.ts
11
+ var INPUT_GROWTH_FACTOR = 3;
12
+ var MIN_TOOL_CALLS = 3;
13
+ var VERIFY_RE = /verif|eval|inspect|check|assert|validat|review|confirm/i;
14
+ function num(v) {
15
+ return typeof v === "number" && Number.isFinite(v) ? v : null;
16
+ }
17
+ function inputTokensOf(s) {
18
+ return num(s.attributes["llm.input_tokens"]) ?? num(s.attributes["llm.usage.input_tokens"]);
19
+ }
20
+ function outputTokensOf(s) {
21
+ return num(s.attributes["llm.output_tokens"]) ?? num(s.attributes["llm.usage.output_tokens"]);
22
+ }
23
+ function stepOf(s) {
24
+ return num(s.attributes.step);
25
+ }
26
+ function toolNameOf(s) {
27
+ if (s.tool_name) return s.tool_name;
28
+ const t = s.attributes["tool.name"];
29
+ return typeof t === "string" && t.length > 0 ? t : null;
30
+ }
31
+ function computeTraceMetrics(spans) {
32
+ const ordered = [...spans].sort((a, b) => {
33
+ const sa = stepOf(a);
34
+ const sb = stepOf(b);
35
+ if (sa !== null && sb !== null && sa !== sb) return sa - sb;
36
+ return a.start_time.localeCompare(b.start_time);
37
+ });
38
+ const inputTokenTrajectory = [];
39
+ const outputTokenTrajectory = [];
40
+ const toolHistogram = {};
41
+ let hasSelfVerification = false;
42
+ for (const s of ordered) {
43
+ const inT = inputTokensOf(s);
44
+ if (inT !== null) inputTokenTrajectory.push(inT);
45
+ const outT = outputTokensOf(s);
46
+ if (outT !== null) outputTokenTrajectory.push(outT);
47
+ const tool = toolNameOf(s);
48
+ if (tool) {
49
+ toolHistogram[tool] = (toolHistogram[tool] ?? 0) + 1;
50
+ if (VERIFY_RE.test(tool)) hasSelfVerification = true;
51
+ }
52
+ }
53
+ const totalToolCalls = Object.values(toolHistogram).reduce((a, b) => a + b, 0);
54
+ const distinctTools = Object.keys(toolHistogram).length;
55
+ const toolDiversityRatio = totalToolCalls === 0 ? 1 : distinctTools / totalToolCalls;
56
+ const signals = [];
57
+ if (inputTokenTrajectory.length >= 3) {
58
+ const first = inputTokenTrajectory[0];
59
+ const last = inputTokenTrajectory[inputTokenTrajectory.length - 1];
60
+ const growth = first > 0 ? last / first : 0;
61
+ if (last > first && growth >= INPUT_GROWTH_FACTOR) {
62
+ signals.push({
63
+ code: "monotonic-input-growth",
64
+ severity: "high",
65
+ detail: `LLM input tokens grew ${growth.toFixed(1)}x (${first}\u2192${last}) across ${inputTokenTrajectory.length} calls \u2014 full history re-sent each step with no compression.`,
66
+ evidence: {
67
+ first,
68
+ last,
69
+ growth_x: Number(growth.toFixed(2)),
70
+ calls: inputTokenTrajectory.length
71
+ }
72
+ });
73
+ }
74
+ }
75
+ if (outputTokenTrajectory.length >= 3) {
76
+ const first = outputTokenTrajectory[0];
77
+ const last = outputTokenTrajectory[outputTokenTrajectory.length - 1];
78
+ if (last < first) {
79
+ signals.push({
80
+ code: "output-length-decay",
81
+ severity: "medium",
82
+ detail: `LLM output tokens shrank ${first}\u2192${last} over ${outputTokenTrajectory.length} calls \u2014 less planning/reasoning per step as context grows.`,
83
+ evidence: { first, last, calls: outputTokenTrajectory.length }
84
+ });
85
+ }
86
+ }
87
+ if (totalToolCalls >= MIN_TOOL_CALLS && distinctTools === 1) {
88
+ const only = Object.keys(toolHistogram)[0];
89
+ signals.push({
90
+ code: "single-tool-dependency",
91
+ severity: "medium",
92
+ detail: `All ${totalToolCalls} tool calls are \`${only}\` \u2014 no tool diversity and no fallback path.`,
93
+ evidence: { tool: only, calls: totalToolCalls, distinct_tools: 1 }
94
+ });
95
+ }
96
+ if (totalToolCalls >= MIN_TOOL_CALLS && !hasSelfVerification) {
97
+ signals.push({
98
+ code: "no-self-verification",
99
+ severity: "medium",
100
+ detail: `${totalToolCalls} tool calls and none verify/inspect/check state \u2014 the agent never validates its own actions.`,
101
+ evidence: { tool_calls: totalToolCalls, verification_calls: 0 }
102
+ });
103
+ }
104
+ return {
105
+ llmCallCount: inputTokenTrajectory.length,
106
+ inputTokenTrajectory,
107
+ outputTokenTrajectory,
108
+ toolHistogram,
109
+ totalToolCalls,
110
+ distinctTools,
111
+ toolDiversityRatio,
112
+ hasSelfVerification,
113
+ signals
114
+ };
115
+ }
116
+
117
+ // src/analyst/behavioral-analyst.ts
118
+ var RECOMMENDED_ACTION = {
119
+ "monotonic-input-growth": "Add a context-budget instruction: once prior context exceeds a threshold, summarize earlier steps into a short status line instead of re-sending full history.",
120
+ "output-length-decay": "Require a minimum planning/reasoning budget per step so late steps do not degrade into terse, error-prone commands.",
121
+ "single-tool-dependency": "Direct the agent to use the full toolset (verify / inspect / alternate actions), not a single execute call, and to plan a fallback when a call returns an unexpected result.",
122
+ "no-self-verification": "After every state-mutating action, verify the result (eval / inspect / assert) before proceeding."
123
+ };
124
+ var ANALYST_ID = "efficiency-behavioral";
125
+ function deriveEfficiencyFindings(metrics, opts = {}) {
126
+ const analystId = opts.analystId ?? ANALYST_ID;
127
+ return metrics.signals.map(
128
+ (sig) => makeFinding({
129
+ analyst_id: analystId,
130
+ area: "efficiency",
131
+ subject: sig.code,
132
+ // kebab — passes the cluster grammar; stable key for diffFindings
133
+ claim: sig.detail,
134
+ severity: sig.severity,
135
+ // Deterministic arithmetic over spans, not a model judgment → certain.
136
+ confidence: 1,
137
+ evidence_refs: [
138
+ {
139
+ kind: "metric",
140
+ uri: `metric://efficiency/${sig.code}`,
141
+ excerpt: JSON.stringify(sig.evidence)
142
+ }
143
+ ],
144
+ recommended_action: RECOMMENDED_ACTION[sig.code],
145
+ metadata: { deterministic: true, evidence: sig.evidence },
146
+ ...opts.producedAt ? { produced_at: opts.producedAt } : {}
147
+ })
148
+ );
149
+ }
150
+ function behavioralAnalyst() {
151
+ return {
152
+ id: ANALYST_ID,
153
+ description: "Deterministic behavioral/efficiency findings over OTLP spans \u2014 token-growth, output-decay, tool-monoculture, missing self-verification. Zero LLM; model-agnostic by construction.",
154
+ inputKind: "trace-store",
155
+ cost: { kind: "deterministic" },
156
+ version: "1.0.0",
157
+ async analyze(store) {
158
+ const overview = await store.getOverview();
159
+ const spans = [];
160
+ for (const traceId of overview.sample_trace_ids) {
161
+ const viewed = await store.viewTrace({ trace_id: traceId });
162
+ if (viewed.spans) spans.push(...viewed.spans);
163
+ }
164
+ return deriveEfficiencyFindings(computeTraceMetrics(spans));
165
+ }
166
+ };
167
+ }
168
+
169
+ // src/analyst/default-registry.ts
170
+ function buildDefaultAnalystRegistry(opts = {}) {
171
+ const registry = new AnalystRegistry(opts.registry);
172
+ if (opts.includeBehavioral !== false) {
173
+ registry.register(behavioralAnalyst());
174
+ }
175
+ if (opts.ai) {
176
+ const kinds = opts.kinds ?? DEFAULT_TRACE_ANALYST_KINDS;
177
+ for (const spec of kinds) {
178
+ registry.register(createTraceAnalystKind(spec, { ai: opts.ai, model: opts.model }));
179
+ }
180
+ }
181
+ return registry;
182
+ }
183
+
184
+ export {
185
+ computeTraceMetrics,
186
+ deriveEfficiencyFindings,
187
+ behavioralAnalyst,
188
+ buildDefaultAnalystRegistry
189
+ };
190
+ //# sourceMappingURL=chunk-UMMZHCPB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/trace-analyst/behavioral-metrics.ts","../src/analyst/behavioral-analyst.ts","../src/analyst/default-registry.ts"],"sourcesContent":["/**\n * Deterministic behavioral metrics over OTLP spans — pure arithmetic, no LLM.\n *\n * These are the model-independent multiplier: the four trace-quality signals a\n * tolerant analyzer (e.g. HALO) re-derives per run inside the model — token\n * growth, output decay, tool monoculture, missing self-verification — computed\n * here once, in TypeScript, with zero model judgment. A finding that falls out\n * of arithmetic is trivially model-agnostic and cannot hallucinate the trend.\n *\n * General, not trace-specific: the detectors key off token trajectories and\n * tool usage present in any agentic OTLP trace, not any one benchmark.\n */\n\nimport type { TraceAnalystSpan } from './types'\n\nexport type SuboptimalCode =\n | 'monotonic-input-growth'\n | 'output-length-decay'\n | 'single-tool-dependency'\n | 'no-self-verification'\n\nexport interface SuboptimalSignal {\n code: SuboptimalCode\n severity: 'high' | 'medium' | 'low'\n /** Human-readable claim, with the backing numbers inlined. */\n detail: string\n /** The exact figures the detector fired on — auditable, no model in the loop. */\n evidence: Record<string, number | string | boolean>\n}\n\nexport interface BehavioralMetrics {\n llmCallCount: number\n inputTokenTrajectory: number[]\n outputTokenTrajectory: number[]\n toolHistogram: Record<string, number>\n totalToolCalls: number\n distinctTools: number\n /** distinct/total tool calls; 1.0 when there are no tool calls. */\n toolDiversityRatio: number\n hasSelfVerification: boolean\n signals: SuboptimalSignal[]\n}\n\n/** ≥ this input-token growth ratio across a run, with no compression, fires. */\nconst INPUT_GROWTH_FACTOR = 3\n/** Tool-usage signals need at least this many calls to be meaningful. */\nconst MIN_TOOL_CALLS = 3\n/** Tool names matching this are self-verification, not state mutation. */\nconst VERIFY_RE = /verif|eval|inspect|check|assert|validat|review|confirm/i\n\nfunction num(v: unknown): number | null {\n return typeof v === 'number' && Number.isFinite(v) ? v : null\n}\nfunction inputTokensOf(s: TraceAnalystSpan): number | null {\n return num(s.attributes['llm.input_tokens']) ?? num(s.attributes['llm.usage.input_tokens'])\n}\nfunction outputTokensOf(s: TraceAnalystSpan): number | null {\n return num(s.attributes['llm.output_tokens']) ?? num(s.attributes['llm.usage.output_tokens'])\n}\nfunction stepOf(s: TraceAnalystSpan): number | null {\n return num(s.attributes.step)\n}\nfunction toolNameOf(s: TraceAnalystSpan): string | null {\n if (s.tool_name) return s.tool_name\n const t = s.attributes['tool.name']\n return typeof t === 'string' && t.length > 0 ? t : null\n}\n\n/**\n * Reduce a span list to behavioral metrics + fired suboptimality signals.\n * Pure + deterministic: same spans → same output, on any machine, no model.\n */\nexport function computeTraceMetrics(spans: readonly TraceAnalystSpan[]): BehavioralMetrics {\n // Order by step (when present) then start_time so trajectories reflect run order.\n const ordered = [...spans].sort((a, b) => {\n const sa = stepOf(a)\n const sb = stepOf(b)\n if (sa !== null && sb !== null && sa !== sb) return sa - sb\n return a.start_time.localeCompare(b.start_time)\n })\n\n const inputTokenTrajectory: number[] = []\n const outputTokenTrajectory: number[] = []\n const toolHistogram: Record<string, number> = {}\n let hasSelfVerification = false\n\n for (const s of ordered) {\n const inT = inputTokensOf(s)\n if (inT !== null) inputTokenTrajectory.push(inT)\n const outT = outputTokensOf(s)\n if (outT !== null) outputTokenTrajectory.push(outT)\n const tool = toolNameOf(s)\n if (tool) {\n toolHistogram[tool] = (toolHistogram[tool] ?? 0) + 1\n if (VERIFY_RE.test(tool)) hasSelfVerification = true\n }\n }\n\n const totalToolCalls = Object.values(toolHistogram).reduce((a, b) => a + b, 0)\n const distinctTools = Object.keys(toolHistogram).length\n const toolDiversityRatio = totalToolCalls === 0 ? 1 : distinctTools / totalToolCalls\n\n const signals: SuboptimalSignal[] = []\n\n if (inputTokenTrajectory.length >= 3) {\n const first = inputTokenTrajectory[0]!\n const last = inputTokenTrajectory[inputTokenTrajectory.length - 1]!\n const growth = first > 0 ? last / first : 0\n if (last > first && growth >= INPUT_GROWTH_FACTOR) {\n signals.push({\n code: 'monotonic-input-growth',\n severity: 'high',\n detail: `LLM input tokens grew ${growth.toFixed(1)}x (${first}→${last}) across ${inputTokenTrajectory.length} calls — full history re-sent each step with no compression.`,\n evidence: {\n first,\n last,\n growth_x: Number(growth.toFixed(2)),\n calls: inputTokenTrajectory.length,\n },\n })\n }\n }\n\n if (outputTokenTrajectory.length >= 3) {\n const first = outputTokenTrajectory[0]!\n const last = outputTokenTrajectory[outputTokenTrajectory.length - 1]!\n if (last < first) {\n signals.push({\n code: 'output-length-decay',\n severity: 'medium',\n detail: `LLM output tokens shrank ${first}→${last} over ${outputTokenTrajectory.length} calls — less planning/reasoning per step as context grows.`,\n evidence: { first, last, calls: outputTokenTrajectory.length },\n })\n }\n }\n\n if (totalToolCalls >= MIN_TOOL_CALLS && distinctTools === 1) {\n const only = Object.keys(toolHistogram)[0]!\n signals.push({\n code: 'single-tool-dependency',\n severity: 'medium',\n detail: `All ${totalToolCalls} tool calls are \\`${only}\\` — no tool diversity and no fallback path.`,\n evidence: { tool: only, calls: totalToolCalls, distinct_tools: 1 },\n })\n }\n\n if (totalToolCalls >= MIN_TOOL_CALLS && !hasSelfVerification) {\n signals.push({\n code: 'no-self-verification',\n severity: 'medium',\n detail: `${totalToolCalls} tool calls and none verify/inspect/check state — the agent never validates its own actions.`,\n evidence: { tool_calls: totalToolCalls, verification_calls: 0 },\n })\n }\n\n return {\n llmCallCount: inputTokenTrajectory.length,\n inputTokenTrajectory,\n outputTokenTrajectory,\n toolHistogram,\n totalToolCalls,\n distinctTools,\n toolDiversityRatio,\n hasSelfVerification,\n signals,\n }\n}\n","/**\n * `behavioralAnalyst` — a DETERMINISTIC analyst (cost.kind = 'deterministic',\n * never calls the LLM). It produces the efficiency/behavioral findings a\n * tolerant agentic analyzer (HALO) re-derives per run inside the model —\n * context bloat, output decay, tool monoculture, missing self-verification —\n * directly from arithmetic over spans (`computeTraceMetrics`).\n *\n * Why it matters: these findings are model-agnostic BY CONSTRUCTION (no model\n * in the loop), so they cannot return 0 on a weak model the way the Ax-RLM\n * does — and they are strictly more reliable than HALO, which spends tokens\n * re-deriving the same numbers and can hallucinate the trend. The agentic\n * RLM kinds remain for SEMANTIC findings that genuinely need a model; this\n * analyst owns the behavioral class.\n */\n\nimport {\n type BehavioralMetrics,\n computeTraceMetrics,\n type SuboptimalCode,\n} from '../trace-analyst/behavioral-metrics'\nimport type { TraceAnalysisStore } from '../trace-analyst/store'\nimport type { TraceAnalystSpan } from '../trace-analyst/types'\nimport { type Analyst, type AnalystFinding, makeFinding } from './types'\n\nconst RECOMMENDED_ACTION: Record<SuboptimalCode, string> = {\n 'monotonic-input-growth':\n 'Add a context-budget instruction: once prior context exceeds a threshold, summarize earlier steps into a short status line instead of re-sending full history.',\n 'output-length-decay':\n 'Require a minimum planning/reasoning budget per step so late steps do not degrade into terse, error-prone commands.',\n 'single-tool-dependency':\n 'Direct the agent to use the full toolset (verify / inspect / alternate actions), not a single execute call, and to plan a fallback when a call returns an unexpected result.',\n 'no-self-verification':\n 'After every state-mutating action, verify the result (eval / inspect / assert) before proceeding.',\n}\n\nconst ANALYST_ID = 'efficiency-behavioral'\n\n/**\n * Map computed signals → structured AnalystFindings. Pure: no LLM, no clock\n * dependence beyond `produced_at` (overridable for deterministic tests).\n */\nexport function deriveEfficiencyFindings(\n metrics: BehavioralMetrics,\n opts: { analystId?: string; producedAt?: string } = {},\n): AnalystFinding[] {\n const analystId = opts.analystId ?? ANALYST_ID\n return metrics.signals.map((sig) =>\n makeFinding({\n analyst_id: analystId,\n area: 'efficiency',\n subject: sig.code, // kebab — passes the cluster grammar; stable key for diffFindings\n claim: sig.detail,\n severity: sig.severity,\n // Deterministic arithmetic over spans, not a model judgment → certain.\n confidence: 1,\n evidence_refs: [\n {\n kind: 'metric',\n uri: `metric://efficiency/${sig.code}`,\n excerpt: JSON.stringify(sig.evidence),\n },\n ],\n recommended_action: RECOMMENDED_ACTION[sig.code],\n metadata: { deterministic: true, evidence: sig.evidence },\n ...(opts.producedAt ? { produced_at: opts.producedAt } : {}),\n }),\n )\n}\n\n/** The deterministic behavioral/efficiency analyst (no LLM, any-model). */\nexport function behavioralAnalyst(): Analyst<TraceAnalysisStore> {\n return {\n id: ANALYST_ID,\n description:\n 'Deterministic behavioral/efficiency findings over OTLP spans — token-growth, output-decay, tool-monoculture, missing self-verification. Zero LLM; model-agnostic by construction.',\n inputKind: 'trace-store',\n cost: { kind: 'deterministic' },\n version: '1.0.0',\n async analyze(store) {\n const overview = await store.getOverview()\n const spans: TraceAnalystSpan[] = []\n for (const traceId of overview.sample_trace_ids) {\n const viewed = await store.viewTrace({ trace_id: traceId })\n if (viewed.spans) spans.push(...viewed.spans)\n }\n return deriveEfficiencyFindings(computeTraceMetrics(spans))\n },\n }\n}\n","/**\n * `buildDefaultAnalystRegistry` — the canonical analyst suite, so consumers\n * stop hand-wiring `new AnalystRegistry()` + per-kind `createTraceAnalystKind`.\n *\n * The deterministic `behavioralAnalyst` is ALWAYS registered (it needs no\n * model and is model-agnostic by construction). The agentic RLM kinds are\n * registered only when an `ai` service is supplied — so a caller with no LLM\n * still gets the full behavioral/efficiency diagnosis, and the substrate's\n * \"any model (including no model)\" guarantee holds at the suite level.\n */\n\nimport type { AxAIService } from '@ax-llm/ax'\nimport { behavioralAnalyst } from './behavioral-analyst'\nimport { createTraceAnalystKind, type TraceAnalystKindSpec } from './kind-factory'\nimport { DEFAULT_TRACE_ANALYST_KINDS } from './kinds'\nimport { AnalystRegistry, type AnalystRegistryOptions } from './registry'\n\nexport interface DefaultAnalystRegistryOptions {\n /** Ax service for the agentic RLM kinds. Omit → only the deterministic analyst. */\n ai?: AxAIService\n /** Model for the agentic kinds (falls back to the ai service default). */\n model?: string\n /** Which agentic kinds to register when `ai` is present. Default = the shipped suite. */\n kinds?: readonly TraceAnalystKindSpec[]\n /** Set false to omit the deterministic behavioral analyst (default: include). */\n includeBehavioral?: boolean\n /** Forwarded to the AnalystRegistry constructor (signal, tags, priorFindings). */\n registry?: AnalystRegistryOptions\n}\n\nexport function buildDefaultAnalystRegistry(\n opts: DefaultAnalystRegistryOptions = {},\n): AnalystRegistry {\n const registry = new AnalystRegistry(opts.registry)\n if (opts.includeBehavioral !== false) {\n registry.register(behavioralAnalyst())\n }\n if (opts.ai) {\n const kinds = opts.kinds ?? DEFAULT_TRACE_ANALYST_KINDS\n for (const spec of kinds) {\n registry.register(createTraceAnalystKind(spec, { ai: opts.ai, model: opts.model }))\n }\n }\n return registry\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,149 @@
1
+ // src/rl/active-curriculum.ts
2
+ function varianceBasedCurriculum(observations, candidateCells, opts) {
3
+ const variancePrior = opts.variancePrior ?? 0.05;
4
+ const floor = opts.floorPerCell ?? 1;
5
+ const budget = opts.budget;
6
+ const grouped = /* @__PURE__ */ new Map();
7
+ for (const o of observations) {
8
+ const k = `${o.variantId}::${o.scenarioId}`;
9
+ const arr = grouped.get(k) ?? [];
10
+ arr.push(o.score);
11
+ grouped.set(k, arr);
12
+ }
13
+ const cellStats = candidateCells.map((c) => {
14
+ const k = `${c.variantId}::${c.scenarioId}`;
15
+ const samples = grouped.get(k) ?? [];
16
+ const n = samples.length;
17
+ const mean = n === 0 ? 0.5 : samples.reduce((s, v) => s + v, 0) / n;
18
+ const variance = n < 2 ? variancePrior : samples.reduce((s, v) => s + (v - mean) ** 2, 0) / (n - 1) + variancePrior;
19
+ const weight = Math.sqrt(variance) + 1 / Math.sqrt(Math.max(1, n));
20
+ return { variantId: c.variantId, scenarioId: c.scenarioId, n, mean, variance, weight };
21
+ });
22
+ const floorTotal = floor * cellStats.length;
23
+ if (floorTotal >= budget) {
24
+ const each = Math.max(1, Math.floor(budget / Math.max(1, cellStats.length)));
25
+ return cellStats.map((c) => ({
26
+ variantId: c.variantId,
27
+ scenarioId: c.scenarioId,
28
+ count: each,
29
+ reason: `floor allocation (budget tight; n=${c.n})`
30
+ }));
31
+ }
32
+ const remaining = budget - floorTotal;
33
+ const totalWeight = cellStats.reduce((s, c) => s + c.weight, 0);
34
+ return cellStats.map((c) => {
35
+ const proportional = totalWeight === 0 ? 0 : Math.round(c.weight / totalWeight * remaining);
36
+ return {
37
+ variantId: c.variantId,
38
+ scenarioId: c.scenarioId,
39
+ count: floor + proportional,
40
+ reason: `variance ${c.variance.toFixed(3)} (n=${c.n}, mean=${c.mean.toFixed(3)})`
41
+ };
42
+ });
43
+ }
44
+ function thompsonCurriculum(observations, candidateCells, opts) {
45
+ const threshold = opts.decisionThreshold ?? 0.5;
46
+ const alpha0 = opts.priorAlpha ?? 1;
47
+ const beta0 = opts.priorBeta ?? 1;
48
+ const rng = makeRng(opts.seed);
49
+ const grouped = /* @__PURE__ */ new Map();
50
+ for (const o of observations) {
51
+ const k = `${o.variantId}::${o.scenarioId}`;
52
+ const cur = grouped.get(k) ?? { passes: 0, failures: 0 };
53
+ const pass = o.pass ?? o.score >= threshold;
54
+ if (pass) cur.passes += 1;
55
+ else cur.failures += 1;
56
+ grouped.set(k, cur);
57
+ }
58
+ const stats = candidateCells.map((c) => {
59
+ const k = `${c.variantId}::${c.scenarioId}`;
60
+ const cur = grouped.get(k) ?? { passes: 0, failures: 0 };
61
+ const a = alpha0 + cur.passes;
62
+ const b = beta0 + cur.failures;
63
+ const sampled = sampleBeta(a, b, rng);
64
+ const distance = Math.abs(sampled - threshold);
65
+ const variance = a * b / ((a + b) ** 2 * (a + b + 1));
66
+ const sigma = Math.max(0.05, Math.sqrt(variance));
67
+ const weight = Math.exp(-((distance / sigma) ** 2));
68
+ return {
69
+ variantId: c.variantId,
70
+ scenarioId: c.scenarioId,
71
+ n: cur.passes + cur.failures,
72
+ sampled,
73
+ sigma,
74
+ weight,
75
+ a,
76
+ b
77
+ };
78
+ });
79
+ const totalWeight = stats.reduce((s, c) => s + c.weight, 0);
80
+ return stats.map((c) => {
81
+ const proportional = totalWeight === 0 ? 0 : Math.round(c.weight / totalWeight * opts.budget);
82
+ return {
83
+ variantId: c.variantId,
84
+ scenarioId: c.scenarioId,
85
+ count: Math.max(0, proportional),
86
+ reason: `Beta(${c.a.toFixed(1)},${c.b.toFixed(1)}) sample=${c.sampled.toFixed(3)} (target ${threshold})`
87
+ };
88
+ });
89
+ }
90
+ function observationsFromRunRecords(runs, opts = {}) {
91
+ const threshold = opts.passThreshold ?? 0.5;
92
+ const useHoldout = opts.useHoldout ?? true;
93
+ const out = [];
94
+ for (const r of runs) {
95
+ if (!r.scenarioId) continue;
96
+ const score = useHoldout ? r.outcome.holdoutScore ?? r.outcome.searchScore : r.outcome.searchScore ?? r.outcome.holdoutScore;
97
+ if (typeof score !== "number" || !Number.isFinite(score)) continue;
98
+ out.push({
99
+ variantId: r.candidateId,
100
+ scenarioId: r.scenarioId,
101
+ score,
102
+ pass: score >= threshold
103
+ });
104
+ }
105
+ return out;
106
+ }
107
+ function makeRng(seed) {
108
+ if (seed === void 0) return Math.random;
109
+ let s = seed >>> 0;
110
+ return () => {
111
+ s = s + 1831565813 >>> 0;
112
+ let t = s;
113
+ t = Math.imul(t ^ t >>> 15, t | 1);
114
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
115
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
116
+ };
117
+ }
118
+ function sampleBeta(alpha, beta, rng) {
119
+ const a = Math.max(1, alpha);
120
+ const b = Math.max(1, beta);
121
+ const x = sampleGamma(a, rng);
122
+ const y = sampleGamma(b, rng);
123
+ return x / (x + y);
124
+ }
125
+ function sampleGamma(shape, rng) {
126
+ const d = shape - 1 / 3;
127
+ const c = 1 / Math.sqrt(9 * d);
128
+ while (true) {
129
+ let x;
130
+ let v;
131
+ do {
132
+ const u1 = rng() || 1e-12;
133
+ const u2 = rng() || 1e-12;
134
+ x = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
135
+ v = 1 + c * x;
136
+ } while (v <= 0);
137
+ v = v * v * v;
138
+ const u = rng();
139
+ if (u < 1 - 0.0331 * x ** 4) return d * v;
140
+ if (Math.log(u) < 0.5 * x * x + d * (1 - v + Math.log(v))) return d * v;
141
+ }
142
+ }
143
+
144
+ export {
145
+ varianceBasedCurriculum,
146
+ thompsonCurriculum,
147
+ observationsFromRunRecords
148
+ };
149
+ //# sourceMappingURL=chunk-VZSRQ272.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rl/active-curriculum.ts"],"sourcesContent":["/**\n * Adaptive curriculum / active scenario selection.\n *\n * Fixed scenario sets waste sample budget on cells the policy already\n * passes (no information left) and cells the policy never passes (no\n * gradient available either). Active learning over scenarios fixes this\n * by allocating the next sample budget to cells where the policy's\n * outcome is *uncertain* — those carry the most decision-relevant signal.\n *\n * This module ships two complementary strategies:\n *\n * 1. **Variance-based** — score each (variant, scenario) cell by the\n * empirical variance of past observations. Allocate next-round budget\n * proportional to variance. Standard active-learning-by-uncertainty\n * heuristic; works well when the policy is non-deterministic and\n * cells differ in observation noise.\n *\n * 2. **Bandit-based (Thompson sampling)** — model each (variant,\n * scenario) cell as a Beta-Bernoulli arm; sample a posterior; pick\n * cells whose posterior mean is closest to the per-scenario decision\n * threshold. The right primitive when scenarios are\n * \"pass/fail\" rather than continuous, and when promotion gates fire\n * at a known threshold (e.g., 0.5).\n *\n * The output is a *next-round budget allocation* — a list of (variant,\n * scenario, count) triples. The consumer's matrix runner consumes the\n * allocation, runs those cells, feeds the new observations back. Loop.\n *\n * Out of scope (deliberate): scenario *generation* — that's the\n * adversarial primitive's job. This module allocates over an existing\n * scenario pool.\n */\n\nimport type { RunRecord } from '../run-record'\n\nexport interface CellObservation {\n variantId: string\n scenarioId: string\n /** Observed score in [0, 1]. */\n score: number\n /** For Bernoulli arms — derive from the score with a threshold if needed. */\n pass?: boolean\n}\n\nexport interface CurriculumAllocation {\n variantId: string\n scenarioId: string\n /** How many additional reps to run on this cell. */\n count: number\n /** Strategy-specific reason for the allocation. */\n reason: string\n}\n\nexport interface VarianceCurriculumOptions {\n /** Total reps to allocate across all cells. */\n budget: number\n /**\n * Smoothing prior on variance — keeps the allocator from concentrating\n * on a cell with one observation just because its 1-sample variance is\n * 0. Default 0.05.\n */\n variancePrior?: number\n /**\n * Minimum reps per cell — even when the variance estimate is low, give\n * every cell at least this many. Default 1.\n */\n floorPerCell?: number\n}\n\n/**\n * Variance-proportional allocation. For each cell, estimate variance from\n * past observations + a prior, then allocate the budget proportional to\n * (sqrt(variance) + 1/sqrt(n)) — a classical optimal-allocation rule\n * (Neyman 1934) that balances \"explore noisy cells\" with \"explore\n * under-sampled cells.\"\n */\nexport function varianceBasedCurriculum(\n observations: CellObservation[],\n candidateCells: Array<{ variantId: string; scenarioId: string }>,\n opts: VarianceCurriculumOptions,\n): CurriculumAllocation[] {\n const variancePrior = opts.variancePrior ?? 0.05\n const floor = opts.floorPerCell ?? 1\n const budget = opts.budget\n\n const grouped = new Map<string, number[]>()\n for (const o of observations) {\n const k = `${o.variantId}::${o.scenarioId}`\n const arr = grouped.get(k) ?? []\n arr.push(o.score)\n grouped.set(k, arr)\n }\n\n const cellStats = candidateCells.map((c) => {\n const k = `${c.variantId}::${c.scenarioId}`\n const samples = grouped.get(k) ?? []\n const n = samples.length\n const mean = n === 0 ? 0.5 : samples.reduce((s, v) => s + v, 0) / n\n const variance =\n n < 2\n ? variancePrior\n : samples.reduce((s, v) => s + (v - mean) ** 2, 0) / (n - 1) + variancePrior\n // Neyman optimal allocation: weight ∝ √variance; add √(1/n) to break\n // ties toward under-sampled cells.\n const weight = Math.sqrt(variance) + 1 / Math.sqrt(Math.max(1, n))\n return { variantId: c.variantId, scenarioId: c.scenarioId, n, mean, variance, weight }\n })\n\n // Reserve floor*N for the floor; allocate the rest proportional to weight.\n const floorTotal = floor * cellStats.length\n if (floorTotal >= budget) {\n const each = Math.max(1, Math.floor(budget / Math.max(1, cellStats.length)))\n return cellStats.map((c) => ({\n variantId: c.variantId,\n scenarioId: c.scenarioId,\n count: each,\n reason: `floor allocation (budget tight; n=${c.n})`,\n }))\n }\n const remaining = budget - floorTotal\n const totalWeight = cellStats.reduce((s, c) => s + c.weight, 0)\n return cellStats.map((c) => {\n const proportional = totalWeight === 0 ? 0 : Math.round((c.weight / totalWeight) * remaining)\n return {\n variantId: c.variantId,\n scenarioId: c.scenarioId,\n count: floor + proportional,\n reason: `variance ${c.variance.toFixed(3)} (n=${c.n}, mean=${c.mean.toFixed(3)})`,\n }\n })\n}\n\nexport interface ThompsonCurriculumOptions {\n budget: number\n /**\n * The per-scenario decision threshold. Cells whose posterior mean is\n * closest to this get the most budget — that's where the next observation\n * has the highest information value for the gate decision. Default 0.5.\n */\n decisionThreshold?: number\n /** Beta prior parameters. Default α=β=1 (uniform). */\n priorAlpha?: number\n priorBeta?: number\n /** Seed the Thompson sampler. Default unset (Math.random). */\n seed?: number\n}\n\n/**\n * Thompson-sampling-style allocation for pass/fail cells. For each cell:\n *\n * - Maintain Beta(α + passes, β + failures) posterior on pass-rate\n * - Allocation weight ∝ exp(-((sampledMean - threshold) / σ)^2):\n * cells whose sampled posterior straddles the decision boundary get\n * the most weight; cells already clearly above or below get less.\n *\n * This is the right primitive when promotion gates fire at a known\n * threshold and you want to sharpen the posterior near the boundary.\n */\nexport function thompsonCurriculum(\n observations: CellObservation[],\n candidateCells: Array<{ variantId: string; scenarioId: string }>,\n opts: ThompsonCurriculumOptions,\n): CurriculumAllocation[] {\n const threshold = opts.decisionThreshold ?? 0.5\n const alpha0 = opts.priorAlpha ?? 1\n const beta0 = opts.priorBeta ?? 1\n const rng = makeRng(opts.seed)\n\n const grouped = new Map<string, { passes: number; failures: number }>()\n for (const o of observations) {\n const k = `${o.variantId}::${o.scenarioId}`\n const cur = grouped.get(k) ?? { passes: 0, failures: 0 }\n const pass = o.pass ?? o.score >= threshold\n if (pass) cur.passes += 1\n else cur.failures += 1\n grouped.set(k, cur)\n }\n\n const stats = candidateCells.map((c) => {\n const k = `${c.variantId}::${c.scenarioId}`\n const cur = grouped.get(k) ?? { passes: 0, failures: 0 }\n const a = alpha0 + cur.passes\n const b = beta0 + cur.failures\n // Sample a single Beta draw — the Thompson signal.\n const sampled = sampleBeta(a, b, rng)\n const distance = Math.abs(sampled - threshold)\n // Information-near-threshold weight: closer = higher.\n // Use Gaussian-shaped kernel with σ tuned to posterior std.\n const variance = (a * b) / ((a + b) ** 2 * (a + b + 1))\n const sigma = Math.max(0.05, Math.sqrt(variance))\n const weight = Math.exp(-((distance / sigma) ** 2))\n return {\n variantId: c.variantId,\n scenarioId: c.scenarioId,\n n: cur.passes + cur.failures,\n sampled,\n sigma,\n weight,\n a,\n b,\n }\n })\n\n const totalWeight = stats.reduce((s, c) => s + c.weight, 0)\n return stats.map((c) => {\n const proportional = totalWeight === 0 ? 0 : Math.round((c.weight / totalWeight) * opts.budget)\n return {\n variantId: c.variantId,\n scenarioId: c.scenarioId,\n count: Math.max(0, proportional),\n reason: `Beta(${c.a.toFixed(1)},${c.b.toFixed(1)}) sample=${c.sampled.toFixed(3)} (target ${threshold})`,\n }\n })\n}\n\n/** Convenience: extract `CellObservation[]` directly from `RunRecord[]`. */\nexport function observationsFromRunRecords(\n runs: RunRecord[],\n opts: { passThreshold?: number; useHoldout?: boolean } = {},\n): CellObservation[] {\n const threshold = opts.passThreshold ?? 0.5\n const useHoldout = opts.useHoldout ?? true\n const out: CellObservation[] = []\n for (const r of runs) {\n if (!r.scenarioId) continue\n const score = useHoldout\n ? (r.outcome.holdoutScore ?? r.outcome.searchScore)\n : (r.outcome.searchScore ?? r.outcome.holdoutScore)\n if (typeof score !== 'number' || !Number.isFinite(score)) continue\n out.push({\n variantId: r.candidateId,\n scenarioId: r.scenarioId,\n score,\n pass: score >= threshold,\n })\n }\n return out\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────\n\nfunction makeRng(seed?: number): () => number {\n if (seed === undefined) return Math.random\n let s = seed >>> 0\n return () => {\n s = (s + 0x6d2b79f5) >>> 0\n let t = s\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/**\n * Sample from Beta(α, β) via the Marsaglia–Tsang method using two Gamma\n * variates. Accuracy is good for α, β > 1; we floor the parameters at 1\n * to avoid degenerate cases.\n */\nfunction sampleBeta(alpha: number, beta: number, rng: () => number): number {\n const a = Math.max(1, alpha)\n const b = Math.max(1, beta)\n const x = sampleGamma(a, rng)\n const y = sampleGamma(b, rng)\n return x / (x + y)\n}\n\nfunction sampleGamma(shape: number, rng: () => number): number {\n // Marsaglia–Tsang for shape ≥ 1.\n const d = shape - 1 / 3\n const c = 1 / Math.sqrt(9 * d)\n while (true) {\n let x: number\n let v: number\n do {\n const u1 = rng() || 1e-12\n const u2 = rng() || 1e-12\n // Box-Muller for a normal sample.\n x = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2)\n v = 1 + c * x\n } while (v <= 0)\n v = v * v * v\n const u = rng()\n if (u < 1 - 0.0331 * x ** 4) return d * v\n if (Math.log(u) < 0.5 * x * x + d * (1 - v + Math.log(v))) return d * v\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,14 +1,10 @@
1
- import {
2
- AnalystRegistry,
3
- DEFAULT_TRACE_ANALYST_KINDS,
4
- computeFindingId,
5
- createTraceAnalystKind,
6
- makeFinding
7
- } from "./chunk-VIDQF3F5.js";
8
1
  import {
9
2
  LlmClient,
10
3
  callLlmJson
11
4
  } from "./chunk-CVVHBFGN.js";
5
+ import {
6
+ computeFindingId
7
+ } from "./chunk-45EEMHTC.js";
12
8
  import {
13
9
  NotFoundError
14
10
  } from "./chunk-3BFEG2F6.js";
@@ -111,180 +107,6 @@ function resolveModel(req, defaultModel) {
111
107
  return { ...req, model: defaultModel };
112
108
  }
113
109
 
114
- // src/trace-analyst/behavioral-metrics.ts
115
- var INPUT_GROWTH_FACTOR = 3;
116
- var MIN_TOOL_CALLS = 3;
117
- var VERIFY_RE = /verif|eval|inspect|check|assert|validat|review|confirm/i;
118
- function num(v) {
119
- return typeof v === "number" && Number.isFinite(v) ? v : null;
120
- }
121
- function inputTokensOf(s) {
122
- return num(s.attributes["llm.input_tokens"]) ?? num(s.attributes["llm.usage.input_tokens"]);
123
- }
124
- function outputTokensOf(s) {
125
- return num(s.attributes["llm.output_tokens"]) ?? num(s.attributes["llm.usage.output_tokens"]);
126
- }
127
- function stepOf(s) {
128
- return num(s.attributes.step);
129
- }
130
- function toolNameOf(s) {
131
- if (s.tool_name) return s.tool_name;
132
- const t = s.attributes["tool.name"];
133
- return typeof t === "string" && t.length > 0 ? t : null;
134
- }
135
- function computeTraceMetrics(spans) {
136
- const ordered = [...spans].sort((a, b) => {
137
- const sa = stepOf(a);
138
- const sb = stepOf(b);
139
- if (sa !== null && sb !== null && sa !== sb) return sa - sb;
140
- return a.start_time.localeCompare(b.start_time);
141
- });
142
- const inputTokenTrajectory = [];
143
- const outputTokenTrajectory = [];
144
- const toolHistogram = {};
145
- let hasSelfVerification = false;
146
- for (const s of ordered) {
147
- const inT = inputTokensOf(s);
148
- if (inT !== null) inputTokenTrajectory.push(inT);
149
- const outT = outputTokensOf(s);
150
- if (outT !== null) outputTokenTrajectory.push(outT);
151
- const tool = toolNameOf(s);
152
- if (tool) {
153
- toolHistogram[tool] = (toolHistogram[tool] ?? 0) + 1;
154
- if (VERIFY_RE.test(tool)) hasSelfVerification = true;
155
- }
156
- }
157
- const totalToolCalls = Object.values(toolHistogram).reduce((a, b) => a + b, 0);
158
- const distinctTools = Object.keys(toolHistogram).length;
159
- const toolDiversityRatio = totalToolCalls === 0 ? 1 : distinctTools / totalToolCalls;
160
- const signals = [];
161
- if (inputTokenTrajectory.length >= 3) {
162
- const first = inputTokenTrajectory[0];
163
- const last = inputTokenTrajectory[inputTokenTrajectory.length - 1];
164
- const growth = first > 0 ? last / first : 0;
165
- if (last > first && growth >= INPUT_GROWTH_FACTOR) {
166
- signals.push({
167
- code: "monotonic-input-growth",
168
- severity: "high",
169
- detail: `LLM input tokens grew ${growth.toFixed(1)}x (${first}\u2192${last}) across ${inputTokenTrajectory.length} calls \u2014 full history re-sent each step with no compression.`,
170
- evidence: {
171
- first,
172
- last,
173
- growth_x: Number(growth.toFixed(2)),
174
- calls: inputTokenTrajectory.length
175
- }
176
- });
177
- }
178
- }
179
- if (outputTokenTrajectory.length >= 3) {
180
- const first = outputTokenTrajectory[0];
181
- const last = outputTokenTrajectory[outputTokenTrajectory.length - 1];
182
- if (last < first) {
183
- signals.push({
184
- code: "output-length-decay",
185
- severity: "medium",
186
- detail: `LLM output tokens shrank ${first}\u2192${last} over ${outputTokenTrajectory.length} calls \u2014 less planning/reasoning per step as context grows.`,
187
- evidence: { first, last, calls: outputTokenTrajectory.length }
188
- });
189
- }
190
- }
191
- if (totalToolCalls >= MIN_TOOL_CALLS && distinctTools === 1) {
192
- const only = Object.keys(toolHistogram)[0];
193
- signals.push({
194
- code: "single-tool-dependency",
195
- severity: "medium",
196
- detail: `All ${totalToolCalls} tool calls are \`${only}\` \u2014 no tool diversity and no fallback path.`,
197
- evidence: { tool: only, calls: totalToolCalls, distinct_tools: 1 }
198
- });
199
- }
200
- if (totalToolCalls >= MIN_TOOL_CALLS && !hasSelfVerification) {
201
- signals.push({
202
- code: "no-self-verification",
203
- severity: "medium",
204
- detail: `${totalToolCalls} tool calls and none verify/inspect/check state \u2014 the agent never validates its own actions.`,
205
- evidence: { tool_calls: totalToolCalls, verification_calls: 0 }
206
- });
207
- }
208
- return {
209
- llmCallCount: inputTokenTrajectory.length,
210
- inputTokenTrajectory,
211
- outputTokenTrajectory,
212
- toolHistogram,
213
- totalToolCalls,
214
- distinctTools,
215
- toolDiversityRatio,
216
- hasSelfVerification,
217
- signals
218
- };
219
- }
220
-
221
- // src/analyst/behavioral-analyst.ts
222
- var RECOMMENDED_ACTION = {
223
- "monotonic-input-growth": "Add a context-budget instruction: once prior context exceeds a threshold, summarize earlier steps into a short status line instead of re-sending full history.",
224
- "output-length-decay": "Require a minimum planning/reasoning budget per step so late steps do not degrade into terse, error-prone commands.",
225
- "single-tool-dependency": "Direct the agent to use the full toolset (verify / inspect / alternate actions), not a single execute call, and to plan a fallback when a call returns an unexpected result.",
226
- "no-self-verification": "After every state-mutating action, verify the result (eval / inspect / assert) before proceeding."
227
- };
228
- var ANALYST_ID = "efficiency-behavioral";
229
- function deriveEfficiencyFindings(metrics, opts = {}) {
230
- const analystId = opts.analystId ?? ANALYST_ID;
231
- return metrics.signals.map(
232
- (sig) => makeFinding({
233
- analyst_id: analystId,
234
- area: "efficiency",
235
- subject: sig.code,
236
- // kebab — passes the cluster grammar; stable key for diffFindings
237
- claim: sig.detail,
238
- severity: sig.severity,
239
- // Deterministic arithmetic over spans, not a model judgment → certain.
240
- confidence: 1,
241
- evidence_refs: [
242
- {
243
- kind: "metric",
244
- uri: `metric://efficiency/${sig.code}`,
245
- excerpt: JSON.stringify(sig.evidence)
246
- }
247
- ],
248
- recommended_action: RECOMMENDED_ACTION[sig.code],
249
- metadata: { deterministic: true, evidence: sig.evidence },
250
- ...opts.producedAt ? { produced_at: opts.producedAt } : {}
251
- })
252
- );
253
- }
254
- function behavioralAnalyst() {
255
- return {
256
- id: ANALYST_ID,
257
- description: "Deterministic behavioral/efficiency findings over OTLP spans \u2014 token-growth, output-decay, tool-monoculture, missing self-verification. Zero LLM; model-agnostic by construction.",
258
- inputKind: "trace-store",
259
- cost: { kind: "deterministic" },
260
- version: "1.0.0",
261
- async analyze(store) {
262
- const overview = await store.getOverview();
263
- const spans = [];
264
- for (const traceId of overview.sample_trace_ids) {
265
- const viewed = await store.viewTrace({ trace_id: traceId });
266
- if (viewed.spans) spans.push(...viewed.spans);
267
- }
268
- return deriveEfficiencyFindings(computeTraceMetrics(spans));
269
- }
270
- };
271
- }
272
-
273
- // src/analyst/default-registry.ts
274
- function buildDefaultAnalystRegistry(opts = {}) {
275
- const registry = new AnalystRegistry(opts.registry);
276
- if (opts.includeBehavioral !== false) {
277
- registry.register(behavioralAnalyst());
278
- }
279
- if (opts.ai) {
280
- const kinds = opts.kinds ?? DEFAULT_TRACE_ANALYST_KINDS;
281
- for (const spec of kinds) {
282
- registry.register(createTraceAnalystKind(spec, { ai: opts.ai, model: opts.model }));
283
- }
284
- }
285
- return registry;
286
- }
287
-
288
110
  // src/concurrency.ts
289
111
  var Mutex = class {
290
112
  locked = false;
@@ -562,12 +384,12 @@ function buildSkillUsageReport(config) {
562
384
  });
563
385
  return { generatedFromTraces: transcripts, records };
564
386
  }
565
- var ANALYST_ID2 = "skill-usage";
387
+ var ANALYST_ID = "skill-usage";
566
388
  function finding(area, subject, claim, severity, confidence, producedAt, recommended, evidenceUri, rationale) {
567
389
  return {
568
390
  schema_version: "1.0.0",
569
- finding_id: computeFindingId({ analyst_id: ANALYST_ID2, area, subject, claim }),
570
- analyst_id: ANALYST_ID2,
391
+ finding_id: computeFindingId({ analyst_id: ANALYST_ID, area, subject, claim }),
392
+ analyst_id: ANALYST_ID,
571
393
  produced_at: producedAt,
572
394
  severity,
573
395
  area,
@@ -687,7 +509,7 @@ function emitSkillUsageFindings(report, producedAt) {
687
509
  return out;
688
510
  }
689
511
  var SkillUsageAnalyst = class {
690
- id = ANALYST_ID2;
512
+ id = ANALYST_ID;
691
513
  description = "Deterministic multi-signal skill-usage analysis: flags dead skills, measurement-invisible (orchestrated) usage, discovery gaps, public-repo leaks, bloat, missing evals, and missing run-logging.";
692
514
  inputKind = "custom";
693
515
  cost = { kind: "deterministic", est_usd_per_run: 0 };
@@ -1049,10 +871,6 @@ function createSemanticConceptJudge(options = {}) {
1049
871
  export {
1050
872
  createAnalystAi,
1051
873
  createChatClient,
1052
- computeTraceMetrics,
1053
- deriveEfficiencyFindings,
1054
- behavioralAnalyst,
1055
- buildDefaultAnalystRegistry,
1056
874
  Mutex,
1057
875
  LockedJsonlAppender,
1058
876
  resetLockedAppendersForTesting,
@@ -1072,4 +890,4 @@ export {
1072
890
  runSemanticConceptJudge,
1073
891
  createSemanticConceptJudge
1074
892
  };
1075
- //# sourceMappingURL=chunk-L5G7OUKD.js.map
893
+ //# sourceMappingURL=chunk-XY4DDNEG.js.map