@tangle-network/agent-eval 0.221.3 → 0.221.6

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 (133) hide show
  1. package/CHANGELOG.md +159 -0
  2. package/README.md +0 -1
  3. package/dist/adapters/http.d.ts +2 -2
  4. package/dist/{agent-profile-Bc7ADJY4.d.ts → agent-profile-CNmwfaqq.d.ts} +3 -3
  5. package/dist/{agent-profile-Bc7ADJY4.d.ts.map → agent-profile-CNmwfaqq.d.ts.map} +1 -1
  6. package/dist/analyst/index.d.ts +14 -14
  7. package/dist/analyst/index.js +11 -11
  8. package/dist/analyst/index.js.map +1 -1
  9. package/dist/{analyze-runs-D7In4c3z.d.ts → analyze-runs-DfX6dHP0.d.ts} +2 -2
  10. package/dist/{analyze-runs-D7In4c3z.d.ts.map → analyze-runs-DfX6dHP0.d.ts.map} +1 -1
  11. package/dist/{benchmark-command-BDq0VgoP.js → benchmark-command-Cnjfo0Zh.js} +6 -6
  12. package/dist/{benchmark-command-BDq0VgoP.js.map → benchmark-command-Cnjfo0Zh.js.map} +1 -1
  13. package/dist/benchmarks/index.d.ts +4 -4
  14. package/dist/benchmarks/index.js +3 -3
  15. package/dist/campaign/index.d.ts +6 -6
  16. package/dist/campaign/index.js +4 -4
  17. package/dist/{campaign-Bt6rbWIU.js → campaign-uddTlFg5.js} +6 -6
  18. package/dist/{campaign-Bt6rbWIU.js.map → campaign-uddTlFg5.js.map} +1 -1
  19. package/dist/{chat-client-CMQ8_h4J.js → chat-client-BNATB3rJ.js} +2 -2
  20. package/dist/{chat-client-CMQ8_h4J.js.map → chat-client-BNATB3rJ.js.map} +1 -1
  21. package/dist/{chat-json-call-BrZKn0L3.js → chat-json-call-BzWYp18J.js} +2 -2
  22. package/dist/{chat-json-call-BrZKn0L3.js.map → chat-json-call-BzWYp18J.js.map} +1 -1
  23. package/dist/cli.js +3 -3
  24. package/dist/{completion-verifier-YwoeujPl.js → completion-verifier-asaV1nSj.js} +3 -3
  25. package/dist/{completion-verifier-YwoeujPl.js.map → completion-verifier-asaV1nSj.js.map} +1 -1
  26. package/dist/contract/index.d.ts +10 -10
  27. package/dist/contract/index.js +6 -6
  28. package/dist/{default-registry-BZ5FyYl9.js → default-registry-BBTDgkDM.js} +2 -2
  29. package/dist/{default-registry-BZ5FyYl9.js.map → default-registry-BBTDgkDM.js.map} +1 -1
  30. package/dist/{default-registry-BMHqNR6_.d.ts → default-registry-CLG5YqGS.d.ts} +7 -7
  31. package/dist/{default-registry-BMHqNR6_.d.ts.map → default-registry-CLG5YqGS.d.ts.map} +1 -1
  32. package/dist/{define-agent-eval-CJhECJNL.js → define-agent-eval-CCbT7Kfr.js} +3 -3
  33. package/dist/{define-agent-eval-CJhECJNL.js.map → define-agent-eval-CCbT7Kfr.js.map} +1 -1
  34. package/dist/{define-agent-eval-Bsoi2GUV.d.ts → define-agent-eval-JKt3gMQr.d.ts} +7 -7
  35. package/dist/{define-agent-eval-Bsoi2GUV.d.ts.map → define-agent-eval-JKt3gMQr.d.ts.map} +1 -1
  36. package/dist/diagnosis/index.d.ts +2 -2
  37. package/dist/{dspy-rlm-engine-BTy7u0jQ.js → dspy-rlm-engine-DN1p_5jx.js} +2 -2
  38. package/dist/{dspy-rlm-engine-BTy7u0jQ.js.map → dspy-rlm-engine-DN1p_5jx.js.map} +1 -1
  39. package/dist/{engine-BF95CWzH.d.ts → engine-dtRVeuQs.d.ts} +2 -2
  40. package/dist/{engine-BF95CWzH.d.ts.map → engine-dtRVeuQs.d.ts.map} +1 -1
  41. package/dist/{evaluation-D1PGzkPR.d.ts → evaluation-DblGquCB.d.ts} +3 -3
  42. package/dist/{evaluation-D1PGzkPR.d.ts.map → evaluation-DblGquCB.d.ts.map} +1 -1
  43. package/dist/evaluation.d.ts +1 -1
  44. package/dist/{exact-types-CVn1QxFO.d.ts → exact-types-Do63KoKn.d.ts} +2 -2
  45. package/dist/{exact-types-CVn1QxFO.d.ts.map → exact-types-Do63KoKn.d.ts.map} +1 -1
  46. package/dist/experiment/index.d.ts +1 -1
  47. package/dist/{external-optimizer-process-E5yY2SYS.js → external-optimizer-process-DEzJ2CGX.js} +2 -2
  48. package/dist/{external-optimizer-process-E5yY2SYS.js.map → external-optimizer-process-DEzJ2CGX.js.map} +1 -1
  49. package/dist/{external-optimizer-subprocess-DA02c_L6.js → external-optimizer-subprocess-B_ukPtRD.js} +2 -2
  50. package/dist/{external-optimizer-subprocess-DA02c_L6.js.map → external-optimizer-subprocess-B_ukPtRD.js.map} +1 -1
  51. package/dist/{feedback-trajectory-BZhVCWuz.d.ts → feedback-trajectory-CYVJXolG.d.ts} +3 -3
  52. package/dist/{feedback-trajectory-BZhVCWuz.d.ts.map → feedback-trajectory-CYVJXolG.d.ts.map} +1 -1
  53. package/dist/{findings-store-DRiFdoid.js → findings-store-Bg4sdS-e.js} +4 -4
  54. package/dist/{findings-store-DRiFdoid.js.map → findings-store-Bg4sdS-e.js.map} +1 -1
  55. package/dist/{index-BJZ7s-sv.d.ts → index-CTIRz-AB.d.ts} +7 -7
  56. package/dist/{index-BJZ7s-sv.d.ts.map → index-CTIRz-AB.d.ts.map} +1 -1
  57. package/dist/{index-BvX3CFNS.d.ts → index-CdEVr8-O.d.ts} +7 -7
  58. package/dist/{index-BvX3CFNS.d.ts.map → index-CdEVr8-O.d.ts.map} +1 -1
  59. package/dist/{index-DWd1gUwR.d.ts → index-CfJULcW5.d.ts} +116 -3
  60. package/dist/index-CfJULcW5.d.ts.map +1 -0
  61. package/dist/{index-y7i8l6Y_.d.ts → index-jnuFLxOs.d.ts} +8 -8
  62. package/dist/{index-y7i8l6Y_.d.ts.map → index-jnuFLxOs.d.ts.map} +1 -1
  63. package/dist/index.d.ts +21 -21
  64. package/dist/index.js +11 -90
  65. package/dist/index.js.map +1 -1
  66. package/dist/{integrity-CCwvezWS.d.ts → integrity-D9lBnv_K.d.ts} +2 -2
  67. package/dist/{integrity-CCwvezWS.d.ts.map → integrity-D9lBnv_K.d.ts.map} +1 -1
  68. package/dist/{judge-gate-Co5tuh57.js → judge-gate-BVvmP00P.js} +82 -2
  69. package/dist/judge-gate-BVvmP00P.js.map +1 -0
  70. package/dist/{llm-client-DXuOTseU.js → llm-client-b1ysZVIU.js} +7 -6
  71. package/dist/llm-client-b1ysZVIU.js.map +1 -0
  72. package/dist/{llm-judge-3uAeRawE.js → llm-judge-Cybq29bL.js} +4 -4
  73. package/dist/{llm-judge-3uAeRawE.js.map → llm-judge-Cybq29bL.js.map} +1 -1
  74. package/dist/{matrix-BXo3PSqM.d.ts → matrix-DtkZLD_3.d.ts} +2 -2
  75. package/dist/{matrix-BXo3PSqM.d.ts.map → matrix-DtkZLD_3.d.ts.map} +1 -1
  76. package/dist/meta-eval/index.d.ts +2 -2
  77. package/dist/meta-eval/index.js +217 -2
  78. package/dist/meta-eval/index.js.map +1 -1
  79. package/dist/multishot/golden/index.d.ts +1 -1
  80. package/dist/multishot/index.d.ts +47 -3
  81. package/dist/multishot/index.d.ts.map +1 -1
  82. package/dist/multishot/index.js +257 -216
  83. package/dist/multishot/index.js.map +1 -1
  84. package/dist/openapi.json +1 -1
  85. package/dist/{prime-protocol-BEaqBoQE.d.ts → prime-protocol-BGxjfhM4.d.ts} +2 -2
  86. package/dist/{prime-protocol-BEaqBoQE.d.ts.map → prime-protocol-BGxjfhM4.d.ts.map} +1 -1
  87. package/dist/{proposal-findings-D0_zkQ2l.js → proposal-findings-DqLUWsAG.js} +2 -2
  88. package/dist/{proposal-findings-D0_zkQ2l.js.map → proposal-findings-DqLUWsAG.js.map} +1 -1
  89. package/dist/{registry-DDKaUrmM.d.ts → registry-BcdJmyoU.d.ts} +4 -4
  90. package/dist/{registry-DDKaUrmM.d.ts.map → registry-BcdJmyoU.d.ts.map} +1 -1
  91. package/dist/{researcher-H0p44OYC.d.ts → researcher-DKWtoMWk.d.ts} +3 -3
  92. package/dist/{researcher-H0p44OYC.d.ts.map → researcher-DKWtoMWk.d.ts.map} +1 -1
  93. package/dist/rl.d.ts +4 -4
  94. package/dist/search/index.d.ts +2 -2
  95. package/dist/{search-kernel-C6__V0IS.d.ts → search-kernel-CctpiDd2.d.ts} +2 -2
  96. package/dist/{search-kernel-C6__V0IS.d.ts.map → search-kernel-CctpiDd2.d.ts.map} +1 -1
  97. package/dist/{server-8TS8XCvg.js → server-ChhXPCgX.js} +2 -2
  98. package/dist/{server-8TS8XCvg.js.map → server-ChhXPCgX.js.map} +1 -1
  99. package/dist/{skillopt-optimization-method-D21dH_Bh.js → skillopt-optimization-method-sB_-yi1G.js} +5 -5
  100. package/dist/{skillopt-optimization-method-D21dH_Bh.js.map → skillopt-optimization-method-sB_-yi1G.js.map} +1 -1
  101. package/dist/{statistical-heldout-pD1Yah-0.d.ts → statistical-heldout-BjzSOmLH.d.ts} +3 -3
  102. package/dist/{statistical-heldout-pD1Yah-0.d.ts.map → statistical-heldout-BjzSOmLH.d.ts.map} +1 -1
  103. package/dist/{store-tool-spans-Bf9OVKs6.d.ts → store-tool-spans-DxsFwtdr.d.ts} +5 -5
  104. package/dist/{store-tool-spans-Bf9OVKs6.d.ts.map → store-tool-spans-DxsFwtdr.d.ts.map} +1 -1
  105. package/dist/{systemone-D3MrW63C.d.ts → systemone-CHnqwmYg.d.ts} +6 -6
  106. package/dist/{systemone-D3MrW63C.d.ts.map → systemone-CHnqwmYg.d.ts.map} +1 -1
  107. package/dist/{systemone-review-C8DRiT_-.d.ts → systemone-review-cQ9acAsb.d.ts} +2 -2
  108. package/dist/{systemone-review-C8DRiT_-.d.ts.map → systemone-review-cQ9acAsb.d.ts.map} +1 -1
  109. package/dist/systemone.d.ts +3 -3
  110. package/dist/{tool-groups-DK1bu5TD.d.ts → tool-groups-b3oU_D88.d.ts} +3 -3
  111. package/dist/tool-groups-b3oU_D88.d.ts.map +1 -0
  112. package/dist/{trace-questions-D_OKYZ6u.d.ts → trace-questions-BmT8M6UQ.d.ts} +3 -3
  113. package/dist/{trace-questions-D_OKYZ6u.d.ts.map → trace-questions-BmT8M6UQ.d.ts.map} +1 -1
  114. package/dist/trace-repair/index.d.ts +1 -1
  115. package/dist/traces.d.ts +5 -5
  116. package/dist/{types-DGImvHIr.d.ts → types-6Lfdlshp.d.ts} +2 -2
  117. package/dist/{types-DGImvHIr.d.ts.map → types-6Lfdlshp.d.ts.map} +1 -1
  118. package/dist/{types-eMhdacVp.d.ts → types-B_IFcKp5.d.ts} +12 -2
  119. package/dist/types-B_IFcKp5.d.ts.map +1 -0
  120. package/dist/{types-IERw_HxR.d.ts → types-DZ0mYVw9.d.ts} +2 -2
  121. package/dist/{types-IERw_HxR.d.ts.map → types-DZ0mYVw9.d.ts.map} +1 -1
  122. package/dist/{types-DDKfH-N6.d.ts → types-M6eF9m-r.d.ts} +3 -3
  123. package/dist/{types-DDKfH-N6.d.ts.map → types-M6eF9m-r.d.ts.map} +1 -1
  124. package/dist/wire/index.d.ts +2 -2
  125. package/dist/wire/index.js +1 -1
  126. package/docs/finding-layers.md +3 -1
  127. package/docs/multishot-golden-records.md +1 -0
  128. package/package.json +1 -1
  129. package/dist/index-DWd1gUwR.d.ts.map +0 -1
  130. package/dist/judge-gate-Co5tuh57.js.map +0 -1
  131. package/dist/llm-client-DXuOTseU.js.map +0 -1
  132. package/dist/tool-groups-DK1bu5TD.d.ts.map +0 -1
  133. package/dist/types-eMhdacVp.d.ts.map +0 -1
