@aiwg/cli 2026.10.1 → 2026.10.4

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 (361) hide show
  1. package/README.md +2 -2
  2. package/agentic/code/providers/capability-matrix.yaml +270 -0
  3. package/agentic/code/providers/model-capabilities.v1.json +3183 -24
  4. package/bin/aiwg.mjs +135 -47
  5. package/dist/src/a2a/codecs.js +9 -0
  6. package/dist/src/agents/grounding/knowledge-base.js +54 -6
  7. package/dist/src/artifacts/browser-export.js +6 -0
  8. package/dist/src/artifacts/checksum-manifest.js +1 -0
  9. package/dist/src/artifacts/citation-parser.js +173 -92
  10. package/dist/src/artifacts/citation-table-schema.js +59 -0
  11. package/dist/src/artifacts/cli.js +57 -18
  12. package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
  13. package/dist/src/artifacts/corpus-tools/vision-extract.js +107 -15
  14. package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
  15. package/dist/src/artifacts/corpus-views/renderers.js +3 -3
  16. package/dist/src/artifacts/discovery-eval.js +6 -4
  17. package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
  18. package/dist/src/artifacts/fortemi-core-sync.js +166 -40
  19. package/dist/src/artifacts/framework-capabilities.js +171 -0
  20. package/dist/src/artifacts/framework-index-paths.js +73 -0
  21. package/dist/src/artifacts/framework-source-digest.js +75 -0
  22. package/dist/src/artifacts/index-builder.js +285 -131
  23. package/dist/src/artifacts/index-files.js +132 -18
  24. package/dist/src/artifacts/index-preparation.js +65 -0
  25. package/dist/src/artifacts/index-refresh.js +576 -0
  26. package/dist/src/artifacts/index-worker.mjs +9 -0
  27. package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
  28. package/dist/src/artifacts/prebuilt-deployment.js +46 -0
  29. package/dist/src/artifacts/prebuilt-relations.js +80 -0
  30. package/dist/src/artifacts/query-engine.js +410 -88
  31. package/dist/src/artifacts/relations-builder.js +168 -0
  32. package/dist/src/artifacts/relations.js +148 -0
  33. package/dist/src/artifacts/repair.js +9 -4
  34. package/dist/src/artifacts/stats.js +5 -2
  35. package/dist/src/artifacts/types.js +30 -10
  36. package/dist/src/artifacts/watcher.js +1 -1
  37. package/dist/src/auth/credential-store.js +2 -1
  38. package/dist/src/catalog/cli.d.mts +5 -0
  39. package/dist/src/channel/manager.d.mts +20 -0
  40. package/dist/src/channel/manager.mjs +78 -12
  41. package/dist/src/cli/child-process-result.js +40 -0
  42. package/dist/src/cli/command-log.js +2 -3
  43. package/dist/src/cli/env.js +38 -16
  44. package/dist/src/cli/flag-parsing.js +236 -0
  45. package/dist/src/cli/handlers/artifacts.js +3 -0
  46. package/dist/src/cli/handlers/auth.js +3 -0
  47. package/dist/src/cli/handlers/decision.js +218 -5
  48. package/dist/src/cli/handlers/diagnose.js +13 -5
  49. package/dist/src/cli/handlers/effect.js +4 -2
  50. package/dist/src/cli/handlers/evidence.js +3 -0
  51. package/dist/src/cli/handlers/feedback.js +14 -5
  52. package/dist/src/cli/handlers/help.js +1 -0
  53. package/dist/src/cli/handlers/index.js +3 -1
  54. package/dist/src/cli/handlers/init.js +248 -30
  55. package/dist/src/cli/handlers/lint.js +5 -0
  56. package/dist/src/cli/handlers/mc.js +23 -0
  57. package/dist/src/cli/handlers/models.js +169 -10
  58. package/dist/src/cli/handlers/ralph-launcher.js +2 -0
  59. package/dist/src/cli/handlers/ralph.js +3 -0
  60. package/dist/src/cli/handlers/refresh.js +28 -0
  61. package/dist/src/cli/handlers/regenerate.js +14 -0
  62. package/dist/src/cli/handlers/resource-versions.js +3 -0
  63. package/dist/src/cli/handlers/rules.js +195 -0
  64. package/dist/src/cli/handlers/run.js +4 -0
  65. package/dist/src/cli/handlers/runtime-info.js +25 -11
  66. package/dist/src/cli/handlers/script-runner.js +19 -8
  67. package/dist/src/cli/handlers/session.js +406 -62
  68. package/dist/src/cli/handlers/sessions.js +243 -76
  69. package/dist/src/cli/handlers/setup-manifest.js +474 -79
  70. package/dist/src/cli/handlers/setup.js +180 -39
  71. package/dist/src/cli/handlers/steward.js +11 -0
  72. package/dist/src/cli/handlers/subcommands.js +60 -3
  73. package/dist/src/cli/handlers/team.js +18 -8
  74. package/dist/src/cli/handlers/uhp.js +3 -0
  75. package/dist/src/cli/handlers/use.js +307 -55
  76. package/dist/src/cli/handlers/utilities.js +125 -26
  77. package/dist/src/cli/handlers/version.js +8 -0
  78. package/dist/src/cli/prompt-utils.js +72 -0
  79. package/dist/src/cli/provider-resolution.js +11 -0
  80. package/dist/src/cli/router.js +15 -1
  81. package/dist/src/cli/services/deployment-verification.js +19 -9
  82. package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
  83. package/dist/src/cli/skill-usage.js +2 -3
  84. package/dist/src/config/aiwg-config.js +349 -35
  85. package/dist/src/config/cli.js +256 -23
  86. package/dist/src/config/decision-config.js +106 -0
  87. package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
  88. package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -0
  89. package/dist/src/config/gitignore.js +50 -0
  90. package/dist/src/config/index-graph-overrides.js +36 -0
  91. package/dist/src/config/project-artifacts-health.d.mts +37 -0
  92. package/dist/src/config/project-artifacts-health.mjs +14 -3
  93. package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
  94. package/dist/src/config/user-config-dir.d.mts +9 -0
  95. package/dist/src/config/user-config.js +38 -11
  96. package/dist/src/config/user-registry.js +23 -15
  97. package/dist/src/config/user-state.js +7 -0
  98. package/dist/src/decision/adapters/jev.js +75 -27
  99. package/dist/src/decision/ask.js +203 -43
  100. package/dist/src/decision/context-live-qualification.js +2 -2
  101. package/dist/src/decision/driver.js +14 -7
  102. package/dist/src/decision/ensemble/aggregate.js +23 -10
  103. package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
  104. package/dist/src/decision/entry.js +25 -6
  105. package/dist/src/decision/graph-live-qualification.js +1 -1
  106. package/dist/src/decision/graph-run.js +9 -0
  107. package/dist/src/decision/heldout/collector.js +15 -14
  108. package/dist/src/decision/heldout/contract.js +30 -7
  109. package/dist/src/decision/index.js +1 -0
  110. package/dist/src/decision/interop.js +60 -8
  111. package/dist/src/decision/jev-credentials.js +15 -1
  112. package/dist/src/decision/ledger.js +942 -0
  113. package/dist/src/decision/offload-activation.js +137 -0
  114. package/dist/src/decision/offload.js +550 -0
  115. package/dist/src/decision/qualification/load.js +4 -6
  116. package/dist/src/decision/savings.js +28 -0
  117. package/dist/src/decision/validate.js +6 -3
  118. package/dist/src/effects/dispatch-verifier.js +252 -0
  119. package/dist/src/effects/index.js +1 -0
  120. package/dist/src/effects/types.js +5 -1
  121. package/dist/src/effects/verifiers/git.js +2 -1
  122. package/dist/src/effects/verifiers/tracker.js +26 -14
  123. package/dist/src/extensions/commands/definitions.js +19 -0
  124. package/dist/src/extensions/project-local-remove.js +36 -1
  125. package/dist/src/features/installer.js +2 -2
  126. package/dist/src/flow/graph-metadata.js +5 -0
  127. package/dist/src/gates/providers/loader.js +64 -18
  128. package/dist/src/installation/manager-command.d.mts +18 -0
  129. package/dist/src/installation/manager-command.mjs +15 -3
  130. package/dist/src/installation/manager.d.mts +49 -0
  131. package/dist/src/installation/manager.mjs +95 -5
  132. package/dist/src/issues/cli.js +4 -4
  133. package/dist/src/issues/local.js +24 -5
  134. package/dist/src/jobs/runner.js +1 -1
  135. package/dist/src/lint/cli.js +27 -1
  136. package/dist/src/lint/loader.js +2 -1
  137. package/dist/src/lint/reporters.js +25 -1
  138. package/dist/src/lint/rule-activation.js +43 -0
  139. package/dist/src/lint/runner.js +137 -62
  140. package/dist/src/mcp/adapters/codex-runtime.js +366 -56
  141. package/dist/src/mcp/claude-install.d.mts +4 -0
  142. package/dist/src/mcp/claude-install.mjs +41 -0
  143. package/dist/src/mcp/cli.d.mts +1 -0
  144. package/dist/src/mcp/cli.mjs +620 -85
  145. package/dist/src/mcp/config-file.d.mts +28 -0
  146. package/dist/src/mcp/config-file.mjs +279 -0
  147. package/dist/src/mcp/config-layers.d.mts +36 -0
  148. package/dist/src/mcp/config-layers.mjs +348 -0
  149. package/dist/src/mcp/credentials.d.mts +51 -0
  150. package/dist/src/mcp/credentials.mjs +484 -0
  151. package/dist/src/mcp/grok-build-config.d.mts +6 -0
  152. package/dist/src/mcp/grok-build-config.mjs +17 -27
  153. package/dist/src/mcp/helpers.mjs +109 -4
  154. package/dist/src/mcp/muse-native-config.d.mts +13 -0
  155. package/dist/src/mcp/muse-native-config.mjs +1 -0
  156. package/dist/src/mcp/omp-config.d.mts +4 -0
  157. package/dist/src/mcp/omp-config.mjs +50 -29
  158. package/dist/src/mcp/profiles.js +97 -23
  159. package/dist/src/mcp/profiles.mjs +99 -27
  160. package/dist/src/mcp/registry.js +248 -131
  161. package/dist/src/mcp/registry.mjs +169 -100
  162. package/dist/src/mcp/server-config.d.mts +3 -0
  163. package/dist/src/mcp/server-config.mjs +89 -0
  164. package/dist/src/mcp/server.mjs +127 -82
  165. package/dist/src/mcp/test-simple.mjs +2 -1
  166. package/dist/src/mcp/toml-editor.d.mts +4 -0
  167. package/dist/src/mcp/toml-string.d.mts +1 -0
  168. package/dist/src/mcp/toml-string.mjs +9 -0
  169. package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
  170. package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
  171. package/dist/src/mcp/tool-filters.d.mts +46 -0
  172. package/dist/src/mcp/tool-filters.mjs +357 -0
  173. package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
  174. package/dist/src/mcp/tools/decision.mjs +22 -3
  175. package/dist/src/mcp/tools/discovery.mjs +62 -55
  176. package/dist/src/mcp/tools/orchestration.mjs +50 -2
  177. package/dist/src/mcp/tools/subsystems.mjs +39 -8
  178. package/dist/src/mcp/tools/worker-invoke.mjs +2856 -0
  179. package/dist/src/mcp/tools/worker-shell-control.mjs +289 -0
  180. package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
  181. package/dist/src/memory/project-registry.js +2 -2
  182. package/dist/src/mission-protocol/codecs.js +647 -1
  183. package/dist/src/mission-protocol/types.js +110 -0
  184. package/dist/src/mission-protocol/worker-status.js +2 -0
  185. package/dist/src/models/config-loader.js +76 -14
  186. package/dist/src/models/grok-build-models.js +93 -41
  187. package/dist/src/models/model-capabilities.v1.json +3183 -24
  188. package/dist/src/models/model-discovery.js +931 -67
  189. package/dist/src/models/provider-policy.js +719 -11
  190. package/dist/src/models/qualification-digest.d.mts +2 -0
  191. package/dist/src/models/qualification-digest.mjs +17 -0
  192. package/dist/src/models/resolver.js +261 -0
  193. package/dist/src/models/worker-direct-readiness.js +550 -0
  194. package/dist/src/models/worker-dispatch-offer.js +169 -0
  195. package/dist/src/models/worker-health-readiness.js +144 -0
  196. package/dist/src/models/worker-installation.d.mts +39 -0
  197. package/dist/src/models/worker-installation.mjs +986 -0
  198. package/dist/src/models/wrapper-route.js +18 -0
  199. package/dist/src/ops/registry.js +9 -14
  200. package/dist/src/plugins/standalone-packager.js +4 -3
  201. package/dist/src/process/bounded-child-process.d.mts +84 -0
  202. package/dist/src/process/bounded-child-process.mjs +430 -0
  203. package/dist/src/process/cli-command.d.mts +16 -0
  204. package/dist/src/process/cli-command.mjs +75 -0
  205. package/dist/src/process/signal-child.d.mts +6 -0
  206. package/dist/src/process/signal-child.mjs +7 -0
  207. package/dist/src/providers/capability-matrix.js +8 -0
  208. package/dist/src/providers/capability-matrix.yaml +270 -0
  209. package/dist/src/providers/deployed-skill-roots.js +20 -0
  210. package/dist/src/providers/grok-build-acp.js +3 -2
  211. package/dist/src/providers/grok-build-headless.js +53 -23
  212. package/dist/src/providers/grok-build-inspect.js +1 -0
  213. package/dist/src/providers/omp-agent.d.mts +8 -0
  214. package/dist/src/providers/omp-diagnostics.d.mts +1 -0
  215. package/dist/src/providers/omp-paths.d.mts +13 -0
  216. package/dist/src/providers/provider-definitions.js +9 -1
  217. package/dist/src/providers/provider-definitions.mjs +9 -2
  218. package/dist/src/providers/skill-ownership.d.mts +9 -0
  219. package/dist/src/providers/skill-ownership.mjs +88 -0
  220. package/dist/src/rules/activation.d.mts +21 -0
  221. package/dist/src/rules/activation.mjs +62 -0
  222. package/dist/src/rules/inline-budget.d.mts +26 -0
  223. package/dist/src/rules/inline-budget.mjs +66 -0
  224. package/dist/src/rules/inventory.js +129 -0
  225. package/dist/src/rules/pin-state.js +73 -0
  226. package/dist/src/rules/pins.d.mts +14 -0
  227. package/dist/src/rules/pins.mjs +112 -0
  228. package/dist/src/rules/rule-frontmatter.schema.json +197 -0
  229. package/dist/src/serve/executor-registry.js +16 -24
  230. package/dist/src/serve/fleet-mission-conductor.js +9 -0
  231. package/dist/src/serve/mission-conductor.js +47 -12
  232. package/dist/src/serve/shared-host-scheduler.js +2 -0
  233. package/dist/src/serve/worker-invocation.js +558 -0
  234. package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
  235. package/dist/src/serve/worker-stdin-frame.mjs +107 -0
  236. package/dist/src/sessions/adapter-registry.js +49 -0
  237. package/dist/src/sessions/adapters/claude.js +39 -54
  238. package/dist/src/sessions/adapters/codex.js +82 -67
  239. package/dist/src/sessions/adapters/copilot.js +4 -4
  240. package/dist/src/sessions/adapters/cursor.js +40 -57
  241. package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
  242. package/dist/src/sessions/adapters/factory.js +31 -27
  243. package/dist/src/sessions/adapters/generic.js +43 -9
  244. package/dist/src/sessions/adapters/grok-build.js +11 -11
  245. package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
  246. package/dist/src/sessions/adapters/grokbot.js +434 -24
  247. package/dist/src/sessions/adapters/hermes.js +27 -29
  248. package/dist/src/sessions/adapters/muse-workspace-scan.js +128 -0
  249. package/dist/src/sessions/adapters/muse.js +200 -104
  250. package/dist/src/sessions/adapters/omp.js +28 -14
  251. package/dist/src/sessions/adapters/openclaw.js +89 -79
  252. package/dist/src/sessions/adapters/opencode.js +27 -24
  253. package/dist/src/sessions/adapters/openhuman.js +18 -60
  254. package/dist/src/sessions/adapters/pi.js +30 -14
  255. package/dist/src/sessions/adapters/warp.js +3 -3
  256. package/dist/src/sessions/adapters/windsurf.js +19 -48
  257. package/dist/src/sessions/batch-contracts.js +25 -5
  258. package/dist/src/sessions/batch-import.js +72 -51
  259. package/dist/src/sessions/candidates.js +47 -11
  260. package/dist/src/sessions/contracts.js +75 -2
  261. package/dist/src/sessions/coverage-preflight.js +134 -0
  262. package/dist/src/sessions/discovery.js +3 -0
  263. package/dist/src/sessions/export-plan.js +4 -3
  264. package/dist/src/sessions/grok-build-cli.js +67 -5
  265. package/dist/src/sessions/import-exclusions.js +72 -0
  266. package/dist/src/sessions/import-lease.js +34 -18
  267. package/dist/src/sessions/import-limits.js +37 -0
  268. package/dist/src/sessions/importer.js +84 -46
  269. package/dist/src/sessions/index.js +2 -0
  270. package/dist/src/sessions/lifecycle.js +45 -0
  271. package/dist/src/sessions/policy.js +133 -25
  272. package/dist/src/sessions/readers.js +106 -53
  273. package/dist/src/sessions/refresh.js +106 -0
  274. package/dist/src/sessions/repository.js +199 -63
  275. package/dist/src/sessions/workspace-discovery.js +315 -88
  276. package/dist/src/sessions/workspace-matching.js +107 -0
  277. package/dist/src/skills/adapters/clawhub.js +10 -8
  278. package/dist/src/skills/adapters/openclaw.js +10 -7
  279. package/dist/src/skills/importer.js +1 -0
  280. package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
  281. package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
  282. package/dist/src/smiths/context-pipeline/finalization.js +6 -5
  283. package/dist/src/smiths/context-pipeline/generator.js +21 -4
  284. package/dist/src/smiths/context-pipeline/parallelism-section.js +195 -14
  285. package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
  286. package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
  287. package/dist/src/smiths/mcpsmith/example.js +1 -1
  288. package/dist/src/smiths/mcpsmith/generator.js +1 -1
  289. package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
  290. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +85 -31
  291. package/dist/src/storage/backends/fs.js +18 -76
  292. package/dist/src/storage/backends/logseq.js +68 -7
  293. package/dist/src/storage/backends/obsidian.js +1 -0
  294. package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
  295. package/dist/src/testing/generators/test-code-generator.js +57 -30
  296. package/dist/src/tracker/forge-http.js +2 -1
  297. package/dist/src/uhp/client.js +11 -1
  298. package/dist/src/uhp/mission.js +5 -0
  299. package/dist/src/update/checker.d.mts +2 -0
  300. package/dist/src/update/checker.mjs +5 -5
  301. package/dist/src/update/notifier.mjs +54 -2
  302. package/dist/src/update/service.d.mts +22 -0
  303. package/dist/src/utils/contained-filesystem.d.mts +37 -0
  304. package/dist/src/utils/contained-filesystem.mjs +683 -0
  305. package/dist/src/utils/temporary-directory.js +36 -0
  306. package/dist/src/writing/example-generator.js +1 -1
  307. package/dist/src/writing/fidelity.js +18 -1
  308. package/package.json +32 -2
  309. package/schemas/mission-v1.schema.json +2429 -0
  310. package/schemas/models/model-catalog.v1.schema.json +301 -0
  311. package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
  312. package/schemas/models/model-policy.v1.schema.json +14 -0
  313. package/schemas/models/provider-model-capabilities.v1.schema.json +1072 -0
  314. package/tools/_resolve-impl.mjs +39 -2
  315. package/tools/agents/deploy-agents.mjs +64 -23
  316. package/tools/agents/providers/base.mjs +153 -166
  317. package/tools/agents/providers/claude.mjs +8 -24
  318. package/tools/agents/providers/codex.mjs +18 -11
  319. package/tools/agents/providers/cursor.mjs +19 -18
  320. package/tools/agents/providers/grok-build.mjs +3 -1
  321. package/tools/agents/providers/grokbot.mjs +11 -12
  322. package/tools/agents/providers/hermes.mjs +62 -49
  323. package/tools/agents/providers/omp.mjs +5 -2
  324. package/tools/agents/providers/openclaw-translator.mjs +19 -5
  325. package/tools/agents/providers/openclaw.mjs +9 -4
  326. package/tools/agents/providers/pi.mjs +5 -0
  327. package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
  328. package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
  329. package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
  330. package/tools/agents/providers/rule-delivery.mjs +1270 -0
  331. package/tools/agents/providers/warp.mjs +20 -26
  332. package/tools/cli/decision-offload-report.mjs +40 -0
  333. package/tools/cli/workspace-status.mjs +862 -0
  334. package/tools/plugin/package-plugins.mjs +186 -8
  335. package/tools/plugin/plugin-links.mjs +114 -0
  336. package/tools/providers/antigravity-transport.mjs +3 -1
  337. package/tools/providers/deepseek-harness-transport.mjs +360 -0
  338. package/tools/providers/omp-transport.mjs +194 -0
  339. package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
  340. package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
  341. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
  342. package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
  343. package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
  344. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
  345. package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
  346. package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
  347. package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
  348. package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
  349. package/tools/ralph-external/lib/pi-adapter.mjs +329 -0
  350. package/tools/ralph-external/lib/provider-adapter.mjs +748 -0
  351. package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
  352. package/tools/ralph-external/lib/session-environment.mjs +28 -0
  353. package/tools/ralph-external/lib/systemd-client.mjs +49 -0
  354. package/tools/ralph-external/lib/worker-health.mjs +439 -0
  355. package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
  356. package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
  357. package/tools/ralph-external/process-monitor.mjs +1017 -0
  358. package/tools/ralph-external/session-launcher.mjs +5057 -0
  359. package/tools/scripts/resource-preflight.mjs +541 -0
  360. package/tools/skills/deploy-skills-codex.mjs +2 -1
  361. package/vscode-extension/schemas/aiwg.config.v1.json +2916 -0
