ai-capability-framework 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +38 -30
  3. package/ROADMAP.md +14 -0
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +32 -1
  6. package/dist/discovery/bindings.d.ts +7 -0
  7. package/dist/discovery/bindings.d.ts.map +1 -0
  8. package/dist/discovery/bindings.js +24 -0
  9. package/dist/discovery/budget.d.ts +17 -0
  10. package/dist/discovery/budget.d.ts.map +1 -0
  11. package/dist/discovery/budget.js +77 -0
  12. package/dist/discovery/configuration.d.ts +10 -0
  13. package/dist/discovery/configuration.d.ts.map +1 -0
  14. package/dist/discovery/configuration.js +63 -0
  15. package/dist/discovery/corpus.d.ts +8 -0
  16. package/dist/discovery/corpus.d.ts.map +1 -0
  17. package/dist/discovery/corpus.js +64 -0
  18. package/dist/discovery/diagnostics.d.ts +15 -0
  19. package/dist/discovery/diagnostics.d.ts.map +1 -0
  20. package/dist/discovery/diagnostics.js +11 -0
  21. package/dist/discovery/discoverer.d.ts +23 -0
  22. package/dist/discovery/discoverer.d.ts.map +1 -0
  23. package/dist/discovery/discoverer.js +261 -0
  24. package/dist/discovery/embedding-configuration.d.ts +12 -0
  25. package/dist/discovery/embedding-configuration.d.ts.map +1 -0
  26. package/dist/discovery/embedding-configuration.js +81 -0
  27. package/dist/discovery/embedding-types.d.ts +75 -0
  28. package/dist/discovery/embedding-types.d.ts.map +1 -0
  29. package/dist/discovery/embedding-types.js +1 -0
  30. package/dist/discovery/embeddings.d.ts +38 -0
  31. package/dist/discovery/embeddings.d.ts.map +1 -0
  32. package/dist/discovery/embeddings.js +71 -0
  33. package/dist/discovery/evaluation-validation.d.ts +8 -0
  34. package/dist/discovery/evaluation-validation.d.ts.map +1 -0
  35. package/dist/discovery/evaluation-validation.js +19 -0
  36. package/dist/discovery/evaluation.d.ts +84 -0
  37. package/dist/discovery/evaluation.d.ts.map +1 -0
  38. package/dist/discovery/evaluation.js +310 -0
  39. package/dist/discovery/export.d.ts +14 -0
  40. package/dist/discovery/export.d.ts.map +1 -0
  41. package/dist/discovery/export.js +81 -0
  42. package/dist/discovery/facade.d.ts +82 -0
  43. package/dist/discovery/facade.d.ts.map +1 -0
  44. package/dist/discovery/facade.js +266 -0
  45. package/dist/discovery/fingerprint.d.ts +71 -0
  46. package/dist/discovery/fingerprint.d.ts.map +1 -0
  47. package/dist/discovery/fingerprint.js +67 -0
  48. package/dist/discovery/index-builder.d.ts +3 -0
  49. package/dist/discovery/index-builder.d.ts.map +1 -0
  50. package/dist/discovery/index-builder.js +129 -0
  51. package/dist/discovery/index-validation.d.ts +12 -0
  52. package/dist/discovery/index-validation.d.ts.map +1 -0
  53. package/dist/discovery/index-validation.js +117 -0
  54. package/dist/discovery/index.d.ts +32 -0
  55. package/dist/discovery/index.d.ts.map +1 -0
  56. package/dist/discovery/index.js +18 -0
  57. package/dist/discovery/json-snapshot.d.ts +3 -0
  58. package/dist/discovery/json-snapshot.d.ts.map +1 -0
  59. package/dist/discovery/json-snapshot.js +11 -0
  60. package/dist/discovery/lexical.d.ts +12 -0
  61. package/dist/discovery/lexical.d.ts.map +1 -0
  62. package/dist/discovery/lexical.js +26 -0
  63. package/dist/discovery/metadata.d.ts +4 -0
  64. package/dist/discovery/metadata.d.ts.map +1 -0
  65. package/dist/discovery/metadata.js +33 -0
  66. package/dist/discovery/ranking.d.ts +7 -0
  67. package/dist/discovery/ranking.d.ts.map +1 -0
  68. package/dist/discovery/ranking.js +68 -0
  69. package/dist/discovery/replay.d.ts +50 -0
  70. package/dist/discovery/replay.d.ts.map +1 -0
  71. package/dist/discovery/replay.js +175 -0
  72. package/dist/discovery/retrieval-policy.d.ts +5 -0
  73. package/dist/discovery/retrieval-policy.d.ts.map +1 -0
  74. package/dist/discovery/retrieval-policy.js +10 -0
  75. package/dist/discovery/router.d.ts +39 -0
  76. package/dist/discovery/router.d.ts.map +1 -0
  77. package/dist/discovery/router.js +98 -0
  78. package/dist/discovery/selection-attempt.d.ts +20 -0
  79. package/dist/discovery/selection-attempt.d.ts.map +1 -0
  80. package/dist/discovery/selection-attempt.js +39 -0
  81. package/dist/discovery/selection-cards.d.ts +12 -0
  82. package/dist/discovery/selection-cards.d.ts.map +1 -0
  83. package/dist/discovery/selection-cards.js +79 -0
  84. package/dist/discovery/selection-configuration.d.ts +18 -0
  85. package/dist/discovery/selection-configuration.d.ts.map +1 -0
  86. package/dist/discovery/selection-configuration.js +71 -0
  87. package/dist/discovery/selection-response.d.ts +13 -0
  88. package/dist/discovery/selection-response.d.ts.map +1 -0
  89. package/dist/discovery/selection-response.js +47 -0
  90. package/dist/discovery/selection-types.d.ts +18 -0
  91. package/dist/discovery/selection-types.d.ts.map +1 -0
  92. package/dist/discovery/selection-types.js +1 -0
  93. package/dist/discovery/selection.d.ts +6 -0
  94. package/dist/discovery/selection.d.ts.map +1 -0
  95. package/dist/discovery/selection.js +145 -0
  96. package/dist/discovery/types.d.ts +291 -0
  97. package/dist/discovery/types.d.ts.map +1 -0
  98. package/dist/discovery/types.js +1 -0
  99. package/dist/discovery/usage.d.ts +2 -0
  100. package/dist/discovery/usage.d.ts.map +1 -0
  101. package/dist/discovery/usage.js +1 -0
  102. package/dist/discovery/vectors.d.ts +5 -0
  103. package/dist/discovery/vectors.d.ts.map +1 -0
  104. package/dist/discovery/vectors.js +43 -0
  105. package/dist/discovery/versions.d.ts +37 -0
  106. package/dist/discovery/versions.d.ts.map +1 -0
  107. package/dist/discovery/versions.js +36 -0
  108. package/dist/evals-live/live-eval-runner.d.ts +1 -0
  109. package/dist/evals-live/live-eval-runner.d.ts.map +1 -1
  110. package/dist/evals-live/live-eval-runner.js +12 -1
  111. package/dist/evals-live/types.d.ts +3 -0
  112. package/dist/evals-live/types.d.ts.map +1 -1
  113. package/dist/generated/manifest-types.d.ts +3655 -0
  114. package/dist/generated/manifest-types.d.ts.map +1 -1
  115. package/dist/loader.d.ts.map +1 -1
  116. package/dist/loader.js +8 -0
  117. package/dist/mcp-server/server.d.ts.map +1 -1
  118. package/dist/mcp-server/server.js +11 -2
  119. package/dist/mcp-server/types.d.ts +4 -0
  120. package/dist/mcp-server/types.d.ts.map +1 -1
  121. package/dist/observability/redaction.d.ts.map +1 -1
  122. package/dist/observability/redaction.js +6 -2
  123. package/dist/observability/types.d.ts +1 -1
  124. package/dist/observability/types.d.ts.map +1 -1
  125. package/dist/openai/index.d.ts +2 -0
  126. package/dist/openai/index.d.ts.map +1 -1
  127. package/dist/openai/index.js +1 -0
  128. package/dist/openai/responses-runner.d.ts +1 -1
  129. package/dist/openai/responses-runner.d.ts.map +1 -1
  130. package/dist/openai/responses-runner.js +134 -7
  131. package/dist/openai/selection-client.d.ts +28 -0
  132. package/dist/openai/selection-client.d.ts.map +1 -0
  133. package/dist/openai/selection-client.js +95 -0
  134. package/dist/openai/tool-loop.js +1 -1
  135. package/dist/openai/types.d.ts +19 -2
  136. package/dist/openai/types.d.ts.map +1 -1
  137. package/dist/replay/discovery-snapshot.d.ts +10 -0
  138. package/dist/replay/discovery-snapshot.d.ts.map +1 -0
  139. package/dist/replay/discovery-snapshot.js +17 -0
  140. package/dist/replay/recorder.d.ts.map +1 -1
  141. package/dist/replay/recorder.js +4 -0
  142. package/dist/replay/redaction.js +5 -1
  143. package/dist/replay/replayer.d.ts.map +1 -1
  144. package/dist/replay/replayer.js +14 -1
  145. package/dist/replay/types.d.ts +3 -0
  146. package/dist/replay/types.d.ts.map +1 -1
  147. package/dist/replay/validation.d.ts.map +1 -1
  148. package/dist/replay/validation.js +6 -0
  149. package/dist/runtime/bounded-json.d.ts +4 -0
  150. package/dist/runtime/bounded-json.d.ts.map +1 -0
  151. package/dist/runtime/bounded-json.js +74 -0
  152. package/dist/runtime/eligibility.d.ts +4 -0
  153. package/dist/runtime/eligibility.d.ts.map +1 -0
  154. package/dist/runtime/eligibility.js +128 -0
  155. package/dist/runtime/index.d.ts +5 -1
  156. package/dist/runtime/index.d.ts.map +1 -1
  157. package/dist/runtime/index.js +2 -0
  158. package/dist/runtime/provider-attempt.d.ts +11 -0
  159. package/dist/runtime/provider-attempt.d.ts.map +1 -0
  160. package/dist/runtime/provider-attempt.js +221 -0
  161. package/dist/runtime/provider-run.d.ts +10 -0
  162. package/dist/runtime/provider-run.d.ts.map +1 -0
  163. package/dist/runtime/provider-run.js +123 -0
  164. package/dist/runtime/provider-stop.d.ts +9 -0
  165. package/dist/runtime/provider-stop.d.ts.map +1 -0
  166. package/dist/runtime/provider-stop.js +28 -0
  167. package/dist/runtime/provider-types.d.ts +189 -0
  168. package/dist/runtime/provider-types.d.ts.map +1 -0
  169. package/dist/runtime/provider-types.js +1 -0
  170. package/dist/runtime/provider-usage.d.ts +9 -0
  171. package/dist/runtime/provider-usage.d.ts.map +1 -0
  172. package/dist/runtime/provider-usage.js +69 -0
  173. package/dist/runtime/router.d.ts.map +1 -1
  174. package/dist/runtime/router.js +3 -118
  175. package/dist/runtime/types.d.ts +9 -0
  176. package/dist/runtime/types.d.ts.map +1 -1
  177. package/dist/types.d.ts +1 -1
  178. package/dist/types.d.ts.map +1 -1
  179. package/dist/validator.d.ts +3 -0
  180. package/dist/validator.d.ts.map +1 -1
  181. package/dist/validator.js +32 -0
  182. package/docs/api/public-api-policy.md +10 -0
  183. package/docs/api/stable-workflow.md +58 -0
  184. package/docs/api.md +7 -0
  185. package/docs/capability-manifests.md +24 -0
  186. package/docs/development.md +22 -0
  187. package/docs/discovery.md +186 -0
  188. package/docs/evals/discovery-live.md +199 -0
  189. package/docs/evals/discovery-qualification.md +158 -0
  190. package/docs/evals/overview.md +9 -0
  191. package/docs/getting-started/installation.md +11 -12
  192. package/docs/getting-started/openai-quickstart.md +16 -5
  193. package/docs/getting-started/provider-neutral-quickstart.md +247 -88
  194. package/docs/getting-started/quickstart.md +8 -30
  195. package/docs/index.md +10 -0
  196. package/docs/release.md +29 -5
  197. package/docs/runtime.md +27 -0
  198. package/docs/start-here.md +23 -134
  199. package/examples/12-capability-discovery-selection/README.md +346 -0
  200. package/examples/12-capability-discovery-selection/customer-cases.mjs +52 -0
  201. package/examples/12-capability-discovery-selection/customer-harness.mjs +91 -0
  202. package/examples/12-capability-discovery-selection/customer-request-example.mjs +320 -0
  203. package/examples/12-capability-discovery-selection/discovery-example.mjs +175 -0
  204. package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.suite.json +7887 -0
  205. package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.v2.suite.json +8167 -0
  206. package/examples/12-capability-discovery-selection/discovery-fixtures/three-domains.v3.suite.json +8342 -0
  207. package/examples/12-capability-discovery-selection/evaluation/application.mjs +305 -0
  208. package/examples/12-capability-discovery-selection/evaluation/campaign.mjs +248 -0
  209. package/examples/12-capability-discovery-selection/evaluation/clients.mjs +433 -0
  210. package/examples/12-capability-discovery-selection/evaluation/continuation.mjs +208 -0
  211. package/examples/12-capability-discovery-selection/evaluation/durable-budget.mjs +420 -0
  212. package/examples/12-capability-discovery-selection/evaluation/freeze.mjs +9 -0
  213. package/examples/12-capability-discovery-selection/evaluation/harness.mjs +618 -0
  214. package/examples/12-capability-discovery-selection/evaluation/mock-host.mjs +260 -0
  215. package/examples/12-capability-discovery-selection/evaluation/preflight.mjs +115 -0
  216. package/examples/12-capability-discovery-selection/evaluation/qualification-workflows.mjs +87 -0
  217. package/examples/12-capability-discovery-selection/evaluation/regrade.mjs +281 -0
  218. package/examples/12-capability-discovery-selection/evaluation/report.mjs +517 -0
  219. package/examples/12-capability-discovery-selection/evaluation/runner.mjs +613 -0
  220. package/examples/12-capability-discovery-selection/evaluation/saved-campaign.mjs +385 -0
  221. package/examples/12-capability-discovery-selection/evaluation/wording.mjs +115 -0
  222. package/examples/12-capability-discovery-selection/evaluation/workflow-application.mjs +378 -0
  223. package/examples/12-capability-discovery-selection/evaluation/workflow-cases.mjs +90 -0
  224. package/examples/12-capability-discovery-selection/evaluation-example.mjs +154 -0
  225. package/examples/12-capability-discovery-selection/hybrid-example.mjs +174 -0
  226. package/examples/12-capability-discovery-selection/mock-embeddings.mjs +101 -0
  227. package/examples/12-capability-discovery-selection/mock-selection.mjs +78 -0
  228. package/examples/12-capability-discovery-selection/pipeline-example.mjs +496 -0
  229. package/examples/12-capability-discovery-selection/retrieval-cases.mjs +31 -0
  230. package/examples/12-capability-discovery-selection/run-campaign-mock.mjs +40 -0
  231. package/examples/12-capability-discovery-selection/run-customer-mock.mjs +15 -0
  232. package/examples/12-capability-discovery-selection/run-evaluation-live.mjs +10 -0
  233. package/examples/12-capability-discovery-selection/run-evaluation-mock.mjs +3 -0
  234. package/examples/12-capability-discovery-selection/run-evaluation-regrade.mjs +71 -0
  235. package/examples/12-capability-discovery-selection/run-hybrid-mock.mjs +3 -0
  236. package/examples/12-capability-discovery-selection/run-mock.mjs +3 -0
  237. package/examples/12-capability-discovery-selection/run-pipeline-mock.mjs +2 -0
  238. package/examples/12-capability-discovery-selection/run-selector-mock.mjs +3 -0
  239. package/examples/12-capability-discovery-selection/selection-example.mjs +88 -0
  240. package/package.json +17 -4
  241. package/schemas/discovery/embedding-index.schema.json +71 -0
  242. package/schemas/discovery/eval-observations.schema.json +409 -0
  243. package/schemas/discovery/eval-suite.schema.json +375 -0
  244. package/schemas/discovery/metadata.schema.json +30 -0
  245. package/schemas/discovery/selection-response.schema.json +47 -0
  246. package/schemas/replay/discovery-snapshot.schema.json +335 -0
