@tangle-network/agent-eval 0.148.0 → 0.150.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +5 -1
  3. package/dist/analyst/index.d.ts +14 -14
  4. package/dist/analyst/index.d.ts.map +1 -1
  5. package/dist/analyst/index.js +4 -4
  6. package/dist/{backend-integrity-Bz8nSrRE.d.ts → backend-integrity-DaeroaEs.d.ts} +2 -2
  7. package/dist/{backend-integrity-Bz8nSrRE.d.ts.map → backend-integrity-DaeroaEs.d.ts.map} +1 -1
  8. package/dist/{benchmark-Cn7ZMFNW.d.ts → benchmark-A0E-JZ1H.d.ts} +3 -3
  9. package/dist/{benchmark-Cn7ZMFNW.d.ts.map → benchmark-A0E-JZ1H.d.ts.map} +1 -1
  10. package/dist/{benchmark-command-Cz4fDmsk.js → benchmark-command-CqGA0SJQ.js} +8 -7
  11. package/dist/benchmark-command-CqGA0SJQ.js.map +1 -0
  12. package/dist/benchmarks/index.d.ts +4 -4
  13. package/dist/benchmarks/index.js +3 -3
  14. package/dist/campaign/index.d.ts +9 -9
  15. package/dist/campaign/index.js +6 -6
  16. package/dist/{campaign-BcfXzmPM.js → campaign-CR6Yt5ZS.js} +80 -7
  17. package/dist/campaign-CR6Yt5ZS.js.map +1 -0
  18. package/dist/{capture-fetch-BAx3Gntt.d.ts → capture-fetch-Im0UN1i6.d.ts} +2 -2
  19. package/dist/{capture-fetch-BAx3Gntt.d.ts.map → capture-fetch-Im0UN1i6.d.ts.map} +1 -1
  20. package/dist/{chat-client-BX8Wh3fn.js → chat-client-sFiClniQ.js} +2 -2
  21. package/dist/{chat-client-BX8Wh3fn.js.map → chat-client-sFiClniQ.js.map} +1 -1
  22. package/dist/cli.js +2 -2
  23. package/dist/{client-BV7icIfy.d.ts → client-w6vVJhSp.d.ts} +2 -2
  24. package/dist/{client-BV7icIfy.d.ts.map → client-w6vVJhSp.d.ts.map} +1 -1
  25. package/dist/contract/index.d.ts +10 -10
  26. package/dist/contract/index.js +5 -5
  27. package/dist/{default-registry-BUtRMRKk.d.ts → default-registry-BhRNsP-D.d.ts} +6 -6
  28. package/dist/{default-registry-BUtRMRKk.d.ts.map → default-registry-BhRNsP-D.d.ts.map} +1 -1
  29. package/dist/{define-agent-eval-CvZQW4u9.js → define-agent-eval-ChrtErmg.js} +3 -3
  30. package/dist/define-agent-eval-ChrtErmg.js.map +1 -0
  31. package/dist/{define-agent-eval-hXLUBKtb.d.ts → define-agent-eval-Cw3mkB4-.d.ts} +6 -7
  32. package/dist/define-agent-eval-Cw3mkB4-.d.ts.map +1 -0
  33. package/dist/{dspy-rlm-engine-BmtOl_kP.js → dspy-rlm-engine-Csi3CUAT.js} +2 -2
  34. package/dist/{dspy-rlm-engine-BmtOl_kP.js.map → dspy-rlm-engine-Csi3CUAT.js.map} +1 -1
  35. package/dist/{engine-CIy18RTX.d.ts → engine-CgQdUh-r.d.ts} +4 -4
  36. package/dist/{engine-CIy18RTX.d.ts.map → engine-CgQdUh-r.d.ts.map} +1 -1
  37. package/dist/{eval-campaign-bmZ6NIIP.js → eval-campaign-Czc2TcW5.js} +2 -2
  38. package/dist/{eval-campaign-bmZ6NIIP.js.map → eval-campaign-Czc2TcW5.js.map} +1 -1
  39. package/dist/{exact-types-rdKFzEnK.d.ts → exact-types-D9_T83Ol.d.ts} +2 -2
  40. package/dist/{exact-types-rdKFzEnK.d.ts.map → exact-types-D9_T83Ol.d.ts.map} +1 -1
  41. package/dist/experiment/index.d.ts +73 -4
  42. package/dist/experiment/index.d.ts.map +1 -1
  43. package/dist/experiment/index.js +175 -1
  44. package/dist/experiment/index.js.map +1 -1
  45. package/dist/{experiment-tracker-D9VHwRm-.d.ts → experiment-tracker-Cp9ougHs.d.ts} +2 -2
  46. package/dist/{experiment-tracker-D9VHwRm-.d.ts.map → experiment-tracker-Cp9ougHs.d.ts.map} +1 -1
  47. package/dist/{external-optimizer-contracts-CSjDLLmr.d.ts → external-optimizer-contracts-mbhR9Ldu.d.ts} +13 -6
  48. package/dist/{external-optimizer-contracts-CSjDLLmr.d.ts.map → external-optimizer-contracts-mbhR9Ldu.d.ts.map} +1 -1
  49. package/dist/{external-optimizer-process-C4AyCcQd.js → external-optimizer-process-tDe72VRO.js} +4 -4
  50. package/dist/{external-optimizer-process-C4AyCcQd.js.map → external-optimizer-process-tDe72VRO.js.map} +1 -1
  51. package/dist/{external-optimizer-subprocess-DU1KM8yV.js → external-optimizer-subprocess-BepyNwtj.js} +341 -25
  52. package/dist/external-optimizer-subprocess-BepyNwtj.js.map +1 -0
  53. package/dist/{feedback-trajectory-niGeR6uJ.d.ts → feedback-trajectory-Cc9M2tNF.d.ts} +3 -3
  54. package/dist/{feedback-trajectory-niGeR6uJ.d.ts.map → feedback-trajectory-Cc9M2tNF.d.ts.map} +1 -1
  55. package/dist/fuzz.d.ts +1 -1
  56. package/dist/hosted/index.d.ts +2 -2
  57. package/dist/{index-gtjtfLEJ.d.ts → index-DL4MJuJL.d.ts} +8 -8
  58. package/dist/{index-gtjtfLEJ.d.ts.map → index-DL4MJuJL.d.ts.map} +1 -1
  59. package/dist/index-DjpxROrT.d.ts +1 -0
  60. package/dist/{index-Dzn8Q3C2.d.ts → index-dh0kp2GM.d.ts} +42 -11
  61. package/dist/index-dh0kp2GM.d.ts.map +1 -0
  62. package/dist/index.d.ts +21 -21
  63. package/dist/index.js +13 -20
  64. package/dist/index.js.map +1 -1
  65. package/dist/{integrity-CGfpTE5-.d.ts → integrity-D2MAlC_K.d.ts} +2 -2
  66. package/dist/{integrity-CGfpTE5-.d.ts.map → integrity-D2MAlC_K.d.ts.map} +1 -1
  67. package/dist/{llm-client-BMuxYoZy.js → llm-client-CxyJ8Oft.js} +15 -6
  68. package/dist/llm-client-CxyJ8Oft.js.map +1 -0
  69. package/dist/{llm-judge-DWq1Ptco.js → llm-judge-D7UuMI2t.js} +4 -3
  70. package/dist/llm-judge-D7UuMI2t.js.map +1 -0
  71. package/dist/{matrix-Bc111FKv.d.ts → matrix-D3hL68uV.d.ts} +2 -2
  72. package/dist/{matrix-Bc111FKv.d.ts.map → matrix-D3hL68uV.d.ts.map} +1 -1
  73. package/dist/meta-eval/index.d.ts +1 -1
  74. package/dist/{multi-layer-verifier-DIguZc8Z.d.ts → multi-layer-verifier-BUaQ4C17.d.ts} +3 -3
  75. package/dist/{multi-layer-verifier-DIguZc8Z.d.ts.map → multi-layer-verifier-BUaQ4C17.d.ts.map} +1 -1
  76. package/dist/multishot/golden/index.d.ts +1 -1
  77. package/dist/multishot/index.d.ts +2 -2
  78. package/dist/openapi.json +1 -1
  79. package/dist/{produced-state-C0oJ4vr-.js → produced-state-WJs_enbZ.js} +3 -3
  80. package/dist/{produced-state-C0oJ4vr-.js.map → produced-state-WJs_enbZ.js.map} +1 -1
  81. package/dist/{promotion-policy-CewdjfCJ.d.ts → promotion-policy-DYRzXdTV.d.ts} +2 -2
  82. package/dist/{promotion-policy-CewdjfCJ.d.ts.map → promotion-policy-DYRzXdTV.d.ts.map} +1 -1
  83. package/dist/{provenance-DA-Pmyfv.d.ts → provenance-Bh9k3sjl.d.ts} +17 -6
  84. package/dist/{provenance-DA-Pmyfv.d.ts.map → provenance-Bh9k3sjl.d.ts.map} +1 -1
  85. package/dist/{registry-CaqJIDdN.d.ts → registry-DhpX9Bsg.d.ts} +4 -4
  86. package/dist/{registry-CaqJIDdN.d.ts.map → registry-DhpX9Bsg.d.ts.map} +1 -1
  87. package/dist/{researcher-BySlpla_.d.ts → researcher-CbCK-ppF.d.ts} +3 -3
  88. package/dist/{researcher-BySlpla_.d.ts.map → researcher-CbCK-ppF.d.ts.map} +1 -1
  89. package/dist/{reward-hacking-Bu8ev6PR.d.ts → reward-hacking-62tojkQd.d.ts} +2 -2
  90. package/dist/{reward-hacking-Bu8ev6PR.d.ts.map → reward-hacking-62tojkQd.d.ts.map} +1 -1
  91. package/dist/rl.d.ts +4 -4
  92. package/dist/rl.js +1 -1
  93. package/dist/{semantic-concept-judge-BI7Rrl5-.js → semantic-concept-judge-B7KPD57W.js} +2 -2
  94. package/dist/{semantic-concept-judge-BI7Rrl5-.js.map → semantic-concept-judge-B7KPD57W.js.map} +1 -1
  95. package/dist/{series-convergence-D2fsoJ2w.d.ts → series-convergence-VhTad5TM.d.ts} +2 -2
  96. package/dist/{series-convergence-D2fsoJ2w.d.ts.map → series-convergence-VhTad5TM.d.ts.map} +1 -1
  97. package/dist/{server-D9wQclzG.js → server-DNiUVirV.js} +2 -2
  98. package/dist/{server-D9wQclzG.js.map → server-DNiUVirV.js.map} +1 -1
  99. package/dist/{skillopt-optimization-method-CrY0OG17.d.ts → skillopt-optimization-method-BtgL6PMH.d.ts} +20 -14
  100. package/dist/{skillopt-optimization-method-CrY0OG17.d.ts.map → skillopt-optimization-method-BtgL6PMH.d.ts.map} +1 -1
  101. package/dist/{skillopt-optimization-method-Donmq4sq.js → skillopt-optimization-method-L8vi4h4m.js} +92 -14
  102. package/dist/skillopt-optimization-method-L8vi4h4m.js.map +1 -0
  103. package/dist/{statistical-heldout-uxSpFEjm.d.ts → statistical-heldout-Caxd30ZF.d.ts} +2 -2
  104. package/dist/{statistical-heldout-uxSpFEjm.d.ts.map → statistical-heldout-Caxd30ZF.d.ts.map} +1 -1
  105. package/dist/{store-tool-spans-D5FhM0_A.d.ts → store-tool-spans-BGg2QFk1.d.ts} +4 -4
  106. package/dist/{store-tool-spans-D5FhM0_A.d.ts.map → store-tool-spans-BGg2QFk1.d.ts.map} +1 -1
  107. package/dist/{tool-groups-zpufabP8.d.ts → tool-groups-BuhFx38z.d.ts} +3 -3
  108. package/dist/tool-groups-BuhFx38z.d.ts.map +1 -0
  109. package/dist/trace-repair/index.d.ts +1 -1
  110. package/dist/traces.d.ts +7 -7
  111. package/dist/{types-ColZlZtc.d.ts → types-B61iI2ZG.d.ts} +2 -2
  112. package/dist/{types-ColZlZtc.d.ts.map → types-B61iI2ZG.d.ts.map} +1 -1
  113. package/dist/{types-BohHewKK.d.ts → types-BNbhD_Vj.d.ts} +2 -2
  114. package/dist/{types-BohHewKK.d.ts.map → types-BNbhD_Vj.d.ts.map} +1 -1
  115. package/dist/{types-DOhGq4S1.d.ts → types-COAJy8Ku.d.ts} +15 -3
  116. package/dist/types-COAJy8Ku.d.ts.map +1 -0
  117. package/dist/{types-C1Bmeb8X.d.ts → types-COEjZMLY.d.ts} +3 -3
  118. package/dist/{types-C1Bmeb8X.d.ts.map → types-COEjZMLY.d.ts.map} +1 -1
  119. package/dist/wire/index.d.ts +2 -2
  120. package/dist/wire/index.js +1 -1
  121. package/docs/campaign-proposers.md +60 -3
  122. package/docs/experiment.md +2 -0
  123. package/docs/feature-guide.md +1 -1
  124. package/docs/multishot-golden-records.md +3 -0
  125. package/package.json +4 -2
  126. package/dist/benchmark-command-Cz4fDmsk.js.map +0 -1
  127. package/dist/campaign-BcfXzmPM.js.map +0 -1
  128. package/dist/define-agent-eval-CvZQW4u9.js.map +0 -1
  129. package/dist/define-agent-eval-hXLUBKtb.d.ts.map +0 -1
  130. package/dist/external-optimizer-subprocess-DU1KM8yV.js.map +0 -1
  131. package/dist/index-CKblBxtr.d.ts +0 -1
  132. package/dist/index-Dzn8Q3C2.d.ts.map +0 -1
  133. package/dist/llm-client-BMuxYoZy.js.map +0 -1
  134. package/dist/llm-judge-DWq1Ptco.js.map +0 -1
  135. package/dist/skillopt-optimization-method-Donmq4sq.js.map +0 -1
  136. package/dist/tool-groups-zpufabP8.d.ts.map +0 -1
  137. package/dist/types-DOhGq4S1.d.ts.map +0 -1
