@kungfu-tech/buildchain 2.14.14-alpha.4 → 2.14.14-alpha.7

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 (87) hide show
  1. package/bin/buildchain.mjs +46 -2
  2. package/dist/site/agent-index.json +1 -0
  3. package/dist/site/artifact-schemas.json +2 -0
  4. package/dist/site/badge-endpoint-registry.json +1120 -0
  5. package/dist/site/badges/v1/kfd-10/aligned.json +14 -0
  6. package/dist/site/badges/v1/kfd-10/declared.json +14 -0
  7. package/dist/site/badges/v1/kfd-10/downgraded.json +14 -0
  8. package/dist/site/badges/v1/kfd-10/draft.json +14 -0
  9. package/dist/site/badges/v1/kfd-10/failed.json +14 -0
  10. package/dist/site/badges/v1/kfd-10/missing.json +14 -0
  11. package/dist/site/badges/v1/kfd-10/passed.json +14 -0
  12. package/dist/site/badges/v1/kfd-10/planned.json +14 -0
  13. package/dist/site/badges/v1/kfd-11/aligned.json +14 -0
  14. package/dist/site/badges/v1/kfd-11/declared.json +14 -0
  15. package/dist/site/badges/v1/kfd-11/downgraded.json +14 -0
  16. package/dist/site/badges/v1/kfd-11/draft.json +14 -0
  17. package/dist/site/badges/v1/kfd-11/failed.json +14 -0
  18. package/dist/site/badges/v1/kfd-11/missing.json +14 -0
  19. package/dist/site/badges/v1/kfd-11/passed.json +14 -0
  20. package/dist/site/badges/v1/kfd-11/planned.json +14 -0
  21. package/dist/site/badges/v1/kfd-12/aligned.json +14 -0
  22. package/dist/site/badges/v1/kfd-12/declared.json +14 -0
  23. package/dist/site/badges/v1/kfd-12/downgraded.json +14 -0
  24. package/dist/site/badges/v1/kfd-12/draft.json +14 -0
  25. package/dist/site/badges/v1/kfd-12/failed.json +14 -0
  26. package/dist/site/badges/v1/kfd-12/missing.json +14 -0
  27. package/dist/site/badges/v1/kfd-12/passed.json +14 -0
  28. package/dist/site/badges/v1/kfd-12/planned.json +14 -0
  29. package/dist/site/badges/v1/kfd-13/aligned.json +14 -0
  30. package/dist/site/badges/v1/kfd-13/declared.json +14 -0
  31. package/dist/site/badges/v1/kfd-13/downgraded.json +14 -0
  32. package/dist/site/badges/v1/kfd-13/draft.json +14 -0
  33. package/dist/site/badges/v1/kfd-13/failed.json +14 -0
  34. package/dist/site/badges/v1/kfd-13/missing.json +14 -0
  35. package/dist/site/badges/v1/kfd-13/passed.json +14 -0
  36. package/dist/site/badges/v1/kfd-13/planned.json +14 -0
  37. package/dist/site/badges/v1/kfd-7/aligned.json +14 -0
  38. package/dist/site/badges/v1/kfd-7/declared.json +14 -0
  39. package/dist/site/badges/v1/kfd-7/downgraded.json +14 -0
  40. package/dist/site/badges/v1/kfd-7/draft.json +14 -0
  41. package/dist/site/badges/v1/kfd-7/failed.json +14 -0
  42. package/dist/site/badges/v1/kfd-7/missing.json +14 -0
  43. package/dist/site/badges/v1/kfd-7/passed.json +14 -0
  44. package/dist/site/badges/v1/kfd-7/planned.json +14 -0
  45. package/dist/site/badges/v1/kfd-8/aligned.json +14 -0
  46. package/dist/site/badges/v1/kfd-8/declared.json +14 -0
  47. package/dist/site/badges/v1/kfd-8/downgraded.json +14 -0
  48. package/dist/site/badges/v1/kfd-8/draft.json +14 -0
  49. package/dist/site/badges/v1/kfd-8/failed.json +14 -0
  50. package/dist/site/badges/v1/kfd-8/missing.json +14 -0
  51. package/dist/site/badges/v1/kfd-8/passed.json +14 -0
  52. package/dist/site/badges/v1/kfd-8/planned.json +14 -0
  53. package/dist/site/badges/v1/kfd-9/aligned.json +14 -0
  54. package/dist/site/badges/v1/kfd-9/declared.json +14 -0
  55. package/dist/site/badges/v1/kfd-9/downgraded.json +14 -0
  56. package/dist/site/badges/v1/kfd-9/draft.json +14 -0
  57. package/dist/site/badges/v1/kfd-9/failed.json +14 -0
  58. package/dist/site/badges/v1/kfd-9/missing.json +14 -0
  59. package/dist/site/badges/v1/kfd-9/passed.json +14 -0
  60. package/dist/site/badges/v1/kfd-9/planned.json +14 -0
  61. package/dist/site/buildchain-contract.json +42 -27
  62. package/dist/site/buildchain-site.json +62 -11
  63. package/dist/site/capability-registry.json +6 -5
  64. package/dist/site/cli-registry.json +12 -0
  65. package/dist/site/controller-registry.json +16 -4
  66. package/dist/site/kfd-claims.json +65 -13
  67. package/dist/site/kfd-upstream-aggregate.json +12 -12
  68. package/dist/site/manual-registry.json +17 -2
  69. package/dist/site/node-api-registry.json +17 -4
  70. package/dist/site/page-registry.json +48 -5
  71. package/dist/site/public-surface-audit.json +53 -10
  72. package/dist/site/publication-registry.json +4 -4
  73. package/dist/site/release-passport-check-manifest.json +28 -2
  74. package/dist/site/release-provenance.json +2 -0
  75. package/dist/site/schemas/kfd-agent-hub-adoption.schema.json +116 -0
  76. package/dist/site/site-manifest.json +14 -6
  77. package/dist/site/workflow-registry.json +8 -4
  78. package/docs/MAP.md +2 -0
  79. package/docs/kfd-agent-hub.md +110 -0
  80. package/docs/release-candidate.md +7 -0
  81. package/package.json +4 -2
  82. package/packages/core/buildchain-kfd-claims.js +1 -0
  83. package/packages/core/index.js +13 -0
  84. package/packages/core/kfd-agent-hub.js +496 -0
  85. package/packages/core/release-passport.js +105 -0
  86. package/scripts/check-inventory.mjs +2 -0
  87. package/scripts/generate-site-bundle.mjs +8 -0
