@yansirplus/cli 0.5.17

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 (47) hide show
  1. package/PUBLIC_API.md +22 -0
  2. package/README.md +34 -0
  3. package/dist/build/agent-authoring/config.d.ts +177 -0
  4. package/dist/build/agent-authoring/config.js +607 -0
  5. package/dist/build/agent-authoring/manifest-compiler.d.ts +159 -0
  6. package/dist/build/agent-authoring/manifest-compiler.js +737 -0
  7. package/dist/build/agent-authoring/shared.d.ts +10 -0
  8. package/dist/build/agent-authoring/shared.js +57 -0
  9. package/dist/build/agent-authoring/static-target.d.ts +59 -0
  10. package/dist/build/agent-authoring/static-target.js +1857 -0
  11. package/dist/build/agent-authoring.d.ts +9 -0
  12. package/dist/build/agent-authoring.js +5 -0
  13. package/dist/build/build-cli.d.ts +2 -0
  14. package/dist/build/build-cli.js +264 -0
  15. package/dist/check/algorithmic/architecture-checks.mjs +971 -0
  16. package/dist/check/algorithmic/client-boundary-checks.mjs +337 -0
  17. package/dist/check/algorithmic/convergence-smoke-checks.mjs +608 -0
  18. package/dist/check/algorithmic/distribution-checks.mjs +919 -0
  19. package/dist/check/algorithmic/owner-checks.mjs +647 -0
  20. package/dist/check/algorithmic/package-boundary-checks.mjs +985 -0
  21. package/dist/check/algorithmic/projection-boundary-checks.mjs +302 -0
  22. package/dist/check/algorithmic/repo-surface-checks.mjs +267 -0
  23. package/dist/check/algorithmic/runtime-structural-checks.mjs +264 -0
  24. package/dist/check/algorithmic/source-alias-checks.mjs +106 -0
  25. package/dist/check/algorithmic/static-target-checks.mjs +447 -0
  26. package/dist/check/algorithmic-checks.mjs +482 -0
  27. package/dist/check/check-coverage.mjs +231 -0
  28. package/dist/check/command-runner.mjs +22 -0
  29. package/dist/check/default-gate.mjs +51 -0
  30. package/dist/check/gate-selector.mjs +305 -0
  31. package/dist/check/manifest-rules.mjs +223 -0
  32. package/dist/check/package-graph.mjs +464 -0
  33. package/dist/generate/generate-agent-docs.mjs +435 -0
  34. package/dist/generate/generate-carrier-reference.mjs +514 -0
  35. package/dist/generate/generate-docs.mjs +345 -0
  36. package/dist/generate/generate-effect-skill-manifests.mjs +193 -0
  37. package/dist/generate/project-docs-site.mjs +190 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +25 -0
  40. package/dist/lib/agent-docs-model.mjs +888 -0
  41. package/dist/lib/boundary-rules.mjs +63 -0
  42. package/dist/lib/capability-routes.mjs +354 -0
  43. package/dist/lib/projection-sink.mjs +113 -0
  44. package/dist/lib/public-api-model.mjs +306 -0
  45. package/dist/main.mjs +233 -0
  46. package/dist/runner.mjs +127 -0
  47. package/package.json +32 -0