@@ -2,7 +2,7 @@ import { s as ValidationError } from "./errors-Dngq5h35.js";
2
2
  import { t as canonicalJson } from "./verdict-cache-mZf5FEiY.js";
3
3
  import { c as costForTokenPricing, i as CostLedger } from "./cost-ledger-B1qx30B4.js";
4
4
  import { a as appendLedgerLine, o as tryWithLedgerFileLock } from "./ledger-core-DTae9rv_.js";
5
- import { h as ModelSubstitutionError, v as assertServedModel } from "./llm-client-BMuxYoZy.js";
5
+ import { h as ModelSubstitutionError, v as assertServedModel, y as assertServedModelPolicy } from "./llm-client-CxyJ8Oft.js";
6
6
  import { createRequire } from "node:module";
7
7
  import { randomBytes } from "node:crypto";
8
8
  import { isAbsolute, join, resolve } from "node:path";
@@ -247,6 +247,255 @@ function isRecord(value) {
247
247
  return typeof value === "object" && value !== null && !Array.isArray(value);
248
248
  }
249
249
  //#endregion
250
+ //#region src/campaign/external-optimizer-anthropic.ts
251
+ /**
252
+ * Translate between the Anthropic Messages API and the canonical owner-call
253
+ * contract used by the metered optimizer model proxy.
254
+ *
255
+ * The `claude` CLI driven by GEPA agent engines speaks `POST /v1/messages`.
256
+ * Each request becomes one canonical `ExternalOptimizerChatRequest`, so the
257
+ * shared reservation, receipt, and execution-evidence pipeline applies
258
+ * unchanged. Anything without a full-fidelity mapping onto that contract is
259
+ * refused with a loud Anthropic error envelope. The only silent strips are
260
+ * `metadata` and `cache_control`: the CLI always sends both, neither has a
261
+ * canonical slot, and dropping them changes no message content.
262
+ *
263
+ * Tool use is refused because the canonical contract carries text-only
264
+ * messages. Metering CLI tool calls requires a tool extension of
265
+ * `ExternalOptimizerChatRequest` and the execution-owner result.
266
+ */
267
+ var AnthropicRequestRefusal = class extends Error {
268
+ status;
269
+ errorType;
270
+ constructor(status, errorType, message) {
271
+ super(message);
272
+ this.status = status;
273
+ this.errorType = errorType;
274
+ }
275
+ };
276
+ const REQUEST_FIELDS = /* @__PURE__ */ new Set([
277
+ "model",
278
+ "max_tokens",
279
+ "messages",
280
+ "system",
281
+ "temperature",
282
+ "stream",
283
+ "metadata"
284
+ ]);
285
+ const REFUSED_REQUEST_FIELDS = {
286
+ tools: "tools require a tool-call extension of the canonical execution-owner contract",
287
+ tool_choice: "tool_choice requires a tool-call extension of the canonical execution-owner contract",
288
+ thinking: "thinking has no canonical mapping; the injected CLI environment disables it",
289
+ top_p: "top_p has no canonical execution-owner slot",
290
+ top_k: "top_k has no canonical execution-owner slot",
291
+ stop_sequences: "stop_sequences has no canonical execution-owner slot"
292
+ };
293
+ function translateAnthropicMessagesRequest(raw, expectedModel, maxOutputTokensPerRequest) {
294
+ let parsed;
295
+ try {
296
+ parsed = JSON.parse(Buffer.from(raw).toString("utf8"));
297
+ } catch {
298
+ throw refuse("request must be valid JSON");
299
+ }
300
+ if (!isRecord(parsed)) throw refuse("request must be a JSON object");
301
+ for (const key of Object.keys(parsed)) {
302
+ if (REQUEST_FIELDS.has(key)) continue;
303
+ const reason = REFUSED_REQUEST_FIELDS[key];
304
+ throw refuse(reason ?? `request field '${key}' has no metered translation and is not supported`);
305
+ }
306
+ if (parsed.model !== expectedModel) throw refuse(`request must use configured model '${expectedModel}'`);
307
+ const maxTokens = parsed.max_tokens;
308
+ if (!Number.isSafeInteger(maxTokens) || maxTokens <= 0) throw refuse("request requires a positive integer max_tokens");
309
+ if (maxTokens > maxOutputTokensPerRequest) throw refuse("request max_tokens exceeds maxOutputTokensPerRequest");
310
+ if (parsed.stream !== void 0 && typeof parsed.stream !== "boolean") throw refuse("request stream must be a boolean");
311
+ if (parsed.temperature !== void 0 && (typeof parsed.temperature !== "number" || !Number.isFinite(parsed.temperature))) throw refuse("request temperature must be a finite number");
312
+ const messages = [];
313
+ const systemText = translateSystem(parsed.system);
314
+ if (systemText !== void 0) messages.push({
315
+ role: "system",
316
+ content: systemText
317
+ });
318
+ messages.push(...translateConversation(parsed.messages));
319
+ return {
320
+ request: {
321
+ model: expectedModel,
322
+ messages,
323
+ maxTokens,
324
+ ...parsed.temperature !== void 0 ? { temperature: parsed.temperature } : {}
325
+ },
326
+ maxOutputTokens: maxTokens,
327
+ stream: parsed.stream === true
328
+ };
329
+ }
330
+ function translateSystem(value) {
331
+ if (value === void 0) return void 0;
332
+ if (typeof value === "string") return value;
333
+ if (!Array.isArray(value)) throw refuse("system must be a string or an array of text blocks");
334
+ return joinTextBlocks(value, "system");
335
+ }
336
+ function translateConversation(value) {
337
+ if (!Array.isArray(value) || value.length === 0) throw refuse("request requires a non-empty messages array");
338
+ return value.map((entry) => {
339
+ if (!isRecord(entry)) throw refuse("each message must be an object");
340
+ if (entry.role !== "user" && entry.role !== "assistant") throw refuse(`message role must be 'user' or 'assistant'`);
341
+ const content = typeof entry.content === "string" ? entry.content : Array.isArray(entry.content) && entry.content.length > 0 ? joinTextBlocks(entry.content, `${entry.role} message`) : refuseValue(`${entry.role} message content must be a string or a non-empty block array`);
342
+ return {
343
+ role: entry.role,
344
+ content
345
+ };
346
+ });
347
+ }
348
+ function joinTextBlocks(blocks, label) {
349
+ const parts = [];
350
+ for (const block of blocks) {
351
+ if (!isRecord(block)) throw refuse(`${label} content blocks must be objects`);
352
+ if (block.type === "text") {
353
+ if (typeof block.text !== "string") throw refuse(`${label} text blocks require string text`);
354
+ parts.push(block.text);
355
+ continue;
356
+ }
357
+ if (block.type === "tool_use" || block.type === "tool_result") throw refuse(`${label} ${String(block.type)} blocks require a tool-call extension of the canonical execution-owner contract`);
358
+ throw refuse(`${label} content block type '${String(block.type)}' has no metered translation`);
359
+ }
360
+ return parts.join("\n\n");
361
+ }
362
+ const STOP_REASON_BY_FINISH_REASON = {
363
+ stop: "end_turn",
364
+ length: "max_tokens",
365
+ content_filter: "refusal"
366
+ };
367
+ /**
368
+ * Encode one canonical owner response as an Anthropic message.
369
+ *
370
+ * Usage fields are display-level for the CLI; the ledger receipt reads the
371
+ * same canonical response through the proxy's strict receipt checks. An
372
+ * uncaptured usage renders as zeros here while the receipt keeps the honest
373
+ * unknown-cost maximum.
374
+ */
375
+ function encodeAnthropicMessage(response, callId) {
376
+ const finishReason = response.finishReason ?? "stop";
377
+ const stopReason = finishReason === null ? "end_turn" : STOP_REASON_BY_FINISH_REASON[finishReason];
378
+ if (stopReason === void 0) throw new AnthropicRequestRefusal(502, "api_error", `optimizer model finish reason '${String(finishReason)}' has no Anthropic translation`);
379
+ const captured = response.usage.captured !== false;
380
+ const cachedTokens = response.usage.cachedPromptTokens ?? 0;
381
+ return {
382
+ id: `msg_${callId}`,
383
+ type: "message",
384
+ role: "assistant",
385
+ model: response.model,
386
+ content: response.content.length > 0 ? [{
387
+ type: "text",
388
+ text: response.content
389
+ }] : [],
390
+ stop_reason: stopReason,
391
+ stop_sequence: null,
392
+ usage: captured ? {
393
+ input_tokens: response.usage.promptTokens - cachedTokens,
394
+ cache_read_input_tokens: cachedTokens,
395
+ cache_creation_input_tokens: 0,
396
+ output_tokens: response.usage.completionTokens
397
+ } : {
398
+ input_tokens: 0,
399
+ output_tokens: 0
400
+ }
401
+ };
402
+ }
403
+ /**
404
+ * Render one completed Anthropic message as the SSE stream the CLI expects.
405
+ *
406
+ * The whole stream is synthesized after the owner call completes, so the
407
+ * event burst is exact: usage is final and a cap-violating response is
408
+ * rejected with a real status instead of a half-delivered stream.
409
+ */
410
+ function renderAnthropicSseStream(message) {
411
+ const content = Array.isArray(message.content) ? message.content : [];
412
+ const usage = isRecord(message.usage) ? message.usage : {
413
+ input_tokens: 0,
414
+ output_tokens: 0
415
+ };
416
+ const events = [{
417
+ event: "message_start",
418
+ data: {
419
+ type: "message_start",
420
+ message: {
421
+ ...message,
422
+ content: [],
423
+ stop_reason: null,
424
+ stop_sequence: null
425
+ }
426
+ }
427
+ }];
428
+ for (const [index, block] of content.entries()) {
429
+ if (!isRecord(block) || block.type !== "text") continue;
430
+ events.push({
431
+ event: "content_block_start",
432
+ data: {
433
+ type: "content_block_start",
434
+ index,
435
+ content_block: {
436
+ type: "text",
437
+ text: ""
438
+ }
439
+ }
440
+ }, {
441
+ event: "content_block_delta",
442
+ data: {
443
+ type: "content_block_delta",
444
+ index,
445
+ delta: {
446
+ type: "text_delta",
447
+ text: block.text
448
+ }
449
+ }
450
+ }, {
451
+ event: "content_block_stop",
452
+ data: {
453
+ type: "content_block_stop",
454
+ index
455
+ }
456
+ });
457
+ }
458
+ events.push({
459
+ event: "message_delta",
460
+ data: {
461
+ type: "message_delta",
462
+ delta: {
463
+ stop_reason: message.stop_reason,
464
+ stop_sequence: null
465
+ },
466
+ usage: { output_tokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0 }
467
+ }
468
+ }, {
469
+ event: "message_stop",
470
+ data: { type: "message_stop" }
471
+ });
472
+ return events.map((entry) => `event: ${entry.event}\ndata: ${JSON.stringify(entry.data)}\n\n`).join("");
473
+ }
474
+ function anthropicErrorEnvelope(errorType, message) {
475
+ return {
476
+ type: "error",
477
+ error: {
478
+ type: errorType,
479
+ message
480
+ }
481
+ };
482
+ }
483
+ function anthropicErrorTypeForStatus(status) {
484
+ if (status === 401) return "authentication_error";
485
+ if (status === 403) return "permission_error";
486
+ if (status === 404) return "not_found_error";
487
+ if (status === 413) return "request_too_large";
488
+ if (status === 429) return "rate_limit_error";
489
+ if (status >= 500) return "api_error";
490
+ return "invalid_request_error";
491
+ }
492
+ function refuse(message) {
493
+ return new AnthropicRequestRefusal(400, "invalid_request_error", message);
494
+ }
495
+ function refuseValue(message) {
496
+ throw refuse(message);
497
+ }
498
+ //#endregion
250
499
  //#region src/campaign/external-optimizer-http.ts