@@ -0,0 +1,496 @@
1
+ import crypto from "node:crypto";
2
+ import { spawnSync } from "node:child_process";
3
+ import fs from "node:fs";
4
+ import { createRequire } from "node:module";
5
+ import path from "node:path";
6
+
7
+ export const KFD_AGENT_HUB_DECLARATION = ".buildchain/kfd/agent-hub.json";
8
+ export const KFD_AGENT_HUB_OUTPUT_DIR = ".buildchain/artifacts/kfd-agent-hub";
9
+ export const KFD_AGENT_HUB_ADOPTION_CONTRACT = "kungfu-buildchain-kfd-agent-hub-adoption/v1";
10
+ export const KFD_AGENT_HUB_LOCK_CONTRACT = "kungfu-buildchain-kfd-agent-hub-lock/v1";
11
+ export const KFD_AGENT_HUB_VERIFICATION_CONTRACT = "kungfu-buildchain-kfd-agent-hub-verification/v1";
12
+ export const KFD_AGENT_HUB_ADOPTION_SCHEMA = Object.freeze({
13
+ $schema: "https://json-schema.org/draft/2020-12/schema",
14
+ $id: "https://buildchain.libkungfu.dev/schemas/kfd-agent-hub-adoption.schema.json",
15
+ title: "Buildchain KFD Agent Hub adoption declaration",
16
+ type: "object",
17
+ required: ["schemaVersion", "contract", "profile", "adapter", "capabilities"],
18
+ properties: {
19
+ $schema: { const: "https://buildchain.libkungfu.dev/schemas/kfd-agent-hub-adoption.schema.json" },
20
+ schemaVersion: { const: 1 },
21
+ contract: { const: KFD_AGENT_HUB_ADOPTION_CONTRACT },
22
+ profile: {
23
+ type: "object",
24
+ required: ["package", "id"],
25
+ properties: {
26
+ package: { const: "@kungfu-tech/kfd" },
27
+ id: { const: "kfd-agent-hub-conformance" },
28
+ },
29
+ additionalProperties: false,
30
+ },
31
+ adapter: {
32
+ type: "object",
33
+ required: ["id", "version", "path"],
34
+ properties: {
35
+ id: { type: "string", minLength: 1 },
36
+ version: { type: "string", minLength: 1 },
37
+ path: { type: "string", minLength: 1 },
38
+ args: { type: "array", items: { type: "string" } },
39
+ build: { type: "array", minItems: 1, items: { type: "string", minLength: 1 } },
40
+ },
41
+ additionalProperties: false,
42
+ },
43
+ capabilities: {
44
+ type: "object",
45
+ required: ["operations", "topologies", "hubBindings"],
46
+ properties: {
47
+ operations: { type: "array", minItems: 1, uniqueItems: true, items: { type: "string", minLength: 1 } },
48
+ topologies: { type: "array", minItems: 1, uniqueItems: true, items: { type: "string", minLength: 1 } },
49
+ hubBindings: { type: "array", minItems: 1, uniqueItems: true, items: { type: "string", minLength: 1 } },
50
+ },
51
+ additionalProperties: false,
52
+ },
53
+ },
54
+ additionalProperties: false,
55
+ });
56
+
57
+ const PROFILE_PATH = "profiles/agent-hub/manifest.json";
58
+ const FAILURE_CODES_PATH = "profiles/agent-hub/failure-codes.json";
59
+ const VECTORS_PATH = "profiles/agent-hub/vectors/hub-20.json";
60
+ const VERIFIER_PATH = "scripts/agent-hub-report-verifier.mjs";
61
+ const RELEASE_PATH = "kfd.release.json";
62
+ const REQUIRED_PROFILE_CONTRACT = "kfd.agent-hub-conformance-manifest/v1";
63
+ const REQUIRED_REPORT_CONTRACT = "kfd.agent-hub-report/v1";
64
+ const PLACEHOLDER_ROOT = /^sha256:0{64}$/;
65
+
66
+ function sha256(value) {
67
+ return `sha256:${crypto.createHash("sha256").update(value).digest("hex")}`;
68
+ }
69
+
70
+ function stableJson(value) {
71
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
72
+ if (value && typeof value === "object") {
73
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
74
+ }
75
+ return JSON.stringify(value);
76
+ }
77
+
78
+ function semanticRoot(value) {
79
+ return sha256(`${stableJson(value)}\n`);
80
+ }
81
+
82
+ function readJson(filePath, label = filePath) {
83
+ try {
84
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
85
+ } catch (error) {
86
+ throw new Error(`kfd-agent-hub-json-invalid: ${label}: ${error.message}`);
87
+ }
88
+ }
89
+
90
+ function writeJson(filePath, value) {
91
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
92
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
93
+ }
94
+
95
+ function relative(cwd, filePath) {
96
+ return path.relative(cwd, filePath).split(path.sep).join("/");
97
+ }
98
+
99
+ function exactRoot(value, label) {
100
+ const normalized = String(value || "");
101
+ if (!/^sha256:[0-9a-f]{64}$/.test(normalized) || PLACEHOLDER_ROOT.test(normalized)) {
102
+ throw new Error(`kfd-agent-hub-profile-unreleased: ${label} must be a non-placeholder sha256 root`);
103
+ }
104
+ return normalized;
105
+ }
106
+
107
+ function stringArray(value, label, { min = 1 } = {}) {
108
+ if (!Array.isArray(value) || value.length < min || value.some((item) => typeof item !== "string" || !item.trim())) {
109
+ throw new Error(`kfd-agent-hub-declaration-invalid: ${label} must contain at least ${min} non-empty strings`);
110
+ }
111
+ if (new Set(value).size !== value.length) {
112
+ throw new Error(`kfd-agent-hub-declaration-invalid: ${label} must not contain duplicates`);
113
+ }
114
+ return [...value].sort();
115
+ }
116
+
117
+ function argv(value, label) {
118
+ if (!Array.isArray(value) || value.length === 0 || value.some((item) => typeof item !== "string" || !item)) {
119
+ throw new Error(`kfd-agent-hub-declaration-invalid: ${label} must be a non-empty argv array`);
120
+ }
121
+ return [...value];
122
+ }
123
+
124
+ function loadDeclaration({ cwd, declarationPath = KFD_AGENT_HUB_DECLARATION }) {
125
+ const absolutePath = path.resolve(cwd, declarationPath);
126
+ if (!fs.existsSync(absolutePath)) {
127
+ throw new Error(`kfd-agent-hub-declaration-missing: ${relative(cwd, absolutePath)}`);
128
+ }
129
+ const value = readJson(absolutePath, "Agent Hub declaration");
130
+ if (value?.schemaVersion !== 1 || value?.contract !== KFD_AGENT_HUB_ADOPTION_CONTRACT) {
131
+ throw new Error(`kfd-agent-hub-declaration-invalid: expected ${KFD_AGENT_HUB_ADOPTION_CONTRACT}`);
132
+ }
133
+ if (value.profile?.package !== "@kungfu-tech/kfd" || value.profile?.id !== "kfd-agent-hub-conformance") {
134
+ throw new Error("kfd-agent-hub-declaration-invalid: profile must select @kungfu-tech/kfd kfd-agent-hub-conformance");
135
+ }
136
+ const adapterPath = String(value.adapter?.path || "").trim();
137
+ if (!adapterPath) throw new Error("kfd-agent-hub-declaration-invalid: adapter.path is required");
138
+ const absoluteAdapterPath = path.resolve(cwd, adapterPath);
139
+ if (!absoluteAdapterPath.startsWith(`${path.resolve(cwd)}${path.sep}`)) {
140
+ throw new Error("kfd-agent-hub-declaration-invalid: adapter.path must stay inside cwd");
141
+ }
142
+ const adapterId = String(value.adapter.id || "").trim();
143
+ const adapterVersion = String(value.adapter.version || "").trim();
144
+ if (!adapterId || !adapterVersion) {
145
+ throw new Error("kfd-agent-hub-declaration-invalid: adapter.id and adapter.version are required");
146
+ }
147
+ if (value.capabilities?.bindings) {
148
+ throw new Error("kfd-agent-hub-declaration-invalid: use capabilities.hubBindings; runner invocation binding is KFD-owned");
149
+ }
150
+ if (value.adapter.args && (!Array.isArray(value.adapter.args) || value.adapter.args.some((item) => typeof item !== "string"))) {
151
+ throw new Error("kfd-agent-hub-declaration-invalid: adapter.args must be an argv array");
152
+ }
153
+ return {
154
+ path: absolutePath,
155
+ value,
156
+ adapter: {
157
+ id: adapterId,
158
+ version: adapterVersion,
159
+ path: absoluteAdapterPath,
160
+ args: Array.isArray(value.adapter.args) ? value.adapter.args.map(String) : [],
161
+ build: value.adapter.build ? argv(value.adapter.build, "adapter.build") : [],
162
+ },
163
+ capabilities: {
164
+ operations: stringArray(value.capabilities?.operations, "capabilities.operations"),
165
+ topologies: stringArray(value.capabilities?.topologies, "capabilities.topologies"),
166
+ hubBindings: stringArray(value.capabilities?.hubBindings, "capabilities.hubBindings"),
167
+ },
168
+ };
169
+ }
170
+
171
+ function resolveKfdPackage({ cwd, kfdRoot = "" }) {
172
+ let packageJsonPath;
173
+ if (kfdRoot) {
174
+ packageJsonPath = path.join(path.resolve(kfdRoot), "package.json");
175
+ } else {
176
+ const cwdPackage = path.join(path.resolve(cwd), "package.json");
177
+ const consumerRequire = createRequire(fs.existsSync(cwdPackage) ? cwdPackage : path.join(path.resolve(cwd), "noop.js"));
178
+ try {
179
+ packageJsonPath = consumerRequire.resolve("@kungfu-tech/kfd/package.json");
180
+ } catch (consumerError) {
181
+ try {
182
+ packageJsonPath = createRequire(import.meta.url).resolve("@kungfu-tech/kfd/package.json");
183
+ } catch (runtimeError) {
184
+ throw new Error(`kfd-agent-hub-package-missing: install a Buildchain or consumer @kungfu-tech/kfd with the Agent Hub profile (${consumerError.message}; ${runtimeError.message})`);
185
+ }
186
+ }
187
+ }
188
+ const root = path.dirname(packageJsonPath);
189
+ const packageJson = readJson(packageJsonPath, "@kungfu-tech/kfd package.json");
190
+ const required = [PROFILE_PATH, FAILURE_CODES_PATH, VECTORS_PATH, VERIFIER_PATH, RELEASE_PATH];
191
+ for (const requiredPath of required) {
192
+ if (!fs.existsSync(path.join(root, requiredPath))) {
193
+ throw new Error(`kfd-agent-hub-profile-unavailable: @kungfu-tech/kfd@${packageJson.version || "unknown"} is missing ${requiredPath}`);
194
+ }
195
+ }
196
+ const binPath = path.join(root, typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.kfd || "bin/kfd.mjs");
197
+ if (!fs.existsSync(binPath)) throw new Error("kfd-agent-hub-profile-unavailable: @kungfu-tech/kfd does not expose the kfd CLI");
198
+ return { root, packageJson, packageJsonPath, binPath };
199
+ }
200
+
201
+ function inspectKfdCut(kfd) {
202
+ const profilePath = path.join(kfd.root, PROFILE_PATH);
203
+ const failureCodesPath = path.join(kfd.root, FAILURE_CODES_PATH);
204
+ const vectorsPath = path.join(kfd.root, VECTORS_PATH);
205
+ const releasePath = path.join(kfd.root, RELEASE_PATH);
206
+ const verifierPath = path.join(kfd.root, VERIFIER_PATH);
207
+ const profile = readJson(profilePath, PROFILE_PATH);
208
+ readJson(releasePath, RELEASE_PATH);
209
+ if (profile.contract !== REQUIRED_PROFILE_CONTRACT || profile.profile?.id !== "kfd-agent-hub-conformance") {
210
+ throw new Error(`kfd-agent-hub-profile-invalid: ${PROFILE_PATH} must be ${REQUIRED_PROFILE_CONTRACT}`);
211
+ }
212
+ if (profile.adapter?.binding !== "jsonl-stdio/v1" || profile.suite?.id !== "kfd-agent-hub-20") {
213
+ throw new Error("kfd-agent-hub-profile-invalid: expected jsonl-stdio/v1 and kfd-agent-hub-20");
214
+ }
215
+ exactRoot(profile.protocol?.manifestDigest, "profile.protocol.manifestDigest");
216
+ exactRoot(profile.suite?.vectorRoot, "profile.suite.vectorRoot");
217
+ exactRoot(profile.failureInventory?.root, "profile.failureInventory.root");
218
+ exactRoot(profile.runtimeDependency?.manifestDigest, "profile.runtimeDependency.manifestDigest");
219
+ const protocolPath = path.join(kfd.root, String(profile.protocol?.manifest || ""));
220
+ const runtimeDependencyPath = path.join(kfd.root, String(profile.runtimeDependency?.manifest || ""));
221
+ for (const [label, filePath] of [["protocol manifest", protocolPath], ["Runtime 100 manifest", runtimeDependencyPath]]) {
222
+ if (!filePath.startsWith(`${kfd.root}${path.sep}`) || !fs.existsSync(filePath)) {
223
+ throw new Error(`kfd-agent-hub-profile-invalid: ${label} is missing or escaped the KFD package`);
224
+ }
225
+ }
226
+ for (const [label, expected, filePath] of [
227
+ ["protocol manifest", profile.protocol.manifestDigest, protocolPath],
228
+ ["Hub 20 vector registry", profile.suite.vectorRoot, vectorsPath],
229
+ ["failure inventory", profile.failureInventory.root, failureCodesPath],
230
+ ["Runtime 100 manifest", profile.runtimeDependency.manifestDigest, runtimeDependencyPath],
231
+ ]) {
232
+ const actual = sha256(fs.readFileSync(filePath));
233
+ if (actual !== expected) throw new Error(`kfd-agent-hub-profile-root-mismatch: ${label} expected ${expected}, got ${actual}`);
234
+ }
235
+ return {
236
+ package: {
237
+ name: kfd.packageJson.name,
238
+ version: kfd.packageJson.version,
239
+ packageManifestDigest: sha256(fs.readFileSync(kfd.packageJsonPath)),
240
+ releaseAnchorDigest: sha256(fs.readFileSync(releasePath)),
241
+ },
242
+ profile: {
243
+ id: profile.profile.id,
244
+ version: profile.profile.version,
245
+ manifestDigest: sha256(fs.readFileSync(profilePath)),
246
+ },
247
+ protocol: {
248
+ id: profile.protocol.id,
249
+ version: profile.protocol.version,
250
+ manifestDigest: profile.protocol.manifestDigest,
251
+ },
252
+ suite: {
253
+ id: profile.suite.id,
254
+ version: profile.suite.version,
255
+ vectorCount: profile.suite.fixedVectorCount,
256
+ vectorRoot: profile.suite.vectorRoot,
257
+ vectorRegistryDigest: sha256(fs.readFileSync(vectorsPath)),
258
+ },
259
+ failureInventory: {
260
+ root: profile.failureInventory.root,
261
+ digest: sha256(fs.readFileSync(failureCodesPath)),
262
+ },
263
+ verifier: {
264
+ contract: "kfd.agent-hub-report-verifier/v1",
265
+ artifactDigest: sha256(fs.readFileSync(verifierPath)),
266
+ },
267
+ runtimeDependency: {
268
+ id: profile.runtimeDependency.id,
269
+ version: profile.runtimeDependency.version,
270
+ manifestDigest: profile.runtimeDependency.manifestDigest,
271
+ coreVectorCount: profile.runtimeDependency.coreVectorCount,
272
+ experimentalVectorCount: profile.runtimeDependency.experimentalVectorCount,
273
+ qualifying: profile.runtimeDependency.qualifying,
274
+ },
275
+ binding: profile.adapter.binding,
276
+ claimBoundary: profile.claimBoundary,
277
+ };
278
+ }
279
+
280
+ function defaultRun(command, args, { cwd }) {
281
+ const result = spawnSync(command, args, { cwd, encoding: "utf8", env: process.env });
282
+ return { status: result.status ?? 2, stdout: result.stdout || "", stderr: result.stderr || "", error: result.error };
283
+ }
284
+
285
+ function runChecked(run, command, args, options, label) {
286
+ const result = run(command, args, options);
287
+ if (result.error || result.status !== 0) {
288
+ const detail = String(result.stderr || result.stdout || result.error?.message || "unknown error").trim();
289
+ throw new Error(`${label}: exit ${result.status ?? 2}${detail ? `: ${detail}` : ""}`);
290
+ }
291
+ return result;
292
+ }
293
+
294
+ function assertSameSet(declared, actual, label) {
295
+ const normalized = [...new Set(actual.map(String))].sort();
296
+ if (stableJson(declared) !== stableJson(normalized)) {
297
+ throw new Error(`kfd-agent-hub-capability-declaration-mismatch: ${label} declared=${declared.join(",")} observed=${normalized.join(",")}`);
298
+ }
299
+ }
300
+
301
+ function validateReport(report, declaration, cut) {
302
+ if (report?.contract !== REQUIRED_REPORT_CONTRACT || report.valid !== true) {
303
+ throw new Error(`kfd-agent-hub-report-invalid: expected valid ${REQUIRED_REPORT_CONTRACT}`);
304
+ }
305
+ if (report.qualifying !== false || report.certification !== false || report.execution?.offline !== true) {
306
+ throw new Error("kfd-agent-hub-report-invalid: report must remain offline, nonqualifying, and non-certifying");
307
+ }
308
+ if (
309
+ report.sourceCut?.package !== cut.package.name ||
310
+ report.sourceCut?.packageVersion !== cut.package.version ||
311
+ report.sourceCut?.packageManifestDigest !== cut.package.packageManifestDigest ||
312
+ report.sourceCut?.releaseAnchorDigest !== cut.package.releaseAnchorDigest
313
+ ) {
314
+ throw new Error("kfd-agent-hub-source-cut-mismatch: report package cut differs from the resolved KFD package");
315
+ }
316
+ if (
317
+ report.profile?.manifestDigest !== cut.profile.manifestDigest ||
318
+ report.protocol?.manifestDigest !== cut.protocol.manifestDigest ||
319
+ report.suite?.vectorRoot !== cut.suite.vectorRoot ||
320
+ report.suite?.inventoryRoot !== cut.failureInventory.root ||
321
+ report.verifier?.failureInventoryRoot !== cut.failureInventory.root ||
322
+ report.verifier?.artifactDigest !== cut.verifier.artifactDigest
323
+ ) {
324
+ throw new Error("kfd-agent-hub-source-cut-mismatch: report profile, protocol, suite, or failure inventory differs from the resolved KFD package");
325
+ }
326
+ if (report.adapter?.artifactDigest !== sha256(fs.readFileSync(declaration.adapter.path))) {
327
+ throw new Error("kfd-agent-hub-adapter-artifact-digest-mismatch: report does not bind the declared adapter artifact");
328
+ }
329
+ if (report.adapter?.id !== declaration.adapter.id || report.adapter?.version !== declaration.adapter.version) {
330
+ throw new Error("kfd-agent-hub-adapter-identity-mismatch: report adapter identity differs from the declaration");
331
+ }
332
+ const capabilities = Array.isArray(report.capabilities) ? report.capabilities : [];
333
+ if (capabilities.length < 2) throw new Error("kfd-agent-hub-report-invalid: at least two Hub capability documents are required");
334
+ const documents = capabilities.map((entry) => entry.document || {});
335
+ for (const document of documents) {
336
+ assertSameSet(declaration.capabilities.operations, document.operations || [], "operations");
337
+ assertSameSet(declaration.capabilities.topologies, document.topologies || [], "topologies");
338
+ assertSameSet(declaration.capabilities.hubBindings, (document.bindings || []).map((binding) => binding.id), "Hub bindings");
339
+ }
340
+ }
341
+
342
+ function adoptionLock({ cwd, declaration, cut }) {
343
+ const lock = {
344
+ schemaVersion: 1,
345
+ contract: KFD_AGENT_HUB_LOCK_CONTRACT,
346
+ declaration: {
347
+ path: relative(cwd, declaration.path),
348
+ digest: sha256(fs.readFileSync(declaration.path)),
349
+ },
350
+ adapter: {
351
+ id: declaration.adapter.id,
352
+ version: declaration.adapter.version,
353
+ path: relative(cwd, declaration.adapter.path),
354
+ artifactDigest: fs.existsSync(declaration.adapter.path) ? sha256(fs.readFileSync(declaration.adapter.path)) : "",
355
+ invocationBinding: cut.binding,
356
+ },
357
+ sourceCut: cut,
358
+ capabilities: declaration.capabilities,
359
+ };
360
+ return { ...lock, lockRoot: sha256(stableJson(lock)) };
361
+ }
362
+
363
+ export function initKfdAgentHub({ cwd = process.cwd(), declarationPath = KFD_AGENT_HUB_DECLARATION, write = false, force = false } = {}) {
364
+ const filePath = path.resolve(cwd, declarationPath);
365
+ const declaration = {
366
+ $schema: "https://buildchain.libkungfu.dev/schemas/kfd-agent-hub-adoption.schema.json",
367
+ schemaVersion: 1,
368
+ contract: KFD_AGENT_HUB_ADOPTION_CONTRACT,
369
+ profile: { package: "@kungfu-tech/kfd", id: "kfd-agent-hub-conformance" },
370
+ adapter: { id: "replace-with-adapter-id", version: "0.1.0", path: "dist/agent-hub-adapter.mjs" },
371
+ capabilities: {
372
+ operations: ["capability-advertisement", "responsibility-proposal", "fact-admission", "supersession", "completion-assessment", "warrant-revocation"],
373
+ topologies: ["local-peer"],
374
+ hubBindings: ["local-file-bundle"],
375
+ },
376
+ };
377
+ if (write) {
378
+ if (fs.existsSync(filePath) && !force) throw new Error(`kfd-agent-hub-declaration-exists: ${relative(cwd, filePath)}; use --force to replace it`);
379
+ writeJson(filePath, declaration);
380
+ }
381
+ return { schemaVersion: 1, contract: "kungfu-buildchain-kfd-agent-hub-init/v1", path: relative(cwd, filePath), write, declaration };
382
+ }
383
+
384
+ export function inspectKfdAgentHub({ cwd = process.cwd(), declarationPath = KFD_AGENT_HUB_DECLARATION, kfdRoot = "" } = {}) {
385
+ const declaration = loadDeclaration({ cwd, declarationPath });
386
+ const kfd = resolveKfdPackage({ cwd, kfdRoot });
387
+ const cut = inspectKfdCut(kfd);
388
+ const lock = adoptionLock({ cwd, declaration, cut });
389
+ return {
390
+ schemaVersion: 1,
391
+ contract: "kungfu-buildchain-kfd-agent-hub-inspection/v1",
392
+ valid: Boolean(lock.adapter.artifactDigest),
393
+ declaration: declaration.value,
394
+ lock,
395
+ issues: lock.adapter.artifactDigest ? [] : [{ code: "adapter-artifact-missing", path: lock.adapter.path }],
396
+ };
397
+ }
398
+
399
+ export function testKfdAgentHub({ cwd = process.cwd(), declarationPath = KFD_AGENT_HUB_DECLARATION, outputDir = KFD_AGENT_HUB_OUTPUT_DIR, kfdRoot = "", run = defaultRun } = {}) {
400
+ const declaration = loadDeclaration({ cwd, declarationPath });
401
+ if (declaration.adapter.build.length) {
402
+ runChecked(run, declaration.adapter.build[0], declaration.adapter.build.slice(1), { cwd }, "kfd-agent-hub-adapter-build-failed");
403
+ }
404
+ if (!fs.existsSync(declaration.adapter.path)) {
405
+ throw new Error(`kfd-agent-hub-adapter-missing: ${relative(cwd, declaration.adapter.path)}`);
406
+ }
407
+ const kfd = resolveKfdPackage({ cwd, kfdRoot });
408
+ const cut = inspectKfdCut(kfd);
409
+ const absoluteOutputDir = path.resolve(cwd, outputDir);
410
+ const reportPath = path.join(absoluteOutputDir, "report.json");
411
+ fs.mkdirSync(absoluteOutputDir, { recursive: true });
412
+ const candidateReportPath = path.join(absoluteOutputDir, `.report-${crypto.randomUUID()}.json`);
413
+ let report;
414
+ let verifier;
415
+ try {
416
+ runChecked(run, process.execPath, [kfd.binPath, "test", "agent-hub", "--adapter", declaration.adapter.path, ...declaration.adapter.args.flatMap((arg) => ["--adapter-arg", arg]), "--output", candidateReportPath], { cwd }, "kfd-agent-hub-suite-failed");
417
+ const verifyResult = runChecked(run, process.execPath, [kfd.binPath, "verify", "agent-hub-report", candidateReportPath, "--adapter", declaration.adapter.path, "--json"], { cwd }, "kfd-agent-hub-report-verification-failed");
418
+ report = readJson(candidateReportPath, "KFD Agent Hub report");
419
+ verifier = JSON.parse(verifyResult.stdout);
420
+ if (verifier.contract !== "kfd.agent-hub-report-verifier/v1" || verifier.valid !== true) {
421
+ throw new Error("kfd-agent-hub-report-verification-failed: verifier did not return a valid report verdict");
422
+ }
423
+ if (verifier.reportDigest !== semanticRoot(report)) {
424
+ throw new Error("kfd-agent-hub-report-verification-failed: verifier report digest does not match report semantics");
425
+ }
426
+ validateReport(report, declaration, cut);
427
+ writeJson(reportPath, report);
428
+ } finally {
429
+ if (fs.existsSync(candidateReportPath)) fs.unlinkSync(candidateReportPath);
430
+ }
431
+ const lock = adoptionLock({ cwd, declaration, cut });
432
+ const verification = {
433
+ schemaVersion: 1,
434
+ contract: KFD_AGENT_HUB_VERIFICATION_CONTRACT,
435
+ valid: true,
436
+ declarationDigest: lock.declaration.digest,
437
+ lockRoot: lock.lockRoot,
438
+ reportDigest: verifier.reportDigest,
439
+ verifier,
440
+ claimBoundary: cut.claimBoundary,
441
+ residualRisk: report.residualRisk,
442
+ };
443
+ writeJson(path.join(absoluteOutputDir, "adoption-lock.json"), lock);
444
+ writeJson(path.join(absoluteOutputDir, "verification.json"), verification);
445
+ writeJson(path.join(absoluteOutputDir, "evidence.json"), {
446
+ schemaVersion: 1,
447
+ contract: "kungfu-buildchain-kfd-agent-hub-evidence/v1",
448
+ report: { path: "report.json", digest: verification.reportDigest },
449
+ verification: { path: "verification.json", contract: verification.contract, valid: true },
450
+ lock: { path: "adoption-lock.json", root: lock.lockRoot },
451
+ kfd: {
452
+ package: cut.package,
453
+ profile: cut.profile,
454
+ protocol: cut.protocol,
455
+ suite: cut.suite,
456
+ failureInventory: cut.failureInventory,
457
+ },
458
+ scope: {
459
+ adapterArtifactDigest: lock.adapter.artifactDigest,
460
+ invocationBinding: lock.adapter.invocationBinding,
461
+ capabilities: lock.capabilities,
462
+ },
463
+ qualifying: false,
464
+ certification: false,
465
+ claimBoundary: cut.claimBoundary,
466
+ });
467
+ return { schemaVersion: 1, contract: "kungfu-buildchain-kfd-agent-hub-test/v1", valid: true, outputDir: relative(cwd, absoluteOutputDir), lock, report, verification };
468
+ }
469
+
470
+ export function explainKfdAgentHub({ cwd = process.cwd(), declarationPath = KFD_AGENT_HUB_DECLARATION, kfdRoot = "" } = {}) {
471
+ try {
472
+ const result = inspectKfdAgentHub({ cwd, declarationPath, kfdRoot });
473
+ return {
474
+ schemaVersion: 1,
475
+ contract: "kungfu-buildchain-kfd-agent-hub-explanation/v1",
476
+ status: result.valid ? "ready" : "blocked",
477
+ layer: result.valid ? "kfd-agent-hub" : "adapter-artifact",
478
+ owner: result.valid ? "KFD runner and declared adapter" : "consumer adapter",
479
+ evidencePath: result.valid ? KFD_AGENT_HUB_OUTPUT_DIR : result.lock.adapter.path,
480
+ nextAction: result.valid ? "run buildchain kfd hub test --for agent" : `produce ${result.lock.adapter.path}`,
481
+ lockRoot: result.lock.lockRoot,
482
+ };
483
+ } catch (error) {
484
+ const message = String(error.message || error);
485
+ const [code] = message.split(":", 1);
486
+ return {
487
+ schemaVersion: 1,
488
+ contract: "kungfu-buildchain-kfd-agent-hub-explanation/v1",
489
+ status: "blocked",
490
+ layer: code,
491
+ owner: code.includes("package") || code.includes("profile") ? "KFD package" : "consumer declaration",
492
+ evidencePath: declarationPath,
493
+ nextAction: message,
494
+ };
495
+ }
496
+ }