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,613 @@
1
+ import { readFileSync, existsSync, readdirSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath, pathToFileURL } from "node:url";
4
+ import {
5
+ hashDiscoveryContent,
6
+ fingerprintDiscoveryEvaluationSuite,
7
+ evaluateCapabilityDiscovery
8
+ } from "ai-capability-framework/discovery";
9
+ import { validatePublicFixtures } from "ai-capability-framework";
10
+ import {
11
+ loadSuite,
12
+ registryFor,
13
+ campaignBinding,
14
+ validateDataset,
15
+ trialSchedule,
16
+ CAPS,
17
+ MODES,
18
+ EXECUTION
19
+ } from "./campaign.mjs";
20
+ import { WORKFLOWS as DEVELOPMENT_WORKFLOWS } from "./workflow-cases.mjs";
21
+ import { QUALIFICATION_WORKFLOWS as WORKFLOWS } from "./qualification-workflows.mjs";
22
+ import { openContinuationLedger, verifyPredecessor } from "./continuation.mjs";
23
+ import { preflightCampaign } from "./preflight.mjs";
24
+ import { openCampaignLedger } from "./durable-budget.mjs";
25
+ import { createEvaluationClients } from "./clients.mjs";
26
+ import {
27
+ buildEvaluationIndex,
28
+ runTaskTrial,
29
+ runWorkflowTrial,
30
+ validateIndex
31
+ } from "./harness.mjs";
32
+ import { qualificationReport } from "./report.mjs";
33
+ import {
34
+ FROZEN_V2,
35
+ FROZEN_WORKFLOWS,
36
+ FROZEN_V3,
37
+ FROZEN_QUALIFICATION_WORKFLOWS
38
+ } from "./freeze.mjs";
39
+
40
+ export function sourceFingerprint(
41
+ root = fileURLToPath(new URL("../../../", import.meta.url))
42
+ ) {
43
+ const paths = [];
44
+ const visit = (directory) => {
45
+ for (const item of readdirSync(directory, { withFileTypes: true })) {
46
+ if (
47
+ [
48
+ "node_modules",
49
+ ".git",
50
+ ".aicf",
51
+ "_private",
52
+ "private",
53
+ "local",
54
+ "scratch",
55
+ "drafts",
56
+ "archives",
57
+ "backups",
58
+ "dist",
59
+ "dist-source",
60
+ "generated-docs",
61
+ "coverage",
62
+ "tmp",
63
+ "test-results"
64
+ ].includes(item.name) ||
65
+ item.name.startsWith(".")
66
+ )
67
+ continue;
68
+ const target = path.join(directory, item.name);
69
+ if (item.isDirectory()) visit(target);
70
+ else if (/\.(?:ts|mjs|json|yaml|yml|md)$/.test(item.name))
71
+ paths.push({
72
+ path: path.relative(root, target).replaceAll("\\", "/"),
73
+ contentHash: hashDiscoveryContent(readFileSync(target, "utf8"))
74
+ });
75
+ }
76
+ };
77
+ visit(root);
78
+ return hashDiscoveryContent(
79
+ paths.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0))
80
+ );
81
+ }
82
+
83
+ export function validateOffline() {
84
+ const suite = loadSuite(),
85
+ registry = registryFor(suite);
86
+ if (
87
+ fingerprintDiscoveryEvaluationSuite(suite) !== FROZEN_V3 ||
88
+ hashDiscoveryContent(WORKFLOWS) !== FROZEN_QUALIFICATION_WORKFLOWS ||
89
+ hashDiscoveryContent(DEVELOPMENT_WORKFLOWS) !== FROZEN_WORKFLOWS
90
+ )
91
+ throw new Error("Frozen evaluation material changed; use a new dataset version.");
92
+ const oldV2 = JSON.parse(
93
+ readFileSync(
94
+ new URL("../discovery-fixtures/three-domains.v2.suite.json", import.meta.url),
95
+ "utf8"
96
+ )
97
+ );
98
+ if (fingerprintDiscoveryEvaluationSuite(oldV2) !== FROZEN_V2)
99
+ throw new Error("Historical v2 suite changed.");
100
+ const inventory = validateDataset(suite, WORKFLOWS);
101
+ const checked = validatePublicFixtures([
102
+ {
103
+ kind: "discovery_eval_suite",
104
+ fixture: suite,
105
+ path: "synthetic/v2.suite.json",
106
+ absolutePath: "/synthetic/v2.suite.json"
107
+ }
108
+ ]);
109
+ if (!checked.valid)
110
+ throw new Error("Evaluation suite schema or references are invalid.");
111
+ // Validate structure/references through the public evaluator without buying retrieval or model work.
112
+ const sample = JSON.parse(
113
+ readFileSync(
114
+ new URL("../discovery-fixtures/three-domains.suite.json", import.meta.url),
115
+ "utf8"
116
+ )
117
+ );
118
+ if (
119
+ fingerprintDiscoveryEvaluationSuite(sample) !==
120
+ "sha256:01d1ddfeb6c7768bf5108fc00917ce0a4d95283cfaf14dd5910adde2b55969d2"
121
+ )
122
+ throw new Error("Legacy suite changed.");
123
+ const retained = (value) =>
124
+ value.cases.filter((entry) => entry.partition !== "held_out");
125
+ if (hashDiscoveryContent(retained(sample)) !== hashDiscoveryContent(retained(suite)))
126
+ throw new Error("Unrecorded train/calibration request or label correction.");
127
+ return {
128
+ suite,
129
+ registry,
130
+ inventory,
131
+ suiteFingerprint: FROZEN_V3,
132
+ workflowsFingerprint: FROZEN_QUALIFICATION_WORKFLOWS
133
+ };
134
+ }
135
+
136
+ export function readHostConfiguration(filename) {
137
+ if (statSync(filename).size > 64_000)
138
+ throw new Error("Host configuration exceeds its bound.");
139
+ const config = JSON.parse(readFileSync(filename, "utf8"));
140
+ const allowed = [
141
+ "campaignId",
142
+ "hostModule",
143
+ "pricing",
144
+ "tokenizerIdentity",
145
+ "sdkVersion",
146
+ "evidenceDirectory",
147
+ "predecessor"
148
+ ];
149
+ if (
150
+ !config ||
151
+ Object.keys(config).some((key) => !allowed.includes(key)) ||
152
+ !/^[a-zA-Z0-9_.-]{1,80}$/.test(config.campaignId) ||
153
+ !["hostModule", "tokenizerIdentity", "sdkVersion"].every(
154
+ (key) => typeof config[key] === "string" && config[key].trim()
155
+ )
156
+ )
157
+ throw new Error(
158
+ "Invalid host configuration; credentials must remain with the injected client."
159
+ );
160
+ const hostModule = path.resolve(path.dirname(filename), config.hostModule);
161
+ return {
162
+ ...config,
163
+ hostModule,
164
+ predecessor: config.predecessor
165
+ ? {
166
+ ...config.predecessor,
167
+ directory: path.resolve(path.dirname(filename), config.predecessor.directory)
168
+ }
169
+ : undefined,
170
+ hostModuleHash: hashDiscoveryContent(readFileSync(hostModule, "utf8"))
171
+ };
172
+ }
173
+
174
+ /** No host module, SDK, credential or network is touched unless live execution is explicit. */
175
+ export async function runCampaign(options = {}) {
176
+ const { suite, registry, inventory } = validateOffline();
177
+ if (!options.live && !options.mockFactory && options.stage !== "report")
178
+ return {
179
+ status: "validated_offline",
180
+ inventory,
181
+ providerCalls: 0,
182
+ suiteFingerprint: FROZEN_V3,
183
+ workflowsFingerprint: FROZEN_QUALIFICATION_WORKFLOWS,
184
+ schedule: {
185
+ train: trialSchedule(suite, "train").length,
186
+ baseline: trialSchedule(suite, "baseline").length,
187
+ qualifyK8: trialSchedule(suite, "qualify", 8).length,
188
+ workflows: WORKFLOWS.length * MODES.length
189
+ }
190
+ };
191
+ const config = options.mockFactory
192
+ ? {
193
+ campaignId: options.campaignId ?? "mock-integration",
194
+ hostModuleHash: "mock",
195
+ sdkVersion: "deterministic-mock",
196
+ tokenizerIdentity: "synthetic-token-count.v1",
197
+ predecessor: options.predecessor
198
+ }
199
+ : readHostConfiguration(options.configFile);
200
+ if (options.sourceFingerprint && !options.mockFactory)
201
+ throw new Error("Live source bindings cannot be overridden.");
202
+ const source = options.sourceFingerprint ?? sourceFingerprint();
203
+ if (options.live) {
204
+ const gatePath = path.resolve(
205
+ ".aicf/discovery-implementation/phase-7a/offline-acceptance.json"
206
+ );
207
+ const gate =
208
+ existsSync(gatePath) && statSync(gatePath).size <= 64_000
209
+ ? JSON.parse(readFileSync(gatePath, "utf8"))
210
+ : null;
211
+ if (
212
+ !gate ||
213
+ gate.sourceFingerprint !== source ||
214
+ gate.suiteFingerprint !== FROZEN_V3 ||
215
+ gate.workflowsFingerprint !== FROZEN_QUALIFICATION_WORKFLOWS ||
216
+ ![
217
+ "build",
218
+ "typecheck",
219
+ "tests",
220
+ "validate",
221
+ "conformance",
222
+ "package",
223
+ "mockCampaign"
224
+ ].every((key) => gate.checks?.[key] === true)
225
+ )
226
+ throw new Error(
227
+ "Matching offline acceptance evidence is required before live execution."
228
+ );
229
+ }
230
+ const binding = campaignBinding(suite, WORKFLOWS, source, {
231
+ mock: !!options.mockFactory,
232
+ model: EXECUTION.model,
233
+ hostModuleHash: config.hostModuleHash,
234
+ sdkVersion: config.sdkVersion,
235
+ tokenizerIdentity: config.tokenizerIdentity,
236
+ pricing: config.pricing
237
+ ? Object.fromEntries(
238
+ Object.entries(config.pricing).filter(([key]) => key !== "checkedAt")
239
+ )
240
+ : null
241
+ });
242
+ const directory =
243
+ options.directory ??
244
+ path.resolve(
245
+ config.evidenceDirectory ?? `.aicf/discovery-evaluation/${config.campaignId}`
246
+ );
247
+ if (options.live && !config.predecessor)
248
+ throw new Error("Live rerun requires the pinned original budget predecessor.");
249
+ const prior = config.predecessor ? verifyPredecessor(config.predecessor) : null;
250
+ const capacity = await preflightCampaign({
251
+ suite,
252
+ registry,
253
+ workflows: WORKFLOWS,
254
+ caps: { ...(prior?.remaining ?? CAPS), ...options.caps },
255
+ pricing: config.pricing
256
+ });
257
+ if (!capacity.feasible)
258
+ return {
259
+ status: "incomplete",
260
+ reasonCode: "schedule_capacity",
261
+ capacity,
262
+ providerCalls: 0
263
+ };
264
+ if (options.stage === "report" && !existsSync(path.join(directory, "journal.jsonl")))
265
+ throw new Error("No matching campaign evidence exists to report.");
266
+ const ledger = (config.predecessor ? openContinuationLedger : openCampaignLedger)({
267
+ ...(config.predecessor ? { predecessor: config.predecessor } : {}),
268
+ directory,
269
+ binding,
270
+ ...(options.caps ? { caps: options.caps } : {})
271
+ });
272
+ const loadCompleted = () =>
273
+ ledger
274
+ .records()
275
+ .filter((event) => event.type === "trial.end")
276
+ .map((event) => ledger.readArtifact(event.data.artifact));
277
+ let host;
278
+ try {
279
+ if (!ledger.records().some((event) => event.type === "capacity.preflight"))
280
+ ledger.record("capacity.preflight", capacity);
281
+ const stage = options.stage ?? "all";
282
+ if (stage === "report") {
283
+ const choice = ledger
284
+ .records()
285
+ .find((event) => event.type === "profile.freeze")?.data;
286
+ const values = loadCompleted();
287
+ return qualificationReport(
288
+ suite,
289
+ registry,
290
+ values.filter((value) => value.observation),
291
+ values.filter((value) => value.workflowId),
292
+ choice?.k ?? 8,
293
+ ledger.snapshot()
294
+ );
295
+ }
296
+ if (options.mockFactory) host = options.mockFactory({ ledger });
297
+ else {
298
+ const injected = await import(pathToFileURL(config.hostModule).href);
299
+ const supplied = await injected.createHostInputs();
300
+ if (
301
+ supplied.tokenizerIdentity !== config.tokenizerIdentity ||
302
+ supplied.sdkVersion !== config.sdkVersion
303
+ )
304
+ throw new Error("Host identity mismatch.");
305
+ host = createEvaluationClients({
306
+ ...supplied,
307
+ pricing: config.pricing,
308
+ ledger,
309
+ mock: false
310
+ });
311
+ }
312
+ if (
313
+ host.mock !== !!options.mockFactory ||
314
+ host.configuration.model !== EXECUTION.model ||
315
+ host.embeddingConfiguration.model !== EXECUTION.embeddingModel ||
316
+ host.embeddingConfiguration.dimensions !== 1536
317
+ )
318
+ throw new Error("Unexpected live model configuration.");
319
+ let index;
320
+ const indexEvent = ledger.records().find((event) => event.type === "index.ready");
321
+ if (indexEvent) {
322
+ const artifact = ledger.readArtifact(indexEvent.data.artifact),
323
+ checked = validateIndex(artifact.index, registry, host);
324
+ if (!checked.valid)
325
+ throw new Error("Persisted index no longer matches the campaign.");
326
+ index = checked.index;
327
+ }
328
+ if (stage === "index" || stage === "all") {
329
+ if (!index) {
330
+ const prior = ledger.beginTrial("administrative-index", {
331
+ campaign: ledger.fingerprint,
332
+ host: host.identity
333
+ });
334
+ if (prior.completed)
335
+ throw new Error(
336
+ "Completed index build requires its index publication record."
337
+ );
338
+ const built = await buildEvaluationIndex({ registry, host, ledger });
339
+ ledger.finishTrial("administrative-index", built);
340
+ if (built.status !== "built")
341
+ return {
342
+ status: "incomplete",
343
+ reasonCode: "index_unavailable",
344
+ ledger: ledger.snapshot()
345
+ };
346
+ index = built.index;
347
+ ledger.record("index.ready", {
348
+ artifact: ledger.writeArtifact("embedding-index.json", built)
349
+ });
350
+ }
351
+ if (stage === "index")
352
+ return {
353
+ status: "index_built",
354
+ indexFingerprint: index.indexFingerprint,
355
+ ledger: ledger.snapshot()
356
+ };
357
+ }
358
+ if (!index)
359
+ throw new Error(
360
+ "Build the compatible index explicitly before serving evaluation."
361
+ );
362
+ const runStage = async (current) => {
363
+ const prerequisite = {
364
+ baseline: "train",
365
+ qualify: "baseline"
366
+ }[current];
367
+ if (
368
+ prerequisite &&
369
+ !ledger
370
+ .records()
371
+ .some(
372
+ (event) =>
373
+ event.type === "stage.complete" && event.data.stage === prerequisite
374
+ )
375
+ )
376
+ throw new Error(
377
+ "Complete the preceding campaign stage before purchasing this stage."
378
+ );
379
+ const choice = ledger
380
+ .records()
381
+ .find((event) => event.type === "profile.freeze")?.data;
382
+ if (current === "qualify" && !choice)
383
+ throw new Error("Freeze the calibrated configuration before qualification.");
384
+ const schedule = trialSchedule(suite, current, choice?.k ?? 8);
385
+ const scheduleHash = hashDiscoveryContent(schedule),
386
+ prior = ledger
387
+ .records()
388
+ .find((event) => event.type === "schedule" && event.data.stage === current);
389
+ if (prior && prior.data.hash !== scheduleHash)
390
+ throw new Error("Qualification schedule changed.");
391
+ if (!prior)
392
+ ledger.record("schedule", {
393
+ stage: current,
394
+ hash: scheduleHash,
395
+ count: schedule.length
396
+ });
397
+ for (const trial of schedule) {
398
+ const priorTrial = ledger.beginTrial(trial.key, {
399
+ trial,
400
+ campaign: ledger.fingerprint,
401
+ index: trial.mode.startsWith("hybrid") ? index.indexFingerprint : null
402
+ });
403
+ if (priorTrial.completed) {
404
+ ledger.readArtifact(priorTrial.artifact);
405
+ continue;
406
+ }
407
+ const value = await runTaskTrial({
408
+ suite,
409
+ registry,
410
+ trial,
411
+ host,
412
+ ledger,
413
+ index
414
+ });
415
+ ledger.finishTrial(trial.key, value);
416
+ options.onProgress?.({
417
+ stage: current,
418
+ key: trial.key,
419
+ completed: ledger.snapshot().completedTrials
420
+ });
421
+ if (
422
+ ledger.snapshot().halted ||
423
+ value.errors.some((code) =>
424
+ ["accounting_unavailable", "budget_denied"].includes(code)
425
+ )
426
+ )
427
+ return false;
428
+ }
429
+ if (
430
+ !ledger
431
+ .records()
432
+ .some(
433
+ (event) => event.type === "stage.complete" && event.data.stage === current
434
+ )
435
+ )
436
+ ledger.record("stage.complete", {
437
+ stage: current,
438
+ hash: scheduleHash,
439
+ count: schedule.length
440
+ });
441
+ return true;
442
+ };
443
+ for (const current of ["train", "baseline"]) {
444
+ if (stage === current || stage === "all") {
445
+ if (!(await runStage(current)))
446
+ return {
447
+ status: "incomplete",
448
+ reasonCode: "budget_or_accounting",
449
+ ledger: ledger.snapshot()
450
+ };
451
+ if (stage === current)
452
+ return { status: "stage_completed", stage, ledger: ledger.snapshot() };
453
+ }
454
+ }
455
+ if (stage === "freeze" || stage === "all") {
456
+ if (
457
+ !ledger
458
+ .records()
459
+ .some(
460
+ (event) =>
461
+ event.type === "stage.complete" && event.data.stage === "baseline"
462
+ )
463
+ )
464
+ throw new Error("Complete calibration before freezing the profile.");
465
+ if (!ledger.records().some((event) => event.type === "profile.freeze")) {
466
+ const choice = {
467
+ k: 8,
468
+ frozen: true,
469
+ reason: "Phase 7A fixes K=8; no K=4 calibration",
470
+ baselineByMode: Object.fromEntries(
471
+ MODES.map((mode) => [
472
+ mode,
473
+ hashDiscoveryContent(
474
+ trialSchedule(suite, "baseline").filter((x) => x.mode === mode)
475
+ )
476
+ ])
477
+ )
478
+ };
479
+ ledger.record("profile.freeze", choice);
480
+ }
481
+ if (stage === "freeze")
482
+ return {
483
+ status: "configuration_frozen",
484
+ choice: ledger.records().find((event) => event.type === "profile.freeze").data
485
+ };
486
+ }
487
+ if (stage === "qualify" || stage === "all")
488
+ if (!(await runStage("qualify")))
489
+ return {
490
+ status: "incomplete",
491
+ reasonCode: "budget_or_accounting",
492
+ ledger: ledger.snapshot()
493
+ };
494
+ if (stage === "workflows" || stage === "all") {
495
+ if (
496
+ !ledger
497
+ .records()
498
+ .some(
499
+ (event) => event.type === "stage.complete" && event.data.stage === "qualify"
500
+ )
501
+ )
502
+ throw new Error("Complete qualification trials before workflow evaluation.");
503
+ const choice = ledger
504
+ .records()
505
+ .find((event) => event.type === "profile.freeze")?.data;
506
+ if (!choice) throw new Error("Freeze the profile before workflow evaluation.");
507
+ for (const scenario of WORKFLOWS)
508
+ for (const mode of MODES) {
509
+ const key = `workflow.${scenario.id}.${mode.replace("/", ".")}`;
510
+ const prior = ledger.beginTrial(key, {
511
+ workflow: hashDiscoveryContent(scenario),
512
+ mode,
513
+ k: choice.k,
514
+ campaign: ledger.fingerprint
515
+ });
516
+ if (prior.completed) {
517
+ ledger.readArtifact(prior.artifact);
518
+ continue;
519
+ }
520
+ ledger.finishTrial(
521
+ key,
522
+ await runWorkflowTrial({
523
+ suite,
524
+ registry,
525
+ scenario,
526
+ mode,
527
+ k: choice.k,
528
+ host,
529
+ ledger,
530
+ index
531
+ })
532
+ );
533
+ if (
534
+ ledger.snapshot().halted ||
535
+ loadCompleted()
536
+ .at(-1)
537
+ ?.run?.usage.stages.some((item) =>
538
+ ["budget_denied", "accounting_unavailable"].includes(item.reasonCode)
539
+ )
540
+ )
541
+ return {
542
+ status: "incomplete",
543
+ reasonCode: "accounting",
544
+ ledger: ledger.snapshot()
545
+ };
546
+ }
547
+ }
548
+ if (
549
+ (stage === "workflows" || stage === "all") &&
550
+ !ledger
551
+ .records()
552
+ .some(
553
+ (event) => event.type === "stage.complete" && event.data.stage === "workflows"
554
+ )
555
+ )
556
+ ledger.record("stage.complete", { stage: "workflows", count: 48 });
557
+ const choice = ledger
558
+ .records()
559
+ .find((event) => event.type === "profile.freeze")?.data,
560
+ values = loadCompleted();
561
+ const report = qualificationReport(
562
+ suite,
563
+ registry,
564
+ values.filter((value) => value.observation),
565
+ values.filter((value) => value.workflowId),
566
+ choice?.k ?? 8,
567
+ ledger.snapshot()
568
+ );
569
+ return {
570
+ status: options.mockFactory
571
+ ? "mock_integration_completed"
572
+ : report.complete
573
+ ? "evaluation_completed"
574
+ : "incomplete",
575
+ report,
576
+ campaignFingerprint: ledger.fingerprint,
577
+ indexFingerprint: index.indexFingerprint
578
+ };
579
+ } finally {
580
+ ledger.close();
581
+ }
582
+ }
583
+
584
+ export async function campaignCli(argv) {
585
+ const options = {};
586
+ for (let i = 0; i < argv.length; i++) {
587
+ const item = argv[i];
588
+ if (item === "--live") options.live = true;
589
+ else if (["--config", "--stage"].includes(item) && argv[i + 1])
590
+ options[item === "--config" ? "configFile" : "stage"] = argv[++i];
591
+ else
592
+ throw new Error(
593
+ "Use --config <host.json>, --stage <index|train|baseline|freeze|qualify|workflows|report|all>, and explicit --live."
594
+ );
595
+ }
596
+ if (
597
+ options.stage &&
598
+ ![
599
+ "index",
600
+ "train",
601
+ "baseline",
602
+ "freeze",
603
+ "qualify",
604
+ "workflows",
605
+ "report",
606
+ "all"
607
+ ].includes(options.stage)
608
+ )
609
+ throw new Error("Unknown campaign stage.");
610
+ if (options.live && !options.configFile)
611
+ throw new Error("Live evaluation requires an explicit host configuration.");
612
+ return runCampaign(options);
613
+ }