@tangle-network/agent-eval 0.150.0 → 0.150.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/dist/analyst/index.d.ts +13 -13
- package/dist/analyst/index.js +4 -4
- package/dist/{backend-integrity-DaeroaEs.d.ts → backend-integrity-DOCa_QrR.d.ts} +2 -2
- package/dist/{backend-integrity-DaeroaEs.d.ts.map → backend-integrity-DOCa_QrR.d.ts.map} +1 -1
- package/dist/{benchmark-A0E-JZ1H.d.ts → benchmark-BtAWA8nT.d.ts} +3 -3
- package/dist/{benchmark-A0E-JZ1H.d.ts.map → benchmark-BtAWA8nT.d.ts.map} +1 -1
- package/dist/{benchmark-command-CqGA0SJQ.js → benchmark-command-BU1Las59.js} +7 -7
- package/dist/{benchmark-command-CqGA0SJQ.js.map → benchmark-command-BU1Las59.js.map} +1 -1
- package/dist/benchmarks/index.d.ts +4 -4
- package/dist/benchmarks/index.js +3 -3
- package/dist/builder-eval/index.js +1 -1
- package/dist/campaign/index.d.ts +8 -8
- package/dist/campaign/index.js +5 -5
- package/dist/{campaign-CR6Yt5ZS.js → campaign-la-gEYNz.js} +18 -9
- package/dist/campaign-la-gEYNz.js.map +1 -0
- package/dist/{capture-fetch-Im0UN1i6.d.ts → capture-fetch-BBVFzhkk.d.ts} +2 -2
- package/dist/{capture-fetch-Im0UN1i6.d.ts.map → capture-fetch-BBVFzhkk.d.ts.map} +1 -1
- package/dist/{chat-client-sFiClniQ.js → chat-client-Bvmxedyv.js} +3 -3
- package/dist/{chat-client-sFiClniQ.js.map → chat-client-Bvmxedyv.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{client-w6vVJhSp.d.ts → client-kPQYT_56.d.ts} +2 -2
- package/dist/{client-w6vVJhSp.d.ts.map → client-kPQYT_56.d.ts.map} +1 -1
- package/dist/contract/index.d.ts +10 -10
- package/dist/contract/index.js +5 -5
- package/dist/{default-registry-BhRNsP-D.d.ts → default-registry-Cw0Ohdoj.d.ts} +6 -6
- package/dist/{default-registry-BhRNsP-D.d.ts.map → default-registry-Cw0Ohdoj.d.ts.map} +1 -1
- package/dist/{define-agent-eval-ChrtErmg.js → define-agent-eval-C8V8sMqP.js} +7 -7
- package/dist/{define-agent-eval-ChrtErmg.js.map → define-agent-eval-C8V8sMqP.js.map} +1 -1
- package/dist/{define-agent-eval-Cw3mkB4-.d.ts → define-agent-eval-CEQWL9Hy.d.ts} +5 -5
- package/dist/{define-agent-eval-Cw3mkB4-.d.ts.map → define-agent-eval-CEQWL9Hy.d.ts.map} +1 -1
- package/dist/{descriptive-B5MwKfbf.js → descriptive-jDOuI6mz.js} +22 -2
- package/dist/descriptive-jDOuI6mz.js.map +1 -0
- package/dist/{dspy-rlm-engine-Csi3CUAT.js → dspy-rlm-engine-CBYlPvNy.js} +2 -2
- package/dist/{dspy-rlm-engine-Csi3CUAT.js.map → dspy-rlm-engine-CBYlPvNy.js.map} +1 -1
- package/dist/{engine-CgQdUh-r.d.ts → engine-BLzhNzoY.d.ts} +4 -4
- package/dist/{engine-CgQdUh-r.d.ts.map → engine-BLzhNzoY.d.ts.map} +1 -1
- package/dist/{eval-campaign-Czc2TcW5.js → eval-campaign-CQuZrLR_.js} +3 -3
- package/dist/{eval-campaign-Czc2TcW5.js.map → eval-campaign-CQuZrLR_.js.map} +1 -1
- package/dist/{exact-types-D9_T83Ol.d.ts → exact-types-ccQAyut1.d.ts} +2 -2
- package/dist/{exact-types-D9_T83Ol.d.ts.map → exact-types-ccQAyut1.d.ts.map} +1 -1
- package/dist/experiment/index.d.ts +3 -3
- package/dist/{experiment-tracker-Cp9ougHs.d.ts → experiment-tracker-0MhuPArU.d.ts} +2 -2
- package/dist/{experiment-tracker-Cp9ougHs.d.ts.map → experiment-tracker-0MhuPArU.d.ts.map} +1 -1
- package/dist/{external-optimizer-contracts-mbhR9Ldu.d.ts → external-optimizer-contracts-DbLsm4Po.d.ts} +2 -2
- package/dist/{external-optimizer-contracts-mbhR9Ldu.d.ts.map → external-optimizer-contracts-DbLsm4Po.d.ts.map} +1 -1
- package/dist/{external-optimizer-process-tDe72VRO.js → external-optimizer-process-x9oEXKsU.js} +2 -2
- package/dist/{external-optimizer-process-tDe72VRO.js.map → external-optimizer-process-x9oEXKsU.js.map} +1 -1
- package/dist/{external-optimizer-subprocess-BepyNwtj.js → external-optimizer-subprocess-CKNb42oM.js} +278 -43
- package/dist/external-optimizer-subprocess-CKNb42oM.js.map +1 -0
- package/dist/{feedback-trajectory-Cc9M2tNF.d.ts → feedback-trajectory-DpTTjo0q.d.ts} +3 -3
- package/dist/{feedback-trajectory-Cc9M2tNF.d.ts.map → feedback-trajectory-DpTTjo0q.d.ts.map} +1 -1
- package/dist/hosted/index.d.ts +2 -2
- package/dist/{index-DL4MJuJL.d.ts → index-Aj3WO3_a.d.ts} +8 -8
- package/dist/{index-DL4MJuJL.d.ts.map → index-Aj3WO3_a.d.ts.map} +1 -1
- package/dist/index-C1ravkGA.d.ts +1 -0
- package/dist/{index-dh0kp2GM.d.ts → index-IQccV3Ou.d.ts} +15 -10
- package/dist/index-IQccV3Ou.d.ts.map +1 -0
- package/dist/index.d.ts +21 -90
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -13
- package/dist/{integrity-D2MAlC_K.d.ts → integrity-B0dZ96EO.d.ts} +2 -2
- package/dist/{integrity-D2MAlC_K.d.ts.map → integrity-B0dZ96EO.d.ts.map} +1 -1
- package/dist/{integrity-DysDBWDu.js → integrity-DL91tucI.js} +16 -3
- package/dist/integrity-DL91tucI.js.map +1 -0
- package/dist/{judge-calibration-DZkWrm5H.js → judge-calibration-zZjLz8hr.js} +2 -2
- package/dist/{judge-calibration-DZkWrm5H.js.map → judge-calibration-zZjLz8hr.js.map} +1 -1
- package/dist/{llm-client-CxyJ8Oft.js → llm-client-Bg32RW0j.js} +47 -2
- package/dist/llm-client-Bg32RW0j.js.map +1 -0
- package/dist/{llm-judge-D7UuMI2t.js → llm-judge-BqqMS8t7.js} +5 -5
- package/dist/{llm-judge-D7UuMI2t.js.map → llm-judge-BqqMS8t7.js.map} +1 -1
- package/dist/{matrix-D3hL68uV.d.ts → matrix-DrVnRp4G.d.ts} +2 -2
- package/dist/{matrix-D3hL68uV.d.ts.map → matrix-DrVnRp4G.d.ts.map} +1 -1
- package/dist/meta-eval/index.d.ts +1 -1
- package/dist/meta-eval/index.js +2 -2
- package/dist/multishot/golden/index.d.ts +1 -1
- package/dist/multishot/index.d.ts +2 -2
- package/dist/openapi.json +1 -1
- package/dist/pipelines/index.js +1 -1
- package/dist/{produced-state-WJs_enbZ.js → produced-state-Bnq4FaDO.js} +3 -3
- package/dist/{produced-state-WJs_enbZ.js.map → produced-state-Bnq4FaDO.js.map} +1 -1
- package/dist/{promotion-policy-DYRzXdTV.d.ts → promotion-policy-DLOUkYhI.d.ts} +2 -2
- package/dist/{promotion-policy-DYRzXdTV.d.ts.map → promotion-policy-DLOUkYhI.d.ts.map} +1 -1
- package/dist/{provenance-Bh9k3sjl.d.ts → provenance-oA4-zUqm.d.ts} +6 -6
- package/dist/{provenance-Bh9k3sjl.d.ts.map → provenance-oA4-zUqm.d.ts.map} +1 -1
- package/dist/{registry-DhpX9Bsg.d.ts → registry-BQwrSYpC.d.ts} +4 -4
- package/dist/{registry-DhpX9Bsg.d.ts.map → registry-BQwrSYpC.d.ts.map} +1 -1
- package/dist/reporting.js +2 -2
- package/dist/{researcher-CbCK-ppF.d.ts → researcher-DJnoUE8c.d.ts} +3 -3
- package/dist/{researcher-CbCK-ppF.d.ts.map → researcher-DJnoUE8c.d.ts.map} +1 -1
- package/dist/{reward-hacking-DFo2FU5J.js → reward-hacking-C0x0xihA.js} +20 -2
- package/dist/reward-hacking-C0x0xihA.js.map +1 -0
- package/dist/rl.d.ts +2 -2
- package/dist/rl.js +3 -3
- package/dist/{rubric-predictive-validity-Cwwyd7ah.js → rubric-predictive-validity-CzxLoZge.js} +2 -2
- package/dist/{rubric-predictive-validity-Cwwyd7ah.js.map → rubric-predictive-validity-CzxLoZge.js.map} +1 -1
- package/dist/{semantic-concept-judge-B7KPD57W.js → semantic-concept-judge-laMCnTLn.js} +2 -2
- package/dist/{semantic-concept-judge-B7KPD57W.js.map → semantic-concept-judge-laMCnTLn.js.map} +1 -1
- package/dist/{series-convergence-VhTad5TM.d.ts → series-convergence-BxKEgBwA.d.ts} +2 -2
- package/dist/{series-convergence-VhTad5TM.d.ts.map → series-convergence-BxKEgBwA.d.ts.map} +1 -1
- package/dist/{server-DNiUVirV.js → server-dIWwF3j_.js} +2 -2
- package/dist/{server-DNiUVirV.js.map → server-dIWwF3j_.js.map} +1 -1
- package/dist/{skillopt-optimization-method-BtgL6PMH.d.ts → skillopt-optimization-method-BDD_o1xE.d.ts} +5 -5
- package/dist/{skillopt-optimization-method-BtgL6PMH.d.ts.map → skillopt-optimization-method-BDD_o1xE.d.ts.map} +1 -1
- package/dist/{skillopt-optimization-method-L8vi4h4m.js → skillopt-optimization-method-CPBlTcj5.js} +5 -5
- package/dist/{skillopt-optimization-method-L8vi4h4m.js.map → skillopt-optimization-method-CPBlTcj5.js.map} +1 -1
- package/dist/{statistical-heldout-Caxd30ZF.d.ts → statistical-heldout-_woZ9q9j.d.ts} +2 -2
- package/dist/{statistical-heldout-Caxd30ZF.d.ts.map → statistical-heldout-_woZ9q9j.d.ts.map} +1 -1
- package/dist/{store-tool-spans-BGg2QFk1.d.ts → store-tool-spans-Br2_IUhm.d.ts} +4 -4
- package/dist/{store-tool-spans-BGg2QFk1.d.ts.map → store-tool-spans-Br2_IUhm.d.ts.map} +1 -1
- package/dist/{summary-report-Blysd6Z2.js → summary-report-DW2bEpdB.js} +2 -2
- package/dist/{summary-report-Blysd6Z2.js.map → summary-report-DW2bEpdB.js.map} +1 -1
- package/dist/supervisor-run/index.d.ts +26 -4
- package/dist/supervisor-run/index.d.ts.map +1 -1
- package/dist/supervisor-run/index.js +102 -24
- package/dist/supervisor-run/index.js.map +1 -1
- package/dist/{tool-groups-BuhFx38z.d.ts → tool-groups-B4tqh8jB.d.ts} +3 -3
- package/dist/tool-groups-B4tqh8jB.d.ts.map +1 -0
- package/dist/{tool-waste-BDdBZG1F.js → tool-waste-C-VHSRwF.js} +3 -3
- package/dist/{tool-waste-BDdBZG1F.js.map → tool-waste-C-VHSRwF.js.map} +1 -1
- package/dist/trace-repair/index.d.ts +1 -1
- package/dist/traces.d.ts +7 -7
- package/dist/{types-BNbhD_Vj.d.ts → types-B2NsbrNy.d.ts} +2 -2
- package/dist/{types-BNbhD_Vj.d.ts.map → types-B2NsbrNy.d.ts.map} +1 -1
- package/dist/{types-B61iI2ZG.d.ts → types-CLAwnY-L.d.ts} +2 -2
- package/dist/{types-B61iI2ZG.d.ts.map → types-CLAwnY-L.d.ts.map} +1 -1
- package/dist/{types-COEjZMLY.d.ts → types-DdFNuyxQ.d.ts} +3 -3
- package/dist/{types-COEjZMLY.d.ts.map → types-DdFNuyxQ.d.ts.map} +1 -1
- package/dist/{types-yLK8gXE9.d.ts → types-I5WwVzQ7.d.ts} +159 -9
- package/dist/types-I5WwVzQ7.d.ts.map +1 -0
- package/dist/{types-COAJy8Ku.d.ts → types-jUBXJ7Iz.d.ts} +36 -3
- package/dist/types-jUBXJ7Iz.d.ts.map +1 -0
- package/dist/wire/index.d.ts +2 -2
- package/dist/wire/index.js +1 -1
- package/docs/adapters-observability.md +9 -23
- package/docs/campaign-proposers.md +18 -3
- package/docs/concepts.md +3 -4
- package/docs/wire-protocol.md +1 -1
- package/package.json +1 -1
- package/dist/campaign-CR6Yt5ZS.js.map +0 -1
- package/dist/descriptive-B5MwKfbf.js.map +0 -1
- package/dist/external-optimizer-subprocess-BepyNwtj.js.map +0 -1
- package/dist/index-DjpxROrT.d.ts +0 -1
- package/dist/index-dh0kp2GM.d.ts.map +0 -1
- package/dist/integrity-DysDBWDu.js.map +0 -1
- package/dist/llm-client-CxyJ8Oft.js.map +0 -1
- package/dist/reward-hacking-DFo2FU5J.js.map +0 -1
- package/dist/tool-groups-BuhFx38z.d.ts.map +0 -1
- package/dist/types-COAJy8Ku.d.ts.map +0 -1
- package/dist/types-yLK8gXE9.d.ts.map +0 -1
package/dist/{external-optimizer-process-tDe72VRO.js → external-optimizer-process-x9oEXKsU.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as contentHash } from "./verdict-cache-mZf5FEiY.js";
|
|
2
2
|
import { m as tryAcquireAtomicFileLock, p as probeAtomicFileLock } from "./ledger-core-DTae9rv_.js";
|
|
3
|
-
import { _ as resolveExternalOptimizerCallbackLimits, a as closeServer, h as isRecord, m as isExternalTextCandidate, o as listenLocal, s as sendJson } from "./external-optimizer-subprocess-
|
|
3
|
+
import { _ as resolveExternalOptimizerCallbackLimits, a as closeServer, h as isRecord, m as isExternalTextCandidate, o as listenLocal, s as sendJson } from "./external-optimizer-subprocess-CKNb42oM.js";
|
|
4
4
|
import { createServer } from "node:http";
|
|
5
5
|
//#region src/campaign/single-run-lock.ts
|
|
6
6
|
/**
|
|
@@ -298,4 +298,4 @@ function cloneCandidate(candidate) {
|
|
|
298
298
|
//#endregion
|
|
299
299
|
export { acquireSingleRunLock as n, startExternalOptimizerCallback as t };
|
|
300
300
|
|
|
301
|
-
//# sourceMappingURL=external-optimizer-process-
|
|
301
|
+
//# sourceMappingURL=external-optimizer-process-x9oEXKsU.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-optimizer-process-tDe72VRO.js","names":[],"sources":["../src/campaign/single-run-lock.ts","../src/campaign/external-optimizer-callback.ts"],"sourcesContent":["/**\n * Single-run lock for evaluations that share one mutable environment.\n *\n * Two concurrent runs against a shared stateful gym silently corrupt each\n * other: each resets/mutates environment state mid-cell of the other, and\n * every score from both becomes garbage that LOOKS like worker variance\n * (agent-lab R357 burned hours on flip-flopping scores before tracing them\n * to exactly this). The fix is a pid lockfile: refuse to start while a live\n * holder exists, reclaim stale locks whose pid is gone, release only if the\n * lock is still ours.\n *\n * `alsoCheck` exists because independent runners can guard the same shared\n * resource with differently named lockfiles; a runner must respect all of\n * them even though it writes only its own.\n */\n\nimport {\n type AtomicFileLockUnavailable,\n probeAtomicFileLock,\n tryAcquireAtomicFileLock,\n} from '../ledger-core/atomic-file-lock'\n\nexport interface SingleRunLockOptions {\n /** Lockfile this runner writes (and checks). */\n readonly lockPath: string\n /** Other runners' lockfiles guarding the same resource; checked, never written. */\n readonly alsoCheck?: readonly string[]\n /** Install a process 'exit' hook that releases the lock. Default true. */\n readonly releaseOnExit?: boolean\n /** Owner pid recorded in the lockfile metadata. Default process.pid. */\n readonly pid?: number\n}\n\nexport interface SingleRunLock {\n /** Remove the lockfile if this process still owns it. Idempotent. */\n release(): void\n}\n\nfunction assertAvailable(path: string): void {\n const unavailable = probeAtomicFileLock({ lockPath: path })\n if (unavailable) throw unavailableError(path, unavailable)\n}\n\nfunction unavailableError(path: string, unavailable: AtomicFileLockUnavailable): Error {\n if (unavailable.reason === 'recovery') {\n return new Error(\n `single-run lock recovery is already in progress (${path}); refusing a concurrent run`,\n )\n }\n return new Error(\n `single-run lock held by live pid ${unavailable.holder.pid} (${path}); refusing a concurrent run on the shared resource`,\n )\n}\n\n/**\n * Acquire the lock or throw naming the live holder. A stale lock (holder pid\n * no longer running) is reclaimed by one contender. An interrupted reclaim\n * leaves a marker that fails closed instead of admitting overlapping runs.\n */\nexport function acquireSingleRunLock(opts: SingleRunLockOptions): SingleRunLock {\n const pid = opts.pid ?? process.pid\n if (!Number.isSafeInteger(pid) || pid <= 0)\n throw new Error('single-run lock pid must be a positive integer')\n for (const path of opts.alsoCheck ?? []) assertAvailable(path)\n const acquisition = tryAcquireAtomicFileLock({\n lockPath: opts.lockPath,\n pid,\n })\n if (!acquisition.acquired) throw unavailableError(opts.lockPath, acquisition)\n const releaseOnExit = opts.releaseOnExit ?? true\n let released = false\n const release = (): void => {\n if (released) return\n released = true\n if (releaseOnExit) process.off('exit', release)\n try {\n acquisition.lock.release()\n } catch {\n // release is best-effort; a leftover stale lock is reclaimed on next acquire\n }\n }\n try {\n for (const path of opts.alsoCheck ?? []) assertAvailable(path)\n } catch (error) {\n release()\n throw error\n }\n if (releaseOnExit) process.on('exit', release)\n return { release }\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from 'node:http'\nimport { contentHash } from '../verdict-cache'\nimport {\n type ExternalOptimizerCallback,\n type ExternalOptimizerCallbackLimits,\n type ExternalOptimizerEvaluationObservation,\n type ExternalTextCandidate,\n type ExternalTextEvaluationRequest,\n isExternalTextCandidate,\n isRecord,\n resolveExternalOptimizerCallbackLimits,\n} from './external-optimizer-contracts'\nimport { closeServer, listenLocal, sendJson } from './external-optimizer-http'\n\ntype UnsequencedObservation = ExternalOptimizerEvaluationObservation extends infer T\n ? T extends ExternalOptimizerEvaluationObservation\n ? Omit<T, 'sequence'>\n : never\n : never\n\nexport async function startExternalOptimizerCallback<TResponse>(args: {\n token: string\n maxEvaluations: number\n acceptEvaluation?: () => number | undefined\n evaluate: (request: ExternalTextEvaluationRequest, signal: AbortSignal) => Promise<TResponse>\n observe?: (observation: ExternalOptimizerEvaluationObservation) => void\n /** Loopback JSON byte limits. Omitted fields use finite defaults. */\n limits?: Partial<ExternalOptimizerCallbackLimits>\n signal?: AbortSignal\n}): Promise<ExternalOptimizerCallback> {\n assertCallbackConfig(args)\n const limits = resolveExternalOptimizerCallbackLimits(args.limits)\n args.signal?.throwIfAborted()\n let evaluations = 0\n let accepting = true\n let closePromise: Promise<void> | undefined\n const activeControllers = new Set<AbortController>()\n const activeHandlers = new Set<Promise<void>>()\n const proposedCandidateHashes = new Set<string>()\n let observationSequence = 0\n const server = createServer((request, response) => {\n if (!accepting) {\n sendJsonIfOpen(response, 503, { error: 'external optimizer callback is closing' })\n return\n }\n\n const controller = new AbortController()\n const abortRequest = (): void => {\n request.destroy()\n response.destroy()\n }\n activeControllers.add(controller)\n controller.signal.addEventListener('abort', abortRequest, { once: true })\n\n let handler!: Promise<void>\n handler = handleCallback(\n request,\n response,\n controller.signal,\n args,\n limits,\n () => {\n const accepted = args.acceptEvaluation ? args.acceptEvaluation() : evaluations + 1\n if (accepted === undefined) return undefined\n if (!Number.isSafeInteger(accepted) || accepted <= 0) {\n throw new Error('external optimizer callback: invalid accepted evaluation count')\n }\n if (accepted > args.maxEvaluations) return undefined\n evaluations += 1\n return accepted\n },\n (observation) => {\n if (!args.observe) return\n args.observe({ ...observation, sequence: ++observationSequence })\n },\n proposedCandidateHashes,\n ).finally(() => {\n controller.signal.removeEventListener('abort', abortRequest)\n activeControllers.delete(controller)\n activeHandlers.delete(handler)\n })\n activeHandlers.add(handler)\n void handler.catch(() => undefined)\n })\n const port = await listenLocal(server)\n const close = (): Promise<void> => {\n closePromise ??= closeCallbackServer()\n return closePromise\n }\n const onAbort = (): void => {\n void close().catch(() => undefined)\n }\n args.signal?.addEventListener('abort', onAbort, { once: true })\n if (args.signal?.aborted) onAbort()\n return {\n url: `http://127.0.0.1:${port}/evaluate`,\n token: args.token,\n evaluations: () => evaluations,\n close,\n }\n\n async function closeCallbackServer(): Promise<void> {\n args.signal?.removeEventListener('abort', onAbort)\n accepting = false\n const closingServer = closeServer(server)\n server.closeIdleConnections?.()\n for (const controller of activeControllers) controller.abort()\n const [serverResult] = await Promise.allSettled([\n closingServer,\n waitForActiveHandlers(activeHandlers),\n ])\n if (activeControllers.size !== 0 || activeHandlers.size !== 0) {\n throw new Error('external optimizer callback closed with active request work')\n }\n if (serverResult?.status === 'rejected') throw serverResult.reason\n }\n}\n\nasync function handleCallback<TResponse>(\n request: IncomingMessage,\n response: ServerResponse,\n signal: AbortSignal,\n args: {\n token: string\n maxEvaluations: number\n acceptEvaluation?: () => number | undefined\n evaluate: (request: ExternalTextEvaluationRequest, signal: AbortSignal) => Promise<TResponse>\n observe?: (observation: ExternalOptimizerEvaluationObservation) => void\n },\n limits: ExternalOptimizerCallbackLimits,\n nextEvaluation: () => number | undefined,\n observe: (observation: UnsequencedObservation) => void,\n proposedCandidateHashes: Set<string>,\n): Promise<void> {\n try {\n if (request.method !== 'POST' || request.url !== '/evaluate') {\n sendJsonIfOpen(response, 404, { error: 'not found' })\n return\n }\n if (request.headers.authorization !== `Bearer ${args.token}`) {\n sendJsonIfOpen(response, 401, { error: 'unauthorized' })\n return\n }\n let body: unknown\n try {\n body = await readJson(request, limits.maxRequestBytes)\n } catch {\n observe({ kind: 'refusal', reason: 'invalid-request' })\n sendJsonIfOpen(response, 400, { error: 'request body must be bounded valid JSON' })\n return\n }\n if (\n !isRecord(body) ||\n !isExternalTextCandidate(body.candidate) ||\n typeof body.exampleId !== 'string'\n ) {\n observe({ kind: 'refusal', reason: 'invalid-request' })\n sendJsonIfOpen(response, 400, { error: 'candidate and exampleId are required strings' })\n return\n }\n const candidate = cloneCandidate(body.candidate)\n const candidateHash = contentHash({ kind: 'external-text-candidate', candidate })\n if (!proposedCandidateHashes.has(candidateHash)) {\n observe({ kind: 'proposal', candidate, candidateHash })\n proposedCandidateHashes.add(candidateHash)\n }\n const count = nextEvaluation()\n if (count === undefined) {\n observe({\n kind: 'refusal',\n reason: 'evaluation-limit',\n candidate,\n candidateHash,\n exampleId: body.exampleId,\n })\n sendJsonIfOpen(response, 429, { error: 'evaluation limit reached' })\n return\n }\n let result: TResponse\n try {\n result = await args.evaluate({ candidate, exampleId: body.exampleId }, signal)\n } catch (error) {\n observe({\n kind: 'refusal',\n reason: 'evaluation-failed',\n candidate,\n candidateHash,\n exampleId: body.exampleId,\n })\n // The thrown detail is the only diagnostic for a failed evaluation —\n // an opaque 500 costs the caller a blind debugging round.\n sendJsonIfOpen(response, 500, {\n error: `evaluation failed: ${String(error).slice(0, 400)}`,\n })\n return\n }\n let encoded: string\n try {\n encoded = encodeJsonResponse(result)\n } catch {\n observe({\n kind: 'refusal',\n reason: 'evaluation-failed',\n candidate,\n candidateHash,\n exampleId: body.exampleId,\n })\n sendJsonIfOpen(response, 500, { error: 'evaluation response must be valid JSON' })\n return\n }\n if (Buffer.byteLength(encoded) > limits.maxResponseBytes) {\n observe({\n kind: 'refusal',\n reason: 'evaluation-failed',\n candidate,\n candidateHash,\n exampleId: body.exampleId,\n })\n sendJsonIfOpen(response, 413, { error: 'evaluation response too large' })\n return\n }\n observe({\n kind: 'evaluation',\n candidate,\n candidateHash,\n exampleId: body.exampleId,\n evaluationNumber: count,\n response: structuredClone(result),\n })\n sendEncodedJsonIfOpen(response, 200, encoded)\n } catch {\n sendJsonIfOpen(response, 500, { error: 'evaluation failed' })\n }\n}\n\nasync function waitForActiveHandlers(activeHandlers: Set<Promise<void>>): Promise<void> {\n while (activeHandlers.size > 0) {\n await Promise.allSettled([...activeHandlers])\n }\n}\n\nfunction sendJsonIfOpen(response: ServerResponse, status: number, body: unknown): void {\n if (response.destroyed || response.writableEnded) return\n sendJson(response, status, body)\n}\n\nfunction readJson(request: IncomingMessage, maxRequestBytes: number): Promise<unknown> {\n return new Promise((resolvePromise, reject) => {\n let size = 0\n const chunks: Buffer[] = []\n request.on('data', (chunk: Buffer) => {\n size += chunk.length\n if (size > maxRequestBytes) {\n reject(new Error('callback body too large'))\n request.destroy()\n return\n }\n chunks.push(chunk)\n })\n request.on('error', reject)\n request.on('end', () => {\n try {\n resolvePromise(JSON.parse(Buffer.concat(chunks).toString('utf8')))\n } catch (error) {\n reject(error)\n }\n })\n })\n}\n\nfunction encodeJsonResponse(value: unknown): string {\n const encoded = JSON.stringify(value)\n if (encoded === undefined) throw new Error('evaluation response must be JSON-serializable')\n return encoded\n}\n\nfunction sendEncodedJsonIfOpen(response: ServerResponse, status: number, encoded: string): void {\n if (response.destroyed || response.writableEnded) return\n response.writeHead(status, {\n 'content-type': 'application/json; charset=utf-8',\n 'content-length': String(Buffer.byteLength(encoded)),\n })\n response.end(encoded)\n}\n\nfunction assertCallbackConfig(args: {\n token: string\n maxEvaluations: number\n acceptEvaluation?: () => number | undefined\n evaluate: (request: ExternalTextEvaluationRequest, signal: AbortSignal) => Promise<unknown>\n observe?: (observation: ExternalOptimizerEvaluationObservation) => void\n limits?: Partial<ExternalOptimizerCallbackLimits>\n signal?: AbortSignal\n}): void {\n if (typeof args.token !== 'string' || !args.token.trim()) {\n throw new Error('external optimizer callback: token must be non-empty')\n }\n if (!Number.isSafeInteger(args.maxEvaluations) || args.maxEvaluations <= 0) {\n throw new Error('external optimizer callback: maxEvaluations must be a positive safe integer')\n }\n if (args.acceptEvaluation !== undefined && typeof args.acceptEvaluation !== 'function') {\n throw new Error('external optimizer callback: acceptEvaluation must be a function')\n }\n if (typeof args.evaluate !== 'function') {\n throw new Error('external optimizer callback: evaluate must be a function')\n }\n if (args.observe !== undefined && typeof args.observe !== 'function') {\n throw new Error('external optimizer callback: observe must be a function')\n }\n}\n\nfunction cloneCandidate(candidate: ExternalTextCandidate): ExternalTextCandidate {\n return typeof candidate === 'string' ? candidate : { ...candidate }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAsCA,SAAS,gBAAgB,MAAoB;CAC3C,MAAM,cAAc,oBAAoB,EAAE,UAAU,KAAK,CAAC;CAC1D,IAAI,aAAa,MAAM,iBAAiB,MAAM,WAAW;AAC3D;AAEA,SAAS,iBAAiB,MAAc,aAA+C;CACrF,IAAI,YAAY,WAAW,YACzB,uBAAO,IAAI,MACT,oDAAoD,KAAK,6BAC3D;CAEF,uBAAO,IAAI,MACT,oCAAoC,YAAY,OAAO,IAAI,IAAI,KAAK,oDACtE;AACF;;;;;;AAOA,SAAgB,qBAAqB,MAA2C;CAC9E,MAAM,MAAM,KAAK,OAAO,QAAQ;CAChC,IAAI,CAAC,OAAO,cAAc,GAAG,KAAK,OAAO,GACvC,MAAM,IAAI,MAAM,gDAAgD;CAClE,KAAK,MAAM,QAAQ,KAAK,aAAa,CAAC,GAAG,gBAAgB,IAAI;CAC7D,MAAM,cAAc,yBAAyB;EAC3C,UAAU,KAAK;EACf;CACF,CAAC;CACD,IAAI,CAAC,YAAY,UAAU,MAAM,iBAAiB,KAAK,UAAU,WAAW;CAC5E,MAAM,gBAAgB,KAAK,iBAAiB;CAC5C,IAAI,WAAW;CACf,MAAM,gBAAsB;EAC1B,IAAI,UAAU;EACd,WAAW;EACX,IAAI,eAAe,QAAQ,IAAI,QAAQ,OAAO;EAC9C,IAAI;GACF,YAAY,KAAK,QAAQ;EAC3B,QAAQ,CAER;CACF;CACA,IAAI;EACF,KAAK,MAAM,QAAQ,KAAK,aAAa,CAAC,GAAG,gBAAgB,IAAI;CAC/D,SAAS,OAAO;EACd,QAAQ;EACR,MAAM;CACR;CACA,IAAI,eAAe,QAAQ,GAAG,QAAQ,OAAO;CAC7C,OAAO,EAAE,QAAQ;AACnB;;;ACrEA,eAAsB,+BAA0C,MASzB;CACrC,qBAAqB,IAAI;CACzB,MAAM,SAAS,uCAAuC,KAAK,MAAM;CACjE,KAAK,QAAQ,eAAe;CAC5B,IAAI,cAAc;CAClB,IAAI,YAAY;CAChB,IAAI;CACJ,MAAM,oCAAoB,IAAI,IAAqB;CACnD,MAAM,iCAAiB,IAAI,IAAmB;CAC9C,MAAM,0CAA0B,IAAI,IAAY;CAChD,IAAI,sBAAsB;CAC1B,MAAM,SAAS,cAAc,SAAS,aAAa;EACjD,IAAI,CAAC,WAAW;GACd,eAAe,UAAU,KAAK,EAAE,OAAO,yCAAyC,CAAC;GACjF;EACF;EAEA,MAAM,aAAa,IAAI,gBAAgB;EACvC,MAAM,qBAA2B;GAC/B,QAAQ,QAAQ;GAChB,SAAS,QAAQ;EACnB;EACA,kBAAkB,IAAI,UAAU;EAChC,WAAW,OAAO,iBAAiB,SAAS,cAAc,EAAE,MAAM,KAAK,CAAC;EAExE,IAAI;EACJ,UAAU,eACR,SACA,UACA,WAAW,QACX,MACA,cACM;GACJ,MAAM,WAAW,KAAK,mBAAmB,KAAK,iBAAiB,IAAI,cAAc;GACjF,IAAI,aAAa,KAAA,GAAW,OAAO,KAAA;GACnC,IAAI,CAAC,OAAO,cAAc,QAAQ,KAAK,YAAY,GACjD,MAAM,IAAI,MAAM,gEAAgE;GAElF,IAAI,WAAW,KAAK,gBAAgB,OAAO,KAAA;GAC3C,eAAe;GACf,OAAO;EACT,IACC,gBAAgB;GACf,IAAI,CAAC,KAAK,SAAS;GACnB,KAAK,QAAQ;IAAE,GAAG;IAAa,UAAU,EAAE;GAAoB,CAAC;EAClE,GACA,uBACF,CAAC,CAAC,cAAc;GACd,WAAW,OAAO,oBAAoB,SAAS,YAAY;GAC3D,kBAAkB,OAAO,UAAU;GACnC,eAAe,OAAO,OAAO;EAC/B,CAAC;EACD,eAAe,IAAI,OAAO;EAC1B,QAAa,YAAY,KAAA,CAAS;CACpC,CAAC;CACD,MAAM,OAAO,MAAM,YAAY,MAAM;CACrC,MAAM,cAA6B;EACjC,iBAAiB,oBAAoB;EACrC,OAAO;CACT;CACA,MAAM,gBAAsB;EAC1B,MAAW,CAAC,CAAC,YAAY,KAAA,CAAS;CACpC;CACA,KAAK,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAC9D,IAAI,KAAK,QAAQ,SAAS,QAAQ;CAClC,OAAO;EACL,KAAK,oBAAoB,KAAK;EAC9B,OAAO,KAAK;EACZ,mBAAmB;EACnB;CACF;CAEA,eAAe,sBAAqC;EAClD,KAAK,QAAQ,oBAAoB,SAAS,OAAO;EACjD,YAAY;EACZ,MAAM,gBAAgB,YAAY,MAAM;EACxC,OAAO,uBAAuB;EAC9B,KAAK,MAAM,cAAc,mBAAmB,WAAW,MAAM;EAC7D,MAAM,CAAC,gBAAgB,MAAM,QAAQ,WAAW,CAC9C,eACA,sBAAsB,cAAc,CACtC,CAAC;EACD,IAAI,kBAAkB,SAAS,KAAK,eAAe,SAAS,GAC1D,MAAM,IAAI,MAAM,6DAA6D;EAE/E,IAAI,cAAc,WAAW,YAAY,MAAM,aAAa;CAC9D;AACF;AAEA,eAAe,eACb,SACA,UACA,QACA,MAOA,QACA,gBACA,SACA,yBACe;CACf,IAAI;EACF,IAAI,QAAQ,WAAW,UAAU,QAAQ,QAAQ,aAAa;GAC5D,eAAe,UAAU,KAAK,EAAE,OAAO,YAAY,CAAC;GACpD;EACF;EACA,IAAI,QAAQ,QAAQ,kBAAkB,UAAU,KAAK,SAAS;GAC5D,eAAe,UAAU,KAAK,EAAE,OAAO,eAAe,CAAC;GACvD;EACF;EACA,IAAI;EACJ,IAAI;GACF,OAAO,MAAM,SAAS,SAAS,OAAO,eAAe;EACvD,QAAQ;GACN,QAAQ;IAAE,MAAM;IAAW,QAAQ;GAAkB,CAAC;GACtD,eAAe,UAAU,KAAK,EAAE,OAAO,0CAA0C,CAAC;GAClF;EACF;EACA,IACE,CAAC,SAAS,IAAI,KACd,CAAC,wBAAwB,KAAK,SAAS,KACvC,OAAO,KAAK,cAAc,UAC1B;GACA,QAAQ;IAAE,MAAM;IAAW,QAAQ;GAAkB,CAAC;GACtD,eAAe,UAAU,KAAK,EAAE,OAAO,+CAA+C,CAAC;GACvF;EACF;EACA,MAAM,YAAY,eAAe,KAAK,SAAS;EAC/C,MAAM,gBAAgB,YAAY;GAAE,MAAM;GAA2B;EAAU,CAAC;EAChF,IAAI,CAAC,wBAAwB,IAAI,aAAa,GAAG;GAC/C,QAAQ;IAAE,MAAM;IAAY;IAAW;GAAc,CAAC;GACtD,wBAAwB,IAAI,aAAa;EAC3C;EACA,MAAM,QAAQ,eAAe;EAC7B,IAAI,UAAU,KAAA,GAAW;GACvB,QAAQ;IACN,MAAM;IACN,QAAQ;IACR;IACA;IACA,WAAW,KAAK;GAClB,CAAC;GACD,eAAe,UAAU,KAAK,EAAE,OAAO,2BAA2B,CAAC;GACnE;EACF;EACA,IAAI;EACJ,IAAI;GACF,SAAS,MAAM,KAAK,SAAS;IAAE;IAAW,WAAW,KAAK;GAAU,GAAG,MAAM;EAC/E,SAAS,OAAO;GACd,QAAQ;IACN,MAAM;IACN,QAAQ;IACR;IACA;IACA,WAAW,KAAK;GAClB,CAAC;GAGD,eAAe,UAAU,KAAK,EAC5B,OAAO,sBAAsB,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,GAAG,IACzD,CAAC;GACD;EACF;EACA,IAAI;EACJ,IAAI;GACF,UAAU,mBAAmB,MAAM;EACrC,QAAQ;GACN,QAAQ;IACN,MAAM;IACN,QAAQ;IACR;IACA;IACA,WAAW,KAAK;GAClB,CAAC;GACD,eAAe,UAAU,KAAK,EAAE,OAAO,yCAAyC,CAAC;GACjF;EACF;EACA,IAAI,OAAO,WAAW,OAAO,IAAI,OAAO,kBAAkB;GACxD,QAAQ;IACN,MAAM;IACN,QAAQ;IACR;IACA;IACA,WAAW,KAAK;GAClB,CAAC;GACD,eAAe,UAAU,KAAK,EAAE,OAAO,gCAAgC,CAAC;GACxE;EACF;EACA,QAAQ;GACN,MAAM;GACN;GACA;GACA,WAAW,KAAK;GAChB,kBAAkB;GAClB,UAAU,gBAAgB,MAAM;EAClC,CAAC;EACD,sBAAsB,UAAU,KAAK,OAAO;CAC9C,QAAQ;EACN,eAAe,UAAU,KAAK,EAAE,OAAO,oBAAoB,CAAC;CAC9D;AACF;AAEA,eAAe,sBAAsB,gBAAmD;CACtF,OAAO,eAAe,OAAO,GAC3B,MAAM,QAAQ,WAAW,CAAC,GAAG,cAAc,CAAC;AAEhD;AAEA,SAAS,eAAe,UAA0B,QAAgB,MAAqB;CACrF,IAAI,SAAS,aAAa,SAAS,eAAe;CAClD,SAAS,UAAU,QAAQ,IAAI;AACjC;AAEA,SAAS,SAAS,SAA0B,iBAA2C;CACrF,OAAO,IAAI,SAAS,gBAAgB,WAAW;EAC7C,IAAI,OAAO;EACX,MAAM,SAAmB,CAAC;EAC1B,QAAQ,GAAG,SAAS,UAAkB;GACpC,QAAQ,MAAM;GACd,IAAI,OAAO,iBAAiB;IAC1B,uBAAO,IAAI,MAAM,yBAAyB,CAAC;IAC3C,QAAQ,QAAQ;IAChB;GACF;GACA,OAAO,KAAK,KAAK;EACnB,CAAC;EACD,QAAQ,GAAG,SAAS,MAAM;EAC1B,QAAQ,GAAG,aAAa;GACtB,IAAI;IACF,eAAe,KAAK,MAAM,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC;GACnE,SAAS,OAAO;IACd,OAAO,KAAK;GACd;EACF,CAAC;CACH,CAAC;AACH;AAEA,SAAS,mBAAmB,OAAwB;CAClD,MAAM,UAAU,KAAK,UAAU,KAAK;CACpC,IAAI,YAAY,KAAA,GAAW,MAAM,IAAI,MAAM,+CAA+C;CAC1F,OAAO;AACT;AAEA,SAAS,sBAAsB,UAA0B,QAAgB,SAAuB;CAC9F,IAAI,SAAS,aAAa,SAAS,eAAe;CAClD,SAAS,UAAU,QAAQ;EACzB,gBAAgB;EAChB,kBAAkB,OAAO,OAAO,WAAW,OAAO,CAAC;CACrD,CAAC;CACD,SAAS,IAAI,OAAO;AACtB;AAEA,SAAS,qBAAqB,MAQrB;CACP,IAAI,OAAO,KAAK,UAAU,YAAY,CAAC,KAAK,MAAM,KAAK,GACrD,MAAM,IAAI,MAAM,sDAAsD;CAExE,IAAI,CAAC,OAAO,cAAc,KAAK,cAAc,KAAK,KAAK,kBAAkB,GACvE,MAAM,IAAI,MAAM,6EAA6E;CAE/F,IAAI,KAAK,qBAAqB,KAAA,KAAa,OAAO,KAAK,qBAAqB,YAC1E,MAAM,IAAI,MAAM,kEAAkE;CAEpF,IAAI,OAAO,KAAK,aAAa,YAC3B,MAAM,IAAI,MAAM,0DAA0D;CAE5E,IAAI,KAAK,YAAY,KAAA,KAAa,OAAO,KAAK,YAAY,YACxD,MAAM,IAAI,MAAM,yDAAyD;AAE7E;AAEA,SAAS,eAAe,WAAyD;CAC/E,OAAO,OAAO,cAAc,WAAW,YAAY,EAAE,GAAG,UAAU;AACpE"}
|
|
1
|
+
{"version":3,"file":"external-optimizer-process-x9oEXKsU.js","names":[],"sources":["../src/campaign/single-run-lock.ts","../src/campaign/external-optimizer-callback.ts"],"sourcesContent":["/**\n * Single-run lock for evaluations that share one mutable environment.\n *\n * Two concurrent runs against a shared stateful gym silently corrupt each\n * other: each resets/mutates environment state mid-cell of the other, and\n * every score from both becomes garbage that LOOKS like worker variance\n * (agent-lab R357 burned hours on flip-flopping scores before tracing them\n * to exactly this). The fix is a pid lockfile: refuse to start while a live\n * holder exists, reclaim stale locks whose pid is gone, release only if the\n * lock is still ours.\n *\n * `alsoCheck` exists because independent runners can guard the same shared\n * resource with differently named lockfiles; a runner must respect all of\n * them even though it writes only its own.\n */\n\nimport {\n type AtomicFileLockUnavailable,\n probeAtomicFileLock,\n tryAcquireAtomicFileLock,\n} from '../ledger-core/atomic-file-lock'\n\nexport interface SingleRunLockOptions {\n /** Lockfile this runner writes (and checks). */\n readonly lockPath: string\n /** Other runners' lockfiles guarding the same resource; checked, never written. */\n readonly alsoCheck?: readonly string[]\n /** Install a process 'exit' hook that releases the lock. Default true. */\n readonly releaseOnExit?: boolean\n /** Owner pid recorded in the lockfile metadata. Default process.pid. */\n readonly pid?: number\n}\n\nexport interface SingleRunLock {\n /** Remove the lockfile if this process still owns it. Idempotent. */\n release(): void\n}\n\nfunction assertAvailable(path: string): void {\n const unavailable = probeAtomicFileLock({ lockPath: path })\n if (unavailable) throw unavailableError(path, unavailable)\n}\n\nfunction unavailableError(path: string, unavailable: AtomicFileLockUnavailable): Error {\n if (unavailable.reason === 'recovery') {\n return new Error(\n `single-run lock recovery is already in progress (${path}); refusing a concurrent run`,\n )\n }\n return new Error(\n `single-run lock held by live pid ${unavailable.holder.pid} (${path}); refusing a concurrent run on the shared resource`,\n )\n}\n\n/**\n * Acquire the lock or throw naming the live holder. A stale lock (holder pid\n * no longer running) is reclaimed by one contender. An interrupted reclaim\n * leaves a marker that fails closed instead of admitting overlapping runs.\n */\nexport function acquireSingleRunLock(opts: SingleRunLockOptions): SingleRunLock {\n const pid = opts.pid ?? process.pid\n if (!Number.isSafeInteger(pid) || pid <= 0)\n throw new Error('single-run lock pid must be a positive integer')\n for (const path of opts.alsoCheck ?? []) assertAvailable(path)\n const acquisition = tryAcquireAtomicFileLock({\n lockPath: opts.lockPath,\n pid,\n })\n if (!acquisition.acquired) throw unavailableError(opts.lockPath, acquisition)\n const releaseOnExit = opts.releaseOnExit ?? true\n let released = false\n const release = (): void => {\n if (released) return\n released = true\n if (releaseOnExit) process.off('exit', release)\n try {\n acquisition.lock.release()\n } catch {\n // release is best-effort; a leftover stale lock is reclaimed on next acquire\n }\n }\n try {\n for (const path of opts.alsoCheck ?? []) assertAvailable(path)\n } catch (error) {\n release()\n throw error\n }\n if (releaseOnExit) process.on('exit', release)\n return { release }\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from 'node:http'\nimport { contentHash } from '../verdict-cache'\nimport {\n type ExternalOptimizerCallback,\n type ExternalOptimizerCallbackLimits,\n type ExternalOptimizerEvaluationObservation,\n type ExternalTextCandidate,\n type ExternalTextEvaluationRequest,\n isExternalTextCandidate,\n isRecord,\n resolveExternalOptimizerCallbackLimits,\n} from './external-optimizer-contracts'\nimport { closeServer, listenLocal, sendJson } from './external-optimizer-http'\n\ntype UnsequencedObservation = ExternalOptimizerEvaluationObservation extends infer T\n ? T extends ExternalOptimizerEvaluationObservation\n ? Omit<T, 'sequence'>\n : never\n : never\n\nexport async function startExternalOptimizerCallback<TResponse>(args: {\n token: string\n maxEvaluations: number\n acceptEvaluation?: () => number | undefined\n evaluate: (request: ExternalTextEvaluationRequest, signal: AbortSignal) => Promise<TResponse>\n observe?: (observation: ExternalOptimizerEvaluationObservation) => void\n /** Loopback JSON byte limits. Omitted fields use finite defaults. */\n limits?: Partial<ExternalOptimizerCallbackLimits>\n signal?: AbortSignal\n}): Promise<ExternalOptimizerCallback> {\n assertCallbackConfig(args)\n const limits = resolveExternalOptimizerCallbackLimits(args.limits)\n args.signal?.throwIfAborted()\n let evaluations = 0\n let accepting = true\n let closePromise: Promise<void> | undefined\n const activeControllers = new Set<AbortController>()\n const activeHandlers = new Set<Promise<void>>()\n const proposedCandidateHashes = new Set<string>()\n let observationSequence = 0\n const server = createServer((request, response) => {\n if (!accepting) {\n sendJsonIfOpen(response, 503, { error: 'external optimizer callback is closing' })\n return\n }\n\n const controller = new AbortController()\n const abortRequest = (): void => {\n request.destroy()\n response.destroy()\n }\n activeControllers.add(controller)\n controller.signal.addEventListener('abort', abortRequest, { once: true })\n\n let handler!: Promise<void>\n handler = handleCallback(\n request,\n response,\n controller.signal,\n args,\n limits,\n () => {\n const accepted = args.acceptEvaluation ? args.acceptEvaluation() : evaluations + 1\n if (accepted === undefined) return undefined\n if (!Number.isSafeInteger(accepted) || accepted <= 0) {\n throw new Error('external optimizer callback: invalid accepted evaluation count')\n }\n if (accepted > args.maxEvaluations) return undefined\n evaluations += 1\n return accepted\n },\n (observation) => {\n if (!args.observe) return\n args.observe({ ...observation, sequence: ++observationSequence })\n },\n proposedCandidateHashes,\n ).finally(() => {\n controller.signal.removeEventListener('abort', abortRequest)\n activeControllers.delete(controller)\n activeHandlers.delete(handler)\n })\n activeHandlers.add(handler)\n void handler.catch(() => undefined)\n })\n const port = await listenLocal(server)\n const close = (): Promise<void> => {\n closePromise ??= closeCallbackServer()\n return closePromise\n }\n const onAbort = (): void => {\n void close().catch(() => undefined)\n }\n args.signal?.addEventListener('abort', onAbort, { once: true })\n if (args.signal?.aborted) onAbort()\n return {\n url: `http://127.0.0.1:${port}/evaluate`,\n token: args.token,\n evaluations: () => evaluations,\n close,\n }\n\n async function closeCallbackServer(): Promise<void> {\n args.signal?.removeEventListener('abort', onAbort)\n accepting = false\n const closingServer = closeServer(server)\n server.closeIdleConnections?.()\n for (const controller of activeControllers) controller.abort()\n const [serverResult] = await Promise.allSettled([\n closingServer,\n waitForActiveHandlers(activeHandlers),\n ])\n if (activeControllers.size !== 0 || activeHandlers.size !== 0) {\n throw new Error('external optimizer callback closed with active request work')\n }\n if (serverResult?.status === 'rejected') throw serverResult.reason\n }\n}\n\nasync function handleCallback<TResponse>(\n request: IncomingMessage,\n response: ServerResponse,\n signal: AbortSignal,\n args: {\n token: string\n maxEvaluations: number\n acceptEvaluation?: () => number | undefined\n evaluate: (request: ExternalTextEvaluationRequest, signal: AbortSignal) => Promise<TResponse>\n observe?: (observation: ExternalOptimizerEvaluationObservation) => void\n },\n limits: ExternalOptimizerCallbackLimits,\n nextEvaluation: () => number | undefined,\n observe: (observation: UnsequencedObservation) => void,\n proposedCandidateHashes: Set<string>,\n): Promise<void> {\n try {\n if (request.method !== 'POST' || request.url !== '/evaluate') {\n sendJsonIfOpen(response, 404, { error: 'not found' })\n return\n }\n if (request.headers.authorization !== `Bearer ${args.token}`) {\n sendJsonIfOpen(response, 401, { error: 'unauthorized' })\n return\n }\n let body: unknown\n try {\n body = await readJson(request, limits.maxRequestBytes)\n } catch {\n observe({ kind: 'refusal', reason: 'invalid-request' })\n sendJsonIfOpen(response, 400, { error: 'request body must be bounded valid JSON' })\n return\n }\n if (\n !isRecord(body) ||\n !isExternalTextCandidate(body.candidate) ||\n typeof body.exampleId !== 'string'\n ) {\n observe({ kind: 'refusal', reason: 'invalid-request' })\n sendJsonIfOpen(response, 400, { error: 'candidate and exampleId are required strings' })\n return\n }\n const candidate = cloneCandidate(body.candidate)\n const candidateHash = contentHash({ kind: 'external-text-candidate', candidate })\n if (!proposedCandidateHashes.has(candidateHash)) {\n observe({ kind: 'proposal', candidate, candidateHash })\n proposedCandidateHashes.add(candidateHash)\n }\n const count = nextEvaluation()\n if (count === undefined) {\n observe({\n kind: 'refusal',\n reason: 'evaluation-limit',\n candidate,\n candidateHash,\n exampleId: body.exampleId,\n })\n sendJsonIfOpen(response, 429, { error: 'evaluation limit reached' })\n return\n }\n let result: TResponse\n try {\n result = await args.evaluate({ candidate, exampleId: body.exampleId }, signal)\n } catch (error) {\n observe({\n kind: 'refusal',\n reason: 'evaluation-failed',\n candidate,\n candidateHash,\n exampleId: body.exampleId,\n })\n // The thrown detail is the only diagnostic for a failed evaluation —\n // an opaque 500 costs the caller a blind debugging round.\n sendJsonIfOpen(response, 500, {\n error: `evaluation failed: ${String(error).slice(0, 400)}`,\n })\n return\n }\n let encoded: string\n try {\n encoded = encodeJsonResponse(result)\n } catch {\n observe({\n kind: 'refusal',\n reason: 'evaluation-failed',\n candidate,\n candidateHash,\n exampleId: body.exampleId,\n })\n sendJsonIfOpen(response, 500, { error: 'evaluation response must be valid JSON' })\n return\n }\n if (Buffer.byteLength(encoded) > limits.maxResponseBytes) {\n observe({\n kind: 'refusal',\n reason: 'evaluation-failed',\n candidate,\n candidateHash,\n exampleId: body.exampleId,\n })\n sendJsonIfOpen(response, 413, { error: 'evaluation response too large' })\n return\n }\n observe({\n kind: 'evaluation',\n candidate,\n candidateHash,\n exampleId: body.exampleId,\n evaluationNumber: count,\n response: structuredClone(result),\n })\n sendEncodedJsonIfOpen(response, 200, encoded)\n } catch {\n sendJsonIfOpen(response, 500, { error: 'evaluation failed' })\n }\n}\n\nasync function waitForActiveHandlers(activeHandlers: Set<Promise<void>>): Promise<void> {\n while (activeHandlers.size > 0) {\n await Promise.allSettled([...activeHandlers])\n }\n}\n\nfunction sendJsonIfOpen(response: ServerResponse, status: number, body: unknown): void {\n if (response.destroyed || response.writableEnded) return\n sendJson(response, status, body)\n}\n\nfunction readJson(request: IncomingMessage, maxRequestBytes: number): Promise<unknown> {\n return new Promise((resolvePromise, reject) => {\n let size = 0\n const chunks: Buffer[] = []\n request.on('data', (chunk: Buffer) => {\n size += chunk.length\n if (size > maxRequestBytes) {\n reject(new Error('callback body too large'))\n request.destroy()\n return\n }\n chunks.push(chunk)\n })\n request.on('error', reject)\n request.on('end', () => {\n try {\n resolvePromise(JSON.parse(Buffer.concat(chunks).toString('utf8')))\n } catch (error) {\n reject(error)\n }\n })\n })\n}\n\nfunction encodeJsonResponse(value: unknown): string {\n const encoded = JSON.stringify(value)\n if (encoded === undefined) throw new Error('evaluation response must be JSON-serializable')\n return encoded\n}\n\nfunction sendEncodedJsonIfOpen(response: ServerResponse, status: number, encoded: string): void {\n if (response.destroyed || response.writableEnded) return\n response.writeHead(status, {\n 'content-type': 'application/json; charset=utf-8',\n 'content-length': String(Buffer.byteLength(encoded)),\n })\n response.end(encoded)\n}\n\nfunction assertCallbackConfig(args: {\n token: string\n maxEvaluations: number\n acceptEvaluation?: () => number | undefined\n evaluate: (request: ExternalTextEvaluationRequest, signal: AbortSignal) => Promise<unknown>\n observe?: (observation: ExternalOptimizerEvaluationObservation) => void\n limits?: Partial<ExternalOptimizerCallbackLimits>\n signal?: AbortSignal\n}): void {\n if (typeof args.token !== 'string' || !args.token.trim()) {\n throw new Error('external optimizer callback: token must be non-empty')\n }\n if (!Number.isSafeInteger(args.maxEvaluations) || args.maxEvaluations <= 0) {\n throw new Error('external optimizer callback: maxEvaluations must be a positive safe integer')\n }\n if (args.acceptEvaluation !== undefined && typeof args.acceptEvaluation !== 'function') {\n throw new Error('external optimizer callback: acceptEvaluation must be a function')\n }\n if (typeof args.evaluate !== 'function') {\n throw new Error('external optimizer callback: evaluate must be a function')\n }\n if (args.observe !== undefined && typeof args.observe !== 'function') {\n throw new Error('external optimizer callback: observe must be a function')\n }\n}\n\nfunction cloneCandidate(candidate: ExternalTextCandidate): ExternalTextCandidate {\n return typeof candidate === 'string' ? candidate : { ...candidate }\n}\n"],"mappings":"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"}
|
package/dist/{external-optimizer-subprocess-BepyNwtj.js → external-optimizer-subprocess-CKNb42oM.js}
RENAMED
|
@@ -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, y as assertServedModelPolicy } from "./llm-client-
|
|
5
|
+
import { h as ModelSubstitutionError, v as assertServedModel, y as assertServedModelPolicy } from "./llm-client-Bg32RW0j.js";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { randomBytes } from "node:crypto";
|
|
8
8
|
import { isAbsolute, join, resolve } from "node:path";
|
|
@@ -255,14 +255,22 @@ function isRecord(value) {
|
|
|
255
255
|
* The `claude` CLI driven by GEPA agent engines speaks `POST /v1/messages`.
|
|
256
256
|
* Each request becomes one canonical `ExternalOptimizerChatRequest`, so the
|
|
257
257
|
* shared reservation, receipt, and execution-evidence pipeline applies
|
|
258
|
-
* unchanged.
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
258
|
+
* unchanged. Tool use translates in both directions: Anthropic `tools`,
|
|
259
|
+
* `tool_choice`, `tool_use`, and `tool_result` map onto the canonical
|
|
260
|
+
* function-tool contract, and a tool-calling response is synthesized back as
|
|
261
|
+
* the Anthropic stream shape the CLI expects.
|
|
262
262
|
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
263
|
+
* Anything without a full-fidelity mapping onto the canonical contract is
|
|
264
|
+
* refused with a loud Anthropic error envelope, with two exceptions:
|
|
265
|
+
*
|
|
266
|
+
* - `metadata` and `cache_control` are dropped silently: the CLI always sends
|
|
267
|
+
* both, neither has a canonical slot, and dropping them changes no content.
|
|
268
|
+
* - Claude-specific control fields (`thinking`, `context_management`,
|
|
269
|
+
* `output_config`, replayed reasoning blocks, and unknown tool-entry keys)
|
|
270
|
+
* are STRIPPED AND RECORDED. They carry no token-billing semantics on the
|
|
271
|
+
* owner wire, and the real CLI sends them on every request — refusing them
|
|
272
|
+
* makes every real CLI call impossible. The dropped names are returned in
|
|
273
|
+
* `strippedFields` so accounting discloses exactly what the shim removed.
|
|
266
274
|
*/
|
|
267
275
|
var AnthropicRequestRefusal = class extends Error {
|
|
268
276
|
status;
|
|
@@ -280,12 +288,16 @@ const REQUEST_FIELDS = /* @__PURE__ */ new Set([
|
|
|
280
288
|
"system",
|
|
281
289
|
"temperature",
|
|
282
290
|
"stream",
|
|
283
|
-
"metadata"
|
|
291
|
+
"metadata",
|
|
292
|
+
"tools",
|
|
293
|
+
"tool_choice"
|
|
294
|
+
]);
|
|
295
|
+
const STRIPPED_REQUEST_FIELDS = /* @__PURE__ */ new Set([
|
|
296
|
+
"thinking",
|
|
297
|
+
"context_management",
|
|
298
|
+
"output_config"
|
|
284
299
|
]);
|
|
285
300
|
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
301
|
top_p: "top_p has no canonical execution-owner slot",
|
|
290
302
|
top_k: "top_k has no canonical execution-owner slot",
|
|
291
303
|
stop_sequences: "stop_sequences has no canonical execution-owner slot"
|
|
@@ -298,8 +310,13 @@ function translateAnthropicMessagesRequest(raw, expectedModel, maxOutputTokensPe
|
|
|
298
310
|
throw refuse("request must be valid JSON");
|
|
299
311
|
}
|
|
300
312
|
if (!isRecord(parsed)) throw refuse("request must be a JSON object");
|
|
313
|
+
const stripped = /* @__PURE__ */ new Set();
|
|
301
314
|
for (const key of Object.keys(parsed)) {
|
|
302
315
|
if (REQUEST_FIELDS.has(key)) continue;
|
|
316
|
+
if (STRIPPED_REQUEST_FIELDS.has(key)) {
|
|
317
|
+
stripped.add(key);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
303
320
|
const reason = REFUSED_REQUEST_FIELDS[key];
|
|
304
321
|
throw refuse(reason ?? `request field '${key}' has no metered translation and is not supported`);
|
|
305
322
|
}
|
|
@@ -315,35 +332,183 @@ function translateAnthropicMessagesRequest(raw, expectedModel, maxOutputTokensPe
|
|
|
315
332
|
role: "system",
|
|
316
333
|
content: systemText
|
|
317
334
|
});
|
|
318
|
-
messages.push(...translateConversation(parsed.messages));
|
|
335
|
+
messages.push(...translateConversation(parsed.messages, stripped));
|
|
336
|
+
const tools = translateTools(parsed.tools, stripped);
|
|
337
|
+
const toolChoice = translateToolChoice(parsed.tool_choice, stripped);
|
|
319
338
|
return {
|
|
320
339
|
request: {
|
|
321
340
|
model: expectedModel,
|
|
322
341
|
messages,
|
|
323
342
|
maxTokens,
|
|
324
|
-
...parsed.temperature !== void 0 ? { temperature: parsed.temperature } : {}
|
|
343
|
+
...parsed.temperature !== void 0 ? { temperature: parsed.temperature } : {},
|
|
344
|
+
...tools === void 0 ? {} : { tools },
|
|
345
|
+
...toolChoice === void 0 ? {} : { toolChoice }
|
|
325
346
|
},
|
|
326
347
|
maxOutputTokens: maxTokens,
|
|
327
|
-
stream: parsed.stream === true
|
|
348
|
+
stream: parsed.stream === true,
|
|
349
|
+
strippedFields: [...stripped].sort()
|
|
328
350
|
};
|
|
329
351
|
}
|
|
352
|
+
const TOOL_ENTRY_FIELDS = /* @__PURE__ */ new Set([
|
|
353
|
+
"name",
|
|
354
|
+
"description",
|
|
355
|
+
"input_schema",
|
|
356
|
+
"type",
|
|
357
|
+
"cache_control"
|
|
358
|
+
]);
|
|
359
|
+
function translateTools(value, stripped) {
|
|
360
|
+
if (value === void 0) return void 0;
|
|
361
|
+
if (!Array.isArray(value) || value.length === 0) throw refuse("tools must be a non-empty array of tool definitions");
|
|
362
|
+
return value.map((entry, index) => {
|
|
363
|
+
if (!isRecord(entry)) throw refuse(`tools[${index}] must be an object`);
|
|
364
|
+
if (entry.type !== void 0 && entry.type !== "custom") throw refuse(`tools[${index}] type '${String(entry.type)}' has no metered translation; only client function tools are served`);
|
|
365
|
+
if (typeof entry.name !== "string" || !entry.name.trim()) throw refuse(`tools[${index}] requires a non-empty name`);
|
|
366
|
+
if (entry.description !== void 0 && typeof entry.description !== "string") throw refuse(`tools[${index}] description must be a string`);
|
|
367
|
+
if (!isRecord(entry.input_schema)) throw refuse(`tools[${index}] requires an input_schema object`);
|
|
368
|
+
for (const key of Object.keys(entry)) {
|
|
369
|
+
if (TOOL_ENTRY_FIELDS.has(key)) continue;
|
|
370
|
+
stripped.add(`tools.${key}`);
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
373
|
+
type: "function",
|
|
374
|
+
function: {
|
|
375
|
+
name: entry.name,
|
|
376
|
+
...entry.description === void 0 ? {} : { description: entry.description },
|
|
377
|
+
parameters: entry.input_schema
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
function translateToolChoice(value, stripped) {
|
|
383
|
+
if (value === void 0) return void 0;
|
|
384
|
+
if (!isRecord(value) || typeof value.type !== "string") throw refuse("tool_choice must be an object with a type");
|
|
385
|
+
for (const key of Object.keys(value)) {
|
|
386
|
+
if (key === "type") continue;
|
|
387
|
+
if (key === "name" && value.type === "tool") continue;
|
|
388
|
+
if (key === "disable_parallel_tool_use") {
|
|
389
|
+
stripped.add("tool_choice.disable_parallel_tool_use");
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
throw refuse(`tool_choice field '${key}' has no metered translation`);
|
|
393
|
+
}
|
|
394
|
+
if (value.type === "auto") return "auto";
|
|
395
|
+
if (value.type === "none") return "none";
|
|
396
|
+
if (value.type === "any") return "required";
|
|
397
|
+
if (value.type === "tool") {
|
|
398
|
+
if (typeof value.name !== "string" || !value.name.trim()) throw refuse("tool_choice type tool requires a tool name");
|
|
399
|
+
return {
|
|
400
|
+
type: "function",
|
|
401
|
+
function: { name: value.name }
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
throw refuse(`tool_choice type '${String(value.type)}' has no metered translation`);
|
|
405
|
+
}
|
|
330
406
|
function translateSystem(value) {
|
|
331
407
|
if (value === void 0) return void 0;
|
|
332
408
|
if (typeof value === "string") return value;
|
|
333
409
|
if (!Array.isArray(value)) throw refuse("system must be a string or an array of text blocks");
|
|
334
410
|
return joinTextBlocks(value, "system");
|
|
335
411
|
}
|
|
336
|
-
function translateConversation(value) {
|
|
412
|
+
function translateConversation(value, stripped) {
|
|
337
413
|
if (!Array.isArray(value) || value.length === 0) throw refuse("request requires a non-empty messages array");
|
|
338
|
-
|
|
414
|
+
const out = [];
|
|
415
|
+
for (const entry of value) {
|
|
339
416
|
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
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
417
|
+
if (entry.role !== "system" && entry.role !== "user" && entry.role !== "assistant") throw refuse(`message role must be 'system', 'user', or 'assistant'`);
|
|
418
|
+
if (typeof entry.content === "string") {
|
|
419
|
+
out.push({
|
|
420
|
+
role: entry.role,
|
|
421
|
+
content: entry.content
|
|
422
|
+
});
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (!Array.isArray(entry.content) || entry.content.length === 0) throw refuse(`${entry.role} message content must be a string or a non-empty block array`);
|
|
426
|
+
if (entry.role === "system") out.push({
|
|
427
|
+
role: "system",
|
|
428
|
+
content: joinTextBlocks(entry.content, "system")
|
|
429
|
+
});
|
|
430
|
+
else if (entry.role === "assistant") out.push(translateAssistantBlocks(entry.content, stripped));
|
|
431
|
+
else out.push(...translateUserBlocks(entry.content, stripped));
|
|
432
|
+
}
|
|
433
|
+
return out;
|
|
434
|
+
}
|
|
435
|
+
function translateAssistantBlocks(blocks, stripped) {
|
|
436
|
+
const parts = [];
|
|
437
|
+
const toolCalls = [];
|
|
438
|
+
for (const block of blocks) {
|
|
439
|
+
if (!isRecord(block)) throw refuse("assistant message content blocks must be objects");
|
|
440
|
+
if (block.type === "text") {
|
|
441
|
+
if (typeof block.text !== "string") throw refuse("assistant message text blocks require string text");
|
|
442
|
+
parts.push(block.text);
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
if (block.type === "tool_use") {
|
|
446
|
+
if (typeof block.id !== "string" || !block.id.trim()) throw refuse("assistant tool_use blocks require a string id");
|
|
447
|
+
if (typeof block.name !== "string" || !block.name.trim()) throw refuse("assistant tool_use blocks require a string name");
|
|
448
|
+
if (!isRecord(block.input)) throw refuse("assistant tool_use blocks require an input object");
|
|
449
|
+
toolCalls.push({
|
|
450
|
+
id: block.id,
|
|
451
|
+
name: block.name,
|
|
452
|
+
argumentsJson: JSON.stringify(block.input)
|
|
453
|
+
});
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
if (block.type === "thinking" || block.type === "redacted_thinking") {
|
|
457
|
+
stripped.add(`messages.${String(block.type)}`);
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
throw refuse(`assistant message content block type '${String(block.type)}' has no metered translation`);
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
role: "assistant",
|
|
464
|
+
content: parts.join("\n\n"),
|
|
465
|
+
...toolCalls.length > 0 ? { toolCalls } : {}
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function translateUserBlocks(blocks, stripped) {
|
|
469
|
+
const out = [];
|
|
470
|
+
const textParts = [];
|
|
471
|
+
const flushText = () => {
|
|
472
|
+
if (textParts.length === 0) return;
|
|
473
|
+
out.push({
|
|
474
|
+
role: "user",
|
|
475
|
+
content: textParts.join("\n\n")
|
|
476
|
+
});
|
|
477
|
+
textParts.length = 0;
|
|
478
|
+
};
|
|
479
|
+
for (const block of blocks) {
|
|
480
|
+
if (!isRecord(block)) throw refuse("user message content blocks must be objects");
|
|
481
|
+
if (block.type === "text") {
|
|
482
|
+
if (typeof block.text !== "string") throw refuse("user message text blocks require string text");
|
|
483
|
+
textParts.push(block.text);
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
if (block.type === "tool_result") {
|
|
487
|
+
if (typeof block.tool_use_id !== "string" || !block.tool_use_id.trim()) throw refuse("user tool_result blocks require a string tool_use_id");
|
|
488
|
+
if (block.is_error === true) stripped.add("messages.tool_result.is_error");
|
|
489
|
+
flushText();
|
|
490
|
+
out.push({
|
|
491
|
+
role: "tool",
|
|
492
|
+
content: toolResultText(block.content),
|
|
493
|
+
toolCallId: block.tool_use_id
|
|
494
|
+
});
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
throw refuse(`user message content block type '${String(block.type)}' has no metered translation`);
|
|
498
|
+
}
|
|
499
|
+
flushText();
|
|
500
|
+
return out;
|
|
501
|
+
}
|
|
502
|
+
function toolResultText(value) {
|
|
503
|
+
if (value === void 0) return "";
|
|
504
|
+
if (typeof value === "string") return value;
|
|
505
|
+
if (!Array.isArray(value)) throw refuse("user tool_result content must be a string or a block array");
|
|
506
|
+
const parts = [];
|
|
507
|
+
for (const block of value) {
|
|
508
|
+
if (!isRecord(block) || block.type !== "text" || typeof block.text !== "string") throw refuse("user tool_result content blocks must be text blocks");
|
|
509
|
+
parts.push(block.text);
|
|
510
|
+
}
|
|
511
|
+
return parts.join("\n\n");
|
|
347
512
|
}
|
|
348
513
|
function joinTextBlocks(blocks, label) {
|
|
349
514
|
const parts = [];
|
|
@@ -354,7 +519,6 @@ function joinTextBlocks(blocks, label) {
|
|
|
354
519
|
parts.push(block.text);
|
|
355
520
|
continue;
|
|
356
521
|
}
|
|
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
522
|
throw refuse(`${label} content block type '${String(block.type)}' has no metered translation`);
|
|
359
523
|
}
|
|
360
524
|
return parts.join("\n\n");
|
|
@@ -362,7 +526,9 @@ function joinTextBlocks(blocks, label) {
|
|
|
362
526
|
const STOP_REASON_BY_FINISH_REASON = {
|
|
363
527
|
stop: "end_turn",
|
|
364
528
|
length: "max_tokens",
|
|
365
|
-
content_filter: "refusal"
|
|
529
|
+
content_filter: "refusal",
|
|
530
|
+
tool_calls: "tool_use",
|
|
531
|
+
tool_use: "tool_use"
|
|
366
532
|
};
|
|
367
533
|
/**
|
|
368
534
|
* Encode one canonical owner response as an Anthropic message.
|
|
@@ -370,7 +536,8 @@ const STOP_REASON_BY_FINISH_REASON = {
|
|
|
370
536
|
* Usage fields are display-level for the CLI; the ledger receipt reads the
|
|
371
537
|
* same canonical response through the proxy's strict receipt checks. An
|
|
372
538
|
* uncaptured usage renders as zeros here while the receipt keeps the honest
|
|
373
|
-
* unknown-cost maximum.
|
|
539
|
+
* unknown-cost maximum. Canonical tool calls become `tool_use` content blocks
|
|
540
|
+
* after the text block, matching Anthropic block ordering.
|
|
374
541
|
*/
|
|
375
542
|
function encodeAnthropicMessage(response, callId) {
|
|
376
543
|
const finishReason = response.finishReason ?? "stop";
|
|
@@ -378,15 +545,23 @@ function encodeAnthropicMessage(response, callId) {
|
|
|
378
545
|
if (stopReason === void 0) throw new AnthropicRequestRefusal(502, "api_error", `optimizer model finish reason '${String(finishReason)}' has no Anthropic translation`);
|
|
379
546
|
const captured = response.usage.captured !== false;
|
|
380
547
|
const cachedTokens = response.usage.cachedPromptTokens ?? 0;
|
|
548
|
+
const content = [];
|
|
549
|
+
if (response.content.length > 0) content.push({
|
|
550
|
+
type: "text",
|
|
551
|
+
text: response.content
|
|
552
|
+
});
|
|
553
|
+
for (const call of response.toolCalls ?? []) content.push({
|
|
554
|
+
type: "tool_use",
|
|
555
|
+
id: call.id,
|
|
556
|
+
name: call.name,
|
|
557
|
+
input: parseToolInput(call)
|
|
558
|
+
});
|
|
381
559
|
return {
|
|
382
560
|
id: `msg_${callId}`,
|
|
383
561
|
type: "message",
|
|
384
562
|
role: "assistant",
|
|
385
563
|
model: response.model,
|
|
386
|
-
content
|
|
387
|
-
type: "text",
|
|
388
|
-
text: response.content
|
|
389
|
-
}] : [],
|
|
564
|
+
content,
|
|
390
565
|
stop_reason: stopReason,
|
|
391
566
|
stop_sequence: null,
|
|
392
567
|
usage: captured ? {
|
|
@@ -400,12 +575,26 @@ function encodeAnthropicMessage(response, callId) {
|
|
|
400
575
|
}
|
|
401
576
|
};
|
|
402
577
|
}
|
|
578
|
+
function parseToolInput(call) {
|
|
579
|
+
const text = call.argumentsJson.trim();
|
|
580
|
+
if (text === "") return {};
|
|
581
|
+
let parsed;
|
|
582
|
+
try {
|
|
583
|
+
parsed = JSON.parse(text);
|
|
584
|
+
} catch {
|
|
585
|
+
throw new AnthropicRequestRefusal(502, "api_error", `optimizer model tool call '${call.name}' returned arguments that are not valid JSON`);
|
|
586
|
+
}
|
|
587
|
+
if (!isRecord(parsed)) throw new AnthropicRequestRefusal(502, "api_error", `optimizer model tool call '${call.name}' arguments must encode a JSON object`);
|
|
588
|
+
return parsed;
|
|
589
|
+
}
|
|
403
590
|
/**
|
|
404
591
|
* Render one completed Anthropic message as the SSE stream the CLI expects.
|
|
405
592
|
*
|
|
406
593
|
* The whole stream is synthesized after the owner call completes, so the
|
|
407
594
|
* 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.
|
|
595
|
+
* rejected with a real status instead of a half-delivered stream. A
|
|
596
|
+
* `tool_use` block streams as an empty-input `content_block_start` plus one
|
|
597
|
+
* `input_json_delta` carrying the complete arguments JSON.
|
|
409
598
|
*/
|
|
410
599
|
function renderAnthropicSseStream(message) {
|
|
411
600
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
@@ -426,15 +615,47 @@ function renderAnthropicSseStream(message) {
|
|
|
426
615
|
}
|
|
427
616
|
}];
|
|
428
617
|
for (const [index, block] of content.entries()) {
|
|
429
|
-
if (!isRecord(block)
|
|
430
|
-
|
|
618
|
+
if (!isRecord(block)) continue;
|
|
619
|
+
if (block.type === "text") {
|
|
620
|
+
events.push({
|
|
621
|
+
event: "content_block_start",
|
|
622
|
+
data: {
|
|
623
|
+
type: "content_block_start",
|
|
624
|
+
index,
|
|
625
|
+
content_block: {
|
|
626
|
+
type: "text",
|
|
627
|
+
text: ""
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}, {
|
|
631
|
+
event: "content_block_delta",
|
|
632
|
+
data: {
|
|
633
|
+
type: "content_block_delta",
|
|
634
|
+
index,
|
|
635
|
+
delta: {
|
|
636
|
+
type: "text_delta",
|
|
637
|
+
text: block.text
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}, {
|
|
641
|
+
event: "content_block_stop",
|
|
642
|
+
data: {
|
|
643
|
+
type: "content_block_stop",
|
|
644
|
+
index
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
if (block.type === "tool_use") events.push({
|
|
431
650
|
event: "content_block_start",
|
|
432
651
|
data: {
|
|
433
652
|
type: "content_block_start",
|
|
434
653
|
index,
|
|
435
654
|
content_block: {
|
|
436
|
-
type: "
|
|
437
|
-
|
|
655
|
+
type: "tool_use",
|
|
656
|
+
id: block.id,
|
|
657
|
+
name: block.name,
|
|
658
|
+
input: {}
|
|
438
659
|
}
|
|
439
660
|
}
|
|
440
661
|
}, {
|
|
@@ -443,8 +664,8 @@ function renderAnthropicSseStream(message) {
|
|
|
443
664
|
type: "content_block_delta",
|
|
444
665
|
index,
|
|
445
666
|
delta: {
|
|
446
|
-
type: "
|
|
447
|
-
|
|
667
|
+
type: "input_json_delta",
|
|
668
|
+
partial_json: JSON.stringify(block.input)
|
|
448
669
|
}
|
|
449
670
|
}
|
|
450
671
|
}, {
|
|
@@ -492,9 +713,6 @@ function anthropicErrorTypeForStatus(status) {
|
|
|
492
713
|
function refuse(message) {
|
|
493
714
|
return new AnthropicRequestRefusal(400, "invalid_request_error", message);
|
|
494
715
|
}
|
|
495
|
-
function refuseValue(message) {
|
|
496
|
-
throw refuse(message);
|
|
497
|
-
}
|
|
498
716
|
//#endregion
|
|
499
717
|
//#region src/campaign/external-optimizer-http.ts
|
|
500
718
|
function listenLocal(server) {
|
|
@@ -710,7 +928,8 @@ async function handleModelProxyRequest(args) {
|
|
|
710
928
|
try {
|
|
711
929
|
const requestTags = wire === "anthropic" ? {
|
|
712
930
|
...args.args.tags ?? {},
|
|
713
|
-
wire: "anthropic"
|
|
931
|
+
wire: "anthropic",
|
|
932
|
+
...parsed.strippedFields === void 0 ? {} : { strippedFields: parsed.strippedFields.join(",") }
|
|
714
933
|
} : args.args.tags;
|
|
715
934
|
const paid = await args.args.costLedger.runPaidCall({
|
|
716
935
|
...args.args.callId ? { callId: args.args.callId } : {},
|
|
@@ -928,6 +1147,7 @@ function parseModelProxyRequest(body, path, expectedModel, budget) {
|
|
|
928
1147
|
return {
|
|
929
1148
|
maxOutputTokens: translated.maxOutputTokens,
|
|
930
1149
|
stream: translated.stream,
|
|
1150
|
+
...translated.strippedFields.length > 0 ? { strippedFields: translated.strippedFields } : {},
|
|
931
1151
|
request: freezeJsonSnapshot(translated.request, "optimizer model canonical request")
|
|
932
1152
|
};
|
|
933
1153
|
}
|
|
@@ -1190,6 +1410,7 @@ function snapshotChatResponse(value, expectedModel, allowWithinFamily) {
|
|
|
1190
1410
|
if (!isNonnegativeFiniteNumber(value.durationMs)) throw new OwnerModelContractError("optimizer model callback response durationMs must be finite and non-negative");
|
|
1191
1411
|
if (value.costUsd !== null && !isNonnegativeFiniteNumber(value.costUsd)) throw new OwnerModelContractError("optimizer model callback response costUsd must be null or finite and non-negative");
|
|
1192
1412
|
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");
|
|
1413
|
+
const toolCalls = snapshotResponseToolCalls(value.toolCalls);
|
|
1193
1414
|
if (value.contentEmpty !== void 0 && (typeof value.contentEmpty !== "boolean" || value.contentEmpty !== (value.content.trim().length === 0))) throw new OwnerModelContractError("optimizer model callback response contentEmpty must match content");
|
|
1194
1415
|
if (!isRecord(value.raw)) throw new OwnerModelContractError("optimizer model callback response raw must be an object");
|
|
1195
1416
|
if (!isRecord(value.usage)) throw new OwnerModelContractError("optimizer model callback response usage must be an object");
|
|
@@ -1214,6 +1435,7 @@ function snapshotChatResponse(value, expectedModel, allowWithinFamily) {
|
|
|
1214
1435
|
}
|
|
1215
1436
|
return deepFreezeJson({
|
|
1216
1437
|
content: value.content,
|
|
1438
|
+
...toolCalls === void 0 ? {} : { toolCalls },
|
|
1217
1439
|
usage: {
|
|
1218
1440
|
promptTokens,
|
|
1219
1441
|
completionTokens,
|
|
@@ -1230,6 +1452,19 @@ function snapshotChatResponse(value, expectedModel, allowWithinFamily) {
|
|
|
1230
1452
|
raw
|
|
1231
1453
|
});
|
|
1232
1454
|
}
|
|
1455
|
+
/** Snapshot the owner's canonical tool calls onto the allowlisted response. */
|
|
1456
|
+
function snapshotResponseToolCalls(value) {
|
|
1457
|
+
if (value === void 0) return void 0;
|
|
1458
|
+
if (!Array.isArray(value) || value.length === 0) throw new OwnerModelContractError("optimizer model callback response toolCalls must be a non-empty array when present");
|
|
1459
|
+
return value.map((entry) => {
|
|
1460
|
+
if (!isRecord(entry) || typeof entry.id !== "string" || !entry.id || typeof entry.name !== "string" || !entry.name || typeof entry.argumentsJson !== "string") throw new OwnerModelContractError("optimizer model callback response toolCalls entries require string id, name, and argumentsJson");
|
|
1461
|
+
return {
|
|
1462
|
+
id: entry.id,
|
|
1463
|
+
name: entry.name,
|
|
1464
|
+
argumentsJson: entry.argumentsJson
|
|
1465
|
+
};
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1233
1468
|
function encodeAnthropicResponseBody(args) {
|
|
1234
1469
|
const message = encodeAnthropicMessage(args.response, args.callId);
|
|
1235
1470
|
const body = new TextEncoder().encode(args.stream ? renderAnthropicSseStream(message) : JSON.stringify(message));
|
|
@@ -1666,4 +1901,4 @@ function summarizeProcessOutput(capture, max = 4e3) {
|
|
|
1666
1901
|
//#endregion
|
|
1667
1902
|
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 };
|
|
1668
1903
|
|
|
1669
|
-
//# sourceMappingURL=external-optimizer-subprocess-
|
|
1904
|
+
//# sourceMappingURL=external-optimizer-subprocess-CKNb42oM.js.map
|