@tangle-network/agent-runtime 0.44.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -203
- package/dist/agent.d.ts +5 -4
- package/dist/agent.js +5 -7
- package/dist/agent.js.map +1 -1
- package/dist/analyst-loop.d.ts +65 -4
- package/dist/analyst-loop.js +6 -1
- package/dist/audit.d.ts +93 -0
- package/dist/audit.js +312 -0
- package/dist/audit.js.map +1 -0
- package/dist/chunk-4B6U4CVQ.js +15 -0
- package/dist/chunk-4B6U4CVQ.js.map +1 -0
- package/dist/chunk-FK53TXOP.js +603 -0
- package/dist/chunk-FK53TXOP.js.map +1 -0
- package/dist/{chunk-SKUZZCHE.js → chunk-IJ6FGOPO.js} +5 -5
- package/dist/chunk-IJ6FGOPO.js.map +1 -0
- package/dist/{chunk-HVYOHJHK.js → chunk-IJGS6J7X.js} +2 -2
- package/dist/chunk-IJGS6J7X.js.map +1 -0
- package/dist/chunk-KEWO4KI6.js +3599 -0
- package/dist/chunk-KEWO4KI6.js.map +1 -0
- package/dist/{chunk-NRZOXCJK.js → chunk-KSMX62JF.js} +2 -2
- package/dist/{chunk-GFKVVRQ7.js → chunk-NYN5RTLP.js} +11 -10
- package/dist/chunk-NYN5RTLP.js.map +1 -0
- package/dist/chunk-PRX45WE2.js +264 -0
- package/dist/chunk-PRX45WE2.js.map +1 -0
- package/dist/{chunk-3HMHSN22.js → chunk-QR4UUC5P.js} +6 -6
- package/dist/chunk-QR4UUC5P.js.map +1 -0
- package/dist/chunk-WIR4HOOJ.js +27 -0
- package/dist/chunk-WIR4HOOJ.js.map +1 -0
- package/dist/{chunk-KDMRUD2P.js → chunk-Z2QXVBA6.js} +296 -8
- package/dist/chunk-Z2QXVBA6.js.map +1 -0
- package/dist/coder-CczgMqFx.d.ts +114 -0
- package/dist/dynamic-BvllHV6M.d.ts +221 -0
- package/dist/{improvement-adapter-BC4HhuAR.d.ts → improvement-adapter-CWegd3vw.d.ts} +1 -1
- package/dist/improvement.d.ts +2 -3
- package/dist/improvement.js +0 -5
- package/dist/improvement.js.map +1 -1
- package/dist/index.d.ts +123 -10
- package/dist/index.js +398 -10
- package/dist/index.js.map +1 -1
- package/dist/{kb-gate-D0ZIhFOU.d.ts → kb-gate-D9GBocLN.d.ts} +82 -5
- package/dist/{loop-runner-bin-BLMa8He3.d.ts → loop-runner-bin-CPrCoKqC.d.ts} +14 -10
- package/dist/loop-runner-bin.d.ts +9 -7
- package/dist/loop-runner-bin.js +6 -8
- package/dist/loops.d.ts +7 -393
- package/dist/loops.js +94 -25
- package/dist/mcp/bin.js +7 -7
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +284 -11
- package/dist/mcp/index.js +341 -9
- package/dist/mcp/index.js.map +1 -1
- package/dist/{otel-export-wFDmmurL.d.ts → otel-export-Dy2DyUCU.d.ts} +1 -1
- package/dist/profiles.d.ts +385 -86
- package/dist/profiles.js +549 -4
- package/dist/profiles.js.map +1 -1
- package/dist/{run-loop-C4L1Sted.d.ts → run-loop--hSoIknW.d.ts} +35 -12
- package/dist/runtime-hooks-C7JwKb9E.d.ts +70 -0
- package/dist/runtime.d.ts +1860 -0
- package/dist/runtime.js +114 -0
- package/dist/runtime.js.map +1 -0
- package/dist/substrate-CUgk7F7s.d.ts +77 -0
- package/dist/topology.d.ts +73 -0
- package/dist/topology.js +111 -0
- package/dist/topology.js.map +1 -0
- package/dist/types-1HbsFa7H.d.ts +438 -0
- package/dist/{types-p8dWBIXL.d.ts → types-BtRLF2U3.d.ts} +1 -1
- package/dist/{types-DbJzz2uf.d.ts → types-DdzkffAm.d.ts} +95 -1
- package/dist/workflow.d.ts +3 -2
- package/dist/workflow.js +4 -5
- package/dist/workflow.js.map +1 -1
- package/package.json +26 -6
- package/skills/agent-runtime-adoption/SKILL.md +29 -26
- package/dist/chunk-3HMHSN22.js.map +0 -1
- package/dist/chunk-GFKVVRQ7.js.map +0 -1
- package/dist/chunk-HVYOHJHK.js.map +0 -1
- package/dist/chunk-KDMRUD2P.js.map +0 -1
- package/dist/chunk-PY6NMZYX.js +0 -52
- package/dist/chunk-PY6NMZYX.js.map +0 -1
- package/dist/chunk-S7JXV32P.js +0 -947
- package/dist/chunk-S7JXV32P.js.map +0 -1
- package/dist/chunk-SKUZZCHE.js.map +0 -1
- package/dist/chunk-SQSCRJ7U.js +0 -65
- package/dist/chunk-SQSCRJ7U.js.map +0 -1
- package/dist/chunk-VOX6Z3II.js +0 -90
- package/dist/chunk-VOX6Z3II.js.map +0 -1
- package/dist/chunk-XBUG326M.js +0 -261
- package/dist/chunk-XBUG326M.js.map +0 -1
- package/dist/dynamic-wUgp6UKs.d.ts +0 -108
- package/dist/optimize-prompt-D-urF2wW.d.ts +0 -129
- /package/dist/{chunk-NRZOXCJK.js.map → chunk-KSMX62JF.js.map} +0 -0
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AnalystError,
|
|
3
|
+
extractLlmCallEvent,
|
|
4
|
+
randomSuffix
|
|
5
|
+
} from "./chunk-PRX45WE2.js";
|
|
6
|
+
|
|
7
|
+
// src/analyst-loop/iterations-to-trace-store.ts
|
|
8
|
+
import {
|
|
9
|
+
DEFAULT_TRACE_ANALYST_BUDGETS,
|
|
10
|
+
TRACE_ANALYST_TRUNCATION_MARKER_PREFIX
|
|
11
|
+
} from "@tangle-network/agent-eval";
|
|
12
|
+
var bytesOf = (v) => Buffer.byteLength(JSON.stringify(v) ?? "", "utf8");
|
|
13
|
+
var iso = (ms) => new Date(ms).toISOString();
|
|
14
|
+
function normalizeSignature(message) {
|
|
15
|
+
return message.replace(/0x[0-9a-fA-F]+/g, "HEX").replace(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g, "UUID").replace(/(\/[\w.-]+){2,}/g, "PATH").replace(/\b\d+(\.\d+)?(ms|s|m|h)\b/g, "DUR").replace(/\b\d+\b/g, "#").replace(/\s+/g, " ").trim().slice(0, 200);
|
|
16
|
+
}
|
|
17
|
+
function spanKindFor(event, agentRunName) {
|
|
18
|
+
const llm = extractLlmCallEvent(event, agentRunName);
|
|
19
|
+
if (llm) return { kind: "LLM", model: llm.model ?? null, tool: null };
|
|
20
|
+
const type = String(event?.type ?? "");
|
|
21
|
+
if (/tool/i.test(type)) {
|
|
22
|
+
const d = event?.data;
|
|
23
|
+
const tool = typeof d?.name === "string" ? d.name : typeof d?.tool === "string" ? d.tool : type;
|
|
24
|
+
return { kind: "TOOL", model: null, tool };
|
|
25
|
+
}
|
|
26
|
+
return { kind: "SPAN", model: null, tool: null };
|
|
27
|
+
}
|
|
28
|
+
function errorMessageOf(event) {
|
|
29
|
+
const type = String(event?.type ?? "");
|
|
30
|
+
const d = event?.data;
|
|
31
|
+
if (/error|fail/i.test(type) || d?.error) {
|
|
32
|
+
const m = d?.error ?? d?.message;
|
|
33
|
+
return typeof m === "string" ? m : `${type} error`;
|
|
34
|
+
}
|
|
35
|
+
return void 0;
|
|
36
|
+
}
|
|
37
|
+
function projectIteration(iter) {
|
|
38
|
+
const traceId = iter.events.find((e) => e?.data?.sandboxId)?.data?.sandboxId ?? `iter-${iter.index}`;
|
|
39
|
+
const start = iso(iter.startedAt);
|
|
40
|
+
const end = iso(iter.endedAt || iter.startedAt);
|
|
41
|
+
const durationMs = Math.max(0, (iter.endedAt || iter.startedAt) - iter.startedAt);
|
|
42
|
+
const rootId = `${traceId}:root`;
|
|
43
|
+
const iterErrored = Boolean(iter.error) || iter.verdict?.valid === false;
|
|
44
|
+
const spans = [
|
|
45
|
+
{
|
|
46
|
+
trace_id: traceId,
|
|
47
|
+
span_id: rootId,
|
|
48
|
+
parent_span_id: null,
|
|
49
|
+
name: iter.agentRunName,
|
|
50
|
+
kind: "AGENT",
|
|
51
|
+
start_time: start,
|
|
52
|
+
end_time: end,
|
|
53
|
+
duration_ms: durationMs,
|
|
54
|
+
status: iter.error ? "ERROR" : "OK",
|
|
55
|
+
status_message: iter.error?.message,
|
|
56
|
+
service_name: "agent-runtime",
|
|
57
|
+
agent_name: iter.agentRunName,
|
|
58
|
+
model_name: null,
|
|
59
|
+
tool_name: null,
|
|
60
|
+
attributes: {
|
|
61
|
+
"iteration.index": iter.index,
|
|
62
|
+
"verdict.valid": iter.verdict?.valid,
|
|
63
|
+
"verdict.score": iter.verdict?.score,
|
|
64
|
+
"output.preview": iter.output === void 0 ? void 0 : String(iter.output).slice(0, 2e3)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
];
|
|
68
|
+
const models = /* @__PURE__ */ new Set();
|
|
69
|
+
const tools = /* @__PURE__ */ new Set();
|
|
70
|
+
iter.events.forEach((event, i) => {
|
|
71
|
+
const { kind, model, tool } = spanKindFor(event, iter.agentRunName);
|
|
72
|
+
if (model) models.add(model);
|
|
73
|
+
if (tool) tools.add(tool);
|
|
74
|
+
const errMsg = errorMessageOf(event);
|
|
75
|
+
spans.push({
|
|
76
|
+
trace_id: traceId,
|
|
77
|
+
span_id: `${traceId}:e${i}`,
|
|
78
|
+
parent_span_id: rootId,
|
|
79
|
+
name: String(event?.type ?? "event"),
|
|
80
|
+
kind,
|
|
81
|
+
start_time: start,
|
|
82
|
+
end_time: end,
|
|
83
|
+
duration_ms: 0,
|
|
84
|
+
status: errMsg ? "ERROR" : "OK",
|
|
85
|
+
status_message: errMsg,
|
|
86
|
+
service_name: "agent-runtime",
|
|
87
|
+
agent_name: iter.agentRunName,
|
|
88
|
+
model_name: model,
|
|
89
|
+
tool_name: tool,
|
|
90
|
+
attributes: event?.data ?? {}
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
const hasErrors = spans.some((s) => s.status === "ERROR") || iterErrored;
|
|
94
|
+
const summary = {
|
|
95
|
+
trace_id: traceId,
|
|
96
|
+
service_name: "agent-runtime",
|
|
97
|
+
agent_name: iter.agentRunName,
|
|
98
|
+
span_count: spans.length,
|
|
99
|
+
has_errors: hasErrors,
|
|
100
|
+
start_time: start,
|
|
101
|
+
end_time: end,
|
|
102
|
+
duration_ms: durationMs,
|
|
103
|
+
raw_jsonl_bytes: bytesOf(spans),
|
|
104
|
+
models: [...models],
|
|
105
|
+
tools: [...tools]
|
|
106
|
+
};
|
|
107
|
+
return { summary, spans, rawBytes: summary.raw_jsonl_bytes };
|
|
108
|
+
}
|
|
109
|
+
function matchesFilters(t, f) {
|
|
110
|
+
if (!f) return true;
|
|
111
|
+
if (f.has_errors !== void 0 && t.summary.has_errors !== f.has_errors) return false;
|
|
112
|
+
if (f.service_names?.length && !f.service_names.includes(t.summary.service_name ?? ""))
|
|
113
|
+
return false;
|
|
114
|
+
if (f.agent_names?.length && !f.agent_names.includes(t.summary.agent_name ?? "")) return false;
|
|
115
|
+
if (f.model_names?.length && !f.model_names.some((m) => t.summary.models.includes(m)))
|
|
116
|
+
return false;
|
|
117
|
+
if (f.tool_names?.length && !f.tool_names.some((tn) => t.summary.tools.includes(tn))) return false;
|
|
118
|
+
if (f.start_time_after && t.summary.start_time < f.start_time_after) return false;
|
|
119
|
+
if (f.start_time_before && t.summary.start_time > f.start_time_before) return false;
|
|
120
|
+
if (f.regex_pattern && !new RegExp(f.regex_pattern).test(JSON.stringify(t.spans))) return false;
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
function capAttributes(attributes, perAttrCap) {
|
|
124
|
+
let truncated = 0;
|
|
125
|
+
const capped = {};
|
|
126
|
+
for (const [k, v] of Object.entries(attributes)) {
|
|
127
|
+
const s = typeof v === "string" ? v : JSON.stringify(v);
|
|
128
|
+
if (typeof s === "string" && s.length > perAttrCap) {
|
|
129
|
+
truncated += 1;
|
|
130
|
+
capped[k] = `${TRACE_ANALYST_TRUNCATION_MARKER_PREFIX} ${s.length}b]${s.slice(0, perAttrCap)}`;
|
|
131
|
+
} else {
|
|
132
|
+
capped[k] = v;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return { capped, truncated };
|
|
136
|
+
}
|
|
137
|
+
function iterationsToTraceStore(iterations, budgets = DEFAULT_TRACE_ANALYST_BUDGETS) {
|
|
138
|
+
if (iterations.length === 0) {
|
|
139
|
+
throw new AnalystError("iterationsToTraceStore: no iterations to analyze (empty round)");
|
|
140
|
+
}
|
|
141
|
+
const traces = iterations.map((it) => projectIteration(it));
|
|
142
|
+
const byId = new Map(traces.map((t) => [t.summary.trace_id, t]));
|
|
143
|
+
const buildClusters = (set) => {
|
|
144
|
+
const map = /* @__PURE__ */ new Map();
|
|
145
|
+
for (const t of set) {
|
|
146
|
+
for (const s of t.spans) {
|
|
147
|
+
if (s.status !== "ERROR" || !s.status_message) continue;
|
|
148
|
+
const sig = normalizeSignature(s.status_message);
|
|
149
|
+
const c = map.get(sig) ?? {
|
|
150
|
+
signature: sig,
|
|
151
|
+
status_message_sample: s.status_message,
|
|
152
|
+
span_name: s.name,
|
|
153
|
+
tool_name: s.tool_name,
|
|
154
|
+
trace_count: 0,
|
|
155
|
+
span_count: 0,
|
|
156
|
+
prevalence: 0,
|
|
157
|
+
exemplar_trace_ids: [],
|
|
158
|
+
exemplar_span_ids: []
|
|
159
|
+
};
|
|
160
|
+
c.span_count += 1;
|
|
161
|
+
if (!c.exemplar_trace_ids.includes(t.summary.trace_id) && c.exemplar_trace_ids.length < 10) {
|
|
162
|
+
c.exemplar_trace_ids.push(t.summary.trace_id);
|
|
163
|
+
c.trace_count += 1;
|
|
164
|
+
}
|
|
165
|
+
if (c.exemplar_span_ids.length < 10) c.exemplar_span_ids.push(s.span_id);
|
|
166
|
+
map.set(sig, c);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const errorTraces = set.filter((t) => t.summary.has_errors).length || 1;
|
|
170
|
+
const clusters = [...map.values()].map((c) => ({
|
|
171
|
+
...c,
|
|
172
|
+
prevalence: c.trace_count / errorTraces
|
|
173
|
+
}));
|
|
174
|
+
return clusters.sort((a, b) => b.trace_count - a.trace_count);
|
|
175
|
+
};
|
|
176
|
+
return {
|
|
177
|
+
async getOverview(filters) {
|
|
178
|
+
const set = traces.filter((t) => matchesFilters(t, filters));
|
|
179
|
+
const services = /* @__PURE__ */ new Set();
|
|
180
|
+
const agents = /* @__PURE__ */ new Set();
|
|
181
|
+
const models = /* @__PURE__ */ new Set();
|
|
182
|
+
const tools = /* @__PURE__ */ new Set();
|
|
183
|
+
let errorSpans = 0;
|
|
184
|
+
for (const t of set) {
|
|
185
|
+
if (t.summary.service_name) services.add(t.summary.service_name);
|
|
186
|
+
if (t.summary.agent_name) agents.add(t.summary.agent_name);
|
|
187
|
+
for (const m of t.summary.models) models.add(m);
|
|
188
|
+
for (const tn of t.summary.tools) tools.add(tn);
|
|
189
|
+
errorSpans += t.spans.filter((s) => s.status === "ERROR").length;
|
|
190
|
+
}
|
|
191
|
+
const times = set.map((t) => t.summary.start_time).sort();
|
|
192
|
+
return {
|
|
193
|
+
total_traces: set.length,
|
|
194
|
+
raw_jsonl_bytes: set.reduce((n, t) => n + t.rawBytes, 0),
|
|
195
|
+
services: [...services],
|
|
196
|
+
agents: [...agents],
|
|
197
|
+
models: [...models],
|
|
198
|
+
tool_names: [...tools],
|
|
199
|
+
sample_trace_ids: set.slice(0, 20).map((t) => t.summary.trace_id),
|
|
200
|
+
errors: {
|
|
201
|
+
trace_count: set.filter((t) => t.summary.has_errors).length,
|
|
202
|
+
span_count: errorSpans
|
|
203
|
+
},
|
|
204
|
+
error_clusters: buildClusters(set),
|
|
205
|
+
time_range: times.length ? { earliest: times[0], latest: times[times.length - 1] } : null
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
async queryTraces(opts) {
|
|
209
|
+
const set = traces.filter((t) => matchesFilters(t, opts.filters));
|
|
210
|
+
const offset = opts.offset ?? 0;
|
|
211
|
+
const page = set.slice(offset, offset + opts.limit);
|
|
212
|
+
return {
|
|
213
|
+
traces: page.map((t) => t.summary),
|
|
214
|
+
total: set.length,
|
|
215
|
+
has_more: offset + opts.limit < set.length
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
async countTraces(filters) {
|
|
219
|
+
return traces.filter((t) => matchesFilters(t, filters)).length;
|
|
220
|
+
},
|
|
221
|
+
async viewTrace(opts) {
|
|
222
|
+
const t = byId.get(opts.trace_id);
|
|
223
|
+
if (!t) return { trace_id: opts.trace_id, spans: [] };
|
|
224
|
+
const cap = opts.per_attribute_byte_cap ?? budgets.perAttributeViewBudget;
|
|
225
|
+
const projected = t.spans.map((s) => ({
|
|
226
|
+
...s,
|
|
227
|
+
attributes: capAttributes(s.attributes, cap).capped
|
|
228
|
+
}));
|
|
229
|
+
if (bytesOf(projected) > budgets.perCallByteCeiling) {
|
|
230
|
+
const names = /* @__PURE__ */ new Map();
|
|
231
|
+
for (const s of t.spans) names.set(s.name, (names.get(s.name) ?? 0) + 1);
|
|
232
|
+
return {
|
|
233
|
+
trace_id: opts.trace_id,
|
|
234
|
+
oversized: {
|
|
235
|
+
span_count: t.spans.length,
|
|
236
|
+
top_span_names: [...names.entries()].sort((a, b) => b[1] - a[1]).slice(0, 20),
|
|
237
|
+
span_response_bytes_max: Math.max(...t.spans.map((s) => bytesOf(s))),
|
|
238
|
+
error_span_count: t.spans.filter((s) => s.status === "ERROR").length
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return { trace_id: opts.trace_id, spans: projected };
|
|
243
|
+
},
|
|
244
|
+
async viewSpans(opts) {
|
|
245
|
+
const t = byId.get(opts.trace_id);
|
|
246
|
+
const cap = opts.per_attribute_byte_cap ?? budgets.perAttributeSpanBudget;
|
|
247
|
+
const want = new Set(opts.span_ids);
|
|
248
|
+
const found = (t?.spans ?? []).filter((s) => want.has(s.span_id));
|
|
249
|
+
let truncated = 0;
|
|
250
|
+
const spans = found.map((s) => {
|
|
251
|
+
const { capped, truncated: n } = capAttributes(s.attributes, cap);
|
|
252
|
+
truncated += n;
|
|
253
|
+
return { ...s, attributes: capped };
|
|
254
|
+
});
|
|
255
|
+
const foundIds = new Set(found.map((s) => s.span_id));
|
|
256
|
+
return {
|
|
257
|
+
trace_id: opts.trace_id,
|
|
258
|
+
spans,
|
|
259
|
+
missing_span_ids: opts.span_ids.filter((id) => !foundIds.has(id)),
|
|
260
|
+
truncated_attribute_count: truncated
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
async searchTrace(opts) {
|
|
264
|
+
const t = byId.get(opts.trace_id);
|
|
265
|
+
const max = opts.max_matches ?? 50;
|
|
266
|
+
const hits = [];
|
|
267
|
+
for (const s of t?.spans ?? []) {
|
|
268
|
+
for (const hit of searchSpanAttrs(s, opts.regex_pattern, budgets.perMatchTextBudget)) {
|
|
269
|
+
if (hits.length >= max) break;
|
|
270
|
+
hits.push(hit);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
trace_id: opts.trace_id,
|
|
275
|
+
hits,
|
|
276
|
+
total_matches: hits.length,
|
|
277
|
+
has_more: hits.length >= max
|
|
278
|
+
};
|
|
279
|
+
},
|
|
280
|
+
async searchSpan(opts) {
|
|
281
|
+
const t = byId.get(opts.trace_id);
|
|
282
|
+
const max = opts.max_matches ?? 50;
|
|
283
|
+
const span = (t?.spans ?? []).find((s) => s.span_id === opts.span_id);
|
|
284
|
+
const hits = span ? searchSpanAttrs(span, opts.regex_pattern, budgets.perMatchTextBudget).slice(0, max) : [];
|
|
285
|
+
return {
|
|
286
|
+
trace_id: opts.trace_id,
|
|
287
|
+
span_id: opts.span_id,
|
|
288
|
+
hits,
|
|
289
|
+
total_matches: hits.length,
|
|
290
|
+
has_more: false
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function searchSpanAttrs(span, pattern, textCap) {
|
|
296
|
+
const re = new RegExp(pattern, "g");
|
|
297
|
+
const hits = [];
|
|
298
|
+
for (const [k, v] of Object.entries(span.attributes)) {
|
|
299
|
+
const text = typeof v === "string" ? v : JSON.stringify(v);
|
|
300
|
+
if (typeof text !== "string") continue;
|
|
301
|
+
re.lastIndex = 0;
|
|
302
|
+
const m = re.exec(text);
|
|
303
|
+
if (!m) continue;
|
|
304
|
+
const at = m.index;
|
|
305
|
+
hits.push({
|
|
306
|
+
trace_id: span.trace_id,
|
|
307
|
+
span_id: span.span_id,
|
|
308
|
+
span_name: span.name,
|
|
309
|
+
span_kind: span.kind,
|
|
310
|
+
attribute_path: `attributes.${k}`,
|
|
311
|
+
matched_text: m[0].slice(0, textCap),
|
|
312
|
+
context_before: text.slice(Math.max(0, at - textCap / 2), at),
|
|
313
|
+
context_after: text.slice(at + m[0].length, at + m[0].length + textCap / 2),
|
|
314
|
+
match_offset: at
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
return hits;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/analyst-loop/run-analyst-loop.ts
|
|
321
|
+
import { diffFindings } from "@tangle-network/agent-eval";
|
|
322
|
+
async function runAnalystLoop(opts) {
|
|
323
|
+
const log = opts.log ?? defaultLog;
|
|
324
|
+
const strategy = opts.priorFindingsStrategy ?? "per-kind";
|
|
325
|
+
const emit = makeEmitter(opts.onEvent);
|
|
326
|
+
const startedAt = Date.now();
|
|
327
|
+
const baselineRunId = resolveBaselineRunId(opts);
|
|
328
|
+
const priorAll = baselineRunId ? opts.findingsStore?.loadRun(baselineRunId) ?? [] : [];
|
|
329
|
+
log("baseline resolved", { baselineRunId, prior_findings: priorAll.length });
|
|
330
|
+
await emit({
|
|
331
|
+
type: "baseline-resolved",
|
|
332
|
+
runId: opts.runId,
|
|
333
|
+
baselineRunId,
|
|
334
|
+
priorFindingCount: priorAll.length
|
|
335
|
+
});
|
|
336
|
+
const priorFindings = buildPriorFindingsInput(priorAll, strategy, opts.registry.list());
|
|
337
|
+
const analystResult = await runRegistry(opts, priorFindings, emit);
|
|
338
|
+
log("analyst run complete", {
|
|
339
|
+
findings: analystResult.findings.length,
|
|
340
|
+
cost_usd: analystResult.total_cost_usd,
|
|
341
|
+
per_analyst: analystResult.per_analyst.map((s) => ({
|
|
342
|
+
id: s.analyst_id,
|
|
343
|
+
status: s.status,
|
|
344
|
+
n: s.findings_count
|
|
345
|
+
}))
|
|
346
|
+
});
|
|
347
|
+
if (opts.findingsStore && analystResult.findings.length > 0) {
|
|
348
|
+
await opts.findingsStore.append(opts.runId, analystResult.findings);
|
|
349
|
+
await emit({
|
|
350
|
+
type: "findings-persisted",
|
|
351
|
+
runId: opts.runId,
|
|
352
|
+
count: analystResult.findings.length
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
let diff = null;
|
|
356
|
+
if (baselineRunId && analystResult.findings.length > 0) {
|
|
357
|
+
diff = diffFindings(
|
|
358
|
+
priorAll.map((f) => ({ ...f })),
|
|
359
|
+
analystResult.findings.map((f) => ({ ...f, run_id: opts.runId }))
|
|
360
|
+
);
|
|
361
|
+
log("diff vs baseline", {
|
|
362
|
+
appeared: diff.appeared.length,
|
|
363
|
+
disappeared: diff.disappeared.length,
|
|
364
|
+
persisted: diff.persisted.length,
|
|
365
|
+
changed: diff.changed.length
|
|
366
|
+
});
|
|
367
|
+
await emit({
|
|
368
|
+
type: "diff-computed",
|
|
369
|
+
runId: opts.runId,
|
|
370
|
+
baselineRunId,
|
|
371
|
+
appeared: diff.appeared.length,
|
|
372
|
+
disappeared: diff.disappeared.length,
|
|
373
|
+
persisted: diff.persisted.length,
|
|
374
|
+
changed: diff.changed.length
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
let knowledge = null;
|
|
378
|
+
if (opts.knowledgeAdapter) {
|
|
379
|
+
knowledge = await runKnowledgeAdapter(opts, analystResult.findings, log, emit);
|
|
380
|
+
}
|
|
381
|
+
let improvement = null;
|
|
382
|
+
if (opts.improvementAdapter) {
|
|
383
|
+
improvement = await runImprovementAdapter(opts, analystResult.findings, log, emit);
|
|
384
|
+
}
|
|
385
|
+
await emit({
|
|
386
|
+
type: "loop-completed",
|
|
387
|
+
runId: opts.runId,
|
|
388
|
+
durationMs: Date.now() - startedAt
|
|
389
|
+
});
|
|
390
|
+
return {
|
|
391
|
+
runId: opts.runId,
|
|
392
|
+
baselineRunId,
|
|
393
|
+
analystResult,
|
|
394
|
+
diff,
|
|
395
|
+
knowledge,
|
|
396
|
+
improvement
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function makeEmitter(onEvent) {
|
|
400
|
+
if (!onEvent) return async () => {
|
|
401
|
+
};
|
|
402
|
+
return async (event) => {
|
|
403
|
+
await onEvent(event);
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
async function runRegistry(opts, priorFindings, emit) {
|
|
407
|
+
const reg = opts.registry;
|
|
408
|
+
if (typeof reg.runStream === "function" && opts.onEvent) {
|
|
409
|
+
let final = null;
|
|
410
|
+
for await (const ev of reg.runStream(opts.runId, opts.inputs, { priorFindings })) {
|
|
411
|
+
await emit({ type: "analyst", runId: opts.runId, event: ev });
|
|
412
|
+
if (ev.type === "run-completed") final = ev.result;
|
|
413
|
+
}
|
|
414
|
+
if (!final) {
|
|
415
|
+
throw new Error("runAnalystLoop: registry.runStream ended without run-completed event");
|
|
416
|
+
}
|
|
417
|
+
return final;
|
|
418
|
+
}
|
|
419
|
+
return opts.registry.run(opts.runId, opts.inputs, { priorFindings });
|
|
420
|
+
}
|
|
421
|
+
function resolveBaselineRunId(opts) {
|
|
422
|
+
if (opts.baselineRunId === null) return null;
|
|
423
|
+
if (typeof opts.baselineRunId === "string") return opts.baselineRunId;
|
|
424
|
+
if (!opts.findingsStore) return null;
|
|
425
|
+
const all = opts.findingsStore.loadAll();
|
|
426
|
+
let last = null;
|
|
427
|
+
for (const row of all) {
|
|
428
|
+
if (row.run_id === opts.runId) continue;
|
|
429
|
+
last = row.run_id;
|
|
430
|
+
}
|
|
431
|
+
return last;
|
|
432
|
+
}
|
|
433
|
+
function buildPriorFindingsInput(prior, strategy, registry) {
|
|
434
|
+
if (strategy === "none" || prior.length === 0) return void 0;
|
|
435
|
+
const stripped = prior.map(({ run_id: _run_id, ...rest }) => rest);
|
|
436
|
+
if (strategy === "wildcard") {
|
|
437
|
+
return { "*": stripped };
|
|
438
|
+
}
|
|
439
|
+
void registry;
|
|
440
|
+
return stripped;
|
|
441
|
+
}
|
|
442
|
+
async function runKnowledgeAdapter(opts, findings, log, emit) {
|
|
443
|
+
const adapter = opts.knowledgeAdapter;
|
|
444
|
+
const batch = await adapter.proposeFromFindings(findings);
|
|
445
|
+
log("knowledge.proposeFromFindings", {
|
|
446
|
+
proposals: batch.proposals.length,
|
|
447
|
+
skipped: batch.skipped,
|
|
448
|
+
errors: batch.errors.length
|
|
449
|
+
});
|
|
450
|
+
await emit({
|
|
451
|
+
type: "knowledge-proposed",
|
|
452
|
+
runId: opts.runId,
|
|
453
|
+
proposalCount: batch.proposals.length,
|
|
454
|
+
skipped: batch.skipped,
|
|
455
|
+
errors: batch.errors.length
|
|
456
|
+
});
|
|
457
|
+
const auto = opts.autoApply?.knowledge ?? false;
|
|
458
|
+
const threshold = opts.autoApply?.knowledgeConfidenceThreshold ?? 0.85;
|
|
459
|
+
if (!auto || !adapter.apply) {
|
|
460
|
+
await emit({
|
|
461
|
+
type: "knowledge-applied",
|
|
462
|
+
runId: opts.runId,
|
|
463
|
+
writtenCount: 0,
|
|
464
|
+
withheldForReview: batch.proposals.length
|
|
465
|
+
});
|
|
466
|
+
return {
|
|
467
|
+
proposals: batch.proposals,
|
|
468
|
+
applied: [],
|
|
469
|
+
skipped: batch.skipped,
|
|
470
|
+
errors: batch.errors,
|
|
471
|
+
withheld_for_review: batch.proposals.length
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
const findingsById = new Map(findings.map((f) => [f.finding_id, f]));
|
|
475
|
+
const safe = [];
|
|
476
|
+
let withheld = 0;
|
|
477
|
+
for (const p of batch.proposals) {
|
|
478
|
+
const src = p.sourceFindingId ? findingsById.get(p.sourceFindingId) : void 0;
|
|
479
|
+
if (!src) {
|
|
480
|
+
withheld += 1;
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (src.confidence < threshold) {
|
|
484
|
+
withheld += 1;
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
safe.push(p);
|
|
488
|
+
}
|
|
489
|
+
const result = await adapter.apply(safe);
|
|
490
|
+
log("knowledge.apply", {
|
|
491
|
+
applied: result.written.length,
|
|
492
|
+
withheld_for_review: withheld,
|
|
493
|
+
warnings: result.warnings.length
|
|
494
|
+
});
|
|
495
|
+
await emit({
|
|
496
|
+
type: "knowledge-applied",
|
|
497
|
+
runId: opts.runId,
|
|
498
|
+
writtenCount: result.written.length,
|
|
499
|
+
withheldForReview: withheld
|
|
500
|
+
});
|
|
501
|
+
return {
|
|
502
|
+
proposals: batch.proposals,
|
|
503
|
+
applied: result.written,
|
|
504
|
+
skipped: batch.skipped,
|
|
505
|
+
errors: batch.errors,
|
|
506
|
+
withheld_for_review: withheld
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
async function runImprovementAdapter(opts, findings, log, emit) {
|
|
510
|
+
const adapter = opts.improvementAdapter;
|
|
511
|
+
const batch = await adapter.proposeFromFindings(findings);
|
|
512
|
+
log("improvement.proposeFromFindings", {
|
|
513
|
+
edits: batch.edits.length,
|
|
514
|
+
skipped: batch.skipped,
|
|
515
|
+
errors: batch.errors.length
|
|
516
|
+
});
|
|
517
|
+
await emit({
|
|
518
|
+
type: "improvement-proposed",
|
|
519
|
+
runId: opts.runId,
|
|
520
|
+
editCount: batch.edits.length,
|
|
521
|
+
skipped: batch.skipped,
|
|
522
|
+
errors: batch.errors.length
|
|
523
|
+
});
|
|
524
|
+
const auto = opts.autoApply?.improvement ?? false;
|
|
525
|
+
const threshold = opts.autoApply?.improvementConfidenceThreshold ?? 0.9;
|
|
526
|
+
if (!auto || !adapter.apply) {
|
|
527
|
+
await emit({
|
|
528
|
+
type: "improvement-applied",
|
|
529
|
+
runId: opts.runId,
|
|
530
|
+
appliedCount: 0,
|
|
531
|
+
withheldForReview: batch.edits.length
|
|
532
|
+
});
|
|
533
|
+
return {
|
|
534
|
+
edits: batch.edits,
|
|
535
|
+
applied: [],
|
|
536
|
+
skipped: batch.skipped,
|
|
537
|
+
errors: batch.errors,
|
|
538
|
+
withheld_for_review: batch.edits.length
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
const findingsById = new Map(findings.map((f) => [f.finding_id, f]));
|
|
542
|
+
const safe = [];
|
|
543
|
+
let withheld = 0;
|
|
544
|
+
for (const e of batch.edits) {
|
|
545
|
+
const src = e.sourceFindingId ? findingsById.get(e.sourceFindingId) : void 0;
|
|
546
|
+
if (!src || src.confidence < threshold) {
|
|
547
|
+
withheld += 1;
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
safe.push(e);
|
|
551
|
+
}
|
|
552
|
+
const result = await adapter.apply(safe);
|
|
553
|
+
log("improvement.apply", {
|
|
554
|
+
applied: result.applied.length,
|
|
555
|
+
withheld_for_review: withheld,
|
|
556
|
+
warnings: result.warnings.length
|
|
557
|
+
});
|
|
558
|
+
await emit({
|
|
559
|
+
type: "improvement-applied",
|
|
560
|
+
runId: opts.runId,
|
|
561
|
+
appliedCount: result.applied.length,
|
|
562
|
+
withheldForReview: withheld
|
|
563
|
+
});
|
|
564
|
+
return {
|
|
565
|
+
edits: batch.edits,
|
|
566
|
+
applied: result.applied,
|
|
567
|
+
skipped: batch.skipped,
|
|
568
|
+
errors: batch.errors,
|
|
569
|
+
withheld_for_review: withheld
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function defaultLog(msg, fields) {
|
|
573
|
+
if (fields) console.log(`[analyst-loop] ${msg}`, fields);
|
|
574
|
+
else console.log(`[analyst-loop] ${msg}`);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// src/analyst-loop/analyst-driver-hook.ts
|
|
578
|
+
function createAnalystDriverHook(opts) {
|
|
579
|
+
const baseRunId = opts.runId ?? `analyst-${randomSuffix()}`;
|
|
580
|
+
let priorRunId;
|
|
581
|
+
return async ({ history }) => {
|
|
582
|
+
const traceStore = iterationsToTraceStore(history);
|
|
583
|
+
const runId = `${baseRunId}-r${history.length}`;
|
|
584
|
+
const result = await runAnalystLoop({
|
|
585
|
+
runId,
|
|
586
|
+
registry: opts.registry,
|
|
587
|
+
inputs: { traceStore },
|
|
588
|
+
findingsStore: opts.findingsStore ?? null,
|
|
589
|
+
// thread the prior round's findings as the baseline → cross-round steering memory.
|
|
590
|
+
baselineRunId: priorRunId,
|
|
591
|
+
priorFindingsStrategy: opts.priorFindingsStrategy ?? "per-kind"
|
|
592
|
+
});
|
|
593
|
+
priorRunId = runId;
|
|
594
|
+
return result.analystResult.findings;
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
export {
|
|
599
|
+
iterationsToTraceStore,
|
|
600
|
+
runAnalystLoop,
|
|
601
|
+
createAnalystDriverHook
|
|
602
|
+
};
|
|
603
|
+
//# sourceMappingURL=chunk-FK53TXOP.js.map
|