@@ -138,140 +138,6 @@ function defaultDelegationTools(config = {}) {
138
138
  };
139
139
  }
140
140
  //#endregion
141
- //#region src/multishot/judges.ts
142
- const DEFAULT_JUDGE_MODEL = "openai/gpt-4o-mini";
143
- async function runJudge(judge, input) {
144
- const model = judge.model ?? "openai/gpt-4o-mini";
145
- const prompt = judge.buildPrompt(input);
146
- let raw = "";
147
- let llmCall;
148
- let cost;
149
- const startedAt = Date.now();
150
- try {
151
- const response = await judge.transport({
152
- model,
153
- temperature: 0,
154
- maxTokens: judge.maxTokens ?? 1500,
155
- messages: [{
156
- role: "system",
157
- content: judge.systemPrompt
158
- }, {
159
- role: "user",
160
- content: prompt
161
- }]
162
- });
163
- const call = judgeCallMetadata(response, model, Date.now() - startedAt);
164
- llmCall = call.llmCall;
165
- cost = call.cost;
166
- raw = (response.message.content ?? "").trim();
167
- } catch (err) {
168
- return {
169
- score: {
170
- dimensions: {},
171
- composite: 0,
172
- failed: true,
173
- notes: `judge ${judge.name} call failed: ${err instanceof Error ? err.message : String(err)}`,
174
- llmCall: unknownJudgeCall(model, Date.now() - startedAt)
175
- },
176
- cost: {
177
- kind: "uncaptured",
178
- usd: null
179
- }
180
- };
181
- }
182
- let parsed = null;
183
- try {
184
- const cleaned = raw.replace(/^```json\s*/i, "").replace(/```\s*$/, "").trim();
185
- parsed = JSON.parse(cleaned);
186
- } catch {
187
- return {
188
- score: {
189
- dimensions: {},
190
- composite: 0,
191
- failed: true,
192
- notes: `judge ${judge.name} returned non-JSON: ${raw.slice(0, 200)}`,
193
- llmCall
194
- },
195
- cost
196
- };
197
- }
198
- const dimensions = {};
199
- let sum = 0;
200
- for (const dim of judge.dimensions) {
201
- const v = Number(parsed[dim.key] ?? 0);
202
- const clamped = Number.isFinite(v) ? Math.max(0, Math.min(10, v)) : 0;
203
- dimensions[dim.key] = clamped;
204
- sum += clamped;
205
- }
206
- return {
207
- score: {
208
- dimensions,
209
- composite: judge.dimensions.length === 0 ? 0 : sum / judge.dimensions.length,
210
- notes: typeof parsed.notes === "string" ? parsed.notes : "",
211
- llmCall
212
- },
213
- cost
214
- };
215
- }
216
- function judgeCallMetadata(response, requestedModel, durationMs) {
217
- const usage = canonicalUsage(response.usage);
218
- const model = response.model ?? requestedModel;
219
- return {
220
- llmCall: {
221
- usage,
222
- costUsd: response.costUsd ?? null,
223
- model,
224
- durationMs
225
- },
226
- cost: response.costUsd !== void 0 ? {
227
- kind: "observed",
228
- usd: response.costUsd
229
- } : usage.captured === false || !isModelPriced(model) ? {
230
- kind: "uncaptured",
231
- usd: null
232
- } : {
233
- kind: "estimated",
234
- usd: estimateCost(usage.promptTokens, usage.completionTokens, model)
235
- }
236
- };
237
- }
238
- function canonicalUsage(usage) {
239
- const promptTokens = tokenCount(usage?.prompt_tokens);
240
- const completionTokens = tokenCount(usage?.completion_tokens);
241
- const captured = promptTokens !== void 0 && completionTokens !== void 0;
242
- return {
243
- promptTokens: promptTokens ?? 0,
244
- completionTokens: completionTokens ?? 0,
245
- totalTokens: (promptTokens ?? 0) + (completionTokens ?? 0),
246
- captured
247
- };
248
- }
249
- function unknownJudgeCall(model, durationMs) {
250
- return {
251
- usage: {
252
- promptTokens: 0,
253
- completionTokens: 0,
254
- totalTokens: 0,
255
- captured: false
256
- },
257
- costUsd: null,
258
- model,
259
- durationMs
260
- };
261
- }
262
- function tokenCount(value) {
263
- return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
264
- }
265
- /** Convenience: stringified dimension list for inclusion in a judge prompt.
266
- * Returns lines like `- audience_fit: Does this match what the audience cares about? (0-10)`. */
267
- function renderDimensions(dims) {
268
- return dims.map((d) => `- ${d.key}: ${d.description}`).join("\n");
269
- }
270
- /** Convenience: build the "Respond with ONLY this JSON" footer for a judge prompt. */
271
- function renderJsonFooter(dims) {
272
- return `Respond with ONLY this JSON (no markdown, no preamble):\n{${dims.map((d) => `"${d.key}":N`).join(",")},"notes":"1-2 sentence critique"}`;
273
- }
274
- //#endregion
275
141
  //#region src/multishot/shape-defaults.ts
