@tangle-network/agent-eval 0.221.5 → 0.221.7

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 (130) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/dist/adapters/http.d.ts +2 -2
  3. package/dist/{agent-profile-Bc7ADJY4.d.ts → agent-profile-CNmwfaqq.d.ts} +3 -3
  4. package/dist/{agent-profile-Bc7ADJY4.d.ts.map → agent-profile-CNmwfaqq.d.ts.map} +1 -1
  5. package/dist/analyst/index.d.ts +12 -12
  6. package/dist/analyst/index.js +7 -7
  7. package/dist/{analyze-runs-D7In4c3z.d.ts → analyze-runs-DfX6dHP0.d.ts} +2 -2
  8. package/dist/{analyze-runs-D7In4c3z.d.ts.map → analyze-runs-DfX6dHP0.d.ts.map} +1 -1
  9. package/dist/{benchmark-command-BDq0VgoP.js → benchmark-command-Cnjfo0Zh.js} +6 -6
  10. package/dist/{benchmark-command-BDq0VgoP.js.map → benchmark-command-Cnjfo0Zh.js.map} +1 -1
  11. package/dist/benchmarks/index.d.ts +4 -4
  12. package/dist/benchmarks/index.js +3 -3
  13. package/dist/campaign/index.d.ts +6 -6
  14. package/dist/campaign/index.js +4 -4
  15. package/dist/{campaign-Bt6rbWIU.js → campaign-uddTlFg5.js} +6 -6
  16. package/dist/{campaign-Bt6rbWIU.js.map → campaign-uddTlFg5.js.map} +1 -1
  17. package/dist/{chat-client-CMQ8_h4J.js → chat-client-BNATB3rJ.js} +2 -2
  18. package/dist/{chat-client-CMQ8_h4J.js.map → chat-client-BNATB3rJ.js.map} +1 -1
  19. package/dist/{chat-json-call-BrZKn0L3.js → chat-json-call-BzWYp18J.js} +2 -2
  20. package/dist/{chat-json-call-BrZKn0L3.js.map → chat-json-call-BzWYp18J.js.map} +1 -1
  21. package/dist/cli.js +3 -3
  22. package/dist/{completion-verifier-YwoeujPl.js → completion-verifier-asaV1nSj.js} +3 -3
  23. package/dist/{completion-verifier-YwoeujPl.js.map → completion-verifier-asaV1nSj.js.map} +1 -1
  24. package/dist/contract/index.d.ts +10 -10
  25. package/dist/contract/index.js +6 -6
  26. package/dist/{default-registry-BZ5FyYl9.js → default-registry-BBTDgkDM.js} +2 -2
  27. package/dist/{default-registry-BZ5FyYl9.js.map → default-registry-BBTDgkDM.js.map} +1 -1
  28. package/dist/{default-registry-BMHqNR6_.d.ts → default-registry-CLG5YqGS.d.ts} +7 -7
  29. package/dist/{default-registry-BMHqNR6_.d.ts.map → default-registry-CLG5YqGS.d.ts.map} +1 -1
  30. package/dist/{define-agent-eval-CJhECJNL.js → define-agent-eval-CCbT7Kfr.js} +3 -3
  31. package/dist/{define-agent-eval-CJhECJNL.js.map → define-agent-eval-CCbT7Kfr.js.map} +1 -1
  32. package/dist/{define-agent-eval-Bsoi2GUV.d.ts → define-agent-eval-JKt3gMQr.d.ts} +7 -7
  33. package/dist/{define-agent-eval-Bsoi2GUV.d.ts.map → define-agent-eval-JKt3gMQr.d.ts.map} +1 -1
  34. package/dist/diagnosis/index.d.ts +2 -2
  35. package/dist/{dspy-rlm-engine-BTy7u0jQ.js → dspy-rlm-engine-DN1p_5jx.js} +2 -2
  36. package/dist/{dspy-rlm-engine-BTy7u0jQ.js.map → dspy-rlm-engine-DN1p_5jx.js.map} +1 -1
  37. package/dist/{engine-BF95CWzH.d.ts → engine-dtRVeuQs.d.ts} +2 -2
  38. package/dist/{engine-BF95CWzH.d.ts.map → engine-dtRVeuQs.d.ts.map} +1 -1
  39. package/dist/{evaluation-D1PGzkPR.d.ts → evaluation-DblGquCB.d.ts} +3 -3
  40. package/dist/{evaluation-D1PGzkPR.d.ts.map → evaluation-DblGquCB.d.ts.map} +1 -1
  41. package/dist/evaluation.d.ts +1 -1
  42. package/dist/{exact-types-CVn1QxFO.d.ts → exact-types-Do63KoKn.d.ts} +2 -2
  43. package/dist/{exact-types-CVn1QxFO.d.ts.map → exact-types-Do63KoKn.d.ts.map} +1 -1
  44. package/dist/experiment/index.d.ts +1 -1
  45. package/dist/{external-optimizer-process-E5yY2SYS.js → external-optimizer-process-DEzJ2CGX.js} +2 -2
  46. package/dist/{external-optimizer-process-E5yY2SYS.js.map → external-optimizer-process-DEzJ2CGX.js.map} +1 -1
  47. package/dist/{external-optimizer-subprocess-DA02c_L6.js → external-optimizer-subprocess-B_ukPtRD.js} +2 -2
  48. package/dist/{external-optimizer-subprocess-DA02c_L6.js.map → external-optimizer-subprocess-B_ukPtRD.js.map} +1 -1
  49. package/dist/{feedback-trajectory-BZhVCWuz.d.ts → feedback-trajectory-CYVJXolG.d.ts} +3 -3
  50. package/dist/{feedback-trajectory-BZhVCWuz.d.ts.map → feedback-trajectory-CYVJXolG.d.ts.map} +1 -1
  51. package/dist/{findings-store-DRiFdoid.js → findings-store-Bg4sdS-e.js} +4 -4
  52. package/dist/{findings-store-DRiFdoid.js.map → findings-store-Bg4sdS-e.js.map} +1 -1
  53. package/dist/{index-BJZ7s-sv.d.ts → index-CTIRz-AB.d.ts} +7 -7
  54. package/dist/{index-BJZ7s-sv.d.ts.map → index-CTIRz-AB.d.ts.map} +1 -1
  55. package/dist/{index-BvX3CFNS.d.ts → index-CdEVr8-O.d.ts} +7 -7
  56. package/dist/{index-BvX3CFNS.d.ts.map → index-CdEVr8-O.d.ts.map} +1 -1
  57. package/dist/{index-DWd1gUwR.d.ts → index-CfJULcW5.d.ts} +116 -3
  58. package/dist/index-CfJULcW5.d.ts.map +1 -0
  59. package/dist/{index-y7i8l6Y_.d.ts → index-jnuFLxOs.d.ts} +8 -8
  60. package/dist/{index-y7i8l6Y_.d.ts.map → index-jnuFLxOs.d.ts.map} +1 -1
  61. package/dist/index.d.ts +21 -21
  62. package/dist/index.js +11 -90
  63. package/dist/index.js.map +1 -1
  64. package/dist/{integrity-CCwvezWS.d.ts → integrity-D9lBnv_K.d.ts} +2 -2
  65. package/dist/{integrity-CCwvezWS.d.ts.map → integrity-D9lBnv_K.d.ts.map} +1 -1
  66. package/dist/{judge-gate-Co5tuh57.js → judge-gate-BVvmP00P.js} +82 -2
  67. package/dist/judge-gate-BVvmP00P.js.map +1 -0
  68. package/dist/{llm-client-DXuOTseU.js → llm-client-b1ysZVIU.js} +7 -6
  69. package/dist/llm-client-b1ysZVIU.js.map +1 -0
  70. package/dist/{llm-judge-3uAeRawE.js → llm-judge-Cybq29bL.js} +4 -4
  71. package/dist/{llm-judge-3uAeRawE.js.map → llm-judge-Cybq29bL.js.map} +1 -1
  72. package/dist/{matrix-BXo3PSqM.d.ts → matrix-DtkZLD_3.d.ts} +2 -2
  73. package/dist/{matrix-BXo3PSqM.d.ts.map → matrix-DtkZLD_3.d.ts.map} +1 -1
  74. package/dist/meta-eval/index.d.ts +2 -2
  75. package/dist/meta-eval/index.js +217 -2
  76. package/dist/meta-eval/index.js.map +1 -1
  77. package/dist/multishot/golden/index.d.ts +1 -1
  78. package/dist/multishot/index.d.ts +51 -3
  79. package/dist/multishot/index.d.ts.map +1 -1
  80. package/dist/multishot/index.js +263 -216
  81. package/dist/multishot/index.js.map +1 -1
  82. package/dist/openapi.json +1 -1
  83. package/dist/{prime-protocol-BEaqBoQE.d.ts → prime-protocol-BGxjfhM4.d.ts} +2 -2
  84. package/dist/{prime-protocol-BEaqBoQE.d.ts.map → prime-protocol-BGxjfhM4.d.ts.map} +1 -1
  85. package/dist/{proposal-findings-D0_zkQ2l.js → proposal-findings-DqLUWsAG.js} +2 -2
  86. package/dist/{proposal-findings-D0_zkQ2l.js.map → proposal-findings-DqLUWsAG.js.map} +1 -1
  87. package/dist/{registry-DDKaUrmM.d.ts → registry-BcdJmyoU.d.ts} +4 -4
  88. package/dist/{registry-DDKaUrmM.d.ts.map → registry-BcdJmyoU.d.ts.map} +1 -1
  89. package/dist/{researcher-H0p44OYC.d.ts → researcher-DKWtoMWk.d.ts} +3 -3
  90. package/dist/{researcher-H0p44OYC.d.ts.map → researcher-DKWtoMWk.d.ts.map} +1 -1
  91. package/dist/rl.d.ts +4 -4
  92. package/dist/search/index.d.ts +2 -2
  93. package/dist/{search-kernel-C6__V0IS.d.ts → search-kernel-CctpiDd2.d.ts} +2 -2
  94. package/dist/{search-kernel-C6__V0IS.d.ts.map → search-kernel-CctpiDd2.d.ts.map} +1 -1
  95. package/dist/{server-8TS8XCvg.js → server-ChhXPCgX.js} +2 -2
  96. package/dist/{server-8TS8XCvg.js.map → server-ChhXPCgX.js.map} +1 -1
  97. package/dist/{skillopt-optimization-method-D21dH_Bh.js → skillopt-optimization-method-sB_-yi1G.js} +5 -5
  98. package/dist/{skillopt-optimization-method-D21dH_Bh.js.map → skillopt-optimization-method-sB_-yi1G.js.map} +1 -1
  99. package/dist/{statistical-heldout-pD1Yah-0.d.ts → statistical-heldout-BjzSOmLH.d.ts} +3 -3
  100. package/dist/{statistical-heldout-pD1Yah-0.d.ts.map → statistical-heldout-BjzSOmLH.d.ts.map} +1 -1
  101. package/dist/{store-tool-spans-Bf9OVKs6.d.ts → store-tool-spans-DxsFwtdr.d.ts} +5 -5
  102. package/dist/{store-tool-spans-Bf9OVKs6.d.ts.map → store-tool-spans-DxsFwtdr.d.ts.map} +1 -1
  103. package/dist/{systemone-D3MrW63C.d.ts → systemone-CHnqwmYg.d.ts} +6 -6
  104. package/dist/{systemone-D3MrW63C.d.ts.map → systemone-CHnqwmYg.d.ts.map} +1 -1
  105. package/dist/{systemone-review-C8DRiT_-.d.ts → systemone-review-cQ9acAsb.d.ts} +2 -2
  106. package/dist/{systemone-review-C8DRiT_-.d.ts.map → systemone-review-cQ9acAsb.d.ts.map} +1 -1
  107. package/dist/systemone.d.ts +3 -3
  108. package/dist/{tool-groups-DK1bu5TD.d.ts → tool-groups-b3oU_D88.d.ts} +3 -3
  109. package/dist/tool-groups-b3oU_D88.d.ts.map +1 -0
  110. package/dist/{trace-questions-D_OKYZ6u.d.ts → trace-questions-BmT8M6UQ.d.ts} +3 -3
  111. package/dist/{trace-questions-D_OKYZ6u.d.ts.map → trace-questions-BmT8M6UQ.d.ts.map} +1 -1
  112. package/dist/trace-repair/index.d.ts +1 -1
  113. package/dist/traces.d.ts +5 -5
  114. package/dist/{types-DGImvHIr.d.ts → types-6Lfdlshp.d.ts} +2 -2
  115. package/dist/{types-DGImvHIr.d.ts.map → types-6Lfdlshp.d.ts.map} +1 -1
  116. package/dist/{types-eMhdacVp.d.ts → types-B_IFcKp5.d.ts} +12 -2
  117. package/dist/types-B_IFcKp5.d.ts.map +1 -0
  118. package/dist/{types-IERw_HxR.d.ts → types-DZ0mYVw9.d.ts} +2 -2
  119. package/dist/{types-IERw_HxR.d.ts.map → types-DZ0mYVw9.d.ts.map} +1 -1
  120. package/dist/{types-DDKfH-N6.d.ts → types-M6eF9m-r.d.ts} +3 -3
  121. package/dist/{types-DDKfH-N6.d.ts.map → types-M6eF9m-r.d.ts.map} +1 -1
  122. package/dist/wire/index.d.ts +2 -2
  123. package/dist/wire/index.js +1 -1
  124. package/docs/multishot-golden-records.md +1 -0
  125. package/package.json +1 -1
  126. package/dist/index-DWd1gUwR.d.ts.map +0 -1
  127. package/dist/judge-gate-Co5tuh57.js.map +0 -1
  128. package/dist/llm-client-DXuOTseU.js.map +0 -1
  129. package/dist/tool-groups-DK1bu5TD.d.ts.map +0 -1
  130. 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,14 @@ 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 usdBefore = meter.costUsd;
