@tangle-network/agent-eval 0.71.0 → 0.72.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.
- package/CHANGELOG.md +63 -0
- package/dist/adapters/http.d.ts +1 -1
- package/dist/adapters/langchain.d.ts +1 -1
- package/dist/adapters/otel.d.ts +3 -2
- package/dist/agent-profile-DYRboYWu.d.ts +364 -0
- package/dist/analyst/index.d.ts +221 -0
- package/dist/analyst/index.js +371 -0
- package/dist/analyst/index.js.map +1 -0
- package/dist/analyst-t7zZS3TV.d.ts +88 -0
- package/dist/campaign/index.d.ts +485 -9
- package/dist/campaign/index.js +618 -30
- package/dist/campaign/index.js.map +1 -1
- package/dist/chunk-7W4SM7FD.js +1075 -0
- package/dist/chunk-7W4SM7FD.js.map +1 -0
- package/dist/{chunk-AIWHLG7J.js → chunk-GJJNJVIR.js} +11 -11
- package/dist/chunk-JHA3ZGSO.js +1496 -0
- package/dist/chunk-JHA3ZGSO.js.map +1 -0
- package/dist/{chunk-VMAYE3LM.js → chunk-JYE3WOTE.js} +57 -9
- package/dist/{chunk-VMAYE3LM.js.map → chunk-JYE3WOTE.js.map} +1 -1
- package/dist/chunk-LB2UOI5F.js +412 -0
- package/dist/chunk-LB2UOI5F.js.map +1 -0
- package/dist/{chunk-ODGETRTM.js → chunk-VUINJM5M.js} +234 -1415
- package/dist/chunk-VUINJM5M.js.map +1 -0
- package/dist/chunk-WYIHD6EB.js +1044 -0
- package/dist/chunk-WYIHD6EB.js.map +1 -0
- package/dist/{chunk-6QZUCFKM.js → chunk-XPILG2CA.js} +120 -3
- package/dist/chunk-XPILG2CA.js.map +1 -0
- package/dist/{chunk-6XQIEUQ2.js → chunk-ZPSKPT3V.js} +5 -3
- package/dist/{chunk-6XQIEUQ2.js.map → chunk-ZPSKPT3V.js.map} +1 -1
- package/dist/contract/index.d.ts +17 -13
- package/dist/contract/index.js +14 -8
- package/dist/contract/index.js.map +1 -1
- package/dist/{control-DxvZeV5X.d.ts → control-BgA6BYTm.d.ts} +1 -1
- package/dist/control.d.ts +2 -2
- package/dist/{feedback-trajectory-8hKC5EOb.d.ts → feedback-trajectory-B3rErRsh.d.ts} +1 -1
- package/dist/harness-optimizer-EnEnQPsr.d.ts +106 -0
- package/dist/hosted/index.d.ts +223 -2
- package/dist/index.d.ts +49 -1323
- package/dist/index.js +339 -2627
- package/dist/index.js.map +1 -1
- package/dist/{index-BGBrVS24.d.ts → insight-report-Df3lxYXM.d.ts} +1 -221
- package/dist/kind-factory-DW9XWPvM.d.ts +172 -0
- package/dist/multi-layer-verifier-DlWCXuxL.d.ts +141 -0
- package/dist/openapi.json +1 -1
- package/dist/pareto-E-pembql.d.ts +81 -0
- package/dist/{provenance-C69gLUXH.d.ts → provenance-B-TFszPW.d.ts} +131 -4
- package/dist/redact-B40YG2M_.d.ts +45 -0
- package/dist/registry-DuVYiTvw.d.ts +128 -0
- package/dist/{researcher-WJvIpX3L.d.ts → researcher-C_KJyIGg.d.ts} +1 -141
- package/dist/rl.d.ts +4 -3
- package/dist/rl.js +4 -4
- package/dist/{run-campaign-BVY3RGAZ.js → run-campaign-OVEZF24D.js} +2 -2
- package/dist/run-critic-BAIjX99r.d.ts +56 -0
- package/dist/{run-improvement-loop-Bzamo6GB.d.ts → run-improvement-loop-BqYH2vCR.d.ts} +25 -1
- package/dist/semantic-concept-judge-CV9Wlx4t.d.ts +650 -0
- package/dist/{store-jzKpMl16.d.ts → store-GmBE2pZZ.d.ts} +1 -1
- package/dist/traces.d.ts +371 -308
- package/dist/traces.js +43 -18
- package/dist/{types-CnmZ2bkP.d.ts → types-Bba0vl1V.d.ts} +1 -1
- package/dist/{registry-BGKyX6bw.d.ts → types-CRD68aH7.d.ts} +3 -128
- package/dist/wire/index.d.ts +1 -1
- package/dist/workflow/index.d.ts +494 -0
- package/dist/workflow/index.js +2177 -0
- package/dist/workflow/index.js.map +1 -0
- package/docs/design/self-improvement-roadmap.md +106 -0
- package/package.json +36 -12
- package/dist/agent-profile-DzcPHR1Z.d.ts +0 -114
- package/dist/chunk-6QZUCFKM.js.map +0 -1
- package/dist/chunk-ODGETRTM.js.map +0 -1
- package/dist/chunk-PQV2TKC3.js +0 -27
- package/dist/chunk-PQV2TKC3.js.map +0 -1
- /package/dist/{chunk-AIWHLG7J.js.map → chunk-GJJNJVIR.js.map} +0 -0
- /package/dist/{run-campaign-BVY3RGAZ.js.map → run-campaign-OVEZF24D.js.map} +0 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import {
|
|
2
|
+
canonicalize
|
|
3
|
+
} from "./chunk-VSMTAMNK.js";
|
|
4
|
+
import {
|
|
5
|
+
ValidationError
|
|
6
|
+
} from "./chunk-3BFEG2F6.js";
|
|
7
|
+
|
|
8
|
+
// src/metrics.ts
|
|
9
|
+
var MODEL_PRICING = {
|
|
10
|
+
"gpt-4o": { input: 25e-4, output: 0.01 },
|
|
11
|
+
"gpt-4o-mini": { input: 15e-5, output: 6e-4 },
|
|
12
|
+
"gpt-4-turbo": { input: 0.01, output: 0.03 },
|
|
13
|
+
"claude-sonnet-4-20250514": { input: 3e-3, output: 0.015 },
|
|
14
|
+
"claude-opus-4-20250514": { input: 0.015, output: 0.075 },
|
|
15
|
+
"claude-3-haiku-20240307": { input: 25e-5, output: 125e-5 }
|
|
16
|
+
};
|
|
17
|
+
var FAMILY_PRICING = [
|
|
18
|
+
[/claude.*opus/, { input: 0.015, output: 0.075 }],
|
|
19
|
+
[/claude.*haiku/, { input: 8e-4, output: 4e-3 }],
|
|
20
|
+
[/claude.*sonnet|claude-code|claude-sonnet/, { input: 3e-3, output: 0.015 }],
|
|
21
|
+
[/gpt-4o-mini/, { input: 15e-5, output: 6e-4 }],
|
|
22
|
+
[/gpt-5|gpt-4\.1|o[134]\b/, { input: 125e-5, output: 0.01 }],
|
|
23
|
+
[/gpt-4o|gpt-4/, { input: 25e-4, output: 0.01 }],
|
|
24
|
+
[/deepseek/, { input: 3e-4, output: 11e-4 }],
|
|
25
|
+
[/glm|zhipu|zai/, { input: 6e-4, output: 22e-4 }],
|
|
26
|
+
[/kimi|moonshot/, { input: 6e-4, output: 25e-4 }],
|
|
27
|
+
[/qwen/, { input: 4e-4, output: 12e-4 }],
|
|
28
|
+
[/gemini.*flash/, { input: 1e-4, output: 4e-4 }],
|
|
29
|
+
[/gemini/, { input: 125e-5, output: 5e-3 }],
|
|
30
|
+
[/llama/, { input: 2e-4, output: 6e-4 }]
|
|
31
|
+
];
|
|
32
|
+
function normalizeModelId(model) {
|
|
33
|
+
return (model.split("@")[0] ?? model).trim().toLowerCase();
|
|
34
|
+
}
|
|
35
|
+
function resolveModelPricing(model) {
|
|
36
|
+
if (MODEL_PRICING[model]) return MODEL_PRICING[model];
|
|
37
|
+
const id = normalizeModelId(model);
|
|
38
|
+
if (MODEL_PRICING[id]) return MODEL_PRICING[id];
|
|
39
|
+
for (const [pattern, price] of FAMILY_PRICING) {
|
|
40
|
+
if (pattern.test(id)) return price;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function isModelPriced(model) {
|
|
45
|
+
return resolveModelPricing(model) !== null;
|
|
46
|
+
}
|
|
47
|
+
var warnedUnpricedModels = /* @__PURE__ */ new Set();
|
|
48
|
+
function estimateTokens(text) {
|
|
49
|
+
return Math.ceil(text.length / 4);
|
|
50
|
+
}
|
|
51
|
+
function estimateCost(inputTokens, outputTokens, model) {
|
|
52
|
+
const pricing = resolveModelPricing(model);
|
|
53
|
+
if (!pricing) {
|
|
54
|
+
if (!warnedUnpricedModels.has(model)) {
|
|
55
|
+
warnedUnpricedModels.add(model);
|
|
56
|
+
console.warn(
|
|
57
|
+
`estimateCost: no pricing for model "${model}" \u2014 returning 0; add it to MODEL_PRICING/FAMILY_PRICING (cost/Pareto axes will be blank until then)`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
return inputTokens / 1e3 * pricing.input + outputTokens / 1e3 * pricing.output;
|
|
63
|
+
}
|
|
64
|
+
var TokenCounter = class {
|
|
65
|
+
totalInput = 0;
|
|
66
|
+
totalOutput = 0;
|
|
67
|
+
totalCost = 0;
|
|
68
|
+
model;
|
|
69
|
+
constructor(model = "gpt-4o") {
|
|
70
|
+
this.model = model;
|
|
71
|
+
}
|
|
72
|
+
/** Record tokens for a turn, returns per-turn cost */
|
|
73
|
+
record(inputTokens, outputTokens) {
|
|
74
|
+
this.totalInput += inputTokens;
|
|
75
|
+
this.totalOutput += outputTokens;
|
|
76
|
+
const cost = estimateCost(inputTokens, outputTokens, this.model);
|
|
77
|
+
this.totalCost += cost;
|
|
78
|
+
return cost;
|
|
79
|
+
}
|
|
80
|
+
/** Estimate and record from raw text */
|
|
81
|
+
recordFromText(inputText, outputText) {
|
|
82
|
+
const inputTokens = estimateTokens(inputText);
|
|
83
|
+
const outputTokens = estimateTokens(outputText);
|
|
84
|
+
const cost = this.record(inputTokens, outputTokens);
|
|
85
|
+
return { inputTokens, outputTokens, cost };
|
|
86
|
+
}
|
|
87
|
+
getTotalInput() {
|
|
88
|
+
return this.totalInput;
|
|
89
|
+
}
|
|
90
|
+
getTotalOutput() {
|
|
91
|
+
return this.totalOutput;
|
|
92
|
+
}
|
|
93
|
+
getTotalCost() {
|
|
94
|
+
return this.totalCost;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
var MetricsCollector = class {
|
|
98
|
+
client;
|
|
99
|
+
workspaceId;
|
|
100
|
+
metrics = [];
|
|
101
|
+
constructor(client, workspaceId) {
|
|
102
|
+
this.client = client;
|
|
103
|
+
this.workspaceId = workspaceId;
|
|
104
|
+
}
|
|
105
|
+
/** Collect metrics after a turn completes */
|
|
106
|
+
async collect(turn, responseLatencyMs, responseChars, codeBlocksProduced, blocksExtracted, completionCriteriaMet, completionCriteriaTotal, qualityScore, inputTokens = 0, outputTokens = 0, estimatedCostUsd = 0) {
|
|
107
|
+
const state = await this.getState();
|
|
108
|
+
const m = {
|
|
109
|
+
turn,
|
|
110
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
111
|
+
tasks: state.tasks,
|
|
112
|
+
events: state.events,
|
|
113
|
+
proposals: state.proposals,
|
|
114
|
+
vaultFiles: state.vaultFiles.length,
|
|
115
|
+
responseLatencyMs,
|
|
116
|
+
responseChars,
|
|
117
|
+
codeBlocksProduced,
|
|
118
|
+
blocksExtracted,
|
|
119
|
+
qualityScore,
|
|
120
|
+
inputTokens,
|
|
121
|
+
outputTokens,
|
|
122
|
+
estimatedCostUsd,
|
|
123
|
+
totalCostUsd: estimatedCostUsd,
|
|
124
|
+
completionPercent: completionCriteriaTotal > 0 ? completionCriteriaMet / completionCriteriaTotal * 100 : 0
|
|
125
|
+
};
|
|
126
|
+
this.metrics.push(m);
|
|
127
|
+
return m;
|
|
128
|
+
}
|
|
129
|
+
/** Get current product state */
|
|
130
|
+
async getState() {
|
|
131
|
+
const [tasks, events, approvals, vaultFiles] = await Promise.all([
|
|
132
|
+
this.client.getTasks(this.workspaceId),
|
|
133
|
+
this.client.getEvents(this.workspaceId),
|
|
134
|
+
this.client.getApprovals(this.workspaceId),
|
|
135
|
+
this.client.getVaultTree(this.workspaceId)
|
|
136
|
+
]);
|
|
137
|
+
return {
|
|
138
|
+
tasks: tasks.length,
|
|
139
|
+
events: events.length,
|
|
140
|
+
proposals: {
|
|
141
|
+
pending: approvals.filter((a) => a.status === "pending").length,
|
|
142
|
+
approved: approvals.filter((a) => a.status === "approved").length,
|
|
143
|
+
rejected: approvals.filter((a) => a.status === "rejected").length
|
|
144
|
+
},
|
|
145
|
+
vaultFiles,
|
|
146
|
+
codeBlocks: 0,
|
|
147
|
+
generations: 0
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/** Get all collected metrics */
|
|
151
|
+
getMetrics() {
|
|
152
|
+
return [...this.metrics];
|
|
153
|
+
}
|
|
154
|
+
/** Get convergence curve (completion% over turns) */
|
|
155
|
+
getConvergenceCurve() {
|
|
156
|
+
return this.metrics.map((m) => m.completionPercent);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// src/completion-verifier.ts
|
|
161
|
+
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
162
|
+
"the",
|
|
163
|
+
"a",
|
|
164
|
+
"an",
|
|
165
|
+
"of",
|
|
166
|
+
"for",
|
|
167
|
+
"and",
|
|
168
|
+
"or",
|
|
169
|
+
"to",
|
|
170
|
+
"in",
|
|
171
|
+
"on",
|
|
172
|
+
"with",
|
|
173
|
+
"by"
|
|
174
|
+
]);
|
|
175
|
+
var MATCH_THRESHOLD = 0.5;
|
|
176
|
+
var MIN_CONTENT_CHARS = 50;
|
|
177
|
+
function tokens(s) {
|
|
178
|
+
return new Set(
|
|
179
|
+
s.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 1 && !STOPWORDS.has(t))
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
function tokenRecall(requirementText, candidateText) {
|
|
183
|
+
const req = tokens(requirementText);
|
|
184
|
+
if (req.size === 0) return 0;
|
|
185
|
+
const cand = tokens(candidateText);
|
|
186
|
+
let hit = 0;
|
|
187
|
+
for (const t of req) if (cand.has(t)) hit++;
|
|
188
|
+
return hit / req.size;
|
|
189
|
+
}
|
|
190
|
+
function artifactCandidates(req, reqIndex, artifacts) {
|
|
191
|
+
const reqText = `${req.title} ${req.category ?? ""}`;
|
|
192
|
+
const out = [];
|
|
193
|
+
artifacts.forEach((a, i) => {
|
|
194
|
+
if ((a.content ?? "").trim().length < MIN_CONTENT_CHARS) return;
|
|
195
|
+
let score = tokenRecall(reqText, `${a.path ?? ""} ${a.kind}`);
|
|
196
|
+
if (req.category && a.kind && req.category.toLowerCase() === a.kind.toLowerCase()) {
|
|
197
|
+
score = Math.max(score, 1);
|
|
198
|
+
}
|
|
199
|
+
if (score < MATCH_THRESHOLD) return;
|
|
200
|
+
out.push({
|
|
201
|
+
reqIndex,
|
|
202
|
+
itemKey: `artifact:${i}`,
|
|
203
|
+
score,
|
|
204
|
+
evidence: `artifact '${a.path ?? a.kind}' matched (token recall ${score.toFixed(2)})`,
|
|
205
|
+
content: a.content ?? null
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
function proposalCandidates(req, reqIndex, proposals) {
|
|
211
|
+
const reqText = `${req.title} ${req.category ?? ""}`;
|
|
212
|
+
const out = [];
|
|
213
|
+
for (const p of proposals) {
|
|
214
|
+
if (p.status !== "approved") continue;
|
|
215
|
+
const score = tokenRecall(reqText, p.title);
|
|
216
|
+
if (score < MATCH_THRESHOLD) continue;
|
|
217
|
+
const body = p.content ?? "";
|
|
218
|
+
out.push({
|
|
219
|
+
reqIndex,
|
|
220
|
+
itemKey: `proposal:${p.id}`,
|
|
221
|
+
score,
|
|
222
|
+
evidence: `approved proposal '${p.title}' matched (token recall ${score.toFixed(2)})`,
|
|
223
|
+
content: body.trim().length >= MIN_CONTENT_CHARS ? body : null
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return out;
|
|
227
|
+
}
|
|
228
|
+
function toolCallCandidates(req, reqIndex, toolCalls) {
|
|
229
|
+
const out = [];
|
|
230
|
+
toolCalls.forEach((name, i) => {
|
|
231
|
+
const score = tokenRecall(req.title, name);
|
|
232
|
+
if (score < MATCH_THRESHOLD) return;
|
|
233
|
+
out.push({
|
|
234
|
+
reqIndex,
|
|
235
|
+
itemKey: `tool:${i}`,
|
|
236
|
+
score,
|
|
237
|
+
evidence: `tool call '${name}' matched (token recall ${score.toFixed(2)})`,
|
|
238
|
+
content: null
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
return out;
|
|
242
|
+
}
|
|
243
|
+
async function verifyCompletion(gold, state, checkCorrectness) {
|
|
244
|
+
if (gold.requirements.length === 0) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`verifyCompletion: task '${gold.taskId}' has no requirements \u2014 malformed gold spec`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
const candidates = [];
|
|
250
|
+
gold.requirements.forEach((req, i) => {
|
|
251
|
+
const by = req.satisfiedBy ?? "any";
|
|
252
|
+
if (by === "artifact" || by === "any") {
|
|
253
|
+
candidates.push(...artifactCandidates(req, i, state.artifacts));
|
|
254
|
+
}
|
|
255
|
+
if (by === "proposal" || by === "any") {
|
|
256
|
+
candidates.push(...proposalCandidates(req, i, state.proposals));
|
|
257
|
+
}
|
|
258
|
+
if (by === "tool-call" || by === "any") {
|
|
259
|
+
candidates.push(...toolCallCandidates(req, i, state.toolCalls));
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
263
|
+
const assigned = /* @__PURE__ */ new Map();
|
|
264
|
+
const itemTaken = /* @__PURE__ */ new Set();
|
|
265
|
+
for (const c of candidates) {
|
|
266
|
+
if (assigned.has(c.reqIndex) || itemTaken.has(c.itemKey)) continue;
|
|
267
|
+
assigned.set(c.reqIndex, c);
|
|
268
|
+
itemTaken.add(c.itemKey);
|
|
269
|
+
}
|
|
270
|
+
const requirements = [];
|
|
271
|
+
for (let i = 0; i < gold.requirements.length; i++) {
|
|
272
|
+
const req = gold.requirements[i];
|
|
273
|
+
const match = assigned.get(i);
|
|
274
|
+
const evidence = [];
|
|
275
|
+
let correct = null;
|
|
276
|
+
if (match) {
|
|
277
|
+
evidence.push(match.evidence);
|
|
278
|
+
if (match.content !== null) {
|
|
279
|
+
const r = await checkCorrectness(req, match.content);
|
|
280
|
+
correct = r.correct;
|
|
281
|
+
evidence.push(`correctness: ${r.correct ? "pass" : "fail"} \u2014 ${r.reason}`);
|
|
282
|
+
} else {
|
|
283
|
+
evidence.push("correctness: not assessed \u2014 matched item carries no content");
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
const by = req.satisfiedBy ?? "any";
|
|
287
|
+
const kind = by === "any" ? "artifact/proposal/tool-call" : by;
|
|
288
|
+
evidence.push(`no produced ${kind} matched this requirement`);
|
|
289
|
+
}
|
|
290
|
+
const structurallyPresent = match !== void 0;
|
|
291
|
+
const satisfied = structurallyPresent && correct !== false;
|
|
292
|
+
requirements.push({
|
|
293
|
+
reqId: req.reqId,
|
|
294
|
+
title: req.title,
|
|
295
|
+
structurallyPresent,
|
|
296
|
+
correct,
|
|
297
|
+
satisfied,
|
|
298
|
+
evidence
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
const satisfiedCount = requirements.filter((r) => r.satisfied).length;
|
|
302
|
+
return {
|
|
303
|
+
taskId: gold.taskId,
|
|
304
|
+
requirements,
|
|
305
|
+
completionRate: satisfiedCount / requirements.length,
|
|
306
|
+
fullyComplete: satisfiedCount === requirements.length
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function parseCorrectnessResponse(raw) {
|
|
310
|
+
const match = raw.match(/\{[\s\S]*\}/);
|
|
311
|
+
if (!match) {
|
|
312
|
+
throw new Error(`correctness checker: no JSON object in model response: ${raw.slice(0, 200)}`);
|
|
313
|
+
}
|
|
314
|
+
const parsed = JSON.parse(match[0]);
|
|
315
|
+
if (typeof parsed.correct !== "boolean") {
|
|
316
|
+
throw new Error(`correctness checker: 'correct' is not a boolean in: ${match[0].slice(0, 200)}`);
|
|
317
|
+
}
|
|
318
|
+
return { correct: parsed.correct, reason: typeof parsed.reason === "string" ? parsed.reason : "" };
|
|
319
|
+
}
|
|
320
|
+
function createLlmCorrectnessChecker(tc, opts = {}) {
|
|
321
|
+
const model = opts.model ?? "claude-sonnet-4-6";
|
|
322
|
+
const maxContentChars = opts.maxContentChars ?? 8e3;
|
|
323
|
+
return async (requirement, content) => {
|
|
324
|
+
const resp = await tc.chat({
|
|
325
|
+
model,
|
|
326
|
+
messages: [
|
|
327
|
+
{
|
|
328
|
+
role: "system",
|
|
329
|
+
content: 'You verify whether a produced work artifact actually fulfils a stated requirement. Judge fulfilment only \u2014 is the deliverable substantively present and on-point \u2014 not polish. A plan to do it later, a vague gesture, or a description of what should be done does NOT fulfil a requirement; the artifact must BE the deliverable. Respond with a single JSON object: {"correct": boolean, "reason": string (<= 30 words)}.'
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
role: "user",
|
|
333
|
+
content: `Requirement: ${requirement.title}
|
|
334
|
+
${requirement.category ? `Category: ${requirement.category}
|
|
335
|
+
` : ""}
|
|
336
|
+
Produced artifact:
|
|
337
|
+
${content.slice(0, maxContentChars)}`
|
|
338
|
+
}
|
|
339
|
+
],
|
|
340
|
+
temperature: 0,
|
|
341
|
+
maxTokens: 200
|
|
342
|
+
});
|
|
343
|
+
const raw = resp.choices?.[0]?.message?.content ?? "";
|
|
344
|
+
return parseCorrectnessResponse(raw);
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/produced-state.ts
|
|
349
|
+
function artifactKind(mimeType) {
|
|
350
|
+
if (!mimeType) return "file";
|
|
351
|
+
if (mimeType.includes("json")) return "json";
|
|
352
|
+
if (mimeType.startsWith("text/")) return "text";
|
|
353
|
+
return "file";
|
|
354
|
+
}
|
|
355
|
+
function extractProducedState(events) {
|
|
356
|
+
const artifacts = [];
|
|
357
|
+
const proposals = [];
|
|
358
|
+
const toolCalls = [];
|
|
359
|
+
const seenTools = /* @__PURE__ */ new Set();
|
|
360
|
+
for (const ev of events) {
|
|
361
|
+
if (ev.type === "tool_call") {
|
|
362
|
+
const name = ev.toolName;
|
|
363
|
+
if (name && !seenTools.has(name)) {
|
|
364
|
+
seenTools.add(name);
|
|
365
|
+
toolCalls.push(name);
|
|
366
|
+
}
|
|
367
|
+
} else if (ev.type === "artifact") {
|
|
368
|
+
const a = ev;
|
|
369
|
+
artifacts.push({
|
|
370
|
+
kind: artifactKind(a.mimeType),
|
|
371
|
+
path: a.name ?? a.uri ?? a.artifactId,
|
|
372
|
+
content: a.content ?? ""
|
|
373
|
+
});
|
|
374
|
+
} else if (ev.type === "proposal_created") {
|
|
375
|
+
const p = ev;
|
|
376
|
+
proposals.push({ id: p.proposalId, title: p.title, status: p.status ?? "pending" });
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return { artifacts, proposals, toolCalls };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// src/agent-profile.ts
|
|
383
|
+
import { createHash } from "crypto";
|
|
384
|
+
function agentProfileHash(profile) {
|
|
385
|
+
if (typeof profile.model !== "string" || profile.model.trim().length === 0) {
|
|
386
|
+
throw new ValidationError(`AgentProfile "${profile.id}" has no model \u2014 cannot hash`);
|
|
387
|
+
}
|
|
388
|
+
const behaviour = {
|
|
389
|
+
model: profile.model.trim(),
|
|
390
|
+
skills: [...profile.skills ?? []].sort(),
|
|
391
|
+
promptVersion: profile.promptVersion ?? null,
|
|
392
|
+
tools: [...profile.tools ?? []].sort(),
|
|
393
|
+
metadata: profile.metadata ?? {}
|
|
394
|
+
};
|
|
395
|
+
return createHash("sha256").update(JSON.stringify(canonicalize(behaviour))).digest("hex");
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export {
|
|
399
|
+
MODEL_PRICING,
|
|
400
|
+
resolveModelPricing,
|
|
401
|
+
isModelPriced,
|
|
402
|
+
estimateTokens,
|
|
403
|
+
estimateCost,
|
|
404
|
+
TokenCounter,
|
|
405
|
+
MetricsCollector,
|
|
406
|
+
verifyCompletion,
|
|
407
|
+
parseCorrectnessResponse,
|
|
408
|
+
createLlmCorrectnessChecker,
|
|
409
|
+
extractProducedState,
|
|
410
|
+
agentProfileHash
|
|
411
|
+
};
|
|
412
|
+
//# sourceMappingURL=chunk-LB2UOI5F.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/metrics.ts","../src/completion-verifier.ts","../src/produced-state.ts","../src/agent-profile.ts"],"sourcesContent":["import type { ProductClient } from './client'\nimport type { DriverState, TurnMetrics } from './types'\n\ninterface TokenPrice {\n input: number\n output: number\n}\n\n/** Per-1K token pricing for exact model ids. */\nexport const MODEL_PRICING: Record<string, TokenPrice> = {\n 'gpt-4o': { input: 0.0025, output: 0.01 },\n 'gpt-4o-mini': { input: 0.00015, output: 0.0006 },\n 'gpt-4-turbo': { input: 0.01, output: 0.03 },\n 'claude-sonnet-4-20250514': { input: 0.003, output: 0.015 },\n 'claude-opus-4-20250514': { input: 0.015, output: 0.075 },\n 'claude-3-haiku-20240307': { input: 0.00025, output: 0.00125 },\n}\n\n/** Family-level pricing fallbacks (per-1K), matched against a normalized id\n * after exact lookup misses. Ordered — first match wins. Covers the model\n * ids actually used through the Tangle router + cli-bridge harnesses\n * (`claude-code/sonnet`, `opencode/zai-coding-plan/glm-5.1`,\n * `kimi-code/kimi-k2.6`, `deepseek-v4-pro`, `anthropic/claude-sonnet-4-6`, …),\n * none of which appear in the exact table above — without this they priced\n * to a silent $0, blanking every cost/Pareto axis downstream. */\nconst FAMILY_PRICING: Array<[RegExp, TokenPrice]> = [\n [/claude.*opus/, { input: 0.015, output: 0.075 }],\n [/claude.*haiku/, { input: 0.0008, output: 0.004 }],\n [/claude.*sonnet|claude-code|claude-sonnet/, { input: 0.003, output: 0.015 }],\n [/gpt-4o-mini/, { input: 0.00015, output: 0.0006 }],\n [/gpt-5|gpt-4\\.1|o[134]\\b/, { input: 0.00125, output: 0.01 }],\n [/gpt-4o|gpt-4/, { input: 0.0025, output: 0.01 }],\n [/deepseek/, { input: 0.0003, output: 0.0011 }],\n [/glm|zhipu|zai/, { input: 0.0006, output: 0.0022 }],\n [/kimi|moonshot/, { input: 0.0006, output: 0.0025 }],\n [/qwen/, { input: 0.0004, output: 0.0012 }],\n [/gemini.*flash/, { input: 0.0001, output: 0.0004 }],\n [/gemini/, { input: 0.00125, output: 0.005 }],\n [/llama/, { input: 0.0002, output: 0.0006 }],\n]\n\n/** Normalize a model id for pricing: drop a `@snapshot` suffix, lowercase,\n * and keep the final harness/provider-prefixed segment so family regexes\n * match (`opencode/zai-coding-plan/glm-5.1` → `glm-5.1`). */\nfunction normalizeModelId(model: string): string {\n return (model.split('@')[0] ?? model).trim().toLowerCase()\n}\n\n/** Resolve pricing for a model id: exact table, then family fallback.\n * Returns null when the id matches nothing (caller decides — never a\n * silent-zero masquerading as a real $0 cost). */\nexport function resolveModelPricing(model: string): TokenPrice | null {\n if (MODEL_PRICING[model]) return MODEL_PRICING[model]\n const id = normalizeModelId(model)\n if (MODEL_PRICING[id]) return MODEL_PRICING[id]\n for (const [pattern, price] of FAMILY_PRICING) {\n if (pattern.test(id)) return price\n }\n return null\n}\n\n/** True when `model` has known pricing (exact or family). Lets cost-aware\n * callers distinguish a real $0 from an unpriced model. */\nexport function isModelPriced(model: string): boolean {\n return resolveModelPricing(model) !== null\n}\n\nconst warnedUnpricedModels = new Set<string>()\n\n/** Estimate token count from string length (chars / 4 approximation) */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4)\n}\n\n/** Calculate cost in USD from token counts and model. Unknown models warn\n * once (not a silent zero) and return 0 so callers that ignore pricing keep\n * working; cost-sensitive callers should gate on {@link isModelPriced}. */\nexport function estimateCost(inputTokens: number, outputTokens: number, model: string): number {\n const pricing = resolveModelPricing(model)\n if (!pricing) {\n if (!warnedUnpricedModels.has(model)) {\n warnedUnpricedModels.add(model)\n console.warn(\n `estimateCost: no pricing for model \"${model}\" — returning 0; add it to ` +\n 'MODEL_PRICING/FAMILY_PRICING (cost/Pareto axes will be blank until then)',\n )\n }\n return 0\n }\n return (inputTokens / 1000) * pricing.input + (outputTokens / 1000) * pricing.output\n}\n\n/**\n * TokenCounter — accumulates token usage and cost across turns.\n */\nexport class TokenCounter {\n private totalInput = 0\n private totalOutput = 0\n private totalCost = 0\n private model: string\n\n constructor(model = 'gpt-4o') {\n this.model = model\n }\n\n /** Record tokens for a turn, returns per-turn cost */\n record(inputTokens: number, outputTokens: number): number {\n this.totalInput += inputTokens\n this.totalOutput += outputTokens\n const cost = estimateCost(inputTokens, outputTokens, this.model)\n this.totalCost += cost\n return cost\n }\n\n /** Estimate and record from raw text */\n recordFromText(\n inputText: string,\n outputText: string,\n ): { inputTokens: number; outputTokens: number; cost: number } {\n const inputTokens = estimateTokens(inputText)\n const outputTokens = estimateTokens(outputText)\n const cost = this.record(inputTokens, outputTokens)\n return { inputTokens, outputTokens, cost }\n }\n\n getTotalInput(): number {\n return this.totalInput\n }\n getTotalOutput(): number {\n return this.totalOutput\n }\n getTotalCost(): number {\n return this.totalCost\n }\n}\n\n/**\n * MetricsCollector — collects per-turn metrics from the product.\n *\n * After each turn, queries the product's APIs to measure state changes.\n */\nexport class MetricsCollector {\n private client: ProductClient\n private workspaceId: string\n private metrics: TurnMetrics[] = []\n constructor(client: ProductClient, workspaceId: string) {\n this.client = client\n this.workspaceId = workspaceId\n }\n\n /** Collect metrics after a turn completes */\n async collect(\n turn: number,\n responseLatencyMs: number,\n responseChars: number,\n codeBlocksProduced: number,\n blocksExtracted: number,\n completionCriteriaMet: number,\n completionCriteriaTotal: number,\n qualityScore?: number,\n inputTokens = 0,\n outputTokens = 0,\n estimatedCostUsd = 0,\n ): Promise<TurnMetrics> {\n const state = await this.getState()\n\n const m: TurnMetrics = {\n turn,\n timestamp: new Date().toISOString(),\n tasks: state.tasks,\n events: state.events,\n proposals: state.proposals,\n vaultFiles: state.vaultFiles.length,\n responseLatencyMs,\n responseChars,\n codeBlocksProduced,\n blocksExtracted,\n qualityScore,\n inputTokens,\n outputTokens,\n estimatedCostUsd,\n totalCostUsd: estimatedCostUsd,\n completionPercent:\n completionCriteriaTotal > 0 ? (completionCriteriaMet / completionCriteriaTotal) * 100 : 0,\n }\n\n this.metrics.push(m)\n return m\n }\n\n /** Get current product state */\n async getState(): Promise<DriverState> {\n const [tasks, events, approvals, vaultFiles] = await Promise.all([\n this.client.getTasks(this.workspaceId),\n this.client.getEvents(this.workspaceId),\n this.client.getApprovals(this.workspaceId),\n this.client.getVaultTree(this.workspaceId),\n ])\n\n return {\n tasks: tasks.length,\n events: events.length,\n proposals: {\n pending: approvals.filter((a) => a.status === 'pending').length,\n approved: approvals.filter((a) => a.status === 'approved').length,\n rejected: approvals.filter((a) => a.status === 'rejected').length,\n },\n vaultFiles,\n codeBlocks: 0,\n generations: 0,\n }\n }\n\n /** Get all collected metrics */\n getMetrics(): TurnMetrics[] {\n return [...this.metrics]\n }\n\n /** Get convergence curve (completion% over turns) */\n getConvergenceCurve(): number[] {\n return this.metrics.map((m) => m.completionPercent)\n }\n}\n","/**\n * Completion verifier — the task-completion oracle.\n *\n * Answers the only eval question that is not a proxy: did the agent actually\n * COMPLETE the task — produce every required deliverable, persisted and\n * correct — rather than describe what should be done. A fluent transcript\n * that never produces the artifact scores zero here.\n *\n * Per requirement, a two-stage check:\n * 1. Structural — a produced item (vault artifact / approved proposal /\n * tool call) of the right kind is matched against the requirement and\n * carries non-empty content. Deterministic; no LLM.\n * 2. Correctness — only if structurally present AND the matched item\n * carries content, one targeted check decides whether that item\n * actually fulfils the requirement. A hallucinated artifact fails here;\n * an absent one already failed stage 1.\n *\n * `completionRate` is satisfied / total. Quality dimensions are meaningless\n * on an incomplete task — callers gate on `fullyComplete` / `completionRate`\n * before scoring quality.\n */\n\nimport type { TCloud } from '@tangle-network/tcloud'\nimport type { Artifact } from './artifact-validator'\n\n/** What kind of produced state can satisfy a requirement structurally. */\nexport type SatisfiedBy = 'artifact' | 'proposal' | 'tool-call' | 'any'\n\nexport interface CompletionRequirement {\n /** Stable id from the task gold (e.g. a persona's `expected_requirements[].req_id`). */\n reqId: string\n /** Human-readable description of the required deliverable. */\n title: string\n /** Optional kind/category hint, matched against a produced item's kind. */\n category?: string\n /** What produced state satisfies this requirement. Defaults to 'any'. */\n satisfiedBy?: SatisfiedBy\n}\n\nexport interface TaskGold {\n taskId: string\n requirements: CompletionRequirement[]\n}\n\nexport interface ProducedProposal {\n id: string\n title: string\n status: 'pending' | 'approved' | 'rejected'\n /** Optional persisted body — when present, enables a correctness check. */\n content?: string\n}\n\n/** Everything observable about what a run actually produced. */\nexport interface ProducedState {\n /** Persisted vault artifacts. Reuses the shared `Artifact` shape. */\n artifacts: Artifact[]\n /** Proposals / filings the agent created. */\n proposals: ProducedProposal[]\n /** Names of tools the agent invoked. */\n toolCalls: string[]\n}\n\nexport interface RequirementCheck {\n reqId: string\n title: string\n /** A produced item of the right kind matched the requirement, non-empty. */\n structurallyPresent: boolean\n /**\n * Whether the matched item actually fulfils the requirement. `null` when\n * not structurally present, or when the matched item carries no content\n * to assess.\n */\n correct: boolean | null\n /** structurallyPresent && correct !== false. */\n satisfied: boolean\n /** Human-readable evidence for the verdict. */\n evidence: string[]\n}\n\nexport interface CompletionVerdict {\n taskId: string\n requirements: RequirementCheck[]\n /** satisfied / total requirements. */\n completionRate: number\n /** Every requirement satisfied. */\n fullyComplete: boolean\n}\n\n/**\n * Decides whether a produced item's content actually fulfils a requirement.\n * Injected so the structural verifier stays pure and unit-testable; the\n * production implementation is `createLlmCorrectnessChecker`.\n */\nexport type CorrectnessChecker = (\n requirement: CompletionRequirement,\n content: string,\n) => Promise<{ correct: boolean; reason: string }>\n\nconst STOPWORDS = new Set([\n 'the',\n 'a',\n 'an',\n 'of',\n 'for',\n 'and',\n 'or',\n 'to',\n 'in',\n 'on',\n 'with',\n 'by',\n])\n\nconst MATCH_THRESHOLD = 0.5\nconst MIN_CONTENT_CHARS = 50\n\nfunction tokens(s: string): Set<string> {\n return new Set(\n s\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((t) => t.length > 1 && !STOPWORDS.has(t)),\n )\n}\n\n/**\n * Recall of the requirement's tokens within a candidate's identifying text.\n * Recall, not Jaccard — a candidate's path/id legitimately carries extra\n * tokens the requirement does not name.\n */\nfunction tokenRecall(requirementText: string, candidateText: string): number {\n const req = tokens(requirementText)\n if (req.size === 0) return 0\n const cand = tokens(candidateText)\n let hit = 0\n for (const t of req) if (cand.has(t)) hit++\n return hit / req.size\n}\n\ninterface Candidate {\n reqIndex: number\n /** Unique key for a produced item — each item satisfies at most one requirement. */\n itemKey: string\n score: number\n evidence: string\n /** Content to correctness-check, or null when the matched item has none. */\n content: string | null\n}\n\nfunction artifactCandidates(\n req: CompletionRequirement,\n reqIndex: number,\n artifacts: Artifact[],\n): Candidate[] {\n const reqText = `${req.title} ${req.category ?? ''}`\n const out: Candidate[] = []\n artifacts.forEach((a, i) => {\n if ((a.content ?? '').trim().length < MIN_CONTENT_CHARS) return\n let score = tokenRecall(reqText, `${a.path ?? ''} ${a.kind}`)\n if (req.category && a.kind && req.category.toLowerCase() === a.kind.toLowerCase()) {\n score = Math.max(score, 1)\n }\n if (score < MATCH_THRESHOLD) return\n out.push({\n reqIndex,\n itemKey: `artifact:${i}`,\n score,\n evidence: `artifact '${a.path ?? a.kind}' matched (token recall ${score.toFixed(2)})`,\n content: a.content ?? null,\n })\n })\n return out\n}\n\nfunction proposalCandidates(\n req: CompletionRequirement,\n reqIndex: number,\n proposals: ProducedProposal[],\n): Candidate[] {\n const reqText = `${req.title} ${req.category ?? ''}`\n const out: Candidate[] = []\n for (const p of proposals) {\n // Pending or rejected work is not a completed deliverable.\n if (p.status !== 'approved') continue\n const score = tokenRecall(reqText, p.title)\n if (score < MATCH_THRESHOLD) continue\n const body = p.content ?? ''\n out.push({\n reqIndex,\n itemKey: `proposal:${p.id}`,\n score,\n evidence: `approved proposal '${p.title}' matched (token recall ${score.toFixed(2)})`,\n content: body.trim().length >= MIN_CONTENT_CHARS ? body : null,\n })\n }\n return out\n}\n\nfunction toolCallCandidates(\n req: CompletionRequirement,\n reqIndex: number,\n toolCalls: string[],\n): Candidate[] {\n const out: Candidate[] = []\n toolCalls.forEach((name, i) => {\n const score = tokenRecall(req.title, name)\n if (score < MATCH_THRESHOLD) return\n out.push({\n reqIndex,\n itemKey: `tool:${i}`,\n score,\n evidence: `tool call '${name}' matched (token recall ${score.toFixed(2)})`,\n content: null,\n })\n })\n return out\n}\n\n/**\n * Verify whether a run completed the task. `checkCorrectness` is injected —\n * `createLlmCorrectnessChecker` for production, a deterministic stub in tests.\n *\n * Throws on a gold spec with no requirements: an eval task that requires\n * nothing is a misconfiguration, not a vacuously-complete task.\n */\nexport async function verifyCompletion(\n gold: TaskGold,\n state: ProducedState,\n checkCorrectness: CorrectnessChecker,\n): Promise<CompletionVerdict> {\n if (gold.requirements.length === 0) {\n throw new Error(\n `verifyCompletion: task '${gold.taskId}' has no requirements — malformed gold spec`,\n )\n }\n\n // Collect every above-threshold (requirement, produced-item) candidate, then\n // assign greedily by descending score: each requirement and each produced\n // item is used at most once. One deliverable fulfils one requirement.\n const candidates: Candidate[] = []\n gold.requirements.forEach((req, i) => {\n const by = req.satisfiedBy ?? 'any'\n if (by === 'artifact' || by === 'any') {\n candidates.push(...artifactCandidates(req, i, state.artifacts))\n }\n if (by === 'proposal' || by === 'any') {\n candidates.push(...proposalCandidates(req, i, state.proposals))\n }\n if (by === 'tool-call' || by === 'any') {\n candidates.push(...toolCallCandidates(req, i, state.toolCalls))\n }\n })\n candidates.sort((a, b) => b.score - a.score)\n\n const assigned = new Map<number, Candidate>()\n const itemTaken = new Set<string>()\n for (const c of candidates) {\n if (assigned.has(c.reqIndex) || itemTaken.has(c.itemKey)) continue\n assigned.set(c.reqIndex, c)\n itemTaken.add(c.itemKey)\n }\n\n const requirements: RequirementCheck[] = []\n for (let i = 0; i < gold.requirements.length; i++) {\n const req = gold.requirements[i]!\n const match = assigned.get(i)\n const evidence: string[] = []\n let correct: boolean | null = null\n\n if (match) {\n evidence.push(match.evidence)\n if (match.content !== null) {\n const r = await checkCorrectness(req, match.content)\n correct = r.correct\n evidence.push(`correctness: ${r.correct ? 'pass' : 'fail'} — ${r.reason}`)\n } else {\n evidence.push('correctness: not assessed — matched item carries no content')\n }\n } else {\n const by = req.satisfiedBy ?? 'any'\n const kind = by === 'any' ? 'artifact/proposal/tool-call' : by\n evidence.push(`no produced ${kind} matched this requirement`)\n }\n\n const structurallyPresent = match !== undefined\n const satisfied = structurallyPresent && correct !== false\n requirements.push({\n reqId: req.reqId,\n title: req.title,\n structurallyPresent,\n correct,\n satisfied,\n evidence,\n })\n }\n\n const satisfiedCount = requirements.filter((r) => r.satisfied).length\n return {\n taskId: gold.taskId,\n requirements,\n completionRate: satisfiedCount / requirements.length,\n fullyComplete: satisfiedCount === requirements.length,\n }\n}\n\nexport interface LlmCorrectnessCheckerOpts {\n model?: string\n /** Max chars of artifact content sent to the checker. */\n maxContentChars?: number\n}\n\n/** Parse the correctness checker's model response. Fails loud on a bad shape. */\nexport function parseCorrectnessResponse(raw: string): { correct: boolean; reason: string } {\n const match = raw.match(/\\{[\\s\\S]*\\}/)\n if (!match) {\n throw new Error(`correctness checker: no JSON object in model response: ${raw.slice(0, 200)}`)\n }\n const parsed = JSON.parse(match[0]) as { correct?: unknown; reason?: unknown }\n if (typeof parsed.correct !== 'boolean') {\n throw new Error(`correctness checker: 'correct' is not a boolean in: ${match[0].slice(0, 200)}`)\n }\n return { correct: parsed.correct, reason: typeof parsed.reason === 'string' ? parsed.reason : '' }\n}\n\n/**\n * Production `CorrectnessChecker` — one LLM call per matched artifact,\n * deterministic (temperature 0), structured JSON out. Judges fulfilment\n * only: a plan, a gesture, or a description of what should be done does not\n * fulfil a requirement — the artifact must BE the deliverable.\n */\nexport function createLlmCorrectnessChecker(\n tc: TCloud,\n opts: LlmCorrectnessCheckerOpts = {},\n): CorrectnessChecker {\n const model = opts.model ?? 'claude-sonnet-4-6'\n const maxContentChars = opts.maxContentChars ?? 8000\n return async (requirement, content) => {\n const resp = await tc.chat({\n model,\n messages: [\n {\n role: 'system',\n content:\n 'You verify whether a produced work artifact actually fulfils a stated requirement. Judge fulfilment only — is the deliverable substantively present and on-point — not polish. A plan to do it later, a vague gesture, or a description of what should be done does NOT fulfil a requirement; the artifact must BE the deliverable. Respond with a single JSON object: {\"correct\": boolean, \"reason\": string (<= 30 words)}.',\n },\n {\n role: 'user',\n content: `Requirement: ${requirement.title}\\n${\n requirement.category ? `Category: ${requirement.category}\\n` : ''\n }\\nProduced artifact:\\n${content.slice(0, maxContentChars)}`,\n },\n ],\n temperature: 0,\n maxTokens: 200,\n })\n const raw =\n (resp as { choices?: { message?: { content?: string } }[] }).choices?.[0]?.message?.content ??\n ''\n return parseCorrectnessResponse(raw)\n }\n}\n","/**\n * Produced-state extraction — normalize a run's runtime event stream into the\n * typed `ProducedState` the completion oracle consumes.\n *\n * `ProducedState` answers \"what did the agent actually produce\" — vault\n * artifacts, proposals, tool calls. The runtime emits these as a stream of\n * events; this module is the single normalization point from that stream to\n * the shape `verifyCompletion` expects.\n *\n * Input is structurally typed (`RuntimeEventLike`) so this module does not\n * depend on agent-runtime — agent-runtime's `RuntimeStreamEvent` satisfies it\n * structurally. The `content` on `ArtifactEventLike` and the whole\n * `proposal_created` variant are the runtime-side enrichments this contract\n * requires; the runtime emits them, this module consumes them.\n */\n\nimport type { Artifact } from './artifact-validator'\nimport type { ProducedProposal, ProducedState } from './completion-verifier'\n\n/** A tool the agent invoked. */\nexport interface ToolCallEventLike {\n type: 'tool_call'\n toolName: string\n}\n\n/**\n * An artifact the agent produced. `content` is the enriched field — the\n * runtime's base `artifact` event carries only metadata; the completion\n * oracle needs the body to verify the deliverable, so the runtime emits it.\n */\nexport interface ArtifactEventLike {\n type: 'artifact'\n artifactId: string\n name?: string\n mimeType?: string\n uri?: string\n content?: string\n}\n\n/** A proposal / filing the agent created. */\nexport interface ProposalEventLike {\n type: 'proposal_created'\n proposalId: string\n title: string\n status?: 'pending' | 'approved' | 'rejected'\n}\n\n/**\n * The subset of runtime stream events `extractProducedState` consumes.\n * agent-runtime's full `RuntimeStreamEvent` union satisfies this structurally;\n * the `{ type: string }` catch-all keeps the input permissive so callers can\n * pass the whole unfiltered telemetry stream — unrecognized events are skipped.\n */\nexport type RuntimeEventLike =\n | ToolCallEventLike\n | ArtifactEventLike\n | ProposalEventLike\n | { type: string }\n\nfunction artifactKind(mimeType: string | undefined): string {\n if (!mimeType) return 'file'\n if (mimeType.includes('json')) return 'json'\n if (mimeType.startsWith('text/')) return 'text'\n return 'file'\n}\n\n/**\n * Normalize a run's runtime event stream into `ProducedState`.\n *\n * Pure and total — unrecognized event types are skipped. `toolCalls` is\n * deduplicated by name in first-seen order (completion cares about a tool's\n * presence, not its call count). An artifact with neither a name nor a uri\n * still yields an entry keyed by its `artifactId` so it is never silently\n * dropped; an artifact with no `content` yields empty content, which the\n * completion oracle's structural check then rejects on its own.\n */\nexport function extractProducedState(events: readonly RuntimeEventLike[]): ProducedState {\n const artifacts: Artifact[] = []\n const proposals: ProducedProposal[] = []\n const toolCalls: string[] = []\n const seenTools = new Set<string>()\n\n for (const ev of events) {\n if (ev.type === 'tool_call') {\n const name = (ev as ToolCallEventLike).toolName\n if (name && !seenTools.has(name)) {\n seenTools.add(name)\n toolCalls.push(name)\n }\n } else if (ev.type === 'artifact') {\n const a = ev as ArtifactEventLike\n artifacts.push({\n kind: artifactKind(a.mimeType),\n path: a.name ?? a.uri ?? a.artifactId,\n content: a.content ?? '',\n })\n } else if (ev.type === 'proposal_created') {\n const p = ev as ProposalEventLike\n proposals.push({ id: p.proposalId, title: p.title, status: p.status ?? 'pending' })\n }\n }\n\n return { artifacts, proposals, toolCalls }\n}\n","/**\n * @stable\n *\n * AgentProfile — the eval harness's unit of variation.\n *\n * A profile pins everything that changes agent behaviour for a benchmark\n * cell: the model, the active skills, the prompt version, the available\n * tools. Vary the profile — swap a model, add a skill — and re-run the suite\n * to benchmark the change. The scorecard keys a cell on\n * `(scenarioId, profileHash)`, so the model is not a separate axis: it lives\n * inside the profile, and two profiles with the same model but different\n * skills are different cells.\n *\n * `agentProfileHash` is the profile's behaviour identity. Two profiles that\n * produce the same agent behaviour share a hash (and a scorecard cell);\n * reordering `skills` or `tools` does not change it; the human-facing `id`\n * label does not affect it.\n */\n\nimport { createHash } from 'node:crypto'\nimport { ValidationError } from './errors'\nimport { canonicalize } from './pre-registration'\n\nexport interface AgentProfile {\n /** Human-facing label, e.g. `sonnet-legal-skills-v3`. Not part of the hash. */\n id: string\n /** Model snapshot id this profile pins, e.g. `claude-sonnet-4-6@2025-04-15`. */\n model: string\n /** Skill ids/versions active in this profile — the primary behaviour lever. */\n skills?: string[]\n /** Prompt version identifier. */\n promptVersion?: string\n /** Tool ids available to the agent. */\n tools?: string[]\n /** Any other behaviour-bearing knobs that should fingerprint into the hash. */\n metadata?: Record<string, string | number | boolean>\n}\n\n/**\n * Deterministic behaviour identity of a profile — a sha256 over the\n * behaviour-bearing fields. `skills` and `tools` are order-insensitive; the\n * `id` label is excluded. Throws on a profile with no `model` — an unkeyable\n * profile must fail loud rather than collapse into a blank-model cell.\n */\nexport function agentProfileHash(profile: AgentProfile): string {\n if (typeof profile.model !== 'string' || profile.model.trim().length === 0) {\n throw new ValidationError(`AgentProfile \"${profile.id}\" has no model — cannot hash`)\n }\n const behaviour = {\n model: profile.model.trim(),\n skills: [...(profile.skills ?? [])].sort(),\n promptVersion: profile.promptVersion ?? null,\n tools: [...(profile.tools ?? [])].sort(),\n metadata: profile.metadata ?? {},\n }\n return createHash('sha256')\n .update(JSON.stringify(canonicalize(behaviour)))\n .digest('hex')\n}\n"],"mappings":"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","names":[]}
|