276
142
  /** Persona payload rendered as stable `- key: value` lines. `id` is identity,
277
143
  * not voice, and structured values are serialized so nothing is dropped. */
@@ -321,31 +187,37 @@ function defaultShapeFromProfile(profile, shape) {
321
187
  };
322
188
  }
323
189
  //#endregion
324
- //#region src/multishot/multishot.ts
325
- /** The package's reference conversation engine, and the referent of three
326
- * things that stop working without it.
327
- *
328
- * 1. `runMultishotMatrix` runs it as the per-cell engine when the caller names
329
- * no `runShot`, so the matrix always has an engine to run.
330
- * 2. `golden.test.ts` proves the frozen records against it on every run, and
331
- * perturbs it to prove the records detect a change. Records checked only
332
- * against a replay of themselves prove nothing.
333
- * 3. The repo's recording command names it as `--engine`. The script defaults
334
- * no engine, so a version records whichever one the caller names — and a
335
- * record set minted from a consumer's engine would let a product's wiring
336
- * define this package's frozen contract.
190
+ //#region src/multishot/conversation.ts
191
+ /**
192
+ * Run one multishot conversation whose leg order `drive` decides.
337
193
  *
338
- * An alternative engine reaches the matrix through `runShot` and reaches the
339
- * records through `assertMultishotGoldenScenario`. Neither seam removes the
340
- * three roles above. */
341
- async function runMultishot(opts) {
194
+ * Returns the conversation's `MultishotResult`. When any leg or `drive` throws,
195
+ * the error is rethrown carrying the spend so far (`withCellSpend`), so a
196
+ * matrix bills the failed cell for the turns it already paid for.
197
+ */
198
+ async function runMultishotConversation(opts, drive) {
342
199
  const meter = {
343
200
  costUsd: 0,
344
201
  startedAt: Date.now(),
345
202
  uncaptured: false
346
203
  };
347
204
  try {
348
- return await runShotTurns(opts, meter);
205
+ const state = openConversation(opts, meter);
206
+ await drive(state.conversation);
207
+ return {
208
+ transcript: state.transcript,
209
+ artifacts: state.artifacts,
210
+ toolCalls: state.toolCalls(),
211
+ durationMs: Date.now() - meter.startedAt,
212
+ costUsd: meter.costUsd,
213
+ costProvenance: meter.uncaptured ? {
214
+ kind: "uncaptured",
215
+ usd: null
216
+ } : {
217
+ kind: "estimated",
218
+ usd: meter.costUsd
219
+ }
220
+ };
349
221
  } catch (err) {
350
222
  throw withCellSpend(err, {
351
223
  costUsd: meter.costUsd,
@@ -354,11 +226,12 @@ async function runMultishot(opts) {
354
226
  });
355
227
  }
356
228
  }
357
- async function runShotTurns(opts, meter) {
229
+ function openConversation(opts, meter) {
358
230
  const maxTurns = opts.maxTurns ?? 10;
359
231
  const maxToolDispatches = opts.maxToolDispatches ?? 4;
360
232
  const agentModel = opts.agentModel ?? "openai/gpt-5.4";
361
- const driverModels = [opts.driverModel ?? "openai/gpt-4o-mini", ...opts.driverFallbackModels ?? []];
233
+ const driverModel = opts.driverModel ?? "openai/gpt-4o-mini";
234
+ const driverModels = [driverModel, ...opts.driverFallbackModels ?? []];
362
235
  const agentMaxTokens = opts.agentMaxTokens ?? 2500;
363
236
  const toolFollowupMaxTokens = opts.toolFollowupMaxTokens ?? 2e3;
364
237
  const driverMaxTokens = opts.driverMaxTokens ?? 600;
@@ -389,8 +262,13 @@ async function runShotTurns(opts, meter) {
389
262
  role: "user",
390
263
  content: opener
391
264
  }];
392
- for (let turn = 0; turn < maxTurns; turn++) {
265
+ const driverSystemPrompt = shape.buildDriverSystemPrompt(opts.persona);
266
+ async function agentTurn(turn) {
393
267
  if (opts.signal?.aborted) throw new Error("multishot aborted");
268
+ const tokens = {
269
+ input: 0,
270
+ output: 0
271
+ };
394
272
  let dispatchesThisTurn = 0;
395
273
  while (true) {
396
274
  const { message: agentMsg, usage: agentUsage, costUsd: agentCostUsd } = await agentTransport({
@@ -403,6 +281,8 @@ async function runShotTurns(opts, meter) {
403
281
  });
404
282
  meter.costUsd += agentCostUsd ?? estimateMultishotCost(agentModel, agentUsage);
405
283
  if (agentCostUsd === void 0 && agentUsage === void 0) meter.uncaptured = true;
284
+ tokens.input += agentUsage?.prompt_tokens ?? 0;
285
+ tokens.output += agentUsage?.completion_tokens ?? 0;
406
286
  const agentText = (agentMsg.content ?? "").trim();
407
287
  const agentToolCalls = (agentMsg.tool_calls ?? []).map((tc) => ({
408
288
  id: tc.id,
@@ -468,76 +348,77 @@ async function runShotTurns(opts, meter) {
468
348
  });
469
349
  }
470
350
  }
471
- if (turn < maxTurns - 1) {
472
- const driver = await driverTurn({
473
- transport: driverTransport,
474
- persona: opts.persona,
475
- shape,
476
- transcript,
477
- turn,
478
- models: driverModels,
479
- maxTokens: driverMaxTokens,
480
- signal: opts.signal,
481
- meter
482
- });
483
- agentMessages.push({
351
+ return { tokens };
352
+ }
353
+ async function driverTurn(turn) {
354
+ const driverMessages = [{
355
+ role: "system",
356
+ content: driverSystemPrompt
357
+ }];
358
+ for (const msg of transcript) {
359
+ if (msg.role === "tool") continue;
360
+ const content = driverVisibleContent(msg);
361
+ if (!content) continue;
362
+ if (msg.role === "assistant") driverMessages.push({
484
363
  role: "user",
485
- content: driver.content
364
+ content
486
365
  });
487
- transcript.push({
488
- role: "user",
489
- content: driver.content
366
+ else if (msg.role === "user") driverMessages.push({
367
+ role: "assistant",
368
+ content
490
369
  });
491
370
  }
371
+ const tokens = {
372
+ input: 0,
373
+ output: 0
374
+ };
375
+ for (const model of driverModels) for (let attempt = 0; attempt < 2; attempt++) {
376
+ const { message, usage, costUsd } = await driverTransport({
377
+ model,
378
+ messages: driverMessages,
379
+ temperature: .9,
380
+ maxTokens: driverMaxTokens,
381
+ signal: opts.signal
382
+ });
383
+ meter.costUsd += costUsd ?? estimateMultishotCost(model, usage);
384
+ if (costUsd === void 0 && usage === void 0) meter.uncaptured = true;
385
+ tokens.input += usage?.prompt_tokens ?? 0;
386
+ tokens.output += usage?.completion_tokens ?? 0;
387
+ const content = (message.content ?? "").trim();
388
+ if (content.length > 0) {
389
+ agentMessages.push({
390
+ role: "user",
391
+ content
392
+ });
393
+ transcript.push({
394
+ role: "user",
395
+ content
396
+ });
397
+ return {
398
+ content,
399
+ tokens
400
+ };
401
+ }
402
+ }
403
+ throw new MultishotDriverEmptyError(turn);
492
404
  }
493
405
  return {
406
+ conversation: {
407
+ maxTurns,
408
+ agentModel,
409
+ driverModel,
410
+ opener,
411
+ driverSystemPrompt,
412
+ agentTurn,
413
+ driverTurn
414
+ },
494
415
  transcript,
495
416
  artifacts,
496
- toolCalls,
497
- durationMs: Date.now() - meter.startedAt,
498
- costUsd: meter.costUsd,
499
- costProvenance: meter.uncaptured ? {
500
- kind: "uncaptured",
501
- usd: null
502
- } : {
503
- kind: "estimated",
504
- usd: meter.costUsd
505
- }
417
+ toolCalls: () => toolCalls
506
418
  };
507
419
  }
508
- async function driverTurn(opts) {
509
- const driverMessages = [{
510
- role: "system",
511
- content: opts.shape.buildDriverSystemPrompt(opts.persona)
512
- }];
513
- for (const msg of opts.transcript) {
514
- if (msg.role === "tool") continue;
515
- const content = driverVisibleContent(msg);
516
- if (!content) continue;
517
- if (msg.role === "assistant") driverMessages.push({
518
- role: "user",
519
- content
520
- });
521
- else if (msg.role === "user") driverMessages.push({
522
- role: "assistant",
523
- content
524
- });
525
- }
526
- for (const model of opts.models) for (let attempt = 0; attempt < 2; attempt++) {
527
- const { message, usage, costUsd } = await opts.transport({
528
- model,
529
- messages: driverMessages,
530
- temperature: .9,
531
- maxTokens: opts.maxTokens,
532
- signal: opts.signal
533
- });
534
- opts.meter.costUsd += costUsd ?? estimateMultishotCost(model, usage);
535
- if (costUsd === void 0 && usage === void 0) opts.meter.uncaptured = true;
536
- const content = (message.content ?? "").trim();
537
- if (content.length > 0) return { content };
538
- }
539
- throw new MultishotDriverEmptyError(opts.turn);
540
- }
420
+ /** Silent tool-calling agent turns surface to the driver as
421
+ * "Agent called tools: …" so the simulated user can react to them. */
541
422
  function driverVisibleContent(msg) {
542
423
  const text = msg.content.trim();
543
424
  if (text.length > 0) return text;
@@ -547,6 +428,166 @@ function driverVisibleContent(msg) {
547
428
  return `Agent called ${toolNames.length === 1 ? "tool" : "tools"}: ${toolNames.join(", ")}.`;
548
429
  }
549
430
  //#endregion
431
+ //#region src/multishot/judges.ts
432
+ const DEFAULT_JUDGE_MODEL = "openai/gpt-4o-mini";
433
+ async function runJudge(judge, input) {
434
+ const model = judge.model ?? "openai/gpt-4o-mini";
435
+ const prompt = judge.buildPrompt(input);
436
+ let raw = "";
437
+ let llmCall;
438
+ let cost;
439
+ const startedAt = Date.now();
440
+ try {
441
+ const response = await judge.transport({
442
+ model,
443
+ temperature: 0,
444
+ maxTokens: judge.maxTokens ?? 1500,
445
+ messages: [{
446
+ role: "system",
447
+ content: judge.systemPrompt
448
+ }, {
449
+ role: "user",
450
+ content: prompt
451
+ }]
452
+ });
453
+ const call = judgeCallMetadata(response, model, Date.now() - startedAt);
454
+ llmCall = call.llmCall;
455
+ cost = call.cost;
456
+ raw = (response.message.content ?? "").trim();
457
+ } catch (err) {
458
+ return {
459
+ score: {
460
+ dimensions: {},
461
+ composite: 0,
462
+ failed: true,
463
+ notes: `judge ${judge.name} call failed: ${err instanceof Error ? err.message : String(err)}`,
464
+ llmCall: unknownJudgeCall(model, Date.now() - startedAt)
465
+ },
466
+ cost: {
467
+ kind: "uncaptured",
468
+ usd: null
469
+ }
470
+ };
471
+ }
472
+ let parsed = null;
473
+ try {
474
+ const cleaned = raw.replace(/^```json\s*/i, "").replace(/```\s*$/, "").trim();
475
+ parsed = JSON.parse(cleaned);
476
+ } catch {
477
+ return {
478
+ score: {
479
+ dimensions: {},
480
+ composite: 0,
481
+ failed: true,
482
+ notes: `judge ${judge.name} returned non-JSON: ${raw.slice(0, 200)}`,
483
+ llmCall
484
+ },
485
+ cost
486
+ };
487
+ }
488
+ const dimensions = {};
489
+ let sum = 0;
490
+ for (const dim of judge.dimensions) {
491
+ const v = Number(parsed[dim.key] ?? 0);
492
+ const clamped = Number.isFinite(v) ? Math.max(0, Math.min(10, v)) : 0;
493
+ dimensions[dim.key] = clamped;
494
+ sum += clamped;
495
+ }
496
+ return {
497
+ score: {
498
+ dimensions,
499
+ composite: judge.dimensions.length === 0 ? 0 : sum / judge.dimensions.length,
500
+ notes: typeof parsed.notes === "string" ? parsed.notes : "",
501
+ llmCall
502
+ },
503
+ cost
504
+ };
505
+ }
506
+ function judgeCallMetadata(response, requestedModel, durationMs) {
507
+ const usage = canonicalUsage(response.usage);
508
+ const model = response.model ?? requestedModel;
509
+ return {
510
+ llmCall: {
511
+ usage,
512
+ costUsd: response.costUsd ?? null,
513
+ model,
514
+ durationMs
515
+ },
516
+ cost: response.costUsd !== void 0 ? {
517
+ kind: "observed",
518
+ usd: response.costUsd
519
+ } : usage.captured === false || !isModelPriced(model) ? {
520
+ kind: "uncaptured",
521
+ usd: null
522
+ } : {
523
+ kind: "estimated",
524
+ usd: estimateCost(usage.promptTokens, usage.completionTokens, model)
525
+ }
526
+ };
527
+ }
528
+ function canonicalUsage(usage) {
529
+ const promptTokens = tokenCount(usage?.prompt_tokens);
530
+ const completionTokens = tokenCount(usage?.completion_tokens);
531
+ const captured = promptTokens !== void 0 && completionTokens !== void 0;
532
+ return {
533
+ promptTokens: promptTokens ?? 0,
534
+ completionTokens: completionTokens ?? 0,
535
+ totalTokens: (promptTokens ?? 0) + (completionTokens ?? 0),
536
+ captured
537
+ };
538
+ }
539
+ function unknownJudgeCall(model, durationMs) {
540
+ return {
541
+ usage: {
542
+ promptTokens: 0,
543
+ completionTokens: 0,
544
+ totalTokens: 0,
545
+ captured: false
546
+ },
547
+ costUsd: null,
548
+ model,
549
+ durationMs
550
+ };
551
+ }
552
+ function tokenCount(value) {
553
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
554
+ }
555
+ /** Convenience: stringified dimension list for inclusion in a judge prompt.
556
+ * Returns lines like `- audience_fit: Does this match what the audience cares about? (0-10)`. */
557
+ function renderDimensions(dims) {
558
+ return dims.map((d) => `- ${d.key}: ${d.description}`).join("\n");
559
+ }
560
+ /** Convenience: build the "Respond with ONLY this JSON" footer for a judge prompt. */
561
+ function renderJsonFooter(dims) {
562
+ return `Respond with ONLY this JSON (no markdown, no preamble):\n{${dims.map((d) => `"${d.key}":N`).join(",")},"notes":"1-2 sentence critique"}`;
563
+ }
564
+ //#endregion
565
+ //#region src/multishot/multishot.ts
566
+ /** The package's reference conversation engine, and the referent of three
567
+ * things that stop working without it.
568
+ *
569
+ * 1. `runMultishotMatrix` runs it as the per-cell engine when the caller names
570
+ * no `runShot`, so the matrix always has an engine to run.
571
+ * 2. `golden.test.ts` proves the frozen records against it on every run, and
572
+ * perturbs it to prove the records detect a change. Records checked only
573
+ * against a replay of themselves prove nothing.
574
+ * 3. The repo's recording command names it as `--engine`. The script defaults
575
+ * no engine, so a version records whichever one the caller names — and a
576
+ * record set minted from a consumer's engine would let a product's wiring
577
+ * define this package's frozen contract.
578
+ *
579
+ * An alternative engine reaches the matrix through `runShot` and reaches the
580
+ * records through `assertMultishotGoldenScenario`. Neither seam removes the
581
+ * three roles above. */
582
+ async function runMultishot(opts) {
583
+ return runMultishotConversation(opts, async (conversation) => {
584
+ for (let turn = 0; turn < conversation.maxTurns; turn++) {
585
+ await conversation.agentTurn(turn);
586
+ if (turn < conversation.maxTurns - 1) await conversation.driverTurn(turn);
587
+ }
588
+ });
589
+ }
590
+ //#endregion
550
591
  //#region src/multishot/matrix.ts
551
592
  /** Mean composite over non-failed scores. `0` when the list is empty (a
552
593
  * configured judge with nothing to score contributes 0, matching the cell
@@ -784,6 +825,6 @@ function withJudgeMaxTokens(judge, maxTokens) {
784
825
  };
785
826
  }
786
827
  //#endregion
787
- export { DEFAULT_CODER_MODEL, DEFAULT_JUDGE_MODEL, MultishotDriverEmptyError, MultishotFatalToolError, assertMultishotShotResult, computeCellComposite, defaultDelegationTools, defaultShapeFromProfile, estimateMultishotCost, renderDimensions, renderJsonFooter, runJudge, runMultishot, runMultishotMatrix };
828
+ export { DEFAULT_CODER_MODEL, DEFAULT_JUDGE_MODEL, MultishotDriverEmptyError, MultishotFatalToolError, assertMultishotShotResult, computeCellComposite, defaultDelegationTools, defaultShapeFromProfile, estimateMultishotCost, renderDimensions, renderJsonFooter, runJudge, runMultishot, runMultishotConversation, runMultishotMatrix };
788
829
 
789
830
  //# sourceMappingURL=index.js.map