251
500
  function listenLocal(server) {
252
501
  return new Promise((resolvePromise, reject) => {
@@ -289,6 +538,16 @@ async function startExternalOptimizerModelProxy(args) {
289
538
  const token = randomLocalToken();
290
539
  let requestCount = 0;
291
540
  let successfulCompletionCount = 0;
541
+ const wireCounts = {
542
+ openai: {
543
+ requestAttempts: 0,
544
+ successfulCompletions: 0
545
+ },
546
+ anthropic: {
547
+ requestAttempts: 0,
548
+ successfulCompletions: 0
549
+ }
550
+ };
292
551
  let modelCallInvocations = 0;
293
552
  let executionRecordCount = 0;
294
553
  let executionSequence = 0;
@@ -319,7 +578,7 @@ async function startExternalOptimizerModelProxy(args) {
319
578
  controller,
320
579
  token,
321
580
  args,
322
- nextReservation: (maximumCostUsd) => {
581
+ nextReservation: (maximumCostUsd, wire) => {
323
582
  if (totalRequestCount >= args.budget.maxRequests) return {
324
583
  accepted: false,
325
584
  reason: "optimizer model request limit reached"
@@ -329,6 +588,7 @@ async function startExternalOptimizerModelProxy(args) {
329
588
  reason: "optimizer model cost limit reached"
330
589
  };
331
590
  requestCount += 1;
591
+ wireCounts[wire].requestAttempts += 1;
332
592
  totalRequestCount += 1;
333
593
  reservedForBudget += maximumCostUsd ?? 0;
334
594
  return { accepted: true };
@@ -337,8 +597,9 @@ async function startExternalOptimizerModelProxy(args) {
337
597
  reservedForBudget = Math.max(0, reservedForBudget - (maximumCostUsd ?? 0));
338
598
  committedForBudget += chargedCostUsd ?? 0;
339
599
  },
340
- recordSuccessfulCompletion: () => {
600
+ recordSuccessfulCompletion: (wire) => {
341
601
  successfulCompletionCount += 1;
602
+ wireCounts[wire].successfulCompletions += 1;
342
603
  },
343
604
  recordExecutionReceipt: (observation) => {
344
605
  try {
@@ -385,6 +646,10 @@ async function startExternalOptimizerModelProxy(args) {
385
646
  apiKey: token,
386
647
  requestAttempts: () => requestCount,
387
648
  successfulCompletions: () => successfulCompletionCount,
649
+ wireUsage: () => ({
650
+ openai: { ...wireCounts.openai },
651
+ anthropic: { ...wireCounts.anthropic }
652
+ }),
388
653
  assertExecutionComplete,
389
654
  failures: () => [...failures],
390
655
  close
@@ -409,35 +674,50 @@ async function startExternalOptimizerModelProxy(args) {
409
674
  }
410
675
  async function handleModelProxyRequest(args) {
411
676
  const { controller, request, response } = args;
677
+ let wire = "openai";
678
+ const respondError = (status, message, errorType) => {
679
+ if (wire === "anthropic") {
680
+ const mapped = status === 429 ? 402 : status;
681
+ sendJsonIfOpen(response, mapped, anthropicErrorEnvelope(errorType ?? anthropicErrorTypeForStatus(mapped), message));
682
+ return;
683
+ }
684
+ sendJsonIfOpen(response, status, { error: message });
685
+ };
412
686
  try {
413
687
  const path = request.url ? new URL(request.url, "http://127.0.0.1").pathname : "";
414
- if (request.method !== "POST" || !MODEL_PROXY_PATHS.has(path)) {
688
+ const anthropicPath = path === "/v1/messages" && args.args.anthropicEndpoint === true;
689
+ if (request.method !== "POST" || !(MODEL_PROXY_PATHS.has(path) || anthropicPath)) {
415
690
  sendJsonIfOpen(response, 404, { error: "not found" });
416
691
  return;
417
692
  }
693
+ if (anthropicPath) wire = "anthropic";
418
694
  const modelPath = path;
419
- if (request.headers.authorization !== `Bearer ${args.token}`) {
420
- sendJsonIfOpen(response, 401, { error: "unauthorized" });
695
+ if (!(request.headers.authorization === `Bearer ${args.token}` || wire === "anthropic" && request.headers["x-api-key"] === args.token)) {
696
+ respondError(401, "unauthorized");
421
697
  return;
422
698
  }
423
699
  const body = await readBody(request, args.args.budget.maxRequestBytes);
424
700
  const parsed = parseModelProxyRequest(body, modelPath, args.args.model, args.args.budget);
425
701
  const maximumUsage = conservativeMaximumUsage(body.byteLength, parsed.maxOutputTokens + (args.args.budget.maxReasoningTokensPerRequest ?? 0), args.args.budget.pricing);
426
702
  const maximumCostUsd = args.args.budget.pricing ? costForTokenPricing(args.args.budget.pricing, maximumUsage) : void 0;
427
- const reservation = args.nextReservation(maximumCostUsd);
703
+ const reservation = args.nextReservation(maximumCostUsd, wire);
428
704
  if (!reservation.accepted) {
429
- sendJsonIfOpen(response, 429, { error: reservation.reason });
705
+ respondError(429, reservation.reason);
430
706
  return;
431
707
  }
432
708
  const timeout = setTimeout(() => controller.abort(), args.args.budget.requestTimeoutMs ?? 3e5);
433
709
  let chargedForBudget = maximumCostUsd;
434
710
  try {
711
+ const requestTags = wire === "anthropic" ? {
712
+ ...args.args.tags ?? {},
713
+ wire: "anthropic"
714
+ } : args.args.tags;
435
715
  const paid = await args.args.costLedger.runPaidCall({
436
716
  ...args.args.callId ? { callId: args.args.callId } : {},
437
717
  channel: args.args.channel ?? "optimizer",
438
718
  phase: args.args.phase,
439
719
  actor: args.args.actor,
440
- ...args.args.tags ? { tags: args.args.tags } : {},
720
+ ...requestTags ? { tags: requestTags } : {},
441
721
  model: args.args.model,
442
722
  ...args.args.budget.pricing ? { maximumCharge: {
443
723
  customTokenPricing: args.args.budget.pricing,
@@ -452,7 +732,9 @@ async function handleModelProxyRequest(args) {
452
732
  recordModelCallInvocation: args.recordModelCallInvocation,
453
733
  path: modelPath,
454
734
  request: parsed.request,
735
+ ...parsed.stream === void 0 ? {} : { anthropicStream: parsed.stream },
455
736
  model: args.args.model,
737
+ allowWithinFamily: args.args.servedModelPolicy === "allow-within-family",
456
738
  maxOutputTokens: parsed.maxOutputTokens,
457
739
  ...args.args.budget.maxReasoningTokensPerRequest === void 0 ? {} : { maxReasoningTokens: args.args.budget.maxReasoningTokensPerRequest },
458
740
  maxResponseBytes: args.args.budget.maxResponseBytes,
@@ -470,19 +752,19 @@ async function handleModelProxyRequest(args) {
470
752
  if (!paid.succeeded) {
471
753
  args.recordFailure(paid.error);
472
754
  chargedForBudget = paid.receipt ? paid.receipt.usageUnknown || paid.receipt.costUnknown ? maximumCostUsd : paid.receipt.costUsd : void 0;
473
- sendJsonIfOpen(response, isAbortError(paid.error) ? 504 : paid.error instanceof ProviderResponseTooLargeError || paid.error instanceof MissingModelExecutionError || paid.error instanceof ModelExecutionPersistenceError || paid.error instanceof OwnerModelContractError || paid.error instanceof ModelSubstitutionError ? 502 : 429, { error: paid.error.message });
755
+ respondError(isAbortError(paid.error) ? 504 : paid.error instanceof AnthropicRequestRefusal ? paid.error.status : paid.error instanceof ProviderResponseTooLargeError || paid.error instanceof MissingModelExecutionError || paid.error instanceof ModelExecutionPersistenceError || paid.error instanceof OwnerModelContractError || paid.error instanceof ModelSubstitutionError ? 502 : 429, paid.error.message, paid.error instanceof AnthropicRequestRefusal ? paid.error.errorType : void 0);
474
756
  return;
475
757
  }
476
758
  if (paid.value.modelCallFailed) {
477
- sendJsonIfOpen(response, 502, { error: paid.value.modelCallFailed });
759
+ respondError(502, paid.value.modelCallFailed);
478
760
  return;
479
761
  }
480
762
  chargedForBudget = paid.value.usageComplete ? paid.receipt.costUsd : maximumCostUsd;
481
763
  if (paid.value.usageRejected) {
482
- sendJsonIfOpen(response, 502, { error: paid.value.usageRejected });
764
+ respondError(502, paid.value.usageRejected);
483
765
  return;
484
766
  }
485
- if (paid.value.status >= 200 && paid.value.status < 300) args.recordSuccessfulCompletion();
767
+ if (paid.value.status >= 200 && paid.value.status < 300) args.recordSuccessfulCompletion(wire);
486
768
  if (response.destroyed || response.writableEnded) return;
487
769
  response.writeHead(paid.value.status, {
488
770
  "content-type": paid.value.contentType,
@@ -494,7 +776,7 @@ async function handleModelProxyRequest(args) {
494
776
  args.settleReservation(maximumCostUsd, chargedForBudget);
495
777
  }
496
778
  } catch (error) {
497
- sendJsonIfOpen(response, error instanceof RequestBodyTooLargeError ? 413 : error instanceof Error && isAbortError(error) ? 503 : 400, { error: toErrorMessage(error) });
779
+ respondError(error instanceof AnthropicRequestRefusal ? error.status : error instanceof RequestBodyTooLargeError ? 413 : error instanceof Error && isAbortError(error) ? 503 : 400, toErrorMessage(error), error instanceof AnthropicRequestRefusal ? error.errorType : void 0);
498
780
  }
499
781
  }
500
782
  async function waitForActiveHandlers(activeHandlers) {
@@ -528,8 +810,8 @@ async function forwardModelProxyRequest(args) {
528
810
  let authoritativeReceipt;
529
811
  let canonicalResponse;
530
812
  try {
531
- authoritativeReceipt = snapshotModelReceipt(called.receipt, args.model);
532
- canonicalResponse = snapshotChatResponse(called.response, args.model);
813
+ authoritativeReceipt = snapshotModelReceipt(called.receipt, args.model, args.allowWithinFamily);
814
+ canonicalResponse = snapshotChatResponse(called.response, args.model, args.allowWithinFamily);
533
815
  assertResponseUsageMatchesReceipt(canonicalResponse, authoritativeReceipt);
534
816
  } catch (error) {
535
817
  args.recordExecutionReceipt({
@@ -556,8 +838,12 @@ async function forwardModelProxyRequest(args) {
556
838
  const reasoningTokens = canonicalResponse.usage.reasoningTokens ?? 0;
557
839
  const completionTokens = canonicalResponse.usage.completionTokens - reasoningTokens;
558
840
  const usageRejected = usageKnown && completionTokens > args.maxOutputTokens ? `optimizer model execution reported ${completionTokens} completion tokens, exceeding requested limit ${args.maxOutputTokens}` : usageKnown && args.maxReasoningTokens !== void 0 && reasoningTokens > args.maxReasoningTokens ? `optimizer model execution reported ${reasoningTokens} reasoning tokens, exceeding the declared budget ${args.maxReasoningTokens}` : void 0;
559
- return {
560
- status: 200,
841
+ const encoded = args.path === "/v1/messages" ? encodeAnthropicResponseBody({
842
+ callId: args.callId,
843
+ response: canonicalResponse,
844
+ stream: args.anthropicStream === true,
845
+ maxBytes: args.maxResponseBytes
846
+ }) : {
561
847
  contentType: "application/json",
562
848
  body: encodeCanonicalModelResponse({
563
849
  path: args.path,
@@ -565,7 +851,12 @@ async function forwardModelProxyRequest(args) {
565
851
  response: canonicalResponse,
566
852
  receipt: authoritativeReceipt,
567
853
  maxBytes: args.maxResponseBytes
568
- }),
854
+ })
855
+ };
856
+ return {
857
+ status: 200,
858
+ contentType: encoded.contentType,
859
+ body: encoded.body,
569
860
  receipt: authoritativeReceipt,
570
861
  usageComplete: authoritativeReceipt.usageUnknown !== true,
571
862
  ...usageRejected ? { usageRejected } : {}
@@ -581,7 +872,7 @@ async function forwardModelProxyRequest(args) {
581
872
  error: called.error,
582
873
  execution
583
874
  });
584
- const failedReceipt = snapshotModelReceipt(called.receipt, args.model);
875
+ const failedReceipt = snapshotModelReceipt(called.receipt, args.model, args.allowWithinFamily);
585
876
  return {
586
877
  status: 502,
587
878
  contentType: "application/json",
@@ -592,7 +883,7 @@ async function forwardModelProxyRequest(args) {
592
883
  };
593
884
  }
594
885
  }
595
- function snapshotModelReceipt(value, expectedModel) {
886
+ function snapshotModelReceipt(value, expectedModel, allowWithinFamily) {
596
887
  let snapshot;
597
888
  try {
598
889
  assertJsonValue(value, "optimizer model callback receipt");
@@ -600,7 +891,10 @@ function snapshotModelReceipt(value, expectedModel) {
600
891
  } catch (error) {
601
892
  throw new OwnerModelContractError(`optimizer model callback returned an invalid receipt: ${toErrorMessage(error)}`);
602
893
  }
603
- assertServedModel(expectedModel, snapshot.model, { context: "optimizer model callback receipt" });
894
+ assertServedModel(expectedModel, snapshot.model, {
895
+ context: "optimizer model callback receipt",
896
+ allowWithinFamily
897
+ });
604
898
  return snapshot;
605
899
  }
606
900
  function freezeJsonSnapshot(value, label) {
@@ -629,6 +923,14 @@ function assertResponseUsageMatchesReceipt(response, receipt) {
629
923
  if (response.costUsd !== receiptCostUsd) throw new OwnerModelContractError("optimizer model response billed cost disagrees with the execution receipt");
630
924
  }
631
925
  function parseModelProxyRequest(body, path, expectedModel, budget) {
926
+ if (path === "/v1/messages") {
927
+ const translated = translateAnthropicMessagesRequest(body, expectedModel, budget.maxOutputTokensPerRequest);
928
+ return {
929
+ maxOutputTokens: translated.maxOutputTokens,
930
+ stream: translated.stream,
931
+ request: freezeJsonSnapshot(translated.request, "optimizer model canonical request")
932
+ };
933
+ }
632
934
  let value;
633
935
  try {
634
936
  value = JSON.parse(Buffer.from(body).toString("utf8"));
@@ -878,10 +1180,13 @@ function assertAllowedKeys(value, allowed, label = "optimizer model request") {
878
1180
  const unknown = Object.keys(value).filter((key) => !allowedSet.has(key));
879
1181
  if (unknown.length > 0) throw new Error(`${label} contains unsupported fields: ${unknown.join(", ")}`);
880
1182
  }
881
- function snapshotChatResponse(value, expectedModel) {
1183
+ function snapshotChatResponse(value, expectedModel, allowWithinFamily) {
882
1184
  if (!isRecord(value)) throw new OwnerModelContractError("optimizer model callback response must be an object");
883
1185
  if (typeof value.content !== "string") throw new OwnerModelContractError("optimizer model callback response content must be a string");
884
- assertServedModel(expectedModel, typeof value.model === "string" ? value.model : null, { context: "optimizer model callback response" });
1186
+ assertServedModel(expectedModel, typeof value.model === "string" ? value.model : null, {
1187
+ context: "optimizer model callback response",
1188
+ allowWithinFamily
1189
+ });
885
1190
  if (!isNonnegativeFiniteNumber(value.durationMs)) throw new OwnerModelContractError("optimizer model callback response durationMs must be finite and non-negative");
886
1191
  if (value.costUsd !== null && !isNonnegativeFiniteNumber(value.costUsd)) throw new OwnerModelContractError("optimizer model callback response costUsd must be null or finite and non-negative");
887
1192
  if (value.finishReason !== void 0 && value.finishReason !== null && typeof value.finishReason !== "string") throw new OwnerModelContractError("optimizer model callback response finishReason must be a string or null");
@@ -925,6 +1230,15 @@ function snapshotChatResponse(value, expectedModel) {
925
1230
  raw
926
1231
  });
927
1232
  }
1233
+ function encodeAnthropicResponseBody(args) {
1234
+ const message = encodeAnthropicMessage(args.response, args.callId);
1235
+ const body = new TextEncoder().encode(args.stream ? renderAnthropicSseStream(message) : JSON.stringify(message));
1236
+ if (body.byteLength > args.maxBytes) throw new ProviderResponseTooLargeError();
1237
+ return {
1238
+ contentType: args.stream ? "text/event-stream; charset=utf-8" : "application/json",
1239
+ body
1240
+ };
1241
+ }
928
1242
  function encodeCanonicalModelResponse(args) {
929
1243
  const usage = encodeProtocolUsage(args.path, args.response, args.receipt);
930
1244
  const finishReason = args.response.finishReason === void 0 ? "stop" : args.response.finishReason;
@@ -969,6 +1283,7 @@ function encodeCanonicalModelResponse(args) {
969
1283
  return encoded;
970
1284
  }
971
1285
  function endpointFormat(path) {
1286
+ if (path === "/v1/messages") return "anthropic-messages";
972
1287
  return path === "/v1/responses" ? "responses" : "chat-completions";
973
1288
  }
974
1289
  function encodeProtocolUsage(path, response, receipt) {
@@ -1015,6 +1330,7 @@ function conservativeMaximumUsage(inputTokenUpperBound, outputTokenUpperBound, p
1015
1330
  };
1016
1331
  }
1017
1332
  function assertModelProxyConfig(args) {
1333
+ assertServedModelPolicy(args.servedModelPolicy, "external optimizer model proxy: servedModelPolicy");
1018
1334
  for (const [label, value] of [
1019
1335
  ["model", args.model],
1020
1336
  ["phase", args.phase],
@@ -1350,4 +1666,4 @@ function summarizeProcessOutput(capture, max = 4e3) {
1350
1666
  //#endregion
1351
1667
  export { SEARCH_LEDGER_FILE_CONTEXT as C, SearchLedgerIntegrityError as E, inMemoryCampaignStorage as S, SearchLedgerError as T, resolveExternalOptimizerCallbackLimits as _, closeServer as a, createRunCostLedger as b, DEFAULT_EXTERNAL_OPTIMIZER_CALLBACK_LIMITS as c, assertJsonValue as d, assertNoCredentialValues as f, removeCredentialEnvironment as g, isRecord as h, startExternalOptimizerModelProxy as i, DEFAULT_EXTERNAL_OPTIMIZER_PROCESS_LIMITS as l, isExternalTextCandidate as m, closeExternalOptimizerResources as n, listenLocal as o, isCandidateText as p, runWithCleanup as r, sendJson as s, runExternalOptimizerProcess as t, assertExternalOptimizerModelBudget as u, resolveExternalOptimizerProcessLimits as v, SearchLedgerConflictError as w, fsCampaignStorage as x, safePathComponent as y };
1352
1668
 
1353
- //# sourceMappingURL=external-optimizer-subprocess-DU1KM8yV.js.map
1669
+ //# sourceMappingURL=external-optimizer-subprocess-BepyNwtj.js.map