@tangle-network/agent-eval 0.215.0 → 0.216.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.
- package/CHANGELOG.md +16 -0
- package/dist/adapters/http.d.ts +3 -3
- package/dist/{agent-profile-BGyt2z8U.d.ts → agent-profile-CIA2Atnt.d.ts} +5 -5
- package/dist/{agent-profile-BGyt2z8U.d.ts.map → agent-profile-CIA2Atnt.d.ts.map} +1 -1
- package/dist/agreement-irr-DsTURxdr.js +186 -0
- package/dist/agreement-irr-DsTURxdr.js.map +1 -0
- package/dist/analyst/index.d.ts +487 -14
- package/dist/analyst/index.d.ts.map +1 -1
- package/dist/analyst/index.js +1545 -10
- package/dist/analyst/index.js.map +1 -1
- package/dist/{analyze-runs-BlslTkOJ.d.ts → analyze-runs-CSDAh8A7.d.ts} +4 -4
- package/dist/{analyze-runs-BlslTkOJ.d.ts.map → analyze-runs-CSDAh8A7.d.ts.map} +1 -1
- package/dist/{benchmark-command-CMmeV-_Z.js → benchmark-command-CByl9dxG.js} +18 -34
- package/dist/benchmark-command-CByl9dxG.js.map +1 -0
- package/dist/benchmarks/index.d.ts +5 -5
- package/dist/benchmarks/index.js +3 -3
- package/dist/campaign/index.d.ts +7 -7
- package/dist/campaign/index.js +5 -5
- package/dist/{campaign-Cuw3DQKF.js → campaign-BQ-Jujuj.js} +7 -7
- package/dist/{campaign-Cuw3DQKF.js.map → campaign-BQ-Jujuj.js.map} +1 -1
- package/dist/{chat-client-Bnx8NZe4.js → chat-client-DthPOYGd.js} +2 -2
- package/dist/{chat-client-Bnx8NZe4.js.map → chat-client-DthPOYGd.js.map} +1 -1
- package/dist/{chat-json-call-CZI1NoGZ.js → chat-json-call-BrKsyspg.js} +2 -2
- package/dist/{chat-json-call-CZI1NoGZ.js.map → chat-json-call-BrKsyspg.js.map} +1 -1
- package/dist/cli.js +4 -4
- package/dist/cluster-bootstrap-B3JywQQn.js +66 -0
- package/dist/cluster-bootstrap-B3JywQQn.js.map +1 -0
- package/dist/contract/index.d.ts +11 -11
- package/dist/contract/index.js +8 -8
- package/dist/{cost-ledger-DbQdN3nO.d.ts → cost-ledger-CO7UyEcQ.d.ts} +3 -8
- package/dist/{cost-ledger-DbQdN3nO.d.ts.map → cost-ledger-CO7UyEcQ.d.ts.map} +1 -1
- package/dist/{cost-ledger-DFOeLeJi.js → cost-ledger-D2FnJPBF.js} +27 -23
- package/dist/cost-ledger-D2FnJPBF.js.map +1 -0
- package/dist/{default-registry-BAiTePcB.d.ts → default-registry-BrWh62zf.d.ts} +6 -6
- package/dist/{default-registry-BAiTePcB.d.ts.map → default-registry-BrWh62zf.d.ts.map} +1 -1
- package/dist/{default-registry-BhYGMVbV.js → default-registry-Cn1LQ2Ve.js} +742 -757
- package/dist/default-registry-Cn1LQ2Ve.js.map +1 -0
- package/dist/{define-agent-eval-CZdBPUkk.d.ts → define-agent-eval-CEWkX5wZ.d.ts} +8 -8
- package/dist/{define-agent-eval-CZdBPUkk.d.ts.map → define-agent-eval-CEWkX5wZ.d.ts.map} +1 -1
- package/dist/{define-agent-eval-FGtpqnh4.js → define-agent-eval-Ch3MWX6k.js} +3 -3
- package/dist/{define-agent-eval-FGtpqnh4.js.map → define-agent-eval-Ch3MWX6k.js.map} +1 -1
- package/dist/diagnosis/index.d.ts +2 -2
- package/dist/{dspy-rlm-engine-OMBXVN0u.js → dspy-rlm-engine-D-KX_e64.js} +3 -3
- package/dist/{dspy-rlm-engine-OMBXVN0u.js.map → dspy-rlm-engine-D-KX_e64.js.map} +1 -1
- package/dist/{engine-BzOR5Ygn.d.ts → engine-C_zNWfM8.d.ts} +5 -5
- package/dist/{engine-BzOR5Ygn.d.ts.map → engine-C_zNWfM8.d.ts.map} +1 -1
- package/dist/evaluation-Cn2dRZIz.d.ts +75 -0
- package/dist/evaluation-Cn2dRZIz.d.ts.map +1 -0
- package/dist/evaluation.d.ts +2 -74
- package/dist/evaluation.js +1 -1
- package/dist/{exact-types-DjhfZglz.d.ts → exact-types-CD0lf5Qd.d.ts} +2 -2
- package/dist/{exact-types-DjhfZglz.d.ts.map → exact-types-CD0lf5Qd.d.ts.map} +1 -1
- package/dist/experiment/index.d.ts +1 -1
- package/dist/{external-optimizer-process-DCZx5PdG.js → external-optimizer-process-nnsY5D_4.js} +2 -2
- package/dist/{external-optimizer-process-DCZx5PdG.js.map → external-optimizer-process-nnsY5D_4.js.map} +1 -1
- package/dist/{external-optimizer-subprocess-CA3aXT8I.js → external-optimizer-subprocess-DcerlZSG.js} +263 -4
- package/dist/external-optimizer-subprocess-DcerlZSG.js.map +1 -0
- package/dist/{feedback-trajectory-CX01700m.d.ts → feedback-trajectory-DkaDj0dj.d.ts} +3 -3
- package/dist/{feedback-trajectory-CX01700m.d.ts.map → feedback-trajectory-DkaDj0dj.d.ts.map} +1 -1
- package/dist/fuzz.d.ts +1 -1
- package/dist/fuzz.js +1 -1
- package/dist/{index-D0_l25We.d.ts → index-CPJJr98o.d.ts} +2 -2
- package/dist/{index-D0_l25We.d.ts.map → index-CPJJr98o.d.ts.map} +1 -1
- package/dist/{index-DNgf5gyG.d.ts → index-Ch9VxCpK.d.ts} +2 -2
- package/dist/{index-DNgf5gyG.d.ts.map → index-Ch9VxCpK.d.ts.map} +1 -1
- package/dist/{index-BTwxP7L0.d.ts → index-DlH0Y9gI.d.ts} +10 -10
- package/dist/{index-BTwxP7L0.d.ts.map → index-DlH0Y9gI.d.ts.map} +1 -1
- package/dist/{index-D_rfXep9.d.ts → index-NvGBiO8H.d.ts} +10 -10
- package/dist/{index-D_rfXep9.d.ts.map → index-NvGBiO8H.d.ts.map} +1 -1
- package/dist/{index-BWiAAWrk.d.ts → index-oQ4aHziK.d.ts} +9 -9
- package/dist/{index-BWiAAWrk.d.ts.map → index-oQ4aHziK.d.ts.map} +1 -1
- package/dist/index.d.ts +25 -25
- package/dist/index.js +16 -15
- package/dist/index.js.map +1 -1
- package/dist/{integrity-zloavsM_.d.ts → integrity-CCAxGVK6.d.ts} +2 -2
- package/dist/{integrity-zloavsM_.d.ts.map → integrity-CCAxGVK6.d.ts.map} +1 -1
- package/dist/{judge-gate-B1_O0QIs.d.ts → judge-gate-DNcUrRFA.d.ts} +2 -2
- package/dist/{judge-gate-B1_O0QIs.d.ts.map → judge-gate-DNcUrRFA.d.ts.map} +1 -1
- package/dist/{kind-factory-CMlOx08-.js → kind-factory-CbZ-6tvu.js} +3 -3
- package/dist/{kind-factory-CMlOx08-.js.map → kind-factory-CbZ-6tvu.js.map} +1 -1
- package/dist/{llm-client-C_o62yG1.js → llm-client-DQveoxSM.js} +2 -2
- package/dist/{llm-client-C_o62yG1.js.map → llm-client-DQveoxSM.js.map} +1 -1
- package/dist/{llm-judge-Dw0rDpve.js → llm-judge-PXV7Xljl.js} +5 -5
- package/dist/{llm-judge-Dw0rDpve.js.map → llm-judge-PXV7Xljl.js.map} +1 -1
- package/dist/matrix/index.d.ts +2 -2
- package/dist/{matrix-CQwid3kj.d.ts → matrix-CHMiebFx.d.ts} +4 -4
- package/dist/{matrix-CQwid3kj.d.ts.map → matrix-CHMiebFx.d.ts.map} +1 -1
- package/dist/meta-eval/index.d.ts +2 -2
- package/dist/meta-eval/index.js +1 -62
- package/dist/meta-eval/index.js.map +1 -1
- package/dist/{mint-CDy-brUR.d.ts → mint-DCGoBYhB.d.ts} +2 -2
- package/dist/{mint-CDy-brUR.d.ts.map → mint-DCGoBYhB.d.ts.map} +1 -1
- 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/{prime-protocol-B7et3tI7.d.ts → prime-protocol-CEE4BR9N.d.ts} +3 -3
- package/dist/{prime-protocol-B7et3tI7.d.ts.map → prime-protocol-CEE4BR9N.d.ts.map} +1 -1
- package/dist/{produced-state-C1cTYK6w.js → produced-state-ZCtDBxAE.js} +4 -4
- package/dist/{produced-state-C1cTYK6w.js.map → produced-state-ZCtDBxAE.js.map} +1 -1
- package/dist/{proposal-findings-CXblsskH.js → proposal-findings-BZiMTzAF.js} +2 -36
- package/dist/proposal-findings-BZiMTzAF.js.map +1 -0
- package/dist/{registry-BL7H33tj.d.ts → registry-JYfPxcVz.d.ts} +11 -13
- package/dist/registry-JYfPxcVz.d.ts.map +1 -0
- package/dist/{release-confidence-C4kI7lQi.d.ts → release-confidence-BK2QCB5M.d.ts} +3 -3
- package/dist/{release-confidence-C4kI7lQi.d.ts.map → release-confidence-BK2QCB5M.d.ts.map} +1 -1
- package/dist/{report-command-CtIIsQKP.js → report-command-y8H6RSX8.js} +23 -7
- package/dist/report-command-y8H6RSX8.js.map +1 -0
- package/dist/reporting.d.ts +3 -3
- package/dist/{researcher-DuIJ9IaA.d.ts → researcher-f6mZhh58.d.ts} +5 -5
- package/dist/{researcher-DuIJ9IaA.d.ts.map → researcher-f6mZhh58.d.ts.map} +1 -1
- package/dist/{reward-hacking-C6Kp54Av.d.ts → reward-hacking-DHA7c1l8.d.ts} +2 -2
- package/dist/{reward-hacking-C6Kp54Av.d.ts.map → reward-hacking-DHA7c1l8.d.ts.map} +1 -1
- package/dist/rl.d.ts +8 -8
- package/dist/rl.js +1 -1
- package/dist/rollout/index.d.ts +2 -2
- package/dist/{rubric-predictive-validity-BiNNEjpG.d.ts → rubric-predictive-validity-2SgHxoa5.d.ts} +2 -2
- package/dist/{rubric-predictive-validity-BiNNEjpG.d.ts.map → rubric-predictive-validity-2SgHxoa5.d.ts.map} +1 -1
- package/dist/{run-record-p2AYTrCn.d.ts → run-record-D1Hw-Jml.d.ts} +2 -2
- package/dist/{run-record-p2AYTrCn.d.ts.map → run-record-D1Hw-Jml.d.ts.map} +1 -1
- package/dist/search/index.d.ts +2 -2
- package/dist/{search-kernel-BWp7hfJn.d.ts → search-kernel-CpcKH5Ih.d.ts} +4 -4
- package/dist/{search-kernel-BWp7hfJn.d.ts.map → search-kernel-CpcKH5Ih.d.ts.map} +1 -1
- package/dist/{semantic-concept-judge-sDBNg_mI.js → semantic-concept-judge-zQBG9hHU.js} +6 -6
- package/dist/{semantic-concept-judge-sDBNg_mI.js.map → semantic-concept-judge-zQBG9hHU.js.map} +1 -1
- package/dist/{server-BRpadnVt.js → server-DbcC7xMl.js} +3 -3
- package/dist/{server-BRpadnVt.js.map → server-DbcC7xMl.js.map} +1 -1
- package/dist/{skillopt-optimization-method-CvaFGm95.js → skillopt-optimization-method-CCtzralq.js} +5 -5
- package/dist/{skillopt-optimization-method-CvaFGm95.js.map → skillopt-optimization-method-CCtzralq.js.map} +1 -1
- package/dist/{statistical-heldout-DZaTo3gZ.d.ts → statistical-heldout-BeR4bOdg.d.ts} +3 -3
- package/dist/{statistical-heldout-DZaTo3gZ.d.ts.map → statistical-heldout-BeR4bOdg.d.ts.map} +1 -1
- package/dist/{store-otlp-0AiS29kg.js → store-otlp-DtB4Slsr.js} +2 -2
- package/dist/{store-otlp-0AiS29kg.js.map → store-otlp-DtB4Slsr.js.map} +1 -1
- package/dist/{store-tool-spans-DuvZ4YWb.d.ts → store-tool-spans-DR1SrCLK.d.ts} +7 -7
- package/dist/{store-tool-spans-DuvZ4YWb.d.ts.map → store-tool-spans-DR1SrCLK.d.ts.map} +1 -1
- package/dist/{store-tool-spans-BAcBBSM8.js → store-tool-spans-fZIigrpP.js} +3 -3
- package/dist/{store-tool-spans-BAcBBSM8.js.map → store-tool-spans-fZIigrpP.js.map} +1 -1
- package/dist/{summary-report-CK0ueoJd.d.ts → summary-report-C-CtItCN.d.ts} +2 -2
- package/dist/{summary-report-CK0ueoJd.d.ts.map → summary-report-C-CtItCN.d.ts.map} +1 -1
- package/dist/supervisor-run/index.d.ts +1 -1
- package/dist/supervisor-run/index.d.ts.map +1 -1
- package/dist/supervisor-run/index.js +1 -1
- package/dist/systemone-CpTLAlqb.d.ts +135 -0
- package/dist/systemone-CpTLAlqb.d.ts.map +1 -0
- package/dist/{systemone-review-DTClwgmE.js → systemone-review-BZWfrtq1.js} +2 -2
- package/dist/{systemone-review-DTClwgmE.js.map → systemone-review-BZWfrtq1.js.map} +1 -1
- package/dist/{systemone-review-BReh3mvm.d.ts → systemone-review-B_yo1xsd.d.ts} +2 -2
- package/dist/{systemone-review-BReh3mvm.d.ts.map → systemone-review-B_yo1xsd.d.ts.map} +1 -1
- package/dist/systemone.d.ts +4 -134
- package/dist/systemone.js +1 -1
- package/dist/terminal-record-BHocwFw1.js.map +1 -1
- package/dist/{tool-groups-DOriCbkR.d.ts → tool-groups-mcxOXVlb.d.ts} +3 -3
- package/dist/tool-groups-mcxOXVlb.d.ts.map +1 -0
- package/dist/{tool-waste-BYt5rdt8.js → tool-waste-D52PqU9Q.js} +3 -156
- package/dist/tool-waste-D52PqU9Q.js.map +1 -0
- package/dist/{trace-questions-RCaYLkwH.d.ts → trace-questions-DG7nqLQ7.d.ts} +4 -4
- package/dist/{trace-questions-RCaYLkwH.d.ts.map → trace-questions-DG7nqLQ7.d.ts.map} +1 -1
- package/dist/trace-repair/index.d.ts +3 -3
- package/dist/traces.d.ts +6 -6
- package/dist/traces.js +3 -3
- package/dist/{types-CMnPcbW5.d.ts → types-B4C5nFw6.d.ts} +6 -6
- package/dist/{types-CMnPcbW5.d.ts.map → types-B4C5nFw6.d.ts.map} +1 -1
- package/dist/{types-DjOMwGY_.d.ts → types-BqkVmHfv.d.ts} +7 -1
- package/dist/types-BqkVmHfv.d.ts.map +1 -0
- package/dist/{types-Dj0Qv2qi.d.ts → types-CfZMIzB_.d.ts} +3 -3
- package/dist/{types-Dj0Qv2qi.d.ts.map → types-CfZMIzB_.d.ts.map} +1 -1
- package/dist/{types-Lub7o6bN.d.ts → types-DBZ7keha.d.ts} +2 -2
- package/dist/{types-Lub7o6bN.d.ts.map → types-DBZ7keha.d.ts.map} +1 -1
- package/dist/{types-CW5b90JH.js → types-IXD2HJzu.js} +16 -3
- package/dist/types-IXD2HJzu.js.map +1 -0
- package/dist/{types-KEkKkETv.d.ts → types-oKRhQAde.d.ts} +347 -6
- package/dist/types-oKRhQAde.d.ts.map +1 -0
- package/dist/wire/index.d.ts +3 -3
- package/dist/wire/index.js +1 -1
- package/docs/campaign-proposers.md +3 -0
- package/docs/concepts.md +1 -0
- package/docs/finding-layers.md +69 -0
- package/package.json +1 -1
- package/dist/benchmark-command-CMmeV-_Z.js.map +0 -1
- package/dist/cost-ledger-DFOeLeJi.js.map +0 -1
- package/dist/default-registry-BhYGMVbV.js.map +0 -1
- package/dist/evaluation.d.ts.map +0 -1
- package/dist/external-optimizer-subprocess-CA3aXT8I.js.map +0 -1
- package/dist/proposal-findings-CXblsskH.js.map +0 -1
- package/dist/registry-BL7H33tj.d.ts.map +0 -1
- package/dist/report-command-CtIIsQKP.js.map +0 -1
- package/dist/systemone.d.ts.map +0 -1
- package/dist/tool-groups-DOriCbkR.d.ts.map +0 -1
- package/dist/tool-waste-BYt5rdt8.js.map +0 -1
- package/dist/types-CW5b90JH.js.map +0 -1
- package/dist/types-DjOMwGY_.d.ts.map +0 -1
- package/dist/types-KEkKkETv.d.ts.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { a as hashCanonical, r as canonicalString } from "./canonical-BsZVgBhW.js";
|
|
2
2
|
import { t as deepFreezeCanonicalJson } from "./deep-freeze-CurcOQg3.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { a as makeFinding } from "./types-IXD2HJzu.js";
|
|
4
|
+
import { C as combineAbortSignals } from "./llm-client-DQveoxSM.js";
|
|
5
|
+
import { A as spanEpochMillis, H as findingSubjectGrammarPromptFor, J as snapshotExactExecutionPlan, q as snapshotExactExecutionComponentIdentity, t as createTraceAnalyst } from "./kind-factory-CbZ-6tvu.js";
|
|
5
6
|
import { i as validateUsageSettlementTimeout, t as assertValidAnalystUsageReceipt } from "./usage-receipt-Cx3xNeyB.js";
|
|
6
7
|
import { LLM_CONTEXT_TOKENS, LLM_INPUT_TOKEN_ATTR_KEYS, LLM_OUTPUT_TOKEN_ATTR_KEYS, TOOL_NAME_ATTR_KEYS } from "./trace-attributes.js";
|
|
7
8
|
import "./otlp-attributes-D9ptWaIJ.js";
|
|
8
|
-
import { r as makeFinding } from "./types-CW5b90JH.js";
|
|
9
9
|
import { t as executionTrackByLane } from "./execution-tracks-CpgFPpS5.js";
|
|
10
10
|
import { t as analyzeSupervisorRunIntegrity } from "./integrity-BV1ZDf42.js";
|
|
11
11
|
import { randomUUID } from "node:crypto";
|
|
@@ -1265,564 +1265,336 @@ const DEFAULT_TRACE_ANALYST_KINDS = [
|
|
|
1265
1265
|
IMPROVEMENT_KIND_SPEC
|
|
1266
1266
|
];
|
|
1267
1267
|
//#endregion
|
|
1268
|
-
//#region src/analyst/registry.ts
|
|
1268
|
+
//#region src/analyst/registry-accounting.ts
|
|
1269
|
+
function exactPlannedAllocation(plan, analystId) {
|
|
1270
|
+
const budget = plan.policy.budget;
|
|
1271
|
+
if (budget.kind === "none") return void 0;
|
|
1272
|
+
const allocated = budget.allocations_usd[analystId];
|
|
1273
|
+
return allocated === null ? void 0 : allocated;
|
|
1274
|
+
}
|
|
1275
|
+
function logUncapturedBudgetWarning(args) {
|
|
1276
|
+
if (args.budgetUsd === void 0 || args.usage.cost.kind !== "uncaptured") return;
|
|
1277
|
+
args.log(`[analyst] WARN ${args.analyst.id} — USD cost uncaptured; budget not reconciled`, {
|
|
1278
|
+
runId: args.runId,
|
|
1279
|
+
budget_usd: args.budgetUsd,
|
|
1280
|
+
cost_captured: false
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1269
1283
|
/**
|
|
1270
|
-
*
|
|
1271
|
-
*
|
|
1272
|
-
* Owns three responsibilities and only three:
|
|
1273
|
-
* 1. Registration — ids must be unique; bad registrations fail loudly
|
|
1274
|
-
* at register-time, not run-time.
|
|
1275
|
-
* 2. Routing — each analyst declares its `inputKind`; the registry
|
|
1276
|
-
* picks the matching field from AnalystRunInputs and skips the
|
|
1277
|
-
* analyst with a logged reason if it's missing.
|
|
1278
|
-
* 3. Isolation — one analyst's exception MUST NOT stop other analysts.
|
|
1279
|
-
* Failed analysts produce zero findings + a 'failed' summary row.
|
|
1280
|
-
*
|
|
1281
|
-
* Cross-cutting concerns (telemetry, error → finding conversion, cost
|
|
1282
|
-
* ingestion, storage rotation) live in `AnalystHooks`. Budget shaping
|
|
1283
|
-
* (equal split vs weighted vs custom) lives in `BudgetPolicy`. Both
|
|
1284
|
-
* have sensible defaults; consumers override only what they need.
|
|
1284
|
+
* Custom allocation takes priority, then weights, then equal shares.
|
|
1285
|
+
* Each allocation stays within the unspent overall budget.
|
|
1285
1286
|
*/
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
};
|
|
1297
|
-
|
|
1298
|
-
analysts = /* @__PURE__ */ new Map();
|
|
1299
|
-
options;
|
|
1300
|
-
constructor(options = {}) {
|
|
1301
|
-
this.options = options;
|
|
1302
|
-
}
|
|
1303
|
-
register(analyst) {
|
|
1304
|
-
const id = analyst.id;
|
|
1305
|
-
const version = analyst.version;
|
|
1306
|
-
const cost = analyst.cost;
|
|
1307
|
-
if (!id) throw new Error("AnalystRegistry.register: analyst.id is required");
|
|
1308
|
-
if (this.analysts.has(id)) throw new Error(`AnalystRegistry.register: duplicate analyst id "${id}"`);
|
|
1309
|
-
if (!version) throw new Error(`AnalystRegistry.register: analyst "${id}" must declare a version`);
|
|
1310
|
-
if (cost.kind === "deterministic" && cost.settlement_timeout_ms !== void 0) throw new TypeError(`AnalystRegistry.register: deterministic analyst "${id}" cannot declare settlement_timeout_ms`);
|
|
1311
|
-
if (cost.settlement_timeout_ms !== void 0) validateUsageSettlementTimeout(cost.settlement_timeout_ms);
|
|
1312
|
-
this.analysts.set(id, analyst);
|
|
1313
|
-
}
|
|
1314
|
-
list() {
|
|
1315
|
-
return Array.from(this.analysts.values()).map((a) => ({
|
|
1316
|
-
id: a.id,
|
|
1317
|
-
description: a.description,
|
|
1318
|
-
version: a.version,
|
|
1319
|
-
cost: a.cost
|
|
1320
|
-
}));
|
|
1321
|
-
}
|
|
1322
|
-
async run(runId, inputs, runOpts = {}) {
|
|
1323
|
-
for await (const ev of this.runStream(runId, inputs, runOpts)) if (ev.type === "run-completed") return ev.result;
|
|
1324
|
-
throw new Error("AnalystRegistry.run: stream completed without run-completed event");
|
|
1325
|
-
}
|
|
1326
|
-
/** Run exactly the ordered analysts and complete policy supplied by the caller. */
|
|
1327
|
-
async runExact(runId, inputs, runOpts) {
|
|
1328
|
-
for await (const ev of this.runExactStream(runId, inputs, runOpts)) if (ev.type === "run-completed") return ev.result;
|
|
1329
|
-
throw new Error("AnalystRegistry.runExact: stream completed without run-completed event");
|
|
1330
|
-
}
|
|
1331
|
-
/** Streaming counterpart to {@link runExact}. */
|
|
1332
|
-
async *runExactStream(runId, inputs, runOpts) {
|
|
1333
|
-
for await (const event of this.executePlanStream(this.normalizeExactPlan(runId, inputs, runOpts))) yield event;
|
|
1334
|
-
}
|
|
1335
|
-
/**
|
|
1336
|
-
* Streaming counterpart to `run()`. Emits `AnalystRunEvent` values
|
|
1337
|
-
* in real time — `run-started`, then per-analyst `skipped` /
|
|
1338
|
-
* `started` / `completed`, then a terminal `run-completed` whose
|
|
1339
|
-
* payload is the full `AnalystRunResult`. UIs use this to render
|
|
1340
|
-
* progress; persistence consumers use `run()` and read the result.
|
|
1341
|
-
*
|
|
1342
|
-
* Hooks (`onBeforeAnalyze` / `onAfterAnalyze` / `onError` /
|
|
1343
|
-
* `onComplete`) fire as before — streaming is additive, not a hook
|
|
1344
|
-
* replacement.
|
|
1345
|
-
*/
|
|
1346
|
-
async *runStream(runId, inputs, runOpts = {}) {
|
|
1347
|
-
yield* this.executePlanStream(this.normalizeLegacyPlan(runId, inputs, runOpts));
|
|
1348
|
-
}
|
|
1349
|
-
normalizeLegacyPlan(runId, inputs, runOpts) {
|
|
1350
|
-
const timeoutMs = validateTimeout(runOpts.timeoutMs) ?? null;
|
|
1351
|
-
const budget = runOpts.budget ?? this.options.defaultBudget;
|
|
1352
|
-
validateBudgetPolicy(budget);
|
|
1353
|
-
return {
|
|
1354
|
-
runId,
|
|
1355
|
-
prepared: this.selectAnalysts(runOpts).map((analyst) => ({
|
|
1356
|
-
analyst,
|
|
1357
|
-
input: this.routeInput(analyst, inputs)
|
|
1358
|
-
})),
|
|
1359
|
-
budget: budget ? {
|
|
1360
|
-
kind: "dynamic",
|
|
1361
|
-
policy: budget
|
|
1362
|
-
} : { kind: "none" },
|
|
1363
|
-
totalTimeoutMs: timeoutMs,
|
|
1364
|
-
signal: runOpts.signal ?? null,
|
|
1365
|
-
costLedger: runOpts.costLedger ?? null,
|
|
1366
|
-
costPhase: runOpts.costPhase ?? null,
|
|
1367
|
-
tags: runOpts.tags ?? null,
|
|
1368
|
-
priorFindings: runOpts.priorFindings ?? null,
|
|
1369
|
-
chainFindings: runOpts.chainFindings ?? false,
|
|
1370
|
-
hooks: this.options.hooks ?? {},
|
|
1371
|
-
chat: this.options.chat,
|
|
1372
|
-
log: this.options.log ?? (() => {}),
|
|
1373
|
-
executionSnapshot: void 0
|
|
1374
|
-
};
|
|
1375
|
-
}
|
|
1376
|
-
normalizeExactPlan(runId, inputs, runOpts) {
|
|
1377
|
-
const exactRunId = snapshotExactRunId(runId);
|
|
1378
|
-
const exact = snapshotExactRegistryRunOpts(runOpts);
|
|
1379
|
-
const selected = normalizeExactAnalysts(this.selectExactAnalysts(exact.analystIds));
|
|
1380
|
-
const registryChat = this.options.chat;
|
|
1381
|
-
const registryChatIdentity = this.options.chatIdentity;
|
|
1382
|
-
const registryHooks = this.options.hooks;
|
|
1383
|
-
const registryHooksIdentity = this.options.hooksIdentity;
|
|
1384
|
-
if (exact.useRegistryChat && registryChat === void 0) throw new TypeError("ExactRegistryRunOpts.useRegistryChat is true but the registry has no chat client");
|
|
1385
|
-
if (exact.applyRegistryHooks && !hasRegistryHooks(registryHooks)) throw new TypeError("ExactRegistryRunOpts.applyRegistryHooks is true but the registry has no lifecycle hooks");
|
|
1386
|
-
const inputSnapshot = snapshotAnalystRunInputChannels(inputs);
|
|
1387
|
-
const prepared = selected.map((analyst) => ({
|
|
1388
|
-
analyst,
|
|
1389
|
-
input: this.routeInput(analyst, inputSnapshot)
|
|
1390
|
-
}));
|
|
1391
|
-
if (exact.missingInputMode === "abort") {
|
|
1392
|
-
const missing = prepared.find((candidate) => candidate.input.kind === "missing")?.analyst;
|
|
1393
|
-
if (missing) throw new TypeError(`ExactRegistryRunOpts.missingInputMode abort preflight found no "${missing.inputKind}" input for "${missing.id}"`);
|
|
1287
|
+
function allocateBudget(policy, args) {
|
|
1288
|
+
if (!policy) return void 0;
|
|
1289
|
+
if (policy.allocate) {
|
|
1290
|
+
const allocated = policy.allocate({
|
|
1291
|
+
analyst: args.analyst,
|
|
1292
|
+
totalUsd: policy.totalUsd,
|
|
1293
|
+
remainingUsd: args.remainingUsd,
|
|
1294
|
+
runningCount: args.runningCount
|
|
1295
|
+
});
|
|
1296
|
+
if (allocated === void 0) {
|
|
1297
|
+
if (policy.totalUsd !== void 0) throw new Error(`BudgetPolicy.allocate('${args.analyst.id}') cannot return undefined when totalUsd is set`);
|
|
1298
|
+
return;
|
|
1394
1299
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
const costLedgerIdentity = exact.costLedger === null ? null : requireExactComponentIdentity(exact.costLedgerIdentity ?? void 0, "cost ledger");
|
|
1398
|
-
const executionSnapshot = exactExecutionSnapshot(selected, exact, exactFixedBudgets(exact.budget, prepared.filter((candidate) => candidate.input.kind === "present").map((candidate) => candidate.analyst), selected), costLedgerIdentity, hooksIdentity, chatIdentity);
|
|
1399
|
-
return {
|
|
1400
|
-
runId: exactRunId,
|
|
1401
|
-
prepared,
|
|
1402
|
-
budget: { kind: "none" },
|
|
1403
|
-
totalTimeoutMs: exact.totalTimeoutMs,
|
|
1404
|
-
signal: exact.signal,
|
|
1405
|
-
costLedger: exact.costLedger,
|
|
1406
|
-
costPhase: exact.costPhase,
|
|
1407
|
-
tags: exact.tags,
|
|
1408
|
-
priorFindings: exact.priorFindings,
|
|
1409
|
-
chainFindings: exact.chainFindings,
|
|
1410
|
-
hooks: exact.applyRegistryHooks && registryHooks ? snapshotHooks(registryHooks) : {},
|
|
1411
|
-
chat: exact.useRegistryChat && registryChat ? snapshotChat(registryChat) : void 0,
|
|
1412
|
-
log: () => {},
|
|
1413
|
-
executionSnapshot
|
|
1414
|
-
};
|
|
1300
|
+
assertBudgetAmount(allocated, `BudgetPolicy.allocate('${args.analyst.id}')`);
|
|
1301
|
+
return args.remainingUsd === void 0 ? allocated : Math.min(allocated, args.remainingUsd);
|
|
1415
1302
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
});
|
|
1451
|
-
log(`[analyst] skip ${analyst.id} — run aborted`, {
|
|
1452
|
-
runId: plan.runId,
|
|
1453
|
-
reason: summary.reason
|
|
1454
|
-
});
|
|
1455
|
-
yield snapshotExecutionEvent({
|
|
1456
|
-
type: "analyst-skipped",
|
|
1457
|
-
summary
|
|
1458
|
-
}, exact);
|
|
1459
|
-
if (exact) {
|
|
1460
|
-
executionFailure = abortReason(runSignal);
|
|
1461
|
-
break;
|
|
1462
|
-
}
|
|
1463
|
-
continue;
|
|
1464
|
-
}
|
|
1465
|
-
if (input.kind === "missing") {
|
|
1466
|
-
const summary = {
|
|
1467
|
-
analyst_id: analyst.id,
|
|
1468
|
-
status: "skipped",
|
|
1469
|
-
reason: `missing input of kind '${analyst.inputKind}'`,
|
|
1470
|
-
findings_count: 0,
|
|
1471
|
-
latency_ms: 0,
|
|
1472
|
-
usage: zeroUsage()
|
|
1473
|
-
};
|
|
1474
|
-
const execution = {
|
|
1475
|
-
summary,
|
|
1476
|
-
findings: [],
|
|
1477
|
-
budgetDebitUsd: 0
|
|
1478
|
-
};
|
|
1479
|
-
executions.push(execution);
|
|
1480
|
-
log(`[analyst] skip ${analyst.id} — missing input`, {
|
|
1481
|
-
runId: plan.runId,
|
|
1482
|
-
kind: analyst.inputKind
|
|
1483
|
-
});
|
|
1484
|
-
const hookValues = snapshotAfterHookValues(summary, [], exact);
|
|
1485
|
-
try {
|
|
1486
|
-
await waitForHook(plan.hooks.onAfterAnalyze ? () => plan.hooks.onAfterAnalyze?.({
|
|
1487
|
-
analyst,
|
|
1488
|
-
summary: hookValues.summary,
|
|
1489
|
-
findings: hookValues.findings,
|
|
1490
|
-
runId: plan.runId
|
|
1491
|
-
}) : void 0, runSignal);
|
|
1492
|
-
} catch (error) {
|
|
1493
|
-
if (!exact) throw error;
|
|
1494
|
-
executionFailure = error;
|
|
1495
|
-
}
|
|
1496
|
-
yield snapshotExecutionEvent({
|
|
1497
|
-
type: "analyst-skipped",
|
|
1498
|
-
summary
|
|
1499
|
-
}, exact);
|
|
1500
|
-
if (executionFailure !== void 0) break;
|
|
1501
|
-
continue;
|
|
1502
|
-
}
|
|
1503
|
-
const allocatedUsd = plan.executionSnapshot === void 0 ? allocateBudget(plan.budget.kind === "dynamic" ? plan.budget.policy : void 0, {
|
|
1504
|
-
analyst,
|
|
1505
|
-
remainingUsd,
|
|
1506
|
-
runningCount: runnable.length,
|
|
1507
|
-
totalWeight
|
|
1508
|
-
}) : exactPlannedAllocation(plan.executionSnapshot, analyst.id);
|
|
1509
|
-
const budgetCeilingUsd = plan.executionSnapshot === void 0 ? remainingUsd : allocatedUsd;
|
|
1510
|
-
const usageReceipts = [];
|
|
1511
|
-
const contextTags = plan.tags === null ? void 0 : { ...plan.tags };
|
|
1512
|
-
const priorFindings = selectPriorFindings(plan.priorFindings ?? void 0, analyst.id);
|
|
1513
|
-
const chainedFindings = plan.chainFindings && upstreamFindings.length > 0 ? [...upstreamFindings] : void 0;
|
|
1514
|
-
const ctx = {
|
|
1515
|
-
runId: plan.runId,
|
|
1516
|
-
correlationId,
|
|
1517
|
-
deadlineMs,
|
|
1518
|
-
budgetUsd: allocatedUsd,
|
|
1519
|
-
costLedger: plan.costLedger ?? void 0,
|
|
1520
|
-
costPhase: plan.costPhase ?? void 0,
|
|
1521
|
-
chat: plan.chat,
|
|
1522
|
-
tags: contextTags,
|
|
1523
|
-
log: (message, fields) => log(`[${analyst.id}] ${message}`, {
|
|
1524
|
-
runId: plan.runId,
|
|
1525
|
-
correlationId,
|
|
1526
|
-
...fields
|
|
1527
|
-
}),
|
|
1528
|
-
signal: runSignal,
|
|
1529
|
-
priorFindings,
|
|
1530
|
-
upstreamFindings: chainedFindings,
|
|
1531
|
-
recordUsage: (receipt) => {
|
|
1532
|
-
if (!exact) {
|
|
1533
|
-
assertValidAnalystUsageReceipt(receipt);
|
|
1534
|
-
usageReceipts.push(receipt);
|
|
1535
|
-
return;
|
|
1536
|
-
}
|
|
1537
|
-
usageReceipts.push(snapshotUsageReceiptOnce(receipt, `AnalystRegistry.runExact analyst "${analyst.id}" usage`));
|
|
1538
|
-
}
|
|
1539
|
-
};
|
|
1540
|
-
if (exact) {
|
|
1541
|
-
if (contextTags) deepFreezeCanonicalJson(contextTags);
|
|
1542
|
-
if (priorFindings) deepFreezeCanonicalJson(priorFindings);
|
|
1543
|
-
if (chainedFindings) deepFreezeCanonicalJson(chainedFindings);
|
|
1544
|
-
Object.freeze(ctx);
|
|
1545
|
-
}
|
|
1546
|
-
try {
|
|
1547
|
-
await waitForHook(plan.hooks.onBeforeAnalyze ? () => plan.hooks.onBeforeAnalyze?.({
|
|
1548
|
-
analyst,
|
|
1549
|
-
ctx,
|
|
1550
|
-
runId: plan.runId
|
|
1551
|
-
}) : void 0, runSignal);
|
|
1552
|
-
} catch (error) {
|
|
1553
|
-
if (!exact) throw error;
|
|
1554
|
-
executionFailure = error;
|
|
1555
|
-
break;
|
|
1556
|
-
}
|
|
1557
|
-
if (runSignal?.aborted) {
|
|
1558
|
-
const summary = abortedBeforeStartSummary(analyst, runSignal, Date.now() - t0);
|
|
1559
|
-
executions.push({
|
|
1560
|
-
summary,
|
|
1561
|
-
findings: [],
|
|
1562
|
-
budgetDebitUsd: 0
|
|
1563
|
-
});
|
|
1564
|
-
yield snapshotExecutionEvent({
|
|
1565
|
-
type: "analyst-skipped",
|
|
1566
|
-
summary
|
|
1567
|
-
}, exact);
|
|
1568
|
-
if (exact) {
|
|
1569
|
-
executionFailure = abortReason(runSignal);
|
|
1570
|
-
break;
|
|
1571
|
-
}
|
|
1572
|
-
continue;
|
|
1573
|
-
}
|
|
1574
|
-
let effectiveBudget;
|
|
1575
|
-
try {
|
|
1576
|
-
effectiveBudget = validateEffectiveBudget(ctx.budgetUsd, budgetCeilingUsd, analyst.id);
|
|
1577
|
-
} catch (error) {
|
|
1578
|
-
if (!exact) throw error;
|
|
1579
|
-
executionFailure = error;
|
|
1580
|
-
break;
|
|
1581
|
-
}
|
|
1582
|
-
const analystContext = exact ? ctx : { ...ctx };
|
|
1583
|
-
const executionSignal = exact ? ctx.signal : runSignal;
|
|
1584
|
-
yield snapshotExecutionEvent({
|
|
1585
|
-
type: "analyst-started",
|
|
1586
|
-
analyst_id: analyst.id,
|
|
1587
|
-
started_at: new Date(t0).toISOString()
|
|
1588
|
-
}, exact);
|
|
1589
|
-
let findings;
|
|
1590
|
-
let summary;
|
|
1591
|
-
let lifecycleFailure;
|
|
1592
|
-
let analysisFailure;
|
|
1593
|
-
try {
|
|
1594
|
-
if (runSignal?.aborted) throw abortReason(runSignal);
|
|
1595
|
-
findings = snapshotExecutionFindings(await waitForOperation(analyst.analyze(input.value, analystContext), executionSignal, analystAbortGraceMs(analyst)), exact, `AnalystRegistry.runExact analyst "${analyst.id}" findings`);
|
|
1596
|
-
} catch (error) {
|
|
1597
|
-
const cause = error instanceof Error ? error : new Error(String(error));
|
|
1598
|
-
analysisFailure = cause;
|
|
1599
|
-
let hookFindings = [];
|
|
1600
|
-
if (!executionSignal?.aborted) try {
|
|
1601
|
-
hookFindings = snapshotExecutionFindings(await waitForHook(plan.hooks.onError ? () => plan.hooks.onError?.({
|
|
1602
|
-
analyst,
|
|
1603
|
-
error: cause,
|
|
1604
|
-
runId: plan.runId
|
|
1605
|
-
}) : void 0, executionSignal) ?? [], exact, `AnalystRegistry.runExact analyst "${analyst.id}" onError findings`);
|
|
1606
|
-
} catch (error) {
|
|
1607
|
-
lifecycleFailure = error;
|
|
1608
|
-
}
|
|
1609
|
-
findings = hookFindings;
|
|
1610
|
-
}
|
|
1611
|
-
let usage;
|
|
1612
|
-
try {
|
|
1613
|
-
usage = resolveUsage(analyst, usageReceipts, exact);
|
|
1614
|
-
} catch (error) {
|
|
1615
|
-
if (!exact) throw error;
|
|
1616
|
-
executionFailure = error;
|
|
1617
|
-
break;
|
|
1618
|
-
}
|
|
1619
|
-
if (analysisFailure === void 0) {
|
|
1620
|
-
summary = {
|
|
1621
|
-
analyst_id: analyst.id,
|
|
1622
|
-
status: "ok",
|
|
1623
|
-
findings_count: findings.length,
|
|
1624
|
-
latency_ms: Date.now() - t0,
|
|
1625
|
-
usage,
|
|
1626
|
-
...exact ? { allocated_budget_usd: effectiveBudget ?? null } : {}
|
|
1627
|
-
};
|
|
1628
|
-
log(`[analyst] ok ${analyst.id}`, {
|
|
1629
|
-
runId: plan.runId,
|
|
1630
|
-
findings: findings.length,
|
|
1631
|
-
latency_ms: summary.latency_ms,
|
|
1632
|
-
cost_usd: knownCostUsd(usage),
|
|
1633
|
-
cost_kind: usage.cost.kind,
|
|
1634
|
-
input_tokens: usage.tokens?.input ?? null,
|
|
1635
|
-
output_tokens: usage.tokens?.output ?? null
|
|
1636
|
-
});
|
|
1637
|
-
} else {
|
|
1638
|
-
const errorClass = analysisFailure.constructor.name || "Error";
|
|
1639
|
-
const errorMessage = exact && analysisFailure.message.length === 0 ? "Analyst failed without an error message" : analysisFailure.message;
|
|
1640
|
-
summary = {
|
|
1641
|
-
analyst_id: analyst.id,
|
|
1642
|
-
status: "failed",
|
|
1643
|
-
findings_count: findings.length,
|
|
1644
|
-
latency_ms: Date.now() - t0,
|
|
1645
|
-
usage,
|
|
1646
|
-
...exact ? { allocated_budget_usd: effectiveBudget ?? null } : {},
|
|
1647
|
-
error: {
|
|
1648
|
-
class: errorClass,
|
|
1649
|
-
message: errorMessage
|
|
1650
|
-
}
|
|
1651
|
-
};
|
|
1652
|
-
log(`[analyst] FAIL ${analyst.id}`, {
|
|
1653
|
-
runId: plan.runId,
|
|
1654
|
-
error_class: errorClass,
|
|
1655
|
-
error: errorMessage,
|
|
1656
|
-
cost_usd: knownCostUsd(usage),
|
|
1657
|
-
cost_kind: usage.cost.kind
|
|
1658
|
-
});
|
|
1659
|
-
}
|
|
1660
|
-
logUncapturedBudgetWarning({
|
|
1661
|
-
analyst,
|
|
1662
|
-
runId: plan.runId,
|
|
1663
|
-
budgetUsd: effectiveBudget,
|
|
1664
|
-
usage,
|
|
1665
|
-
log
|
|
1666
|
-
});
|
|
1667
|
-
const execution = {
|
|
1668
|
-
summary,
|
|
1669
|
-
findings,
|
|
1670
|
-
budgetDebitUsd: budgetDebit(summary.usage, effectiveBudget)
|
|
1671
|
-
};
|
|
1672
|
-
if (exact) try {
|
|
1673
|
-
executionCost([...executions, execution], true);
|
|
1674
|
-
} catch (error) {
|
|
1675
|
-
executionFailure = error;
|
|
1676
|
-
break;
|
|
1677
|
-
}
|
|
1678
|
-
executions.push(execution);
|
|
1679
|
-
if (plan.budget.kind === "dynamic" && remainingUsd !== void 0) remainingUsd = Math.max(0, remainingUsd - execution.budgetDebitUsd);
|
|
1680
|
-
if (plan.chainFindings) upstreamFindings.push(...findings);
|
|
1681
|
-
if (lifecycleFailure !== void 0) {
|
|
1682
|
-
if (!exact) throw lifecycleFailure;
|
|
1683
|
-
executionFailure = lifecycleFailure;
|
|
1684
|
-
break;
|
|
1685
|
-
}
|
|
1686
|
-
const hookValues = snapshotAfterHookValues(summary, findings, exact);
|
|
1687
|
-
try {
|
|
1688
|
-
await waitForHook(plan.hooks.onAfterAnalyze ? () => plan.hooks.onAfterAnalyze?.({
|
|
1689
|
-
analyst,
|
|
1690
|
-
summary: hookValues.summary,
|
|
1691
|
-
findings: hookValues.findings,
|
|
1692
|
-
runId: plan.runId
|
|
1693
|
-
}) : void 0, executionSignal);
|
|
1694
|
-
} catch (error) {
|
|
1695
|
-
if (!exact) throw error;
|
|
1696
|
-
executionFailure = error;
|
|
1697
|
-
break;
|
|
1698
|
-
}
|
|
1699
|
-
yield snapshotExecutionEvent({
|
|
1700
|
-
type: "analyst-completed",
|
|
1701
|
-
summary,
|
|
1702
|
-
findings
|
|
1703
|
-
}, exact);
|
|
1704
|
-
if (exact && runSignal?.aborted) {
|
|
1705
|
-
executionFailure = abortReason(runSignal);
|
|
1706
|
-
break;
|
|
1707
|
-
}
|
|
1303
|
+
if (policy.totalUsd == null) return void 0;
|
|
1304
|
+
const allocated = policy.weights ? policy.totalUsd * analystWeight(policy.weights, args.analyst.id) / args.totalWeight : policy.totalUsd / Math.max(1, args.runningCount);
|
|
1305
|
+
return args.remainingUsd === void 0 ? allocated : Math.min(allocated, args.remainingUsd);
|
|
1306
|
+
}
|
|
1307
|
+
function validateBudgetPolicy(policy) {
|
|
1308
|
+
if (!policy) return;
|
|
1309
|
+
if (policy.totalUsd !== void 0) assertBudgetAmount(policy.totalUsd, "BudgetPolicy.totalUsd");
|
|
1310
|
+
for (const [analystId, weight] of Object.entries(policy.weights ?? {})) assertBudgetAmount(weight, `BudgetPolicy.weights['${analystId}']`);
|
|
1311
|
+
}
|
|
1312
|
+
function assertBudgetAmount(value, field) {
|
|
1313
|
+
if (!Number.isFinite(value) || value < 0) throw new Error(`${field} must be a non-negative finite number`);
|
|
1314
|
+
}
|
|
1315
|
+
function validateEffectiveBudget(budgetUsd, remainingUsd, analystId) {
|
|
1316
|
+
if (budgetUsd !== void 0) assertBudgetAmount(budgetUsd, `AnalystContext.budgetUsd for '${analystId}'`);
|
|
1317
|
+
if (remainingUsd === void 0) return budgetUsd;
|
|
1318
|
+
if (budgetUsd === void 0) throw new Error(`AnalystContext.budgetUsd for '${analystId}' cannot be removed while an overall budget remains`);
|
|
1319
|
+
if (budgetUsd > remainingUsd) throw new Error(`AnalystContext.budgetUsd for '${analystId}' (${budgetUsd}) exceeds the remaining overall budget (${remainingUsd})`);
|
|
1320
|
+
return budgetUsd;
|
|
1321
|
+
}
|
|
1322
|
+
function analystWeight(weights, analystId) {
|
|
1323
|
+
const weight = weights[analystId] ?? 1;
|
|
1324
|
+
assertBudgetAmount(weight, `BudgetPolicy.weights['${analystId}']`);
|
|
1325
|
+
return weight;
|
|
1326
|
+
}
|
|
1327
|
+
function zeroUsage() {
|
|
1328
|
+
return {
|
|
1329
|
+
calls: 0,
|
|
1330
|
+
tokens: {
|
|
1331
|
+
input: 0,
|
|
1332
|
+
output: 0
|
|
1333
|
+
},
|
|
1334
|
+
cost: {
|
|
1335
|
+
kind: "observed",
|
|
1336
|
+
usd: 0
|
|
1708
1337
|
}
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
function resolveUsage(analyst, receipts, exact = false) {
|
|
1341
|
+
if (receipts.length > 0) return mergeUsageReceipts(receipts, exact);
|
|
1342
|
+
if (analyst.cost.kind === "deterministic") return zeroUsage();
|
|
1343
|
+
return {
|
|
1344
|
+
calls: null,
|
|
1345
|
+
tokens: null,
|
|
1346
|
+
cost: {
|
|
1347
|
+
kind: "uncaptured",
|
|
1348
|
+
usd: null
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
function mergeUsageReceipts(receipts, exact = false) {
|
|
1353
|
+
const calls = receipts.every((receipt) => receipt.calls !== null) ? usageSum(receipts.map((receipt) => receipt.calls ?? 0), exact, "calls", true) : null;
|
|
1354
|
+
const tokens = receipts.every((receipt) => receipt.tokens !== null) ? Object.fromEntries([
|
|
1355
|
+
"input",
|
|
1356
|
+
"output",
|
|
1357
|
+
"reasoning",
|
|
1358
|
+
"cached",
|
|
1359
|
+
"cacheWrite"
|
|
1360
|
+
].flatMap((field) => field === "input" || field === "output" || receipts.some((receipt) => receipt.tokens?.[field] !== void 0) ? [[field, usageSum(receipts.map((receipt) => receipt.tokens?.[field] ?? 0), exact, `tokens.${field}`, true)]] : [])) : null;
|
|
1361
|
+
const cost = aggregateCostProvenance(receipts.map((receipt) => receipt.cost), exact);
|
|
1362
|
+
return {
|
|
1363
|
+
calls,
|
|
1364
|
+
tokens,
|
|
1365
|
+
cost,
|
|
1366
|
+
...cost.kind === "uncaptured" ? { knownCostUsd: usageSum(receipts.map(knownCostUsd), exact, "known cost") } : {}
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
function knownCostUsd(receipt) {
|
|
1370
|
+
return receipt.cost.kind === "uncaptured" ? receipt.knownCostUsd ?? 0 : receipt.cost.usd;
|
|
1371
|
+
}
|
|
1372
|
+
function budgetDebit(receipt, allocatedUsd) {
|
|
1373
|
+
const known = knownCostUsd(receipt);
|
|
1374
|
+
return receipt.cost.kind === "uncaptured" && allocatedUsd !== void 0 ? Math.max(known, allocatedUsd) : known;
|
|
1375
|
+
}
|
|
1376
|
+
function aggregateCostProvenance(costs, exact = false) {
|
|
1377
|
+
if (costs.some((cost) => cost.kind === "uncaptured")) return {
|
|
1378
|
+
kind: "uncaptured",
|
|
1379
|
+
usd: null
|
|
1380
|
+
};
|
|
1381
|
+
const usd = usageSum(costs.map((cost) => cost.usd ?? 0), exact, "captured cost");
|
|
1382
|
+
return costs.some((cost) => cost.kind === "estimated") ? {
|
|
1383
|
+
kind: "estimated",
|
|
1384
|
+
usd
|
|
1385
|
+
} : {
|
|
1386
|
+
kind: "observed",
|
|
1387
|
+
usd
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
function executionCost(executions, exact) {
|
|
1391
|
+
const usages = executions.map((execution) => execution.summary.usage);
|
|
1392
|
+
return {
|
|
1393
|
+
known: usageSum(usages.map(knownCostUsd), exact, "run known cost"),
|
|
1394
|
+
provenance: aggregateCostProvenance(usages.map((usage) => usage.cost), exact)
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
function usageSum(values, exact, field, integer = false) {
|
|
1398
|
+
const sum = values.reduce((total, value) => total + value, 0);
|
|
1399
|
+
if (exact && (integer ? !Number.isSafeInteger(sum) : !Number.isFinite(sum))) throw new RangeError(`exact analyst usage ${field} aggregate ${integer ? "exceeds a safe integer" : "is not finite"}`);
|
|
1400
|
+
return sum;
|
|
1401
|
+
}
|
|
1402
|
+
//#endregion
|
|
1403
|
+
//#region src/analyst/registry-plan.ts
|
|
1404
|
+
function normalizeLegacyPlan(runId, inputs, runOpts, analysts, options) {
|
|
1405
|
+
const timeoutMs = validateTimeout(runOpts.timeoutMs) ?? null;
|
|
1406
|
+
const budget = runOpts.budget ?? options.defaultBudget;
|
|
1407
|
+
validateBudgetPolicy(budget);
|
|
1408
|
+
return {
|
|
1409
|
+
runId,
|
|
1410
|
+
prepared: selectAnalysts(analysts, runOpts).map((analyst) => ({
|
|
1411
|
+
analyst,
|
|
1412
|
+
input: routeInput(analyst, inputs)
|
|
1413
|
+
})),
|
|
1414
|
+
budget: budget ? {
|
|
1415
|
+
kind: "dynamic",
|
|
1416
|
+
policy: budget
|
|
1417
|
+
} : { kind: "none" },
|
|
1418
|
+
totalTimeoutMs: timeoutMs,
|
|
1419
|
+
signal: runOpts.signal ?? null,
|
|
1420
|
+
costLedger: runOpts.costLedger ?? null,
|
|
1421
|
+
costPhase: runOpts.costPhase ?? null,
|
|
1422
|
+
tags: runOpts.tags ?? null,
|
|
1423
|
+
priorFindings: runOpts.priorFindings ?? null,
|
|
1424
|
+
chainFindings: runOpts.chainFindings ?? false,
|
|
1425
|
+
hooks: options.hooks ?? {},
|
|
1426
|
+
chat: options.chat,
|
|
1427
|
+
log: options.log ?? (() => {}),
|
|
1428
|
+
executionSnapshot: void 0
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
function routeInput(analyst, inputs) {
|
|
1432
|
+
switch (analyst.inputKind) {
|
|
1433
|
+
case "trace-store": {
|
|
1434
|
+
const value = inputs.traceStore;
|
|
1435
|
+
return value ? {
|
|
1436
|
+
kind: "present",
|
|
1437
|
+
value
|
|
1438
|
+
} : { kind: "missing" };
|
|
1729
1439
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
}, "AnalystRegistry.runExact result");
|
|
1737
|
-
await waitForHook(plan.hooks.onComplete ? () => plan.hooks.onComplete?.({ result: completeResult }) : void 0, runSignal);
|
|
1738
|
-
} catch (error) {
|
|
1739
|
-
executionFailure = error;
|
|
1440
|
+
case "artifact-dir": {
|
|
1441
|
+
const value = inputs.artifactDir;
|
|
1442
|
+
return value ? {
|
|
1443
|
+
kind: "present",
|
|
1444
|
+
value
|
|
1445
|
+
} : { kind: "missing" };
|
|
1740
1446
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
}
|
|
1747
|
-
return;
|
|
1447
|
+
case "run-record": {
|
|
1448
|
+
const value = inputs.runRecord;
|
|
1449
|
+
return value ? {
|
|
1450
|
+
kind: "present",
|
|
1451
|
+
value
|
|
1452
|
+
} : { kind: "missing" };
|
|
1748
1453
|
}
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
completion: {
|
|
1756
|
-
status: "failed",
|
|
1757
|
-
error: {
|
|
1758
|
-
class: errorClass,
|
|
1759
|
-
message: errorMessage
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1762
|
-
}, { cause });
|
|
1763
|
-
}
|
|
1764
|
-
selectAnalysts(opts) {
|
|
1765
|
-
let candidates = Array.from(this.analysts.values());
|
|
1766
|
-
if (opts.only?.length) {
|
|
1767
|
-
const only = new Set(opts.only);
|
|
1768
|
-
candidates = candidates.filter((a) => only.has(a.id));
|
|
1454
|
+
case "judge-input": {
|
|
1455
|
+
const value = inputs.judgeInput;
|
|
1456
|
+
return value ? {
|
|
1457
|
+
kind: "present",
|
|
1458
|
+
value
|
|
1459
|
+
} : { kind: "missing" };
|
|
1769
1460
|
}
|
|
1770
|
-
|
|
1771
|
-
const
|
|
1772
|
-
|
|
1461
|
+
case "custom": {
|
|
1462
|
+
const value = inputs.custom?.[analyst.id];
|
|
1463
|
+
return value !== void 0 ? {
|
|
1464
|
+
kind: "present",
|
|
1465
|
+
value
|
|
1466
|
+
} : { kind: "missing" };
|
|
1773
1467
|
}
|
|
1774
|
-
return candidates;
|
|
1775
1468
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
analyst
|
|
1783
|
-
};
|
|
1784
|
-
});
|
|
1469
|
+
}
|
|
1470
|
+
function selectAnalysts(analysts, opts) {
|
|
1471
|
+
let candidates = Array.from(analysts.values());
|
|
1472
|
+
if (opts.only?.length) {
|
|
1473
|
+
const only = new Set(opts.only);
|
|
1474
|
+
candidates = candidates.filter((a) => only.has(a.id));
|
|
1785
1475
|
}
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
const value = inputs.traceStore;
|
|
1790
|
-
return value ? {
|
|
1791
|
-
kind: "present",
|
|
1792
|
-
value
|
|
1793
|
-
} : { kind: "missing" };
|
|
1794
|
-
}
|
|
1795
|
-
case "artifact-dir": {
|
|
1796
|
-
const value = inputs.artifactDir;
|
|
1797
|
-
return value ? {
|
|
1798
|
-
kind: "present",
|
|
1799
|
-
value
|
|
1800
|
-
} : { kind: "missing" };
|
|
1801
|
-
}
|
|
1802
|
-
case "run-record": {
|
|
1803
|
-
const value = inputs.runRecord;
|
|
1804
|
-
return value ? {
|
|
1805
|
-
kind: "present",
|
|
1806
|
-
value
|
|
1807
|
-
} : { kind: "missing" };
|
|
1808
|
-
}
|
|
1809
|
-
case "judge-input": {
|
|
1810
|
-
const value = inputs.judgeInput;
|
|
1811
|
-
return value ? {
|
|
1812
|
-
kind: "present",
|
|
1813
|
-
value
|
|
1814
|
-
} : { kind: "missing" };
|
|
1815
|
-
}
|
|
1816
|
-
case "custom": {
|
|
1817
|
-
const value = inputs.custom?.[analyst.id];
|
|
1818
|
-
return value !== void 0 ? {
|
|
1819
|
-
kind: "present",
|
|
1820
|
-
value
|
|
1821
|
-
} : { kind: "missing" };
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1476
|
+
if (opts.skip?.length) {
|
|
1477
|
+
const skip = new Set(opts.skip);
|
|
1478
|
+
candidates = candidates.filter((a) => !skip.has(a.id));
|
|
1824
1479
|
}
|
|
1825
|
-
|
|
1480
|
+
return candidates;
|
|
1481
|
+
}
|
|
1482
|
+
function validateTimeout(timeoutMs) {
|
|
1483
|
+
if (timeoutMs === void 0) return void 0;
|
|
1484
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647) throw new TypeError("RegistryRunOpts.timeoutMs must be a positive safe integer no greater than 2147483647");
|
|
1485
|
+
return timeoutMs;
|
|
1486
|
+
}
|
|
1487
|
+
//#endregion
|
|
1488
|
+
//#region src/analyst/registry-snapshot.ts
|
|
1489
|
+
/** Immutable snapshots at exact execution's untrusted input and observational boundaries. */
|
|
1490
|
+
function canonicalJsonSnapshot(value, label) {
|
|
1491
|
+
let snapshot;
|
|
1492
|
+
try {
|
|
1493
|
+
snapshot = JSON.parse(canonicalString(value));
|
|
1494
|
+
} catch (cause) {
|
|
1495
|
+
throw new TypeError(`${label} must be canonical JSON`, { cause });
|
|
1496
|
+
}
|
|
1497
|
+
return deepFreezeCanonicalJson(snapshot);
|
|
1498
|
+
}
|
|
1499
|
+
function snapshotUsageReceiptOnce(receipt, context) {
|
|
1500
|
+
const data = readOwnFields(receipt, [
|
|
1501
|
+
"calls",
|
|
1502
|
+
"tokens",
|
|
1503
|
+
"cost",
|
|
1504
|
+
"knownCostUsd"
|
|
1505
|
+
], context);
|
|
1506
|
+
data.tokens = data.tokens === null ? null : readOwnFields(data.tokens, [
|
|
1507
|
+
"input",
|
|
1508
|
+
"output",
|
|
1509
|
+
"reasoning",
|
|
1510
|
+
"cached",
|
|
1511
|
+
"cacheWrite"
|
|
1512
|
+
], `${context} tokens`);
|
|
1513
|
+
data.cost = readOwnFields(data.cost, ["kind", "usd"], `${context} cost`);
|
|
1514
|
+
const snapshot = canonicalJsonSnapshot(data, context);
|
|
1515
|
+
assertValidAnalystUsageReceipt(snapshot, context);
|
|
1516
|
+
return snapshot;
|
|
1517
|
+
}
|
|
1518
|
+
function readOwnFields(value, fields, context) {
|
|
1519
|
+
if (!isPlainRecord(value)) throw new TypeError(`${context} must be a plain object`);
|
|
1520
|
+
const unexpected = Object.keys(value).filter((key) => !fields.includes(key));
|
|
1521
|
+
if (unexpected.length > 0) throw new TypeError(`${context} contains unknown fields: ${unexpected.sort().join(", ")}`);
|
|
1522
|
+
return Object.fromEntries(fields.flatMap((field) => Object.hasOwn(value, field) ? [[field, value[field]]] : []));
|
|
1523
|
+
}
|
|
1524
|
+
function isPlainRecord(value) {
|
|
1525
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1526
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1527
|
+
return prototype === Object.prototype || prototype === null;
|
|
1528
|
+
}
|
|
1529
|
+
function snapshotExecutionFindings(findings, exact, context) {
|
|
1530
|
+
return exact ? deepFreezeCanonicalJson(snapshotAnalystFindings(findings, context)) : findings;
|
|
1531
|
+
}
|
|
1532
|
+
function snapshotAfterHookValues(summary, findings, exact) {
|
|
1533
|
+
if (!exact) return {
|
|
1534
|
+
summary,
|
|
1535
|
+
findings
|
|
1536
|
+
};
|
|
1537
|
+
return {
|
|
1538
|
+
summary: canonicalJsonSnapshot(summary, "AnalystRegistry.runExact onAfterAnalyze summary"),
|
|
1539
|
+
findings: deepFreezeCanonicalJson(snapshotAnalystFindings(findings, "AnalystRegistry.runExact onAfterAnalyze findings"))
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
function snapshotExecutionEvent(event, exact) {
|
|
1543
|
+
return exact ? canonicalJsonSnapshot(event, "AnalystRegistry.runExact event") : event;
|
|
1544
|
+
}
|
|
1545
|
+
//#endregion
|
|
1546
|
+
//#region src/analyst/registry-exact-plan.ts
|
|
1547
|
+
/** Validate and freeze every input and identity before an exact analyst run starts. */
|
|
1548
|
+
function normalizeExactPlan(runId, inputs, runOpts, analysts, options) {
|
|
1549
|
+
const exactRunId = snapshotExactRunId(runId);
|
|
1550
|
+
const exact = snapshotExactRegistryRunOpts(runOpts);
|
|
1551
|
+
const selected = normalizeExactAnalysts(selectExactAnalysts(analysts, exact.analystIds));
|
|
1552
|
+
const registryChat = options.chat;
|
|
1553
|
+
const registryChatIdentity = options.chatIdentity;
|
|
1554
|
+
const registryHooks = options.hooks;
|
|
1555
|
+
const registryHooksIdentity = options.hooksIdentity;
|
|
1556
|
+
if (exact.useRegistryChat && registryChat === void 0) throw new TypeError("ExactRegistryRunOpts.useRegistryChat is true but the registry has no chat client");
|
|
1557
|
+
if (exact.applyRegistryHooks && !hasRegistryHooks(registryHooks)) throw new TypeError("ExactRegistryRunOpts.applyRegistryHooks is true but the registry has no lifecycle hooks");
|
|
1558
|
+
const inputSnapshot = snapshotAnalystRunInputChannels(inputs);
|
|
1559
|
+
const prepared = selected.map((analyst) => ({
|
|
1560
|
+
analyst,
|
|
1561
|
+
input: routeInput(analyst, inputSnapshot)
|
|
1562
|
+
}));
|
|
1563
|
+
if (exact.missingInputMode === "abort") {
|
|
1564
|
+
const missing = prepared.find((candidate) => candidate.input.kind === "missing")?.analyst;
|
|
1565
|
+
if (missing) throw new TypeError(`ExactRegistryRunOpts.missingInputMode abort preflight found no "${missing.inputKind}" input for "${missing.id}"`);
|
|
1566
|
+
}
|
|
1567
|
+
const hooksIdentity = exact.applyRegistryHooks && registryHooks ? requireExactComponentIdentity(registryHooksIdentity, "registry hooks") : null;
|
|
1568
|
+
const chatIdentity = exact.useRegistryChat ? requireExactComponentIdentity(registryChatIdentity, "registry chat") : null;
|
|
1569
|
+
const costLedgerIdentity = exact.costLedger === null ? null : requireExactComponentIdentity(exact.costLedgerIdentity ?? void 0, "cost ledger");
|
|
1570
|
+
const executionSnapshot = exactExecutionSnapshot(selected, exact, exactFixedBudgets(exact.budget, prepared.filter((candidate) => candidate.input.kind === "present").map((candidate) => candidate.analyst), selected), costLedgerIdentity, hooksIdentity, chatIdentity);
|
|
1571
|
+
return {
|
|
1572
|
+
runId: exactRunId,
|
|
1573
|
+
prepared,
|
|
1574
|
+
budget: { kind: "none" },
|
|
1575
|
+
totalTimeoutMs: exact.totalTimeoutMs,
|
|
1576
|
+
signal: exact.signal,
|
|
1577
|
+
costLedger: exact.costLedger,
|
|
1578
|
+
costPhase: exact.costPhase,
|
|
1579
|
+
tags: exact.tags,
|
|
1580
|
+
priorFindings: exact.priorFindings,
|
|
1581
|
+
chainFindings: exact.chainFindings,
|
|
1582
|
+
hooks: exact.applyRegistryHooks && registryHooks ? snapshotHooks(registryHooks) : {},
|
|
1583
|
+
chat: exact.useRegistryChat && registryChat ? snapshotChat(registryChat) : void 0,
|
|
1584
|
+
log: () => {},
|
|
1585
|
+
executionSnapshot
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
function selectExactAnalysts(analysts, ids) {
|
|
1589
|
+
return ids.map((id) => {
|
|
1590
|
+
const analyst = analysts.get(id);
|
|
1591
|
+
if (!analyst) throw new Error(`ExactRegistryRunOpts.analystIds names unknown analyst "${id}"`);
|
|
1592
|
+
return {
|
|
1593
|
+
registeredId: id,
|
|
1594
|
+
analyst
|
|
1595
|
+
};
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1826
1598
|
const exactRunFields = [
|
|
1827
1599
|
"analystIds",
|
|
1828
1600
|
"budget",
|
|
@@ -2057,45 +1829,6 @@ function exactPriorFindingsSnapshot(findings) {
|
|
|
2057
1829
|
digest: hashCanonical(record)
|
|
2058
1830
|
};
|
|
2059
1831
|
}
|
|
2060
|
-
function canonicalJsonSnapshot(value, label) {
|
|
2061
|
-
let snapshot;
|
|
2062
|
-
try {
|
|
2063
|
-
snapshot = JSON.parse(canonicalString(value));
|
|
2064
|
-
} catch (cause) {
|
|
2065
|
-
throw new TypeError(`${label} must be canonical JSON`, { cause });
|
|
2066
|
-
}
|
|
2067
|
-
return deepFreezeCanonicalJson(snapshot);
|
|
2068
|
-
}
|
|
2069
|
-
function snapshotUsageReceiptOnce(receipt, context) {
|
|
2070
|
-
const data = readOwnFields(receipt, [
|
|
2071
|
-
"calls",
|
|
2072
|
-
"tokens",
|
|
2073
|
-
"cost",
|
|
2074
|
-
"knownCostUsd"
|
|
2075
|
-
], context);
|
|
2076
|
-
data.tokens = data.tokens === null ? null : readOwnFields(data.tokens, [
|
|
2077
|
-
"input",
|
|
2078
|
-
"output",
|
|
2079
|
-
"reasoning",
|
|
2080
|
-
"cached",
|
|
2081
|
-
"cacheWrite"
|
|
2082
|
-
], `${context} tokens`);
|
|
2083
|
-
data.cost = readOwnFields(data.cost, ["kind", "usd"], `${context} cost`);
|
|
2084
|
-
const snapshot = canonicalJsonSnapshot(data, context);
|
|
2085
|
-
assertValidAnalystUsageReceipt(snapshot, context);
|
|
2086
|
-
return snapshot;
|
|
2087
|
-
}
|
|
2088
|
-
function readOwnFields(value, fields, context) {
|
|
2089
|
-
if (!isPlainRecord(value)) throw new TypeError(`${context} must be a plain object`);
|
|
2090
|
-
const unexpected = Object.keys(value).filter((key) => !fields.includes(key));
|
|
2091
|
-
if (unexpected.length > 0) throw new TypeError(`${context} contains unknown fields: ${unexpected.sort().join(", ")}`);
|
|
2092
|
-
return Object.fromEntries(fields.flatMap((field) => Object.hasOwn(value, field) ? [[field, value[field]]] : []));
|
|
2093
|
-
}
|
|
2094
|
-
function isPlainRecord(value) {
|
|
2095
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2096
|
-
const prototype = Object.getPrototypeOf(value);
|
|
2097
|
-
return prototype === Object.prototype || prototype === null;
|
|
2098
|
-
}
|
|
2099
1832
|
function exactFixedBudgets(exact, runnable, selected) {
|
|
2100
1833
|
if (exact === null) return {};
|
|
2101
1834
|
const allocations = Object.fromEntries(selected.map((analyst) => [analyst.id, null]));
|
|
@@ -2105,45 +1838,372 @@ function exactFixedBudgets(exact, runnable, selected) {
|
|
|
2105
1838
|
for (const analyst of runnable) allocations[analyst.id] = each;
|
|
2106
1839
|
return deepFreezeCanonicalJson(allocations);
|
|
2107
1840
|
}
|
|
2108
|
-
const totalWeight = runnable.reduce((sum, analyst) => sum + exact.weights[analyst.id], 0);
|
|
2109
|
-
if (totalWeight === 0) throw new Error("ExactRegistryRunOpts weighted budget must allocate positive weight to a runnable analyst");
|
|
2110
|
-
for (const analyst of runnable) allocations[analyst.id] = exact.totalUsd * exact.weights[analyst.id] / totalWeight;
|
|
2111
|
-
return deepFreezeCanonicalJson(allocations);
|
|
2112
|
-
}
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
1841
|
+
const totalWeight = runnable.reduce((sum, analyst) => sum + exact.weights[analyst.id], 0);
|
|
1842
|
+
if (totalWeight === 0) throw new Error("ExactRegistryRunOpts weighted budget must allocate positive weight to a runnable analyst");
|
|
1843
|
+
for (const analyst of runnable) allocations[analyst.id] = exact.totalUsd * exact.weights[analyst.id] / totalWeight;
|
|
1844
|
+
return deepFreezeCanonicalJson(allocations);
|
|
1845
|
+
}
|
|
1846
|
+
//#endregion
|
|
1847
|
+
//#region src/analyst/registry-execution.ts
|
|
1848
|
+
/** Execute one prepared plan with serial analyst isolation, lifecycle hooks, and accounting. */
|
|
1849
|
+
/** A post-start exact-run failure; completed work remains attached for accounting and review. */
|
|
1850
|
+
var ExactAnalystRunExecutionError = class extends Error {
|
|
1851
|
+
name = "ExactAnalystRunExecutionError";
|
|
1852
|
+
result;
|
|
1853
|
+
constructor(message, result, options) {
|
|
1854
|
+
super(message, options);
|
|
1855
|
+
const snapshot = snapshotExactAnalystRunReceipt(result, "ExactAnalystRunExecutionError result");
|
|
1856
|
+
if (snapshot.completion.status !== "failed") throw new TypeError("ExactAnalystRunExecutionError result must be a failed receipt");
|
|
1857
|
+
this.result = snapshot;
|
|
1858
|
+
}
|
|
1859
|
+
};
|
|
1860
|
+
async function* executePlanStream(plan) {
|
|
1861
|
+
const exact = plan.executionSnapshot !== void 0;
|
|
1862
|
+
if (exact && plan.signal?.aborted) throw abortReason(plan.signal);
|
|
1863
|
+
const correlationId = `ar_${randomUUID().slice(0, 12)}`;
|
|
1864
|
+
const log = plan.log;
|
|
1865
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1866
|
+
const started = Date.now();
|
|
1867
|
+
const timeoutSignal = plan.totalTimeoutMs === null ? void 0 : AbortSignal.timeout(plan.totalTimeoutMs);
|
|
1868
|
+
const runSignal = combineAbortSignals(plan.signal ?? void 0, timeoutSignal);
|
|
1869
|
+
const deadlineMs = plan.totalTimeoutMs === null ? void 0 : started + plan.totalTimeoutMs;
|
|
1870
|
+
const runnable = plan.prepared.filter((candidate) => candidate.input.kind === "present").map((candidate) => candidate.analyst);
|
|
1871
|
+
let remainingUsd = plan.budget.kind === "dynamic" ? plan.budget.policy.totalUsd : void 0;
|
|
1872
|
+
const weights = plan.budget.kind === "dynamic" ? plan.budget.policy.weights : void 0;
|
|
1873
|
+
const totalWeight = weights && plan.budget.kind === "dynamic" && plan.budget.policy.totalUsd != null && !plan.budget.policy.allocate && runnable.length > 0 ? runnable.reduce((sum, analyst) => sum + analystWeight(weights, analyst.id), 0) : void 0;
|
|
1874
|
+
if (totalWeight === 0) throw new Error("BudgetPolicy.weights must allocate positive weight to a runnable analyst");
|
|
1875
|
+
const upstreamFindings = [];
|
|
1876
|
+
yield snapshotExecutionEvent({
|
|
1877
|
+
type: "run-started",
|
|
1878
|
+
run_id: plan.runId,
|
|
1879
|
+
correlation_id: correlationId,
|
|
1880
|
+
started_at: startedAt,
|
|
1881
|
+
analyst_ids: plan.prepared.map(({ analyst }) => analyst.id),
|
|
1882
|
+
...plan.executionSnapshot === void 0 ? {} : { execution_plan: plan.executionSnapshot }
|
|
1883
|
+
}, exact);
|
|
1884
|
+
const executions = [];
|
|
1885
|
+
let executionFailure;
|
|
1886
|
+
for (const { analyst, input } of plan.prepared) {
|
|
1887
|
+
const t0 = Date.now();
|
|
1888
|
+
if (runSignal?.aborted) {
|
|
1889
|
+
const summary = abortedBeforeStartSummary(analyst, runSignal);
|
|
1890
|
+
executions.push({
|
|
1891
|
+
summary,
|
|
1892
|
+
findings: [],
|
|
1893
|
+
budgetDebitUsd: 0
|
|
1894
|
+
});
|
|
1895
|
+
log(`[analyst] skip ${analyst.id} — run aborted`, {
|
|
1896
|
+
runId: plan.runId,
|
|
1897
|
+
reason: summary.reason
|
|
1898
|
+
});
|
|
1899
|
+
yield snapshotExecutionEvent({
|
|
1900
|
+
type: "analyst-skipped",
|
|
1901
|
+
summary
|
|
1902
|
+
}, exact);
|
|
1903
|
+
if (exact) {
|
|
1904
|
+
executionFailure = abortReason(runSignal);
|
|
1905
|
+
break;
|
|
1906
|
+
}
|
|
1907
|
+
continue;
|
|
1908
|
+
}
|
|
1909
|
+
if (input.kind === "missing") {
|
|
1910
|
+
const summary = {
|
|
1911
|
+
analyst_id: analyst.id,
|
|
1912
|
+
status: "skipped",
|
|
1913
|
+
reason: `missing input of kind '${analyst.inputKind}'`,
|
|
1914
|
+
findings_count: 0,
|
|
1915
|
+
latency_ms: 0,
|
|
1916
|
+
usage: zeroUsage()
|
|
1917
|
+
};
|
|
1918
|
+
const execution = {
|
|
1919
|
+
summary,
|
|
1920
|
+
findings: [],
|
|
1921
|
+
budgetDebitUsd: 0
|
|
1922
|
+
};
|
|
1923
|
+
executions.push(execution);
|
|
1924
|
+
log(`[analyst] skip ${analyst.id} — missing input`, {
|
|
1925
|
+
runId: plan.runId,
|
|
1926
|
+
kind: analyst.inputKind
|
|
1927
|
+
});
|
|
1928
|
+
const hookValues = snapshotAfterHookValues(summary, [], exact);
|
|
1929
|
+
try {
|
|
1930
|
+
await waitForHook(plan.hooks.onAfterAnalyze ? () => plan.hooks.onAfterAnalyze?.({
|
|
1931
|
+
analyst,
|
|
1932
|
+
summary: hookValues.summary,
|
|
1933
|
+
findings: hookValues.findings,
|
|
1934
|
+
runId: plan.runId
|
|
1935
|
+
}) : void 0, runSignal);
|
|
1936
|
+
} catch (error) {
|
|
1937
|
+
if (!exact) throw error;
|
|
1938
|
+
executionFailure = error;
|
|
1939
|
+
}
|
|
1940
|
+
yield snapshotExecutionEvent({
|
|
1941
|
+
type: "analyst-skipped",
|
|
1942
|
+
summary
|
|
1943
|
+
}, exact);
|
|
1944
|
+
if (executionFailure !== void 0) break;
|
|
1945
|
+
continue;
|
|
1946
|
+
}
|
|
1947
|
+
const allocatedUsd = plan.executionSnapshot === void 0 ? allocateBudget(plan.budget.kind === "dynamic" ? plan.budget.policy : void 0, {
|
|
1948
|
+
analyst,
|
|
1949
|
+
remainingUsd,
|
|
1950
|
+
runningCount: runnable.length,
|
|
1951
|
+
totalWeight
|
|
1952
|
+
}) : exactPlannedAllocation(plan.executionSnapshot, analyst.id);
|
|
1953
|
+
const budgetCeilingUsd = plan.executionSnapshot === void 0 ? remainingUsd : allocatedUsd;
|
|
1954
|
+
const usageReceipts = [];
|
|
1955
|
+
const contextTags = plan.tags === null ? void 0 : { ...plan.tags };
|
|
1956
|
+
const priorFindings = selectPriorFindings(plan.priorFindings ?? void 0, analyst.id);
|
|
1957
|
+
const chainedFindings = plan.chainFindings && upstreamFindings.length > 0 ? [...upstreamFindings] : void 0;
|
|
1958
|
+
const ctx = {
|
|
1959
|
+
runId: plan.runId,
|
|
1960
|
+
correlationId,
|
|
1961
|
+
deadlineMs,
|
|
1962
|
+
budgetUsd: allocatedUsd,
|
|
1963
|
+
costLedger: plan.costLedger ?? void 0,
|
|
1964
|
+
costPhase: plan.costPhase ?? void 0,
|
|
1965
|
+
chat: plan.chat,
|
|
1966
|
+
tags: contextTags,
|
|
1967
|
+
log: (message, fields) => log(`[${analyst.id}] ${message}`, {
|
|
1968
|
+
runId: plan.runId,
|
|
1969
|
+
correlationId,
|
|
1970
|
+
...fields
|
|
1971
|
+
}),
|
|
1972
|
+
signal: runSignal,
|
|
1973
|
+
priorFindings,
|
|
1974
|
+
upstreamFindings: chainedFindings,
|
|
1975
|
+
recordUsage: (receipt) => {
|
|
1976
|
+
if (!exact) {
|
|
1977
|
+
assertValidAnalystUsageReceipt(receipt);
|
|
1978
|
+
usageReceipts.push(receipt);
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1981
|
+
usageReceipts.push(snapshotUsageReceiptOnce(receipt, `AnalystRegistry.runExact analyst "${analyst.id}" usage`));
|
|
1982
|
+
}
|
|
1983
|
+
};
|
|
1984
|
+
if (exact) {
|
|
1985
|
+
if (contextTags) deepFreezeCanonicalJson(contextTags);
|
|
1986
|
+
if (priorFindings) deepFreezeCanonicalJson(priorFindings);
|
|
1987
|
+
if (chainedFindings) deepFreezeCanonicalJson(chainedFindings);
|
|
1988
|
+
Object.freeze(ctx);
|
|
1989
|
+
}
|
|
1990
|
+
try {
|
|
1991
|
+
await waitForHook(plan.hooks.onBeforeAnalyze ? () => plan.hooks.onBeforeAnalyze?.({
|
|
1992
|
+
analyst,
|
|
1993
|
+
ctx,
|
|
1994
|
+
runId: plan.runId
|
|
1995
|
+
}) : void 0, runSignal);
|
|
1996
|
+
} catch (error) {
|
|
1997
|
+
if (!exact) throw error;
|
|
1998
|
+
executionFailure = error;
|
|
1999
|
+
break;
|
|
2000
|
+
}
|
|
2001
|
+
if (runSignal?.aborted) {
|
|
2002
|
+
const summary = abortedBeforeStartSummary(analyst, runSignal, Date.now() - t0);
|
|
2003
|
+
executions.push({
|
|
2004
|
+
summary,
|
|
2005
|
+
findings: [],
|
|
2006
|
+
budgetDebitUsd: 0
|
|
2007
|
+
});
|
|
2008
|
+
yield snapshotExecutionEvent({
|
|
2009
|
+
type: "analyst-skipped",
|
|
2010
|
+
summary
|
|
2011
|
+
}, exact);
|
|
2012
|
+
if (exact) {
|
|
2013
|
+
executionFailure = abortReason(runSignal);
|
|
2014
|
+
break;
|
|
2015
|
+
}
|
|
2016
|
+
continue;
|
|
2017
|
+
}
|
|
2018
|
+
let effectiveBudget;
|
|
2019
|
+
try {
|
|
2020
|
+
effectiveBudget = validateEffectiveBudget(ctx.budgetUsd, budgetCeilingUsd, analyst.id);
|
|
2021
|
+
} catch (error) {
|
|
2022
|
+
if (!exact) throw error;
|
|
2023
|
+
executionFailure = error;
|
|
2024
|
+
break;
|
|
2025
|
+
}
|
|
2026
|
+
const analystContext = exact ? ctx : { ...ctx };
|
|
2027
|
+
const executionSignal = exact ? ctx.signal : runSignal;
|
|
2028
|
+
yield snapshotExecutionEvent({
|
|
2029
|
+
type: "analyst-started",
|
|
2030
|
+
analyst_id: analyst.id,
|
|
2031
|
+
started_at: new Date(t0).toISOString()
|
|
2032
|
+
}, exact);
|
|
2033
|
+
let findings;
|
|
2034
|
+
let summary;
|
|
2035
|
+
let lifecycleFailure;
|
|
2036
|
+
let analysisFailure;
|
|
2037
|
+
try {
|
|
2038
|
+
if (runSignal?.aborted) throw abortReason(runSignal);
|
|
2039
|
+
findings = snapshotExecutionFindings(await waitForOperation(analyst.analyze(input.value, analystContext), executionSignal, analystAbortGraceMs(analyst)), exact, `AnalystRegistry.runExact analyst "${analyst.id}" findings`);
|
|
2040
|
+
} catch (error) {
|
|
2041
|
+
const cause = error instanceof Error ? error : new Error(String(error));
|
|
2042
|
+
analysisFailure = cause;
|
|
2043
|
+
let hookFindings = [];
|
|
2044
|
+
if (!executionSignal?.aborted) try {
|
|
2045
|
+
hookFindings = snapshotExecutionFindings(await waitForHook(plan.hooks.onError ? () => plan.hooks.onError?.({
|
|
2046
|
+
analyst,
|
|
2047
|
+
error: cause,
|
|
2048
|
+
runId: plan.runId
|
|
2049
|
+
}) : void 0, executionSignal) ?? [], exact, `AnalystRegistry.runExact analyst "${analyst.id}" onError findings`);
|
|
2050
|
+
} catch (error) {
|
|
2051
|
+
lifecycleFailure = error;
|
|
2052
|
+
}
|
|
2053
|
+
findings = hookFindings;
|
|
2054
|
+
}
|
|
2055
|
+
let usage;
|
|
2056
|
+
try {
|
|
2057
|
+
usage = resolveUsage(analyst, usageReceipts, exact);
|
|
2058
|
+
} catch (error) {
|
|
2059
|
+
if (!exact) throw error;
|
|
2060
|
+
executionFailure = error;
|
|
2061
|
+
break;
|
|
2062
|
+
}
|
|
2063
|
+
if (analysisFailure === void 0) {
|
|
2064
|
+
summary = {
|
|
2065
|
+
analyst_id: analyst.id,
|
|
2066
|
+
status: "ok",
|
|
2067
|
+
findings_count: findings.length,
|
|
2068
|
+
latency_ms: Date.now() - t0,
|
|
2069
|
+
usage,
|
|
2070
|
+
...exact ? { allocated_budget_usd: effectiveBudget ?? null } : {}
|
|
2071
|
+
};
|
|
2072
|
+
log(`[analyst] ok ${analyst.id}`, {
|
|
2073
|
+
runId: plan.runId,
|
|
2074
|
+
findings: findings.length,
|
|
2075
|
+
latency_ms: summary.latency_ms,
|
|
2076
|
+
cost_usd: knownCostUsd(usage),
|
|
2077
|
+
cost_kind: usage.cost.kind,
|
|
2078
|
+
input_tokens: usage.tokens?.input ?? null,
|
|
2079
|
+
output_tokens: usage.tokens?.output ?? null
|
|
2080
|
+
});
|
|
2081
|
+
} else {
|
|
2082
|
+
const errorClass = analysisFailure.constructor.name || "Error";
|
|
2083
|
+
const errorMessage = exact && analysisFailure.message.length === 0 ? "Analyst failed without an error message" : analysisFailure.message;
|
|
2084
|
+
summary = {
|
|
2085
|
+
analyst_id: analyst.id,
|
|
2086
|
+
status: "failed",
|
|
2087
|
+
findings_count: findings.length,
|
|
2088
|
+
latency_ms: Date.now() - t0,
|
|
2089
|
+
usage,
|
|
2090
|
+
...exact ? { allocated_budget_usd: effectiveBudget ?? null } : {},
|
|
2091
|
+
error: {
|
|
2092
|
+
class: errorClass,
|
|
2093
|
+
message: errorMessage
|
|
2094
|
+
}
|
|
2095
|
+
};
|
|
2096
|
+
log(`[analyst] FAIL ${analyst.id}`, {
|
|
2097
|
+
runId: plan.runId,
|
|
2098
|
+
error_class: errorClass,
|
|
2099
|
+
error: errorMessage,
|
|
2100
|
+
cost_usd: knownCostUsd(usage),
|
|
2101
|
+
cost_kind: usage.cost.kind
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
logUncapturedBudgetWarning({
|
|
2105
|
+
analyst,
|
|
2106
|
+
runId: plan.runId,
|
|
2107
|
+
budgetUsd: effectiveBudget,
|
|
2108
|
+
usage,
|
|
2109
|
+
log
|
|
2110
|
+
});
|
|
2111
|
+
const execution = {
|
|
2112
|
+
summary,
|
|
2113
|
+
findings,
|
|
2114
|
+
budgetDebitUsd: budgetDebit(summary.usage, effectiveBudget)
|
|
2115
|
+
};
|
|
2116
|
+
if (exact) try {
|
|
2117
|
+
executionCost([...executions, execution], true);
|
|
2118
|
+
} catch (error) {
|
|
2119
|
+
executionFailure = error;
|
|
2120
|
+
break;
|
|
2121
|
+
}
|
|
2122
|
+
executions.push(execution);
|
|
2123
|
+
if (plan.budget.kind === "dynamic" && remainingUsd !== void 0) remainingUsd = Math.max(0, remainingUsd - execution.budgetDebitUsd);
|
|
2124
|
+
if (plan.chainFindings) upstreamFindings.push(...findings);
|
|
2125
|
+
if (lifecycleFailure !== void 0) {
|
|
2126
|
+
if (!exact) throw lifecycleFailure;
|
|
2127
|
+
executionFailure = lifecycleFailure;
|
|
2128
|
+
break;
|
|
2129
|
+
}
|
|
2130
|
+
const hookValues = snapshotAfterHookValues(summary, findings, exact);
|
|
2131
|
+
try {
|
|
2132
|
+
await waitForHook(plan.hooks.onAfterAnalyze ? () => plan.hooks.onAfterAnalyze?.({
|
|
2133
|
+
analyst,
|
|
2134
|
+
summary: hookValues.summary,
|
|
2135
|
+
findings: hookValues.findings,
|
|
2136
|
+
runId: plan.runId
|
|
2137
|
+
}) : void 0, executionSignal);
|
|
2138
|
+
} catch (error) {
|
|
2139
|
+
if (!exact) throw error;
|
|
2140
|
+
executionFailure = error;
|
|
2141
|
+
break;
|
|
2142
|
+
}
|
|
2143
|
+
yield snapshotExecutionEvent({
|
|
2144
|
+
type: "analyst-completed",
|
|
2145
|
+
summary,
|
|
2146
|
+
findings
|
|
2147
|
+
}, exact);
|
|
2148
|
+
if (exact && runSignal?.aborted) {
|
|
2149
|
+
executionFailure = abortReason(runSignal);
|
|
2150
|
+
break;
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
const summaries = executions.map(({ summary }) => summary);
|
|
2154
|
+
const findings = executions.flatMap((execution) => execution.findings);
|
|
2155
|
+
const cost = executionCost(executions, exact);
|
|
2156
|
+
const baseResult = {
|
|
2157
|
+
run_id: plan.runId,
|
|
2158
|
+
correlation_id: correlationId,
|
|
2159
|
+
started_at: startedAt,
|
|
2160
|
+
ended_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2161
|
+
findings,
|
|
2162
|
+
per_analyst: summaries,
|
|
2163
|
+
total_cost_usd: cost.known,
|
|
2164
|
+
total_cost_provenance: cost.provenance
|
|
2124
2165
|
};
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
})
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
if (
|
|
2145
|
-
if (
|
|
2146
|
-
|
|
2166
|
+
if (plan.executionSnapshot === void 0) {
|
|
2167
|
+
await waitForHook(plan.hooks.onComplete ? () => plan.hooks.onComplete?.({ result: baseResult }) : void 0, runSignal);
|
|
2168
|
+
yield {
|
|
2169
|
+
type: "run-completed",
|
|
2170
|
+
result: baseResult
|
|
2171
|
+
};
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
let completeResult;
|
|
2175
|
+
if (executionFailure === void 0) try {
|
|
2176
|
+
completeResult = snapshotExactAnalystRunReceipt({
|
|
2177
|
+
...baseResult,
|
|
2178
|
+
execution_plan: plan.executionSnapshot,
|
|
2179
|
+
completion: { status: "complete" }
|
|
2180
|
+
}, "AnalystRegistry.runExact result");
|
|
2181
|
+
await waitForHook(plan.hooks.onComplete ? () => plan.hooks.onComplete?.({ result: completeResult }) : void 0, runSignal);
|
|
2182
|
+
} catch (error) {
|
|
2183
|
+
executionFailure = error;
|
|
2184
|
+
}
|
|
2185
|
+
if (runSignal?.aborted) executionFailure ??= abortReason(runSignal);
|
|
2186
|
+
if (executionFailure === void 0 && completeResult) {
|
|
2187
|
+
yield snapshotExecutionEvent({
|
|
2188
|
+
type: "run-completed",
|
|
2189
|
+
result: completeResult
|
|
2190
|
+
}, true);
|
|
2191
|
+
return;
|
|
2192
|
+
}
|
|
2193
|
+
const cause = executionFailure instanceof Error ? executionFailure : new Error(String(executionFailure));
|
|
2194
|
+
const errorClass = cause.constructor.name || "Error";
|
|
2195
|
+
const errorMessage = cause.message.trim().length === 0 ? "Exact analyst run failed without a message" : cause.message;
|
|
2196
|
+
throw new ExactAnalystRunExecutionError(`exact analyst run failed after starting: ${errorMessage}; partial result is attached`, {
|
|
2197
|
+
...baseResult,
|
|
2198
|
+
execution_plan: plan.executionSnapshot,
|
|
2199
|
+
completion: {
|
|
2200
|
+
status: "failed",
|
|
2201
|
+
error: {
|
|
2202
|
+
class: errorClass,
|
|
2203
|
+
message: errorMessage
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
}, { cause });
|
|
2147
2207
|
}
|
|
2148
2208
|
async function waitForOperation(operation, signal, abortGraceMs) {
|
|
2149
2209
|
if (!signal) return operation;
|
|
@@ -2211,137 +2271,7 @@ function abortedBeforeStartSummary(analyst, signal, latencyMs = 0) {
|
|
|
2211
2271
|
function abortReason(signal) {
|
|
2212
2272
|
return signal.reason instanceof Error ? signal.reason : new DOMException("The operation was aborted", "AbortError");
|
|
2213
2273
|
}
|
|
2214
|
-
/**
|
|
2215
|
-
* Default budget allocator: prefer the custom `allocate` callback if
|
|
2216
|
-
* provided; else weighted split when weights are set; else equal split
|
|
2217
|
-
* across `runningCount`. Returns undefined when no totalUsd is known.
|
|
2218
|
-
*/
|
|
2219
|
-
function allocateBudget(policy, args) {
|
|
2220
|
-
if (!policy) return void 0;
|
|
2221
|
-
if (policy.allocate) {
|
|
2222
|
-
const allocated = policy.allocate({
|
|
2223
|
-
analyst: args.analyst,
|
|
2224
|
-
totalUsd: policy.totalUsd,
|
|
2225
|
-
remainingUsd: args.remainingUsd,
|
|
2226
|
-
runningCount: args.runningCount
|
|
2227
|
-
});
|
|
2228
|
-
if (allocated === void 0) {
|
|
2229
|
-
if (policy.totalUsd !== void 0) throw new Error(`BudgetPolicy.allocate('${args.analyst.id}') cannot return undefined when totalUsd is set`);
|
|
2230
|
-
return;
|
|
2231
|
-
}
|
|
2232
|
-
assertBudgetAmount(allocated, `BudgetPolicy.allocate('${args.analyst.id}')`);
|
|
2233
|
-
return args.remainingUsd === void 0 ? allocated : Math.min(allocated, args.remainingUsd);
|
|
2234
|
-
}
|
|
2235
|
-
if (policy.totalUsd == null) return void 0;
|
|
2236
|
-
const allocated = policy.weights ? policy.totalUsd * analystWeight(policy.weights, args.analyst.id) / args.totalWeight : policy.totalUsd / Math.max(1, args.runningCount);
|
|
2237
|
-
return args.remainingUsd === void 0 ? allocated : Math.min(allocated, args.remainingUsd);
|
|
2238
|
-
}
|
|
2239
|
-
function validateBudgetPolicy(policy) {
|
|
2240
|
-
if (!policy) return;
|
|
2241
|
-
if (policy.totalUsd !== void 0) assertBudgetAmount(policy.totalUsd, "BudgetPolicy.totalUsd");
|
|
2242
|
-
for (const [analystId, weight] of Object.entries(policy.weights ?? {})) assertBudgetAmount(weight, `BudgetPolicy.weights['${analystId}']`);
|
|
2243
|
-
}
|
|
2244
|
-
function assertBudgetAmount(value, field) {
|
|
2245
|
-
if (!Number.isFinite(value) || value < 0) throw new Error(`${field} must be a non-negative finite number`);
|
|
2246
|
-
}
|
|
2247
|
-
function validateEffectiveBudget(budgetUsd, remainingUsd, analystId) {
|
|
2248
|
-
if (budgetUsd !== void 0) assertBudgetAmount(budgetUsd, `AnalystContext.budgetUsd for '${analystId}'`);
|
|
2249
|
-
if (remainingUsd === void 0) return budgetUsd;
|
|
2250
|
-
if (budgetUsd === void 0) throw new Error(`AnalystContext.budgetUsd for '${analystId}' cannot be removed while an overall budget remains`);
|
|
2251
|
-
if (budgetUsd > remainingUsd) throw new Error(`AnalystContext.budgetUsd for '${analystId}' (${budgetUsd}) exceeds the remaining overall budget (${remainingUsd})`);
|
|
2252
|
-
return budgetUsd;
|
|
2253
|
-
}
|
|
2254
|
-
function analystWeight(weights, analystId) {
|
|
2255
|
-
const weight = weights[analystId] ?? 1;
|
|
2256
|
-
assertBudgetAmount(weight, `BudgetPolicy.weights['${analystId}']`);
|
|
2257
|
-
return weight;
|
|
2258
|
-
}
|
|
2259
|
-
function zeroUsage() {
|
|
2260
|
-
return {
|
|
2261
|
-
calls: 0,
|
|
2262
|
-
tokens: {
|
|
2263
|
-
input: 0,
|
|
2264
|
-
output: 0
|
|
2265
|
-
},
|
|
2266
|
-
cost: {
|
|
2267
|
-
kind: "observed",
|
|
2268
|
-
usd: 0
|
|
2269
|
-
}
|
|
2270
|
-
};
|
|
2271
|
-
}
|
|
2272
|
-
function resolveUsage(analyst, receipts, exact = false) {
|
|
2273
|
-
if (receipts.length > 0) return mergeUsageReceipts(receipts, exact);
|
|
2274
|
-
if (analyst.cost.kind === "deterministic") return zeroUsage();
|
|
2275
|
-
return {
|
|
2276
|
-
calls: null,
|
|
2277
|
-
tokens: null,
|
|
2278
|
-
cost: {
|
|
2279
|
-
kind: "uncaptured",
|
|
2280
|
-
usd: null
|
|
2281
|
-
}
|
|
2282
|
-
};
|
|
2283
|
-
}
|
|
2284
|
-
function mergeUsageReceipts(receipts, exact = false) {
|
|
2285
|
-
const calls = receipts.every((receipt) => receipt.calls !== null) ? usageSum(receipts.map((receipt) => receipt.calls ?? 0), exact, "calls", true) : null;
|
|
2286
|
-
const tokens = receipts.every((receipt) => receipt.tokens !== null) ? Object.fromEntries([
|
|
2287
|
-
"input",
|
|
2288
|
-
"output",
|
|
2289
|
-
"reasoning",
|
|
2290
|
-
"cached",
|
|
2291
|
-
"cacheWrite"
|
|
2292
|
-
].flatMap((field) => field === "input" || field === "output" || receipts.some((receipt) => receipt.tokens?.[field] !== void 0) ? [[field, usageSum(receipts.map((receipt) => receipt.tokens?.[field] ?? 0), exact, `tokens.${field}`, true)]] : [])) : null;
|
|
2293
|
-
const cost = aggregateCostProvenance(receipts.map((receipt) => receipt.cost), exact);
|
|
2294
|
-
return {
|
|
2295
|
-
calls,
|
|
2296
|
-
tokens,
|
|
2297
|
-
cost,
|
|
2298
|
-
...cost.kind === "uncaptured" ? { knownCostUsd: usageSum(receipts.map(knownCostUsd), exact, "known cost") } : {}
|
|
2299
|
-
};
|
|
2300
|
-
}
|
|
2301
|
-
function knownCostUsd(receipt) {
|
|
2302
|
-
return receipt.cost.kind === "uncaptured" ? receipt.knownCostUsd ?? 0 : receipt.cost.usd;
|
|
2303
|
-
}
|
|
2304
|
-
function budgetDebit(receipt, allocatedUsd) {
|
|
2305
|
-
const known = knownCostUsd(receipt);
|
|
2306
|
-
return receipt.cost.kind === "uncaptured" && allocatedUsd !== void 0 ? Math.max(known, allocatedUsd) : known;
|
|
2307
|
-
}
|
|
2308
|
-
function aggregateCostProvenance(costs, exact = false) {
|
|
2309
|
-
if (costs.some((cost) => cost.kind === "uncaptured")) return {
|
|
2310
|
-
kind: "uncaptured",
|
|
2311
|
-
usd: null
|
|
2312
|
-
};
|
|
2313
|
-
const usd = usageSum(costs.map((cost) => cost.usd ?? 0), exact, "captured cost");
|
|
2314
|
-
return costs.some((cost) => cost.kind === "estimated") ? {
|
|
2315
|
-
kind: "estimated",
|
|
2316
|
-
usd
|
|
2317
|
-
} : {
|
|
2318
|
-
kind: "observed",
|
|
2319
|
-
usd
|
|
2320
|
-
};
|
|
2321
|
-
}
|
|
2322
|
-
function executionCost(executions, exact) {
|
|
2323
|
-
const usages = executions.map((execution) => execution.summary.usage);
|
|
2324
|
-
return {
|
|
2325
|
-
known: usageSum(usages.map(knownCostUsd), exact, "run known cost"),
|
|
2326
|
-
provenance: aggregateCostProvenance(usages.map((usage) => usage.cost), exact)
|
|
2327
|
-
};
|
|
2328
|
-
}
|
|
2329
|
-
function usageSum(values, exact, field, integer = false) {
|
|
2330
|
-
const sum = values.reduce((total, value) => total + value, 0);
|
|
2331
|
-
if (exact && (integer ? !Number.isSafeInteger(sum) : !Number.isFinite(sum))) throw new RangeError(`exact analyst usage ${field} aggregate ${integer ? "exceeds a safe integer" : "is not finite"}`);
|
|
2332
|
-
return sum;
|
|
2333
|
-
}
|
|
2334
|
-
/**
|
|
2335
|
-
* Resolve the `priorFindings` slice an analyst sees.
|
|
2336
|
-
*
|
|
2337
|
-
* - Array form → the analyst sees only findings whose `analyst_id`
|
|
2338
|
-
* matches its own id, so a kind never reads
|
|
2339
|
-
* another kind's history by accident.
|
|
2340
|
-
* - Record form → the analyst gets the entry keyed by its id, with
|
|
2341
|
-
* the `'*'` wildcard appended (in that order). Use
|
|
2342
|
-
* the wildcard when several kinds should see the same
|
|
2343
|
-
* historical findings.
|
|
2344
|
-
*/
|
|
2274
|
+
/** Return the analyst's own history, followed by any broadcast findings. */
|
|
2345
2275
|
function selectPriorFindings(source, analystId) {
|
|
2346
2276
|
if (!source) return void 0;
|
|
2347
2277
|
if (Array.isArray(source)) {
|
|
@@ -2355,6 +2285,61 @@ function selectPriorFindings(source, analystId) {
|
|
|
2355
2285
|
return merged.length > 0 ? merged : void 0;
|
|
2356
2286
|
}
|
|
2357
2287
|
//#endregion
|
|
2288
|
+
//#region src/analyst/registry.ts
|
|
2289
|
+
var AnalystRegistry = class {
|
|
2290
|
+
analysts = /* @__PURE__ */ new Map();
|
|
2291
|
+
options;
|
|
2292
|
+
constructor(options = {}) {
|
|
2293
|
+
this.options = options;
|
|
2294
|
+
}
|
|
2295
|
+
register(analyst) {
|
|
2296
|
+
const id = analyst.id;
|
|
2297
|
+
const version = analyst.version;
|
|
2298
|
+
const cost = analyst.cost;
|
|
2299
|
+
if (!id) throw new Error("AnalystRegistry.register: analyst.id is required");
|
|
2300
|
+
if (this.analysts.has(id)) throw new Error(`AnalystRegistry.register: duplicate analyst id "${id}"`);
|
|
2301
|
+
if (!version) throw new Error(`AnalystRegistry.register: analyst "${id}" must declare a version`);
|
|
2302
|
+
if (cost.kind === "deterministic" && cost.settlement_timeout_ms !== void 0) throw new TypeError(`AnalystRegistry.register: deterministic analyst "${id}" cannot declare settlement_timeout_ms`);
|
|
2303
|
+
if (cost.settlement_timeout_ms !== void 0) validateUsageSettlementTimeout(cost.settlement_timeout_ms);
|
|
2304
|
+
this.analysts.set(id, analyst);
|
|
2305
|
+
}
|
|
2306
|
+
list() {
|
|
2307
|
+
return Array.from(this.analysts.values()).map((a) => ({
|
|
2308
|
+
id: a.id,
|
|
2309
|
+
description: a.description,
|
|
2310
|
+
version: a.version,
|
|
2311
|
+
cost: a.cost
|
|
2312
|
+
}));
|
|
2313
|
+
}
|
|
2314
|
+
async run(runId, inputs, runOpts = {}) {
|
|
2315
|
+
for await (const ev of this.runStream(runId, inputs, runOpts)) if (ev.type === "run-completed") return ev.result;
|
|
2316
|
+
throw new Error("AnalystRegistry.run: stream completed without run-completed event");
|
|
2317
|
+
}
|
|
2318
|
+
/** Run exactly the ordered analysts and complete policy supplied by the caller. */
|
|
2319
|
+
async runExact(runId, inputs, runOpts) {
|
|
2320
|
+
for await (const ev of this.runExactStream(runId, inputs, runOpts)) if (ev.type === "run-completed") return ev.result;
|
|
2321
|
+
throw new Error("AnalystRegistry.runExact: stream completed without run-completed event");
|
|
2322
|
+
}
|
|
2323
|
+
/** Streaming counterpart to {@link runExact}. */
|
|
2324
|
+
async *runExactStream(runId, inputs, runOpts) {
|
|
2325
|
+
for await (const event of executePlanStream(normalizeExactPlan(runId, inputs, runOpts, this.analysts, this.options))) yield event;
|
|
2326
|
+
}
|
|
2327
|
+
/**
|
|
2328
|
+
* Streaming counterpart to `run()`. Emits `AnalystRunEvent` values
|
|
2329
|
+
* in real time — `run-started`, then per-analyst `skipped` /
|
|
2330
|
+
* `started` / `completed`, then a terminal `run-completed` whose
|
|
2331
|
+
* payload is the full `AnalystRunResult`. UIs use this to render
|
|
2332
|
+
* progress; persistence consumers use `run()` and read the result.
|
|
2333
|
+
*
|
|
2334
|
+
* Hooks (`onBeforeAnalyze` / `onAfterAnalyze` / `onError` /
|
|
2335
|
+
* `onComplete`) fire as before — streaming is additive, not a hook
|
|
2336
|
+
* replacement.
|
|
2337
|
+
*/
|
|
2338
|
+
async *runStream(runId, inputs, runOpts = {}) {
|
|
2339
|
+
yield* executePlanStream(normalizeLegacyPlan(runId, inputs, runOpts, this.analysts, this.options));
|
|
2340
|
+
}
|
|
2341
|
+
};
|
|
2342
|
+
//#endregion
|
|
2358
2343
|
//#region src/analyst/default-registry.ts
|
|
2359
2344
|
function buildDefaultAnalystRegistry(options = {}) {
|
|
2360
2345
|
if (options.definitions && !options.engine) throw new TypeError("buildDefaultAnalystRegistry: definitions require an engine — a definition cannot run without one");
|
|
@@ -2366,4 +2351,4 @@ function buildDefaultAnalystRegistry(options = {}) {
|
|
|
2366
2351
|
//#endregion
|
|
2367
2352
|
export { completedAnalystReviewQuality as _, KNOWLEDGE_POISONING_KIND_SPEC as a, validateAnalystReviewDecisions as b, FAILURE_MODE_KIND_SPEC as c, emitControlIntegrityFindings as d, behavioralAnalyst as f, assertUniqueFindingIds as g, analystRunDigest as h, DEFAULT_TRACE_ANALYST_KINDS as i, CONTROL_INTEGRITY_ANALYST as l, analystFindingDigest as m, AnalystRegistry as n, KNOWLEDGE_GAP_KIND_SPEC as o, deriveEfficiencyFindings as p, ExactAnalystRunExecutionError as r, IMPROVEMENT_KIND_SPEC as s, buildDefaultAnalystRegistry as t, ControlIntegrityAnalyst as u, readAnalystReview as v, snapshotAnalystRun as y };
|
|
2368
2353
|
|
|
2369
|
-
//# sourceMappingURL=default-registry-
|
|
2354
|
+
//# sourceMappingURL=default-registry-Cn1LQ2Ve.js.map
|