@@ -0,0 +1,608 @@
1
+ export const createConvergenceSmokeChecks = ({
2
+ fs,
3
+ os,
4
+ path,
5
+ execFileSync,
6
+ repoRoot,
7
+ read,
8
+ readJson,
9
+ walk,
10
+ isRecord,
11
+ failIfAny,
12
+ manifestNames,
13
+ checkPublicApi,
14
+ checkClientBoundaries,
15
+ clientSectionBody,
16
+ checkGeneratedStaticTargetLinking,
17
+ checkSpikeHygiene,
18
+ moduleBucketRegistry,
19
+ workspacePackageRecords,
20
+ consumerFacingSpecifierFailures,
21
+ packageUnitPublicSpecifiers,
22
+ packageUnitPublicSpecifierForSource,
23
+ packageUnitRecords,
24
+ packageUnitSourceNames,
25
+ packageUnitsRegistry,
26
+ distributionRootsRegistry,
27
+ selectedSourceSpecifiersForProfileUnit,
28
+ runProfileTypecheck,
29
+ obsoletePublicPackageFailures,
30
+ packageUnitOptionalPeerFindings,
31
+ specifierMatchesPackage,
32
+ projectionFoldBoundaryFailures,
33
+ }) => {
34
+ const checkConvergenceBoundary = () => {
35
+ checkClientBoundaries();
36
+ checkGeneratedStaticTargetLinking();
37
+ checkSpikeHygiene();
38
+ console.log("convergence boundary passed");
39
+ };
40
+
41
+ const publicExportNames = (apiSource) =>
42
+ new Set([
43
+ ...manifestNames(path.join(repoRoot, apiSource), "Public exports"),
44
+ ...manifestNames(path.join(repoRoot, apiSource), "Experimental exports"),
45
+ ...manifestNames(path.join(repoRoot, apiSource), "Deprecated exports"),
46
+ ]);
47
+
48
+ const publicSurfaceSweepManifest = () =>
49
+ readJson("packages/cli/src/check/sources/public-surface-sweep.source.json");
50
+
51
+ const packagePublicSymbols = (pkg) => {
52
+ if (typeof pkg.apiSource !== "string") return new Set();
53
+ return publicExportNames(pkg.apiSource);
54
+ };
55
+
56
+ const packageExportsSymbol = (exports, symbolName) =>
57
+ [...exports].some((entry) => String(entry).endsWith(`:${symbolName}`));
58
+
59
+ const checkConvergencePublicSurface = () => {
60
+ checkPublicApi();
61
+ checkClientBoundaries();
62
+
63
+ const manifest = publicSurfaceSweepManifest();
64
+ const failures = [];
65
+ if (manifest.schemaVersion !== 1) {
66
+ failures.push("public surface sweep manifest schemaVersion must be 1");
67
+ }
68
+ if (manifest.deprecatedExports?.policy !== "forbidden") {
69
+ failures.push("deprecatedExports policy must be forbidden");
70
+ }
71
+
72
+ const surfacePackages = readJson("docs/surface.json").packages ?? [];
73
+ const surfaceByName = new Map(surfacePackages.map((pkg) => [pkg.name, pkg]));
74
+ const retiredPackages = new Set(manifest.retiredPackages ?? []);
75
+ for (const record of workspacePackageRecords()) {
76
+ if (retiredPackages.has(record.name)) {
77
+ failures.push(`${record.name}: retired package remains in workspace`);
78
+ }
79
+ }
80
+ for (const pkg of surfacePackages) {
81
+ if (retiredPackages.has(pkg.name)) {
82
+ failures.push(`${pkg.name}: retired package remains in docs/surface.json`);
83
+ }
84
+ }
85
+
86
+ let deprecatedSectionCount = 0;
87
+ for (const pkg of surfacePackages) {
88
+ if (!isRecord(pkg) || typeof pkg.apiSource !== "string") continue;
89
+ const source = read(pkg.apiSource);
90
+ const body = clientSectionBody(source, "Deprecated exports").trim();
91
+ if (body.length > 0) {
92
+ deprecatedSectionCount += 1;
93
+ if (body !== "None.") failures.push(`${pkg.apiSource}: deprecated exports must be None.`);
94
+ }
95
+ }
96
+
97
+ const moduleBucketIds = new Set(moduleBucketRegistry().buckets.map((bucket) => bucket.id));
98
+ for (const retained of manifest.retainedProjectionVocabulary ?? []) {
99
+ if (!isRecord(retained)) {
100
+ failures.push("retainedProjectionVocabulary entries must be objects");
101
+ continue;
102
+ }
103
+ if (
104
+ typeof retained.moduleBucket !== "string" ||
105
+ !moduleBucketIds.has(retained.moduleBucket)
106
+ ) {
107
+ failures.push(
108
+ `${retained.export}: retained projection vocabulary has invalid moduleBucket`,
109
+ );
110
+ }
111
+ if (typeof retained.reason !== "string" || retained.reason.length === 0) {
112
+ failures.push(`${retained.export}: retained projection vocabulary requires reason`);
113
+ }
114
+ const [packageName, symbolName] =
115
+ typeof retained.export === "string" ? retained.export.split(":") : [];
116
+ const pkg = surfaceByName.get(packageName);
117
+ if (pkg === undefined || symbolName === undefined) {
118
+ failures.push(
119
+ `${retained.export}: retained projection vocabulary must name package:symbol`,
120
+ );
121
+ continue;
122
+ }
123
+ if (!packageExportsSymbol(packagePublicSymbols(pkg), symbolName)) {
124
+ failures.push(
125
+ `${retained.export}: retained projection vocabulary is not publicly exported`,
126
+ );
127
+ }
128
+ }
129
+ failures.push(...consumerFacingSpecifierFailures());
130
+
131
+ failIfAny("convergence public surface", failures);
132
+ console.log(
133
+ `convergence public surface covered ${surfacePackages.length} package surfaces and ${deprecatedSectionCount} deprecated-export sections`,
134
+ );
135
+ };
136
+
137
+ const checkDocsSiteBuild = () => {
138
+ const contentRoot = path.join(repoRoot, "tooling/docs-site/src/content/docs");
139
+ const distRoot = path.join(repoRoot, "tooling/docs-site/dist");
140
+ const pages = walk("tooling/docs-site/src/content/docs").filter((file) => file.endsWith(".md"));
141
+ const failures = [];
142
+ if (pages.length === 0) failures.push("docs-site projected content is empty");
143
+ for (const file of pages) {
144
+ const contentRel = path
145
+ .relative(contentRoot, path.join(repoRoot, file))
146
+ .split(path.sep)
147
+ .join("/");
148
+ const route =
149
+ contentRel === "index.md" ? "index.html" : contentRel.replace(/\.md$/u, "/index.html");
150
+ if (!fs.existsSync(path.join(distRoot, route)))
151
+ failures.push(`${file} did not build tooling/docs-site/dist/${route}`);
152
+ }
153
+ const builtPages = walk("tooling/docs-site/dist").filter((file) => file.endsWith(".html"));
154
+ if (builtPages.length <= 1)
155
+ failures.push(`docs-site build emitted ${builtPages.length} HTML page(s)`);
156
+ failIfAny("docs site build", failures);
157
+ };
158
+
159
+ const checkCliSurface = () => {
160
+ const failures = [];
161
+ const rootPackage = readJson("package.json");
162
+ const records = workspacePackageRecords();
163
+ const packageNames = new Set(records.map((record) => record.name));
164
+ const surfacePackages = readJson("docs/surface.json").packages ?? [];
165
+ const surfaceByName = new Map(surfacePackages.map((pkg) => [pkg.name, pkg]));
166
+ const sourceAliases = readJson("tsconfig.source-paths.json").compilerOptions?.paths ?? {};
167
+ const oldPackageNames = ["@agent-os/agentos-cli", "@agent-os/ops-api", "@agent-os/ops-htmx"];
168
+ const oldPackagePaths = [
169
+ "tooling/agentos-cli/package.json",
170
+ "tooling/ops-api/package.json",
171
+ "tooling/ops-htmx/package.json",
172
+ ];
173
+
174
+ if (
175
+ !Array.isArray(rootPackage.workspaces) ||
176
+ !rootPackage.workspaces.includes("packages/cli")
177
+ ) {
178
+ failures.push("package.json: workspaces must include packages/cli");
179
+ }
180
+ if (rootPackage.scripts?.agentos !== "node packages/cli/src/main.mjs") {
181
+ failures.push("package.json: scripts.agentos must execute packages/cli/src/main.mjs");
182
+ }
183
+
184
+ const cliPackage = readJson("packages/cli/package.json");
185
+ if (cliPackage.name !== "@agent-os/cli") {
186
+ failures.push("packages/cli/package.json: name must be @agent-os/cli");
187
+ }
188
+ if (cliPackage.bin?.agentos !== "./src/main.mjs") {
189
+ failures.push("packages/cli/package.json: bin.agentos must be ./src/main.mjs");
190
+ }
191
+ if (!packageNames.has("@agent-os/cli")) {
192
+ failures.push("@agent-os/cli: workspace package is missing");
193
+ }
194
+
195
+ for (const oldName of oldPackageNames) {
196
+ if (packageNames.has(oldName)) failures.push(`${oldName}: old workspace package remains`);
197
+ if (surfaceByName.has(oldName)) failures.push(`${oldName}: old docs/surface package remains`);
198
+ }
199
+ for (const oldPath of oldPackagePaths) {
200
+ if (fs.existsSync(path.join(repoRoot, oldPath))) {
201
+ failures.push(`${oldPath}: old tooling package manifest remains`);
202
+ }
203
+ }
204
+ for (const specifier of Object.keys(sourceAliases)) {
205
+ if (oldPackageNames.some((oldName) => specifierMatchesPackage(specifier, oldName))) {
206
+ failures.push(`${specifier}: old source alias remains`);
207
+ }
208
+ }
209
+
210
+ const cliSurface = surfaceByName.get("@agent-os/cli");
211
+ if (cliSurface === undefined) {
212
+ failures.push("docs/surface.json: @agent-os/cli package is missing");
213
+ } else {
214
+ if (cliSurface.path !== "packages/cli") {
215
+ failures.push("docs/surface.json: @agent-os/cli path must be packages/cli");
216
+ }
217
+ if (cliSurface.published !== true) {
218
+ failures.push("docs/surface.json: @agent-os/cli must be published");
219
+ }
220
+ }
221
+
222
+ const cliUnits = (packageUnitsRegistry().packageUnits ?? []).filter(
223
+ (unit) => isRecord(unit) && unit.id === "cli",
224
+ );
225
+ if (cliUnits.length !== 1) {
226
+ failures.push(
227
+ `architecture/package-units.json: expected one cli package unit, got ${cliUnits.length}`,
228
+ );
229
+ } else {
230
+ const cliUnit = cliUnits[0];
231
+ if (cliUnit.targetSourcePackageName !== "@agent-os/cli") {
232
+ failures.push("architecture/package-units.json: cli source package must be @agent-os/cli");
233
+ }
234
+ if (cliUnit.publicPackageName !== "@yansirplus/cli") {
235
+ failures.push(
236
+ "architecture/package-units.json: cli public package must be @yansirplus/cli",
237
+ );
238
+ }
239
+ }
240
+
241
+ const roots = distributionRootsRegistry();
242
+ const publicCliRoot = (roots.roots ?? []).find(
243
+ (root) => isRecord(root) && root.packageUnit === "cli",
244
+ );
245
+ if (publicCliRoot?.publicPackageName !== "@yansirplus/cli") {
246
+ failures.push(
247
+ "architecture/distribution-roots.json: public-cli root must publish @yansirplus/cli",
248
+ );
249
+ }
250
+ const nodeProfile = (roots.targetProfiles ?? []).find(
251
+ (profile) => isRecord(profile) && profile.id === "node",
252
+ );
253
+ if (!Array.isArray(nodeProfile?.packageUnits) || !nodeProfile.packageUnits.includes("cli")) {
254
+ failures.push("architecture/distribution-roots.json: node profile must include cli");
255
+ }
256
+ if (
257
+ !Array.isArray(nodeProfile?.selectedSubpaths) ||
258
+ !nodeProfile.selectedSubpaths.includes("@yansirplus/cli")
259
+ ) {
260
+ failures.push(
261
+ "architecture/distribution-roots.json: node profile must select @yansirplus/cli",
262
+ );
263
+ }
264
+
265
+ const runtimePackage = readJson("packages/runtime/package.json");
266
+ if (runtimePackage.exports?.["./cloudflare/ops-api"] === undefined) {
267
+ failures.push("packages/runtime/package.json: missing ./cloudflare/ops-api export");
268
+ }
269
+ if (!fs.existsSync(path.join(repoRoot, "packages/runtime/src/cloudflare/ops-api/index.ts"))) {
270
+ failures.push(
271
+ "packages/runtime/src/cloudflare/ops-api/index.ts: absorbed ops API is missing",
272
+ );
273
+ }
274
+
275
+ failIfAny("cli surface", failures);
276
+ };
277
+
278
+ const stringLiteralCallRecords = ({ file, callee }) => {
279
+ const source = read(file);
280
+ const sourceFile = ts.createSourceFile(
281
+ file,
282
+ source,
283
+ ts.ScriptTarget.Latest,
284
+ true,
285
+ ts.ScriptKind.JS,
286
+ );
287
+ const records = [];
288
+ const visit = (node) => {
289
+ if (
290
+ ts.isCallExpression(node) &&
291
+ callName(node.expression) === callee &&
292
+ node.arguments.length > 0 &&
293
+ ts.isStringLiteralLike(node.arguments[0])
294
+ ) {
295
+ const position = sourceFile.getLineAndCharacterOfPosition(
296
+ node.arguments[0].getStart(sourceFile),
297
+ );
298
+ records.push({
299
+ value: node.arguments[0].text,
300
+ line: position.line + 1,
301
+ column: position.character + 1,
302
+ });
303
+ }
304
+ ts.forEachChild(node, visit);
305
+ };
306
+ visit(sourceFile);
307
+ return records;
308
+ };
309
+
310
+ const consumerImportFailures = () => {
311
+ const failures = [];
312
+ const allowedPublicSpecifiers = packageUnitPublicSpecifiers();
313
+ const distributionTool = "tooling/distribution/distribution.mjs";
314
+
315
+ for (const record of stringLiteralCallRecords({
316
+ file: distributionTool,
317
+ callee: "publicSpecifier",
318
+ })) {
319
+ const sourceSpecifier = record.value;
320
+ const publicSpecifier = sourceSpecifier.startsWith("@agent-os/")
321
+ ? packageUnitPublicSpecifierForSource(sourceSpecifier)
322
+ : sourceSpecifier;
323
+ if (publicSpecifier === undefined) {
324
+ failures.push(
325
+ `${distributionTool}:${record.line}:${record.column}: ${sourceSpecifier} does not map to a final public package/subpath`,
326
+ );
327
+ continue;
328
+ }
329
+ if (
330
+ publicSpecifier.startsWith("@yansirplus/") &&
331
+ !allowedPublicSpecifiers.has(publicSpecifier)
332
+ ) {
333
+ failures.push(
334
+ `${distributionTool}:${record.line}:${record.column}: ${publicSpecifier} is not in the final public package/subpath set`,
335
+ );
336
+ }
337
+ }
338
+
339
+ return failures.sort(compare);
340
+ };
341
+
342
+ const checkConsumerImports = (args = []) => {
343
+ if (args.length !== 1 || args[0] !== "--final-public-set") {
344
+ throw new Error("consumer-imports: expected --final-public-set");
345
+ }
346
+ failIfAny("consumer imports", consumerImportFailures());
347
+ };
348
+
349
+ const checkDogfoodSmoke = (args = []) => {
350
+ if (args.length !== 2 || args[0] !== "--batch") {
351
+ throw new Error("dogfood-smoke: expected --batch <batch>");
352
+ }
353
+ const batch = args[1];
354
+ if (
355
+ batch !== "core" &&
356
+ batch !== "runtime" &&
357
+ batch !== "client" &&
358
+ batch !== "cli" &&
359
+ batch !== "projection" &&
360
+ batch !== "package-collapse" &&
361
+ batch !== "final-consumer"
362
+ ) {
363
+ throw new Error(`dogfood-smoke: unsupported batch ${batch}`);
364
+ }
365
+
366
+ const failures = [];
367
+ const records = workspacePackageRecords();
368
+ const packageNames = new Set(records.map((record) => record.name));
369
+ const unitNames = packageUnitSourceNames();
370
+ const retiredNames =
371
+ batch === "package-collapse"
372
+ ? records
373
+ .map((record) => record.name)
374
+ .filter(
375
+ (name) =>
376
+ typeof name === "string" &&
377
+ name.startsWith("@agent-os/") &&
378
+ !unitNames.has(name) &&
379
+ name !== "@agent-os/docs-site",
380
+ )
381
+ : batch === "core"
382
+ ? [
383
+ "@agent-os/kernel",
384
+ "@agent-os/runtime-protocol",
385
+ "@agent-os/llm-protocol",
386
+ "@agent-os/telemetry-protocol",
387
+ "@agent-os/backend-protocol",
388
+ ]
389
+ : batch === "runtime"
390
+ ? [
391
+ "@agent-os/backend-cloudflare-do",
392
+ "@agent-os/backend-in-memory",
393
+ "@agent-os/backend-node-postgres",
394
+ "@agent-os/llm-transport-effect-ai",
395
+ "@agent-os/telemetry-otlp",
396
+ ]
397
+ : batch === "client"
398
+ ? ["@agent-os/client-react", "@agent-os/client-svelte"]
399
+ : batch === "cli"
400
+ ? ["@agent-os/agentos-cli", "@agent-os/ops-api", "@agent-os/ops-htmx"]
401
+ : [];
402
+ for (const retiredName of retiredNames) {
403
+ if (packageNames.has(retiredName)) {
404
+ failures.push(`${retiredName}: retired ${batch} package remains in workspace`);
405
+ }
406
+ }
407
+ if (!packageNames.has("@agent-os/core")) {
408
+ failures.push("@agent-os/core: core package is missing from workspace");
409
+ }
410
+ if (batch === "runtime" && !packageNames.has("@agent-os/runtime")) {
411
+ failures.push("@agent-os/runtime: runtime package is missing from workspace");
412
+ }
413
+ if (batch === "client" && !packageNames.has("@agent-os/client")) {
414
+ failures.push("@agent-os/client: client package is missing from workspace");
415
+ }
416
+ if (batch === "cli" && !packageNames.has("@agent-os/cli")) {
417
+ failures.push("@agent-os/cli: cli package is missing from workspace");
418
+ }
419
+ if (batch === "package-collapse") {
420
+ for (const unitName of unitNames) {
421
+ if (!packageNames.has(unitName)) {
422
+ failures.push(`${String(unitName)}: final package unit is missing from workspace`);
423
+ }
424
+ }
425
+ failures.push(...obsoletePublicPackageFailures());
426
+ failures.push(...packageUnitOptionalPeerFindings());
427
+ }
428
+
429
+ const sourceAliases = readJson("tsconfig.source-paths.json").compilerOptions?.paths ?? {};
430
+ for (const specifier of Object.keys(sourceAliases)) {
431
+ if (retiredNames.some((name) => specifier === name || specifier.startsWith(`${name}/`))) {
432
+ failures.push(`${specifier}: retired ${batch} source alias remains`);
433
+ }
434
+ }
435
+
436
+ const surfaceNames = new Set(
437
+ (readJson("docs/surface.json").packages ?? []).map((pkg) => pkg.name),
438
+ );
439
+ for (const retiredName of retiredNames) {
440
+ if (surfaceNames.has(retiredName)) {
441
+ failures.push(`${retiredName}: retired ${batch} package remains in docs/surface.json`);
442
+ }
443
+ }
444
+
445
+ if (batch === "runtime" && failures.length === 0) {
446
+ const runtimeUnit = (packageUnitsRegistry().packageUnits ?? []).find(
447
+ (unit) => isRecord(unit) && unit.id === "runtime",
448
+ );
449
+ if (runtimeUnit === undefined) {
450
+ failures.push("runtime dogfood: package unit runtime is missing");
451
+ } else {
452
+ for (const profile of distributionRootsRegistry().targetProfiles ?? []) {
453
+ if (!isRecord(profile) || !(profile.packageUnits ?? []).includes("runtime")) continue;
454
+ const sourceSpecifiers = selectedSourceSpecifiersForProfileUnit({
455
+ profile,
456
+ unit: runtimeUnit,
457
+ });
458
+ if (sourceSpecifiers.length === 0) {
459
+ failures.push(`${profile.id}: runtime dogfood selects no runtime subpath`);
460
+ continue;
461
+ }
462
+ failures.push(
463
+ ...runProfileTypecheck({
464
+ batch: "runtime-dogfood",
465
+ profile,
466
+ sourceSpecifiers,
467
+ }),
468
+ );
469
+ }
470
+ }
471
+ }
472
+
473
+ if (batch === "client" && failures.length === 0) {
474
+ const clientUnit = (packageUnitsRegistry().packageUnits ?? []).find(
475
+ (unit) => isRecord(unit) && unit.id === "client",
476
+ );
477
+ if (clientUnit === undefined) {
478
+ failures.push("client dogfood: package unit client is missing");
479
+ } else {
480
+ for (const profile of distributionRootsRegistry().targetProfiles ?? []) {
481
+ if (!isRecord(profile) || !(profile.packageUnits ?? []).includes("client")) continue;
482
+ const sourceSpecifiers = selectedSourceSpecifiersForProfileUnit({
483
+ profile,
484
+ unit: clientUnit,
485
+ });
486
+ if (sourceSpecifiers.length === 0) {
487
+ failures.push(`${profile.id}: client dogfood selects no client subpath`);
488
+ continue;
489
+ }
490
+ failures.push(
491
+ ...runProfileTypecheck({
492
+ batch: "client-dogfood",
493
+ profile,
494
+ sourceSpecifiers,
495
+ }),
496
+ );
497
+ }
498
+ }
499
+ }
500
+
501
+ if (batch === "cli" && failures.length === 0) {
502
+ const cliUnit = (packageUnitsRegistry().packageUnits ?? []).find(
503
+ (unit) => isRecord(unit) && unit.id === "cli",
504
+ );
505
+ if (cliUnit === undefined) {
506
+ failures.push("cli dogfood: package unit cli is missing");
507
+ }
508
+ try {
509
+ const output = execFileSync("node", ["packages/cli/src/main.mjs", "--version"], {
510
+ cwd: repoRoot,
511
+ encoding: "utf8",
512
+ stdio: ["ignore", "pipe", "pipe"],
513
+ }).trim();
514
+ if (output !== readJson("package.json").agentOsRelease?.version) {
515
+ failures.push(`cli dogfood: --version returned ${output}`);
516
+ }
517
+ } catch (error) {
518
+ failures.push(
519
+ `cli dogfood command failed: ${error.stderr?.toString() || error.message || error}`,
520
+ );
521
+ }
522
+ }
523
+
524
+ if (batch === "projection" && failures.length === 0) {
525
+ failures.push(...projectionFoldBoundaryFailures());
526
+ failures.push(
527
+ ...runProfileTypecheck({
528
+ batch: "projection-dogfood",
529
+ profile: { id: "projection", ambient: "neutral" },
530
+ sourceSpecifiers: ["@agent-os/runtime/run-projector", "@agent-os/client"],
531
+ }),
532
+ );
533
+ }
534
+
535
+ if (batch === "final-consumer" && failures.length === 0) {
536
+ failures.push(...consumerImportFailures());
537
+ const packageUnitsById = new Map(packageUnitRecords().map((unit) => [unit.id, unit]));
538
+ for (const profile of distributionRootsRegistry().targetProfiles ?? []) {
539
+ if (!isRecord(profile)) continue;
540
+ const sourceSpecifiers = [];
541
+ for (const unitId of profile.packageUnits ?? []) {
542
+ const unit = packageUnitsById.get(unitId);
543
+ if (unit === undefined) {
544
+ failures.push(
545
+ `${profile.id}: target profile references missing package unit ${unitId}`,
546
+ );
547
+ continue;
548
+ }
549
+ sourceSpecifiers.push(...selectedSourceSpecifiersForProfileUnit({ profile, unit }));
550
+ }
551
+ if (sourceSpecifiers.length === 0) {
552
+ failures.push(`${profile.id}: final consumer profile selects no public subpaths`);
553
+ continue;
554
+ }
555
+ failures.push(
556
+ ...runProfileTypecheck({
557
+ batch: "final-consumer-dogfood",
558
+ profile,
559
+ sourceSpecifiers,
560
+ }),
561
+ );
562
+ }
563
+ }
564
+
565
+ if (batch === "core" && failures.length === 0) {
566
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "agentos-core-dogfood-"));
567
+ const scopeDir = path.join(dir, "node_modules", "@agent-os");
568
+ fs.mkdirSync(scopeDir, { recursive: true });
569
+ fs.symlinkSync(path.join(repoRoot, "packages", "core"), path.join(scopeDir, "core"));
570
+ fs.symlinkSync(
571
+ path.join(repoRoot, "node_modules", "effect"),
572
+ path.join(dir, "node_modules", "effect"),
573
+ );
574
+ fs.writeFileSync(path.join(dir, "package.json"), '{"type":"module"}\n');
575
+ const code = [
576
+ 'import { ABORT } from "@yansirplus/core";',
577
+ 'import { defineAgentBindings } from "@yansirplus/core/runtime-protocol";',
578
+ 'import { LLM_WIRE_DESCRIPTOR_VERSION } from "@yansirplus/core/llm-protocol";',
579
+ 'import { TRACE_CONTEXT_VERSION } from "@yansirplus/core/telemetry-protocol";',
580
+ 'import { DISPATCH_INBOUND_ACCEPTED } from "@yansirplus/core/backend-protocol";',
581
+ "const bindings = defineAgentBindings({ handlers: {} });",
582
+ "if (!ABORT || !bindings || !LLM_WIRE_DESCRIPTOR_VERSION || !TRACE_CONTEXT_VERSION || !DISPATCH_INBOUND_ACCEPTED) throw new Error('missing core import');",
583
+ ].join("\n");
584
+ try {
585
+ execFileSync("bun", ["--eval", code], {
586
+ cwd: dir,
587
+ encoding: "utf8",
588
+ stdio: ["ignore", "pipe", "pipe"],
589
+ });
590
+ } catch (error) {
591
+ failures.push(
592
+ `core dogfood import failed: ${error.stderr?.toString() || error.message || error}`,
593
+ );
594
+ }
595
+ }
596
+
597
+ failIfAny(`dogfood smoke ${batch}`, failures);
598
+ };
599
+
600
+ return {
601
+ checkConvergenceBoundary,
602
+ checkConvergencePublicSurface,
603
+ checkDocsSiteBuild,
604
+ checkCliSurface,
605
+ checkConsumerImports,
606
+ checkDogfoodSmoke,
607
+ };
608
+ };