package/bin/aiwg.mjs CHANGED
@@ -30,41 +30,52 @@ import { randomUUID } from 'crypto';
30
30
  const __filename = fileURLToPath(import.meta.url);
31
31
  const __dirname = path.dirname(__filename);
32
32
  const packageRoot = path.resolve(__dirname, '..');
33
+ process.env['AIWG_LAUNCHER_ROOT'] = packageRoot;
33
34
 
34
- function readPackageVersion() {
35
+ function readPackageVersion(root = packageRoot) {
35
36
  try {
36
- const pkg = JSON.parse(readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
37
+ const pkg = JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf8'));
37
38
  return typeof pkg.version === 'string' ? pkg.version : '(unknown)';
38
39
  } catch {
39
40
  return '(unknown)';
40
41
  }
41
42
  }
42
43
 
43
- function detectVersionChannel(version) {
44
- if (version.includes('-rc.')) return 'rc';
45
- if (version.includes('-beta.')) return 'beta';
46
- if (version.includes('-alpha.')) return 'alpha';
47
- if (version.includes('-nightly.')) return 'nightly';
44
+ function readVersionConfig() {
48
45
  try {
49
46
  const legacyDir = path.join(os.homedir(), '.aiwg');
50
47
  const xdgDir = path.join(os.homedir(), '.config', 'aiwg');
51
48
  const configDir = process.env.AIWG_CONFIG
52
49
  ? path.resolve(process.env.AIWG_CONFIG)
53
50
  : existsSync(legacyDir) ? legacyDir : existsSync(xdgDir) ? xdgDir : legacyDir;
51
+ let config = {};
52
+ try {
53
+ config = JSON.parse(readFileSync(path.join(configDir, 'channel.json'), 'utf8'));
54
+ } catch { /* Legacy config is optional. */ }
54
55
  const installationFile = path.join(configDir, 'installation.json');
55
56
  if (existsSync(installationFile)) {
56
57
  const installation = JSON.parse(readFileSync(installationFile, 'utf8'));
57
- if (installation?.runMode === 'development') return 'dev';
58
- if (typeof installation?.channel === 'string') return installation.channel;
58
+ return {
59
+ ...config,
60
+ devMode: installation?.runMode === 'development',
61
+ channel: installation?.channel,
62
+ edgePath: installation?.edgePath ?? config.edgePath,
63
+ };
59
64
  }
60
- const raw = readFileSync(path.join(configDir, 'channel.json'), 'utf8');
61
- const cfg = JSON.parse(raw);
62
- if (cfg?.devMode) return 'dev';
63
- if (typeof cfg?.channel === 'string') return cfg.channel;
65
+ return config;
64
66
  } catch {
65
67
  // Default below.
66
68
  }
67
- return 'stable';
69
+ return {};
70
+ }
71
+
72
+ function detectVersionChannel(version, config) {
73
+ if (config.devMode) return 'dev';
74
+ if (version.includes('-rc.')) return 'rc';
75
+ if (version.includes('-beta.')) return 'beta';
76
+ if (version.includes('-alpha.')) return 'alpha';
77
+ if (version.includes('-nightly.')) return 'nightly';
78
+ return config.channel || 'stable';
68
79
  }
69
80
 
70
81
  function maybeHandleFastVersion(args) {
@@ -72,8 +83,15 @@ function maybeHandleFastVersion(args) {
72
83
  if (args[0] !== '--version' && args[0] !== '-version') return false;
73
84
 
74
85
  const version = readPackageVersion();
75
- const channel = detectVersionChannel(version);
86
+ const config = readVersionConfig();
87
+ const channel = detectVersionChannel(version, config);
88
+ const runtimePath = config.devMode && config.edgePath ? config.edgePath : packageRoot;
76
89
  console.log(` aiwg ${version} [${channel}]`);
90
+ if (config.devMode) {
91
+ console.log(` launcher: ${version}`);
92
+ console.log(` runtime: ${readPackageVersion(runtimePath)}`);
93
+ console.log(` runtime path: ${runtimePath}`);
94
+ }
77
95
  console.log(` path: ${packageRoot}`);
78
96
  return true;
79
97
  }
@@ -240,14 +258,91 @@ maybeWarnUnbuiltDist();
240
258
 
241
259
  // Propagate the strict no-write contract through helpers that may resolve
242
260
  // installation/channel state before the command context exists.
243
- if (process.argv.slice(2).includes('--dry-run')) {
261
+ function isReadOnlyReport(args) {
262
+ return (args[0] === 'rules' && ['inventory', 'list'].includes(args[1]))
263
+ || (['doctor', '-doctor', '--doctor'].includes(args[0]) && args.includes('--decision-offload'))
264
+ || (args[0] === 'steward' && args[1] === 'decision-offload');
265
+ }
266
+ const readOnlyReport = isReadOnlyReport(process.argv.slice(2));
267
+ if (process.argv.slice(2).includes('--dry-run') || readOnlyReport) {
244
268
  process.env['AIWG_CLI_DRY_RUN'] = '1';
245
269
  }
246
270
 
271
+ // Startup tracing — set AIWG_TRACE_STARTUP=1 to print per-phase timings to
272
+ // stderr. Useful for diagnosing cold-start regressions, and for a test to
273
+ // observe whether the credential-handling skip below actually fired,
274
+ // without needing the update notifier to otherwise be eligible to run (it
275
+ // also needs a real terminal — see `isCredentialHandlingCommand()`'s own
276
+ // tests). No-op by default so it doesn't cost anything on the hot path.
277
+ // Defined ahead of its first use below (moved up from just before
278
+ // `resolveRouterPath()`, where it used to sit) so the notifier gate can use
279
+ // it too.
280
+ const traceStartup = process.env['AIWG_TRACE_STARTUP'] === '1' ||
281
+ process.env['AIWG_TRACE_STARTUP']?.toLowerCase() === 'true';
282
+ const startHr = process.hrtime.bigint();
283
+ function trace(phase) {
284
+ if (!traceStartup) return;
285
+ const ms = Number(process.hrtime.bigint() - startHr) / 1_000_000;
286
+ process.stderr.write(`[trace] +${ms.toFixed(1)}ms ${phase}\n`);
287
+ }
288
+ trace('bin:entry');
289
+
290
+ /**
291
+ * True iff this invocation is a command that reads a secret directly from
292
+ * the environment or from an argument as part of its own documented
293
+ * contract (go/no-go review, blocker 1A). Checked from argv alone — no
294
+ * dist/ import, no router resolution — so it costs nothing on the
295
+ * `--version`/`--help` fast paths and can run before any bootstrap
296
+ * subprocess starts.
297
+ *
298
+ * No bootstrap subprocess may start before dispatch for one of these,
299
+ * because a bootstrap child that copies the parent's environment (or is
300
+ * merely spawned while `NODE_DEBUG=child_process` is set) would otherwise
301
+ * see whatever the command's own secret-bearing variable holds, before the
302
+ * command itself ever ran. Scrubbing a specific variable out of every
303
+ * possible bootstrap child is not attempted here — not starting the
304
+ * bootstrap at all for these commands is the smaller, certain fix. (The
305
+ * background update-check child also gets its own environment allow-list,
306
+ * independent of this list — see `notifierChildEnv()` in
307
+ * `src/update/notifier.mjs` — so a secret in the parent's environment that
308
+ * this list does not yet name is still not copied into that one child.)
309
+ *
310
+ * Qualifying commands, and why:
311
+ * - `mcp profile login` — reads an API key via `--api-key-env`,
312
+ * `--api-key-stdin`, or `--api-key-file`.
313
+ * - `decision setup jev` — without `--token-stdin`, reads the token
314
+ * directly from `JEV_API_KEY`/`AIWG_DECISION_JEV_API_KEY`.
315
+ *
316
+ * Checked and left out, with reasons (see the go/no-go review response):
317
+ * - `auth login` — an OAuth device/browser flow; it does not read a
318
+ * pre-existing secret from the environment or argv at invocation, it
319
+ * obtains one over the network after the user authorizes it.
320
+ * - `mcp inject` / `mcp add` / `mcp update` (including `--header-env`) —
321
+ * `headerEnv`/`envFrom` references only ever carry a variable *name*
322
+ * into a provider's config (resolved by that provider later, not by
323
+ * this process); a literal `--env NAME=value` on `mcp add` is stored
324
+ * to the registry file and never touches `process.env` here.
325
+ */
326
+ function isCredentialHandlingCommand(args) {
327
+ const positionals = [];
328
+ for (const arg of args) {
329
+ if (arg === '--') break;
330
+ if (!arg.startsWith('-')) {
331
+ positionals.push(arg);
332
+ if (positionals.length >= 3) break;
333
+ }
334
+ }
335
+ if (positionals[0] === 'mcp' && positionals[1] === 'profile' && positionals[2] === 'login') return true;
336
+ if (positionals[0] === 'decision' && positionals[1] === 'setup' && positionals[2] === 'jev') return true;
337
+ return false;
338
+ }
339
+
247
340
  // Display a cached notice and schedule its refresh before every eligible CLI
248
341
  // path, including fast help/version, channel recovery, and later preflight
249
- // failures. This local-only bootstrap never waits on the registry and failures
250
- // are deliberately ignored so update advice cannot change command behavior.
342
+ // failures — except a credential-handling command (above), which never gets
343
+ // this or any other bootstrap subprocess before dispatch. This local-only
344
+ // bootstrap never waits on the registry and failures are deliberately
345
+ // ignored so update advice cannot change command behavior.
251
346
  async function runUpdateNotifierBootstrap() {
252
347
  try {
253
348
  const notifierPath = path.join(packageRoot, 'dist', 'src', 'update', 'notifier.mjs');
@@ -260,12 +355,21 @@ async function runUpdateNotifierBootstrap() {
260
355
  }
261
356
  }
262
357
 
263
- await runUpdateNotifierBootstrap();
358
+ const cliArgs = process.argv.slice(2);
359
+ const indexRefresh = cliArgs[0] === 'index' && cliArgs[1] === 'refresh';
360
+ if (indexRefresh || readOnlyReport) {
361
+ trace(`notifier:skip (${indexRefresh ? 'index refresh' : 'read-only report'})`);
362
+ } else if (isCredentialHandlingCommand(cliArgs)) {
363
+ trace('notifier:skip (credential-handling command)');
364
+ } else {
365
+ await runUpdateNotifierBootstrap();
366
+ trace('notifier:bootstrap-complete');
367
+ }
264
368
 
265
- if (maybeHandleFastVersion(process.argv.slice(2))) {
369
+ if (maybeHandleFastVersion(cliArgs)) {
266
370
  process.exit(0);
267
371
  }
268
- if (maybeHandleFastHelp(process.argv.slice(2))) {
372
+ if (maybeHandleFastHelp(cliArgs)) {
269
373
  process.exit(0);
270
374
  }
271
375
 
@@ -286,19 +390,6 @@ function ensureInvocationId() {
286
390
  }
287
391
  const invocationId = ensureInvocationId();
288
392
 
289
- // Startup tracing — set AIWG_TRACE_STARTUP=1 to print per-phase timings to
290
- // stderr. Useful for diagnosing cold-start regressions. No-op by default so
291
- // it doesn't cost anything on the hot path.
292
- const traceStartup = process.env['AIWG_TRACE_STARTUP'] === '1' ||
293
- process.env['AIWG_TRACE_STARTUP']?.toLowerCase() === 'true';
294
- const startHr = process.hrtime.bigint();
295
- function trace(phase) {
296
- if (!traceStartup) return;
297
- const ms = Number(process.hrtime.bigint() - startHr) / 1_000_000;
298
- process.stderr.write(`[trace] +${ms.toFixed(1)}ms ${phase}\n`);
299
- }
300
- trace('bin:entry');
301
-
302
393
  /**
303
394
  * Resolve the path to the compiled router.
304
395
  *
@@ -309,10 +400,11 @@ trace('bin:entry');
309
400
  * emit a clear error and exit rather than falling back to a tsx fork.
310
401
  */
311
402
  async function resolveRouterPath() {
312
- const { loadConfig } = await import('../dist/src/channel/manager.mjs');
403
+ const { loadConfig, resolveEffectiveInstallationRoot } = await import('../dist/src/channel/manager.mjs');
313
404
  const config = await loadConfig({ createIfMissing: !process.argv.slice(2).includes('--dry-run') });
314
- if (config.devMode && config.edgePath && config.edgePath !== packageRoot) {
315
- const devRouter = path.join(config.edgePath, 'dist', 'src', 'cli', 'router.js');
405
+ const effectiveRoot = resolveEffectiveInstallationRoot(packageRoot, config);
406
+ if (effectiveRoot !== packageRoot) {
407
+ const devRouter = path.join(effectiveRoot, 'dist', 'src', 'cli', 'router.js');
316
408
  if (!existsSync(devRouter)) {
317
409
  console.error(`Dev mode: compiled router not found at ${devRouter}`);
318
410
  console.error(` Run: (cd ${config.edgePath} && npm run build)`);
@@ -376,7 +468,7 @@ async function applyVerbosityFromArgs(args, routerPath) {
376
468
  setInvocationId(invocationId);
377
469
  // One-shot prune of old JSONL files on startup. Bounded work; safe to
378
470
  // call on every invocation because the work is a single directory list.
379
- pruneOldLogs();
471
+ if (!readOnlyReport) pruneOldLogs();
380
472
  }
381
473
  } catch {
382
474
  // If the logger isn't importable yet (fresh clone without dist/), the
@@ -398,6 +490,7 @@ const RECOVERY_SAFE_COMMANDS = new Set([
398
490
  const RECOVERY_SAFE_INDEX_SUBCOMMANDS = new Set(['query', 'deps', 'stats']);
399
491
 
400
492
  function isRecoverySafeCommand(args) {
493
+ if (isReadOnlyReport(args)) return true;
401
494
  const command = args.find((arg) => !arg.startsWith('-'));
402
495
  if (!command) return false;
403
496
  if (RECOVERY_SAFE_COMMANDS.has(command)) return true;
@@ -447,8 +540,8 @@ async function main() {
447
540
  console.error(` Or switch back: aiwg --use-stable`);
448
541
  process.exit(1);
449
542
  }
450
- const { assertCanonicalInstallation } = await import(pathToFileURL(identityPath).href);
451
- const readOnlyPreview = args.includes('--dry-run');
543
+ const { assertCanonicalInstallation, formatInstallationDiagnostic } = await import(pathToFileURL(identityPath).href);
544
+ const readOnlyPreview = args.includes('--dry-run') || indexRefresh || readOnlyReport;
452
545
  try {
453
546
  assertCanonicalInstallation({
454
547
  actualRoot: activePackageRoot,
@@ -467,12 +560,7 @@ async function main() {
467
560
  drift,
468
561
  });
469
562
  process.stderr.write(
470
- `aiwg: warning: installation identity drift detected; \`${args[0]}\` is read-only and continues, ` +
471
- 'but update, refresh, and deployment stay blocked until the identity is repaired.\n' +
472
- drift.map((item) => `- ${item}\n`).join('') +
473
- 'Inspect: aiwg installation show\n' +
474
- 'Adopt this installation: aiwg installation adopt\n' +
475
- 'Switch deliberately: aiwg installation switch --root <path> --method <npm|web|source> [--manager <absolute-path>]\n',
563
+ `aiwg: warning: ${formatInstallationDiagnostic(error.status, { readOnlyCommand: args[0] })}\n`,
476
564
  );
477
565
  }
478
566
  }
@@ -480,7 +568,7 @@ async function main() {
480
568
  // Wire up the logger level from -v/-vv/--quiet/AIWG_LOG_LEVEL before any
481
569
  // handler runs, and stamp the top-level invocation ID so the logger can
482
570
  // tag every record with it.
483
- await applyVerbosityFromArgs(args, routerPath);
571
+ if (!indexRefresh) await applyVerbosityFromArgs(args, routerPath);
484
572
 
485
573
  // Top-level cancellation controller. SIGINT / SIGTERM flip it, long-running
486
574
  // handlers plumb ctx.signal through fetches and loops so Ctrl-C cancels
@@ -1,3 +1,4 @@
1
+ import { assertMissionWorkerHandoff } from '../mission-protocol/codecs.js';
1
2
  export function encodePushNotificationConfig(version, config) {
2
3
  assertNonEmptyString(version, '$.url', config.url);
3
4
  if (version === '0.3') {
@@ -81,6 +82,7 @@ const V1_TO_STATE = {
81
82
  };
82
83
  const STATE_TO_V1 = Object.fromEntries(Object.entries(V1_TO_STATE).map(([wire, normalized]) => [normalized, wire]));
83
84
  export function encodeMessage(version, message) {
85
+ assertMissionWorkerHandoff(message, 'a2a');
84
86
  assertNonEmptyString(version, '$.messageId', message.messageId);
85
87
  if (message.role !== 'user' && message.role !== 'agent') {
86
88
  fail(version, '$.role', 'must be user or agent');
@@ -101,6 +103,7 @@ export function encodeMessage(version, message) {
101
103
  return out;
102
104
  }
103
105
  export function decodeMessage(version, input, path = '$') {
106
+ assertMissionWorkerHandoff(input, 'a2a');
104
107
  const obj = objectAt(version, path, input);
105
108
  const messageId = stringAt(version, `${path}.messageId`, obj.messageId);
106
109
  const role = decodeRole(version, obj.role, `${path}.role`);
@@ -120,6 +123,7 @@ export function decodeMessage(version, input, path = '$') {
120
123
  return message;
121
124
  }
122
125
  export function encodeTask(version, task) {
126
+ assertMissionWorkerHandoff(task, 'a2a');
123
127
  assertNonEmptyString(version, '$.id', task.id);
124
128
  const out = {
125
129
  id: task.id,
@@ -137,6 +141,7 @@ export function encodeTask(version, task) {
137
141
  return out;
138
142
  }
139
143
  export function decodeTask(version, input, path = '$') {
144
+ assertMissionWorkerHandoff(input, 'a2a');
140
145
  const obj = objectAt(version, path, input);
141
146
  if (version === '1.0' && typeof obj.kind === 'string') {
142
147
  fail(version, `${path}.kind`, 'legacy kind discriminator is not valid in 1.0');
@@ -219,6 +224,7 @@ export function decodeStatus(version, input, path = '$') {
219
224
  return status;
220
225
  }
221
226
  export function encodeArtifact(version, artifact, path = '$') {
227
+ assertMissionWorkerHandoff(artifact, 'a2a');
222
228
  assertNonEmptyString(version, `${path}.artifactId`, artifact.artifactId);
223
229
  if (!Array.isArray(artifact.parts) || artifact.parts.length === 0) {
224
230
  fail(version, `${path}.parts`, 'must contain at least one part');
@@ -234,6 +240,7 @@ export function encodeArtifact(version, artifact, path = '$') {
234
240
  return out;
235
241
  }
236
242
  export function decodeArtifact(version, input, path = '$') {
243
+ assertMissionWorkerHandoff(input, 'a2a');
237
244
  const obj = objectAt(version, path, input);
238
245
  const partsRaw = arrayAt(version, `${path}.parts`, obj.parts);
239
246
  if (partsRaw.length === 0)
@@ -260,6 +267,7 @@ function encodeMessageAt(version, message, path) {
260
267
  }
261
268
  }
262
269
  function encodePart(version, part, path) {
270
+ assertMissionWorkerHandoff(part, 'a2a');
263
271
  const common = {};
264
272
  copyJsonObject(version, common, 'metadata', part.metadata, `${path}.metadata`);
265
273
  copyOptional(common, version === '0.3' ? 'mimeType' : 'mediaType', part.mediaType);
@@ -288,6 +296,7 @@ function encodePart(version, part, path) {
288
296
  };
289
297
  }
290
298
  function decodePart(version, input, path) {
299
+ assertMissionWorkerHandoff(input, 'a2a');
291
300
  const obj = objectAt(version, path, input);
292
301
  if (version === '0.3')
293
302
  return decodeLegacyPart(obj, path);
@@ -108,7 +108,11 @@ export class KnowledgeStore {
108
108
  .slice(0, limit);
109
109
  }
110
110
  /**
111
- * Verify a claim against domain knowledge
111
+ * Verify literal support against the top three retrieved knowledge entries.
112
+ * Only a whole-entry statement match establishes support; direct copular
113
+ * negation vetoes it. Other wording abstains, including paraphrases and
114
+ * multi-sentence passages. This is not a general semantic entailment judge
115
+ * or a check that the stored knowledge is itself true.
112
116
  */
113
117
  verifyClaim(claim, domain) {
114
118
  const fragments = this.search(claim, { domain, limit: 3 });
@@ -118,17 +122,31 @@ export class KnowledgeStore {
118
122
  verified: false,
119
123
  confidence: 0,
120
124
  sources: [],
125
+ evidence: [],
121
126
  correction: 'No relevant knowledge found in knowledge base',
122
127
  };
123
128
  }
124
- // Simple verification: check if top fragments support or contradict
125
- const topFragment = fragments[0];
126
- const confidence = Math.min(topFragment.relevance_score, 1.0);
129
+ // Relevance selects candidates; only their content can establish support.
130
+ const evidence = fragments.map((fragment) => ({
131
+ source: fragment.source,
132
+ entry_id: fragment.metadata?.id,
133
+ content: fragment.content,
134
+ relation: compareStatements(claim, fragment.content),
135
+ }));
136
+ const contradictions = evidence.filter((item) => item.relation === 'contradicts');
137
+ const verified = contradictions.length === 0
138
+ && evidence.some((item) => item.relation === 'supports');
127
139
  return {
128
140
  claim,
129
- verified: confidence > 0.3,
130
- confidence,
141
+ verified,
142
+ confidence: verified ? 1 : 0,
131
143
  sources: fragments.map((f) => f.source),
144
+ evidence,
145
+ ...(!verified && {
146
+ correction: contradictions.length > 0
147
+ ? `Conflicting knowledge: ${contradictions.map((item) => item.content).join(' | ')}`
148
+ : 'Relevant knowledge was retrieved, but explicit support was not established',
149
+ }),
132
150
  };
133
151
  }
134
152
  listDomains() {
@@ -144,6 +162,36 @@ export class KnowledgeStore {
144
162
  // ============================================================================
145
163
  // Helpers
146
164
  // ============================================================================
165
+ function normalizeStatement(text) {
166
+ // Preserve case, numbers and internal punctuation: they can change meaning.
167
+ return text.trim().replace(/\s+/g, ' ').replace(/\.$/, '');
168
+ }
169
+ function compareStatements(claim, content) {
170
+ const statement = normalizeStatement(claim);
171
+ const evidence = normalizeStatement(content);
172
+ if (!statement || !evidence)
173
+ return 'unknown';
174
+ // Do not extract assertions from questions, quotations or multi-sentence text.
175
+ if ([statement, evidence].some((text) => /[?!;"“”]|\.(?:\s|$)/.test(text)))
176
+ return 'unknown';
177
+ if (statement === evidence)
178
+ return 'supports';
179
+ // A deliberately limited conflict rule: the same whole statement with a
180
+ // single "not" following is/are/was/were. Never infer support by dropping
181
+ // negation, and abstain on complex negation (e.g. "not only" or "not always").
182
+ const affirmative = (text) => {
183
+ if (/[.!?;:\n]/.test(text))
184
+ return undefined;
185
+ const match = text.match(/^(.+?\b(?:is|are|was|were)) not (.+)$/);
186
+ if (!match || /\b(?:not|only|just|always|necessarily)\b/.test(match[2]))
187
+ return undefined;
188
+ return `${match[1]} ${match[2]}`;
189
+ };
190
+ if (affirmative(statement) === evidence || affirmative(evidence) === statement) {
191
+ return 'contradicts';
192
+ }
193
+ return 'unknown';
194
+ }
147
195
  function calculateRelevance(entry, queryTerms) {
148
196
  const searchable = [
149
197
  entry.topic,
@@ -455,6 +455,12 @@ function recordForEntry(cwd, entry, graphName, dependencyGraph, privacy, schemaV
455
455
  ? { aiwg_search_terms: uniqueSorted(entry.searchTerms) }
456
456
  : {}),
457
457
  ...(entry.script ? { aiwg_script: entry.script } : {}),
458
+ ...(entry.activation ? { activation: entry.activation } : {}),
459
+ ...(entry.bound_to ? { bound_to: entry.bound_to } : {}),
460
+ ...(entry.ruleEnforcement ? { enforcement: entry.ruleEnforcement } : {}),
461
+ ...(entry.rulePaths ? { paths: entry.rulePaths } : {}),
462
+ ...(entry.governedPaths ? { aiwg_governed_paths: entry.governedPaths } : {}),
463
+ ...(entry.ruleSummary !== undefined ? { summary: entry.ruleSummary } : {}),
458
464
  },
459
465
  },
460
466
  }
@@ -58,6 +58,7 @@ export function writeManifest(indexDir, manifest) {
58
58
  version: 1,
59
59
  generated: new Date().toISOString(),
60
60
  entries: manifest.entries,
61
+ ...(manifest.receipt ? { receipt: manifest.receipt } : {}),
61
62
  };
62
63
  fs.writeFileSync(tmpPath, JSON.stringify(payload, null, 2), 'utf-8');
63
64
  fs.renameSync(tmpPath, manifestPath);