@@ -0,0 +1,420 @@
1
+ import {
2
+ appendFileSync,
3
+ closeSync,
4
+ existsSync,
5
+ fsyncSync,
6
+ mkdirSync,
7
+ openSync,
8
+ readFileSync,
9
+ renameSync,
10
+ statSync,
11
+ unlinkSync,
12
+ writeFileSync
13
+ } from "node:fs";
14
+ import path from "node:path";
15
+ import { hashDiscoveryContent } from "ai-capability-framework/discovery";
16
+ import { CAPS } from "./campaign.mjs";
17
+
18
+ const quantities = ["providerCalls", "inputTokens", "outputTokens", "estimatedCost"];
19
+ const zero = () => Object.fromEntries(quantities.map((key) => [key, 0]));
20
+ const nonnegative = (value) =>
21
+ typeof value === "number" && Number.isFinite(value) && value >= 0;
22
+
23
+ /** Evaluation-host storage only: one owner, durable reservations, conservative unknown charges. */
24
+ export function openCampaignLedger({ directory, binding, caps = CAPS }) {
25
+ if (existsSync(path.join(directory, "continuation.json")))
26
+ throw new Error(
27
+ "Campaign budget belongs to its continuation; predecessor spending is closed."
28
+ );
29
+ const resolvedCaps = { ...CAPS, ...caps };
30
+ for (const [key, maximum] of Object.entries(CAPS))
31
+ if (
32
+ !nonnegative(resolvedCaps[key]) ||
33
+ resolvedCaps[key] > maximum ||
34
+ (key !== "estimatedCost" && !Number.isSafeInteger(resolvedCaps[key]))
35
+ )
36
+ throw new Error("Invalid campaign caps.");
37
+ const fingerprint = hashDiscoveryContent({ binding, caps: resolvedCaps });
38
+ mkdirSync(directory, { recursive: true });
39
+ const lockPath = path.join(directory, "campaign.lock");
40
+ let lock;
41
+ try {
42
+ lock = openSync(lockPath, "wx");
43
+ writeFileSync(lock, String(process.pid));
44
+ fsyncSync(lock);
45
+ } catch {
46
+ throw new Error(
47
+ "Campaign is locked; do not remove a lock until its owner is stopped."
48
+ );
49
+ }
50
+ const journalPath = path.join(directory, "journal.jsonl");
51
+ let descriptor,
52
+ closed = false,
53
+ halted = false,
54
+ sequence = 0,
55
+ previous = null;
56
+ const reservations = new Map(),
57
+ settled = new Set(),
58
+ http = new Map(),
59
+ trials = new Map(),
60
+ records = [];
61
+ const charged = zero();
62
+ let httpCount = 0;
63
+ const apply = (event) => {
64
+ const data = event.data;
65
+ if (event.type === "reservation") reservations.set(data.id, data);
66
+ if (event.type === "settlement") {
67
+ reservations.delete(data.id);
68
+ settled.add(data.id);
69
+ for (const key of quantities) charged[key] += data.charged[key];
70
+ }
71
+ if (event.type === "http.start") {
72
+ http.set(data.id, data);
73
+ httpCount++;
74
+ charged.estimatedCost += data.fee;
75
+ }
76
+ if (event.type === "http.end") http.delete(data.id);
77
+ if (event.type === "trial.start")
78
+ trials.set(data.key, { status: "pending", ...data });
79
+ if (event.type === "trial.end")
80
+ trials.set(data.key, { status: "completed", ...data });
81
+ if (event.type === "halt") halted = true;
82
+ records.push(event);
83
+ };
84
+ try {
85
+ if (existsSync(journalPath)) {
86
+ if (statSync(journalPath).size > 64 * 1024 * 1024)
87
+ throw new Error("Campaign journal exceeds its bound.");
88
+ const raw = readFileSync(journalPath, "utf8");
89
+ if (raw && !raw.endsWith("\n"))
90
+ throw new Error("Incomplete journal tail requires reconciliation.");
91
+ for (const line of raw.split("\n").filter(Boolean)) {
92
+ if (line.length > 128_000) throw new Error("Oversized journal record.");
93
+ const record = JSON.parse(line),
94
+ { hash, ...envelope } = record;
95
+ if (
96
+ envelope.sequence !== ++sequence ||
97
+ envelope.previous !== previous ||
98
+ hashDiscoveryContent(envelope) !== hash
99
+ )
100
+ throw new Error("Campaign journal integrity failure.");
101
+ if (
102
+ sequence === 1 &&
103
+ (envelope.type !== "campaign" || envelope.data.fingerprint !== fingerprint)
104
+ )
105
+ throw new Error("Campaign fingerprint mismatch.");
106
+ previous = hash;
107
+ apply(envelope);
108
+ }
109
+ }
110
+ descriptor = openSync(journalPath, "a");
111
+ } catch (error) {
112
+ closeSync(lock);
113
+ unlinkSync(lockPath);
114
+ throw error;
115
+ }
116
+
117
+ const append = (type, data) => {
118
+ if (closed) throw new Error("Campaign is closed.");
119
+ const envelope = { sequence: sequence + 1, previous, type, data };
120
+ const hash = hashDiscoveryContent(envelope),
121
+ serialized = JSON.stringify({ ...envelope, hash }) + "\n";
122
+ if (serialized.length > 128_000) throw new Error("Oversized campaign record.");
123
+ try {
124
+ appendFileSync(descriptor, serialized);
125
+ fsyncSync(descriptor);
126
+ } catch {
127
+ halted = true;
128
+ throw new Error("Campaign journal unavailable; no more paid work permitted.");
129
+ }
130
+ sequence++;
131
+ previous = hash;
132
+ apply(envelope);
133
+ };
134
+ if (sequence === 0) append("campaign", { fingerprint, binding, caps: resolvedCaps });
135
+ const held = () => {
136
+ const value = zero();
137
+ for (const reservation of reservations.values())
138
+ for (const key of quantities) value[key] += reservation.amounts[key];
139
+ return value;
140
+ };
141
+ const halt = (reasonCode) => {
142
+ if (!halted) append("halt", { reasonCode });
143
+ halted = true;
144
+ };
145
+ const available = (amounts) => {
146
+ const outstanding = held();
147
+ return (
148
+ !closed &&
149
+ !halted &&
150
+ quantities.every(
151
+ (key) => charged[key] + outstanding[key] + amounts[key] <= resolvedCaps[key]
152
+ )
153
+ );
154
+ };
155
+ const policy = Object.freeze({
156
+ id: "evaluation.campaign.v1",
157
+ scope: Object.freeze({ type: "global" }),
158
+ mode: "hard",
159
+ maxProviderCallsPerRun: resolvedCaps.providerCalls,
160
+ maxInputTokensPerRun: resolvedCaps.inputTokens,
161
+ maxOutputTokensPerRun: resolvedCaps.outputTokens,
162
+ maxEstimatedCostPerRun: resolvedCaps.estimatedCost
163
+ });
164
+
165
+ return Object.freeze({
166
+ fingerprint,
167
+ directory,
168
+ scope(runId) {
169
+ if (typeof runId !== "string" || !/^[a-zA-Z0-9_.-]{1,200}$/.test(runId))
170
+ throw new Error("Invalid campaign run ID.");
171
+ return {
172
+ policy,
173
+ ledger: Object.freeze({
174
+ async reserve(input) {
175
+ const denied = () => ({
176
+ status: "denied",
177
+ diagnostic: {
178
+ code: "budget_denied",
179
+ message: "The campaign budget denied this reservation."
180
+ }
181
+ });
182
+ const usage = input.requestedUsage;
183
+ if (
184
+ input.runId !== runId ||
185
+ hashDiscoveryContent(input.policy) !== hashDiscoveryContent(policy) ||
186
+ input.signal?.aborted ||
187
+ !Number.isFinite(input.deadlineAt) ||
188
+ input.deadlineAt <= Date.now() ||
189
+ !usage ||
190
+ usage.providerCalls !== 1 ||
191
+ !Number.isSafeInteger(usage.inputTokens) ||
192
+ usage.inputTokens < 1 ||
193
+ (usage.outputTokens !== undefined &&
194
+ (!Number.isSafeInteger(usage.outputTokens) ||
195
+ usage.outputTokens < 0)) ||
196
+ !nonnegative(usage.estimatedCost)
197
+ )
198
+ return denied();
199
+ const amounts = {
200
+ providerCalls: 1,
201
+ inputTokens: usage.inputTokens,
202
+ outputTokens: usage.outputTokens ?? 0,
203
+ estimatedCost: usage.estimatedCost
204
+ };
205
+ if (!available(amounts)) return denied();
206
+ const id = `reservation-${sequence + 1}`;
207
+ // Capacity check, durable append and in-memory update happen without yielding.
208
+ append("reservation", {
209
+ id,
210
+ runId,
211
+ requestId: input.requestId,
212
+ stage: input.stage,
213
+ amounts
214
+ });
215
+ return { status: "reserved", reservationId: id };
216
+ },
217
+ async settle(id, result) {
218
+ const reservation = reservations.get(id);
219
+ if (!reservation || reservation.runId !== runId || settled.has(id)) {
220
+ halt("invalid_settlement");
221
+ throw new Error("Invalid or duplicate campaign settlement.");
222
+ }
223
+ const usage = result.usage ?? {};
224
+ if (
225
+ !Number.isSafeInteger(result.providerAttempts) ||
226
+ result.providerAttempts < 0 ||
227
+ !Number.isSafeInteger(result.completedResponses) ||
228
+ result.completedResponses < 0 ||
229
+ !["completed", "failed", "cancelled", "discarded"].includes(
230
+ result.outcome
231
+ ) ||
232
+ Object.values(usage).some((value) => !nonnegative(value))
233
+ ) {
234
+ halt("invalid_settlement");
235
+ throw new Error("Malformed campaign settlement.");
236
+ }
237
+ const attempts = Math.max(
238
+ result.providerAttempts,
239
+ result.completedResponses
240
+ );
241
+ const chargedUsage = {
242
+ providerCalls: attempts,
243
+ inputTokens:
244
+ usage.inputTokens ?? (attempts ? reservation.amounts.inputTokens : 0),
245
+ outputTokens:
246
+ usage.outputTokens ?? (attempts ? reservation.amounts.outputTokens : 0),
247
+ estimatedCost:
248
+ usage.estimatedCost ??
249
+ (attempts ? reservation.amounts.estimatedCost : 0)
250
+ };
251
+ append("settlement", {
252
+ id,
253
+ runId,
254
+ outcome: result.outcome,
255
+ usage,
256
+ providerAttempts: result.providerAttempts,
257
+ completedResponses: result.completedResponses,
258
+ charged: chargedUsage,
259
+ unknown: quantities.filter(
260
+ (key) =>
261
+ key !== "providerCalls" && usage[key] === undefined && attempts > 0
262
+ )
263
+ });
264
+ if (
265
+ quantities.some((key) => chargedUsage[key] > reservation.amounts[key]) ||
266
+ attempts > 1
267
+ ) {
268
+ halt("reservation_exceeded");
269
+ throw new Error("Provider consumption exceeded its reserved allowance.");
270
+ }
271
+ }
272
+ })
273
+ };
274
+ },
275
+ async transport(kind, invoke, options = {}) {
276
+ const { signal, deadlineAt = Date.now() + 30_000, fee = 0 } = options;
277
+ if (signal?.aborted || deadlineAt <= Date.now())
278
+ throw Object.assign(new Error("Transport cancelled before dispatch."), {
279
+ providerAttempts: 0,
280
+ completedResponses: 0,
281
+ usage: {}
282
+ });
283
+ if (
284
+ !["generation", "embedding", "token_count"].includes(kind) ||
285
+ !nonnegative(fee) ||
286
+ httpCount >= resolvedCaps.httpRequests ||
287
+ !available({ ...zero(), estimatedCost: fee })
288
+ )
289
+ throw Object.assign(new Error("Campaign transport budget unavailable."), {
290
+ providerAttempts: 0,
291
+ completedResponses: 0,
292
+ usage: {}
293
+ });
294
+ const id = `http-${sequence + 1}`;
295
+ append("http.start", { id, kind, fee });
296
+ try {
297
+ const value = await invoke();
298
+ append("http.end", {
299
+ id,
300
+ status: "completed",
301
+ model: typeof value?.model === "string" ? value.model.slice(0, 100) : null,
302
+ responseId: typeof value?.id === "string" ? value.id.slice(0, 200) : null
303
+ });
304
+ return value;
305
+ } catch (error) {
306
+ append("http.end", { id, status: "failed", model: null, responseId: null });
307
+ const raw = error?.usage ?? {};
308
+ const usage = Object.fromEntries(
309
+ Object.entries({
310
+ inputTokens: raw.inputTokens ?? raw.input_tokens ?? raw.prompt_tokens,
311
+ outputTokens: raw.outputTokens ?? raw.output_tokens,
312
+ totalTokens: raw.totalTokens ?? raw.total_tokens,
313
+ cachedInputTokens:
314
+ raw.cachedInputTokens ?? raw.input_tokens_details?.cached_tokens,
315
+ reasoningTokens:
316
+ raw.reasoningTokens ?? raw.output_tokens_details?.reasoning_tokens,
317
+ estimatedCost: raw.estimatedCost
318
+ }).filter(([, value]) => nonnegative(value))
319
+ );
320
+ throw Object.assign(new Error("Evaluation transport unavailable."), {
321
+ usage,
322
+ providerAttempts: Number.isSafeInteger(error?.providerAttempts)
323
+ ? error.providerAttempts
324
+ : 1,
325
+ completedResponses: Number.isSafeInteger(error?.completedResponses)
326
+ ? error.completedResponses
327
+ : 0
328
+ });
329
+ }
330
+ },
331
+ beginTrial(key, trialBinding) {
332
+ const prior = trials.get(key);
333
+ if (prior && prior.binding !== hashDiscoveryContent(trialBinding))
334
+ throw new Error("Trial fingerprint mismatch.");
335
+ if (prior?.status === "completed")
336
+ return { completed: true, artifact: prior.artifact };
337
+ if (prior)
338
+ throw new Error(
339
+ "Uncertain prior trial requires reconciliation; it will not be reissued."
340
+ );
341
+ if (halted) throw new Error("Campaign accounting is halted.");
342
+ append("trial.start", { key, binding: hashDiscoveryContent(trialBinding) });
343
+ return { completed: false };
344
+ },
345
+ finishTrial(key, value) {
346
+ const prior = trials.get(key);
347
+ if (!prior || prior.status !== "pending")
348
+ throw new Error("Trial was not pending.");
349
+ const artifact = this.writeArtifact(
350
+ `trial-${hashDiscoveryContent(key).slice(7)}.json`,
351
+ value
352
+ );
353
+ append("trial.end", { key, binding: prior.binding, artifact });
354
+ },
355
+ writeArtifact(name, value) {
356
+ if (!/^[a-zA-Z0-9_.-]+\.json$/.test(name))
357
+ throw new Error("Invalid artifact name.");
358
+ const serialized = JSON.stringify(value, null, 2) + "\n";
359
+ if (Buffer.byteLength(serialized) > 16 * 1024 * 1024)
360
+ throw new Error("Artifact exceeds its bound.");
361
+ const target = path.join(directory, name),
362
+ temporary = target + ".pending";
363
+ if (existsSync(target)) {
364
+ if (readFileSync(target, "utf8") !== serialized)
365
+ throw new Error(
366
+ "Immutable campaign artifact already exists with different content."
367
+ );
368
+ } else {
369
+ const fd = openSync(temporary, "wx");
370
+ try {
371
+ writeFileSync(fd, serialized);
372
+ fsyncSync(fd);
373
+ } finally {
374
+ closeSync(fd);
375
+ }
376
+ renameSync(temporary, target);
377
+ }
378
+ return { name, hash: hashDiscoveryContent(value) };
379
+ },
380
+ readArtifact(artifact) {
381
+ if (!artifact || !/^[a-zA-Z0-9_.-]+\.json$/.test(artifact.name))
382
+ throw new Error("Invalid artifact reference.");
383
+ const target = path.join(directory, artifact.name);
384
+ if (statSync(target).size > 16 * 1024 * 1024)
385
+ throw new Error("Artifact exceeds its bound.");
386
+ const value = JSON.parse(readFileSync(target, "utf8"));
387
+ if (hashDiscoveryContent(value) !== artifact.hash)
388
+ throw new Error("Campaign artifact integrity failure.");
389
+ return value;
390
+ },
391
+ record(type, data) {
392
+ append(type, data);
393
+ },
394
+ records() {
395
+ return structuredClone(records);
396
+ },
397
+ snapshot() {
398
+ return structuredClone({
399
+ fingerprint,
400
+ charged,
401
+ held: held(),
402
+ pendingReservations: reservations.size,
403
+ pendingHttp: http.size,
404
+ httpRequests: httpCount,
405
+ halted,
406
+ completedTrials: [...trials.values()].filter((x) => x.status === "completed")
407
+ .length,
408
+ pendingTrials: [...trials.values()].filter((x) => x.status === "pending").length
409
+ });
410
+ },
411
+ close() {
412
+ if (!closed) {
413
+ closed = true;
414
+ closeSync(descriptor);
415
+ closeSync(lock);
416
+ unlinkSync(lockPath);
417
+ }
418
+ }
419
+ });
420
+ }
@@ -0,0 +1,9 @@
1
+ // Frozen before live evaluation. Revisions require a new dataset version and recorded reason.
2
+ export const FROZEN_V2 =
3
+ "sha256:5cdbd4803da0e9be680694e2b6556a86a9689780f92df657138b3c33ed5c37b2";
4
+ export const FROZEN_WORKFLOWS =
5
+ "sha256:bed2686fc0e6e79da1dd931eea588628dcac4a2e2cc68d681e441bb57835f056";
6
+ export const FROZEN_V3 =
7
+ "sha256:5e5f0cb1a848b733174e6e2e444e7c3bd8a90680fcd6009c7323d635c3605f69";
8
+ export const FROZEN_QUALIFICATION_WORKFLOWS =
9
+ "sha256:8fabf71450f0d3b04a9d4b543d844bc94b0e70e20c698897db127a7d516cff61";