269
+ const tokens = {
270
+ input: 0,
271
+ output: 0
272
+ };
394
273
  let dispatchesThisTurn = 0;
395
274
  while (true) {
396
275
  const { message: agentMsg, usage: agentUsage, costUsd: agentCostUsd } = await agentTransport({
@@ -403,6 +282,8 @@ async function runShotTurns(opts, meter) {
403
282
  });
404
283
  meter.costUsd += agentCostUsd ?? estimateMultishotCost(agentModel, agentUsage);
405
284
  if (agentCostUsd === void 0 && agentUsage === void 0) meter.uncaptured = true;
285
+ tokens.input += agentUsage?.prompt_tokens ?? 0;
286
+ tokens.output += agentUsage?.completion_tokens ?? 0;
406
287
  const agentText = (agentMsg.content ?? "").trim();
407
288
  const agentToolCalls = (agentMsg.tool_calls ?? []).map((tc) => ({
408
289
  id: tc.id,
@@ -468,76 +349,82 @@ async function runShotTurns(opts, meter) {
468
349
  });
469
350
  }
470
351
  }
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({
352
+ return {
353
+ tokens,
354
+ usd: meter.costUsd - usdBefore
355
+ };
356
+ }
357
+ async function driverTurn(turn) {
358
+ const usdBefore = meter.costUsd;
359
+ const driverMessages = [{
360
+ role: "system",
361
+ content: driverSystemPrompt
362
+ }];
363
+ for (const msg of transcript) {
364
+ if (msg.role === "tool") continue;
365
+ const content = driverVisibleContent(msg);
366
+ if (!content) continue;
367
+ if (msg.role === "assistant") driverMessages.push({
484
368
  role: "user",
485
- content: driver.content
369
+ content
486
370
  });
487
- transcript.push({
488
- role: "user",
489
- content: driver.content
371
+ else if (msg.role === "user") driverMessages.push({
372
+ role: "assistant",
373
+ content
490
374
  });
491
375
  }
376
+ const tokens = {
377
+ input: 0,
378
+ output: 0
379
+ };
380
+ for (const model of driverModels) for (let attempt = 0; attempt < 2; attempt++) {
381
+ const { message, usage, costUsd } = await driverTransport({
382
+ model,
383
+ messages: driverMessages,
384
+ temperature: .9,
385
+ maxTokens: driverMaxTokens,
386
+ signal: opts.signal
387
+ });
388
+ meter.costUsd += costUsd ?? estimateMultishotCost(model, usage);
389
+ if (costUsd === void 0 && usage === void 0) meter.uncaptured = true;
390
+ tokens.input += usage?.prompt_tokens ?? 0;
391
+ tokens.output += usage?.completion_tokens ?? 0;
392
+ const content = (message.content ?? "").trim();
393
+ if (content.length > 0) {
394
+ agentMessages.push({
395
+ role: "user",
396
+ content
397
+ });
398
+ transcript.push({
399
+ role: "user",
400
+ content
401
+ });
402
+ return {
403
+ content,
404
+ tokens,
405
+ usd: meter.costUsd - usdBefore
406
+ };
407
+ }
408
+ }
409
+ throw new MultishotDriverEmptyError(turn);
492
410
  }
493
411
  return {
412
+ conversation: {
413
+ maxTurns,
414
+ agentModel,
415
+ driverModel,
416
+ opener,
417
+ driverSystemPrompt,
418
+ agentTurn,
419
+ driverTurn
420
+ },
494
421
  transcript,
495
422
  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
- }
423
+ toolCalls: () => toolCalls
506
424
  };
507
425
  }
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
- }
426
+ /** Silent tool-calling agent turns surface to the driver as
427
+ * "Agent called tools: …" so the simulated user can react to them. */
541
428
  function driverVisibleContent(msg) {
542
429
  const text = msg.content.trim();
543
430
  if (text.length > 0) return text;
@@ -547,6 +434,166 @@ function driverVisibleContent(msg) {
547
434
  return `Agent called ${toolNames.length === 1 ? "tool" : "tools"}: ${toolNames.join(", ")}.`;
548
435
  }
549
436
  //#endregion
437
+ //#region src/multishot/judges.ts
438
+ const DEFAULT_JUDGE_MODEL = "openai/gpt-4o-mini";
439
+ async function runJudge(judge, input) {
440
+ const model = judge.model ?? "openai/gpt-4o-mini";
441
+ const prompt = judge.buildPrompt(input);
442
+ let raw = "";
443
+ let llmCall;
444
+ let cost;
445
+ const startedAt = Date.now();
446
+ try {
447
+ const response = await judge.transport({
448
+ model,
449
+ temperature: 0,
450
+ maxTokens: judge.maxTokens ?? 1500,
451
+ messages: [{
452
+ role: "system",
453
+ content: judge.systemPrompt
454
+ }, {
455
+ role: "user",
456
+ content: prompt
457
+ }]
458
+ });
459
+ const call = judgeCallMetadata(response, model, Date.now() - startedAt);
460
+ llmCall = call.llmCall;
461
+ cost = call.cost;
462
+ raw = (response.message.content ?? "").trim();
463
+ } catch (err) {
464
+ return {
465
+ score: {
466
+ dimensions: {},
467
+ composite: 0,
468
+ failed: true,
469
+ notes: `judge ${judge.name} call failed: ${err instanceof Error ? err.message : String(err)}`,
470
+ llmCall: unknownJudgeCall(model, Date.now() - startedAt)
471
+ },
472
+ cost: {
473
+ kind: "uncaptured",
474
+ usd: null
475
+ }
476
+ };
477
+ }
478
+ let parsed = null;
479
+ try {
480
+ const cleaned = raw.replace(/^```json\s*/i, "").replace(/```\s*$/, "").trim();
481
+ parsed = JSON.parse(cleaned);
482
+ } catch {
483
+ return {
484
+ score: {
485
+ dimensions: {},
486
+ composite: 0,
487
+ failed: true,
488
+ notes: `judge ${judge.name} returned non-JSON: ${raw.slice(0, 200)}`,
489
+ llmCall
490
+ },
491
+ cost
492
+ };
493
+ }
494
+ const dimensions = {};
495
+ let sum = 0;
496
+ for (const dim of judge.dimensions) {
497
+ const v = Number(parsed[dim.key] ?? 0);
498
+ const clamped = Number.isFinite(v) ? Math.max(0, Math.min(10, v)) : 0;
499
+ dimensions[dim.key] = clamped;
500
+ sum += clamped;
501
+ }
502
+ return {
503
+ score: {
504
+ dimensions,
505
+ composite: judge.dimensions.length === 0 ? 0 : sum / judge.dimensions.length,
506
+ notes: typeof parsed.notes === "string" ? parsed.notes : "",
507
+ llmCall
508
+ },
509
+ cost
510
+ };
511
+ }
512
+ function judgeCallMetadata(response, requestedModel, durationMs) {
513
+ const usage = canonicalUsage(response.usage);
514
+ const model = response.model ?? requestedModel;
515
+ return {
516
+ llmCall: {
517
+ usage,
518
+ costUsd: response.costUsd ?? null,
519
+ model,
520
+ durationMs
521
+ },
522
+ cost: response.costUsd !== void 0 ? {
523
+ kind: "observed",
524
+ usd: response.costUsd
525
+ } : usage.captured === false || !isModelPriced(model) ? {
526
+ kind: "uncaptured",
527
+ usd: null
528
+ } : {
529
+ kind: "estimated",
530
+ usd: estimateCost(usage.promptTokens, usage.completionTokens, model)
531
+ }
532
+ };
533
+ }
534
+ function canonicalUsage(usage) {
535
+ const promptTokens = tokenCount(usage?.prompt_tokens);
536
+ const completionTokens = tokenCount(usage?.completion_tokens);
537
+ const captured = promptTokens !== void 0 && completionTokens !== void 0;
538
+ return {
539
+ promptTokens: promptTokens ?? 0,
540
+ completionTokens: completionTokens ?? 0,
541
+ totalTokens: (promptTokens ?? 0) + (completionTokens ?? 0),
542
+ captured
543
+ };
544
+ }
545
+ function unknownJudgeCall(model, durationMs) {
546
+ return {
547
+ usage: {
548
+ promptTokens: 0,
549
+ completionTokens: 0,
550
+ totalTokens: 0,
551
+ captured: false
552
+ },
553
+ costUsd: null,
554
+ model,
555
+ durationMs
556
+ };
557
+ }
558
+ function tokenCount(value) {
559
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
560
+ }
561
+ /** Convenience: stringified dimension list for inclusion in a judge prompt.
562
+ * Returns lines like `- audience_fit: Does this match what the audience cares about? (0-10)`. */
563
+ function renderDimensions(dims) {
564
+ return dims.map((d) => `- ${d.key}: ${d.description}`).join("\n");
565
+ }
566
+ /** Convenience: build the "Respond with ONLY this JSON" footer for a judge prompt. */
567
+ function renderJsonFooter(dims) {
568
+ return `Respond with ONLY this JSON (no markdown, no preamble):\n{${dims.map((d) => `"${d.key}":N`).join(",")},"notes":"1-2 sentence critique"}`;
569
+ }
570
+ //#endregion
571
+ //#region src/multishot/multishot.ts
572
+ /** The package's reference conversation engine, and the referent of three
573
+ * things that stop working without it.
574
+ *
575
+ * 1. `runMultishotMatrix` runs it as the per-cell engine when the caller names
576
+ * no `runShot`, so the matrix always has an engine to run.
577
+ * 2. `golden.test.ts` proves the frozen records against it on every run, and
578
+ * perturbs it to prove the records detect a change. Records checked only
579
+ * against a replay of themselves prove nothing.
580
+ * 3. The repo's recording command names it as `--engine`. The script defaults
581
+ * no engine, so a version records whichever one the caller names — and a
582
+ * record set minted from a consumer's engine would let a product's wiring
583
+ * define this package's frozen contract.
584
+ *
585
+ * An alternative engine reaches the matrix through `runShot` and reaches the
586
+ * records through `assertMultishotGoldenScenario`. Neither seam removes the
587
+ * three roles above. */
588
+ async function runMultishot(opts) {
589
+ return runMultishotConversation(opts, async (conversation) => {
590
+ for (let turn = 0; turn < conversation.maxTurns; turn++) {
591
+ await conversation.agentTurn(turn);
592
+ if (turn < conversation.maxTurns - 1) await conversation.driverTurn(turn);
593
+ }
594
+ });
595
+ }
596
+ //#endregion
550
597
  //#region src/multishot/matrix.ts
551
598
  /** Mean composite over non-failed scores. `0` when the list is empty (a
552
599
  * configured judge with nothing to score contributes 0, matching the cell
@@ -784,6 +831,6 @@ function withJudgeMaxTokens(judge, maxTokens) {
784
831
  };
785
832
  }
786
833
  //#endregion
787
- export { DEFAULT_CODER_MODEL, DEFAULT_JUDGE_MODEL, MultishotDriverEmptyError, MultishotFatalToolError, assertMultishotShotResult, computeCellComposite, defaultDelegationTools, defaultShapeFromProfile, estimateMultishotCost, renderDimensions, renderJsonFooter, runJudge, runMultishot, runMultishotMatrix };
834
+ export { DEFAULT_CODER_MODEL, DEFAULT_JUDGE_MODEL, MultishotDriverEmptyError, MultishotFatalToolError, assertMultishotShotResult, computeCellComposite, defaultDelegationTools, defaultShapeFromProfile, estimateMultishotCost, renderDimensions, renderJsonFooter, runJudge, runMultishot, runMultishotConversation, runMultishotMatrix };
788
835
 
789
836
  //# sourceMappingURL=index.js.map