@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
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Layered MCP configuration roots.
3
+ *
4
+ * AIWG_CONFIG_LAYERS lists configuration directories separated by the
5
+ * platform path delimiter (`:` on POSIX, `;` on Windows), lowest precedence
6
+ * first. Each directory may hold mcp-servers.json and mcp-profiles.json. An
7
+ * entry (server or profile) in a later directory replaces the entry of the
8
+ * same name in an earlier one; entries are not merged field by field.
9
+ *
10
+ * The last directory is the write layer: add, update and recordInjection
11
+ * write there, and nothing from an earlier layer is copied into it unless
12
+ * that entry is updated.
13
+ *
14
+ * When AIWG_CONFIG_LAYERS is unset, or a registry is constructed with an
15
+ * explicit directory, the single-directory behaviour applies unchanged.
16
+ */
17
+
18
+ import { lstat, mkdtemp, readFile, readlink, realpath, rmdir } from 'fs/promises';
19
+ import { basename, dirname, isAbsolute, posix, relative, resolve, sep, win32 } from 'path';
20
+ import { assertConfigDestination, assertNoSymlinkParents, writeConfigAtomic } from './config-file.mjs';
21
+ import { resolveCredentialPolicy, stricterCredentialPolicy, warnCredentialPolicyRelaxation, warnDroppedOverlayPolicy } from './credentials.mjs';
22
+
23
+ /**
24
+ * `platform` lets tests validate Windows path semantics (`C:\org`, UNC
25
+ * `\\server`, root-relative `\org`/`/org`) deterministically on any host; it
26
+ * defaults to the real host platform, so production calls behave exactly as
27
+ * the native `path` module already would.
28
+ */
29
+ export function resolveConfigLayers(configDirOverride, env = process.env, { platform = process.platform } = {}) {
30
+ if (configDirOverride) return null;
31
+ const raw = env.AIWG_CONFIG_LAYERS;
32
+ if (!raw || !raw.trim()) return null;
33
+ const pathForPlatform = platform === 'win32' ? win32 : posix;
34
+ // A Windows-formatted value uses `;`; splitting a drive-qualified entry
35
+ // (`C:\org`) on the real host's POSIX `:` would wrongly cut it at the drive
36
+ // letter, so the simulated platform's own delimiter is used, not the real
37
+ // host's - matching the native `path` module's behaviour when the real host
38
+ // actually is that platform.
39
+ const entries = raw.split(pathForPlatform.delimiter).map(entry => entry.trim()).filter(Boolean);
40
+ // A bare leading `\` or `/` with no drive letter is current-drive-relative
41
+ // under Windows path semantics: resolving it from C:\work versus D:\work
42
+ // selects different drives, so a layer list could change meaning across
43
+ // hosts. The backslash form is refused on every platform, since it is never
44
+ // a valid POSIX absolute path either. The forward-slash form is refused
45
+ // only when interpreting entries under actual Windows path semantics; on
46
+ // POSIX, a leading `/` is an unambiguous, fully-qualified absolute path and
47
+ // must keep working. Drive-qualified (`C:\`, `C:/`) and UNC (`\\server`,
48
+ // `//server`) forms remain allowed on every platform.
49
+ const driveRelativePattern = platform === 'win32' ? /^[\\/](?![\\/])/ : /^\\(?!\\)/;
50
+ const driveRelativeEntries = entries.filter(entry => driveRelativePattern.test(entry));
51
+ if (driveRelativeEntries.length) {
52
+ throw new Error(`AIWG_CONFIG_LAYERS entries must be absolute paths; refusing Windows current-drive-relative entries: ${driveRelativeEntries.join(', ')}`);
53
+ }
54
+ // A relative entry would resolve against the current working directory, so the
55
+ // same AIWG_CONFIG_LAYERS value would name different layers from different
56
+ // invocation directories. Refuse instead of silently picking a directory.
57
+ const relativeEntries = entries.filter(entry => !pathForPlatform.isAbsolute(entry));
58
+ if (relativeEntries.length) {
59
+ throw new Error(`AIWG_CONFIG_LAYERS entries must be absolute paths; refusing relative entries: ${relativeEntries.join(', ')}`);
60
+ }
61
+ const layers = entries.map(entry => pathForPlatform.resolve(entry));
62
+ return layers.length > 0 ? layers : null;
63
+ }
64
+
65
+ async function readLayer(path) {
66
+ try {
67
+ return JSON.parse(await readFile(path, 'utf-8'));
68
+ } catch (error) {
69
+ if (error?.code === 'ENOENT') return null;
70
+ if (error instanceof SyntaxError) throw new Error(`Invalid JSON in MCP configuration layer ${path}`);
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ /** File-format fields every layer file keeps, whichever layer set them. */
76
+ const FORMAT_FIELDS = ['apiVersion', 'kind'];
77
+
78
+ /** Bookkeeping fields that do not make an entry "changed" for layering purposes. */
79
+ const BOOKKEEPING_FIELDS = ['injectedProviders', 'addedAt', 'createdAt', 'updatedAt'];
80
+
81
+ function sortKeys(value) {
82
+ if (Array.isArray(value)) return value.map(sortKeys);
83
+ if (value === null || typeof value !== 'object') return value;
84
+ return Object.fromEntries(Object.keys(value).sort().map(key => [key, sortKeys(value[key])]));
85
+ }
86
+
87
+ function comparable(entry) {
88
+ const copy = { ...entry };
89
+ for (const field of BOOKKEEPING_FIELDS) delete copy[field];
90
+ return JSON.stringify(sortKeys(copy));
91
+ }
92
+
93
+ /**
94
+ * Read `filename` from every layer and merge the `collection` map by entry
95
+ * name. Top-level fields come from the highest layer that sets them, except
96
+ * credentialPolicy: lower (non-write) layers fold together with the
97
+ * strictest value winning - an enforced organisation floor - and the write
98
+ * layer's own declared value (a personal default) is then bounded below by
99
+ * that floor, never copying the floor itself into the write layer's record.
100
+ * `lowerPolicySource` names the config file of the lower layer that set the
101
+ * folded floor's value (the first layer to set it; a later layer repeating
102
+ * the same strictness does not move attribution), for diagnostics.
103
+ *
104
+ * Returns the merged data and a layering record: the names defined in the
105
+ * write layer, and comparable forms of entries and top-level fields in lower layers.
106
+ */
107
+ export async function loadLayered(layers, filename, collection, defaults) {
108
+ const merged = { ...defaults, [collection]: {} };
109
+ const lower = new Map();
110
+ const own = new Set();
111
+ const lowerFields = new Map();
112
+ const ownFields = new Set();
113
+ let ownPolicy;
114
+ let lowerPolicy;
115
+ let lowerPolicySource;
116
+ const writeLayer = layers.length - 1;
117
+ for (const [index, dir] of layers.entries()) {
118
+ const parsed = await readLayer(resolve(dir, filename));
119
+ if (!parsed) continue;
120
+ const { [collection]: entries, ...rest } = parsed;
121
+ if (index === writeLayer) {
122
+ ownPolicy = rest.credentialPolicy;
123
+ } else if (Object.hasOwn(rest, 'credentialPolicy')) {
124
+ try {
125
+ const folded = stricterCredentialPolicy(lowerPolicy, rest.credentialPolicy);
126
+ if (folded !== lowerPolicy) lowerPolicySource = resolve(dir, filename);
127
+ lowerPolicy = folded;
128
+ } catch (error) {
129
+ throw new Error(`Invalid MCP credential policy in layer ${resolve(dir, filename)}: ${error.message}`);
130
+ }
131
+ }
132
+ Object.assign(merged, rest);
133
+ // Across several lower layers, credentialPolicy folds by strictness, not
134
+ // by "last layer wins" like every other field: keep the running snapshot
135
+ // in sync with the folded floor so a later lowerFields comparison (used
136
+ // to decide whether to copy an unchanged inherited value) reflects it,
137
+ // not one lower layer's own raw declared value.
138
+ if (index < writeLayer) merged.credentialPolicy = lowerPolicy;
139
+ for (const field of Object.keys(rest)) {
140
+ if (index === writeLayer) ownFields.add(field);
141
+ else lowerFields.set(field, comparable({ value: merged[field] }));
142
+ }
143
+ for (const [name, entry] of Object.entries(entries || {})) {
144
+ merged[collection][name] = entry;
145
+ if (index === writeLayer) own.add(name);
146
+ else lower.set(name, comparable(entry));
147
+ }
148
+ }
149
+ // No layer declared a policy anywhere: leave the field absent, exactly as
150
+ // when `defaults` carries no credentialPolicy, rather than materializing
151
+ // the built-in `literal` default as a stored value.
152
+ let effectivePolicy;
153
+ if (ownPolicy !== undefined || lowerPolicy !== undefined) {
154
+ try {
155
+ effectivePolicy = resolveCredentialPolicy({
156
+ registryPolicy: ownPolicy, floorPolicy: lowerPolicy, floorSource: lowerPolicySource, env: {},
157
+ });
158
+ } catch (error) {
159
+ throw new Error(`Invalid MCP credential policy in layer ${resolve(layers[writeLayer], filename)}: ${error.message}`);
160
+ }
161
+ merged.credentialPolicy = effectivePolicy;
162
+ } else {
163
+ delete merged.credentialPolicy;
164
+ }
165
+ return { data: merged, layering: { lower, own, lowerFields, ownFields, ownPolicy, lowerPolicy, lowerPolicySource, effectivePolicy } };
166
+ }
167
+
168
+ /** True when the entry is defined only below the write layer. */
169
+ export function isLowerLayerEntry(layering, name) {
170
+ return layering.lower.has(name) && !layering.own.has(name);
171
+ }
172
+
173
+ /**
174
+ * True when the write layer overrides a lower-layer entry that still carries
175
+ * worker qualification. Removing that override would silently re-expose the
176
+ * lower entry's qualification, so removal must be refused instead.
177
+ */
178
+ export function lowerEntryQualified(layering, name) {
179
+ if (!layering?.lower?.has(name) || !layering?.own?.has(name)) return false;
180
+ try {
181
+ return JSON.parse(layering.lower.get(name))?.workerQualification !== undefined;
182
+ } catch {
183
+ return false;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * The data to persist in the write layer: entries defined there, new entries,
189
+ * and lower-layer entries whose content (bookkeeping aside) was changed.
190
+ * Top-level fields follow the same rule, without entry bookkeeping exclusions;
191
+ * apiVersion and kind are always kept so the file stays self-describing.
192
+ */
193
+ export function writeLayerData(data, collection, layering, explicitPolicy = false) {
194
+ const entries = Object.entries(data[collection] || {}).filter(([name, entry]) =>
195
+ layering.own.has(name) || !layering.lower.has(name) || comparable(entry) !== layering.lower.get(name));
196
+ const fields = Object.entries(data).filter(([field, value]) => field !== collection && (
197
+ FORMAT_FIELDS.includes(field) || layering.ownFields.has(field) || !layering.lowerFields.has(field) ||
198
+ comparable({ value }) !== layering.lowerFields.get(field)));
199
+ const persisted = { ...Object.fromEntries(fields), [collection]: Object.fromEntries(entries) };
200
+ // A setter explicitly writes even when its value equals the merged floor.
201
+ // Other saves retain policy ownership, without copying an inherited floor.
202
+ if (explicitPolicy || data.credentialPolicy !== layering.effectivePolicy) {
203
+ const effective = resolveCredentialPolicy({
204
+ flag: data.credentialPolicy, floorPolicy: layering.lowerPolicy, floorSource: layering.lowerPolicySource, env: {},
205
+ });
206
+ if (effective !== data.credentialPolicy) {
207
+ const location = layering.lowerPolicySource ? ` (set in ${layering.lowerPolicySource})` : '';
208
+ throw new Error(`Cannot relax MCP credential policy floor "${effective}"${location} to "${data.credentialPolicy}".`);
209
+ }
210
+ persisted.credentialPolicy = data.credentialPolicy;
211
+ } else if (layering.ownPolicy !== undefined &&
212
+ resolveCredentialPolicy({ flag: layering.ownPolicy, floorPolicy: layering.lowerPolicy, floorSource: layering.lowerPolicySource, env: {} }) !== layering.ownPolicy) {
213
+ delete persisted.credentialPolicy;
214
+ warnDroppedOverlayPolicy(layering.ownPolicy, layering.lowerPolicy, layering.lowerPolicySource);
215
+ }
216
+ return persisted;
217
+ }
218
+
219
+ /** Resolve a target, including a missing suffix, without creating it. */
220
+ async function targetRealpath(path, aliases = []) {
221
+ const absolute = resolve(path);
222
+ if (aliases.includes(absolute)) throw new Error(`MCP configuration target symlink cycle: ${[...aliases, absolute].join(' -> ')}`);
223
+ try {
224
+ return await realpath(path);
225
+ } catch (error) {
226
+ if (error.code !== 'ENOENT') throw error;
227
+ // Resolve dangling aliases too: creating their missing referent would change
228
+ // a lower layer even though realpath currently reports ENOENT.
229
+ if (dirname(absolute) === absolute) throw error;
230
+ const candidate = resolve(await targetRealpath(dirname(absolute), aliases), basename(absolute));
231
+ try {
232
+ if ((await lstat(candidate)).isSymbolicLink()) {
233
+ return targetRealpath(resolve(dirname(candidate), await readlink(candidate)), [...aliases, absolute]);
234
+ }
235
+ } catch (statError) {
236
+ if (statError.code !== 'ENOENT') throw statError;
237
+ }
238
+ return candidate;
239
+ }
240
+ }
241
+
242
+ /** Probe the filesystem containing the nearest existing ancestor of a target. */
243
+ export async function isCaseInsensitivePath(path) {
244
+ let ancestor = resolve(path);
245
+ while (true) {
246
+ try {
247
+ await realpath(ancestor);
248
+ break;
249
+ } catch (error) {
250
+ if (error.code !== 'ENOENT' || dirname(ancestor) === ancestor) throw error;
251
+ ancestor = dirname(ancestor);
252
+ }
253
+ }
254
+ let probe;
255
+ try {
256
+ probe = await mkdtemp(resolve(ancestor, '.aiwg-case-probe-a-'));
257
+ const alternate = resolve(dirname(probe), basename(probe).toUpperCase());
258
+ try {
259
+ const [original, folded] = await Promise.all([lstat(probe), lstat(alternate)]);
260
+ return original.dev === folded.dev && original.ino === folded.ino;
261
+ } catch (error) {
262
+ if (error.code === 'ENOENT') return false;
263
+ throw error;
264
+ }
265
+ } catch (error) {
266
+ // Read-only ancestors cannot be probed: compare conservatively rather than allow overlap.
267
+ if (error.code === 'EACCES' || error.code === 'EPERM' || error.code === 'EROFS') return true;
268
+ throw error;
269
+ } finally {
270
+ if (probe) await rmdir(probe);
271
+ }
272
+ }
273
+
274
+ function inside(target, parent, caseInsensitive) {
275
+ const rel = relative(caseInsensitive ? parent.toLowerCase() : parent, caseInsensitive ? target.toLowerCase() : target);
276
+ return !rel || (rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
277
+ }
278
+
279
+ /** Refuse a write that could modify another configuration layer. */
280
+ export async function assertLayerWriteDestination(layers, filename) {
281
+ const targets = await Promise.all(layers.map(async dir => ({
282
+ dir: await targetRealpath(dir),
283
+ files: await Promise.all(['mcp-servers.json', 'mcp-profiles.json'].map(name => targetRealpath(resolve(dir, name)))),
284
+ })));
285
+ const write = targets[targets.length - 1];
286
+ const caseInsensitive = await isCaseInsensitivePath(write.dir);
287
+ const writeFile = await targetRealpath(resolve(layers[layers.length - 1], filename));
288
+ for (const lower of targets.slice(0, -1)) {
289
+ if (inside(write.dir, lower.dir, caseInsensitive) || inside(writeFile, lower.dir, caseInsensitive) || lower.files.some(file => inside(writeFile, file, caseInsensitive))) {
290
+ throw new Error(`Refusing overlapping MCP configuration layer targets: ${writeFile} overlaps ${lower.dir}`);
291
+ }
292
+ }
293
+ const file = resolve(layers[layers.length - 1], filename);
294
+ await assertNoSymlinkParents(file, layers[layers.length - 1]);
295
+ await assertConfigDestination(file);
296
+ }
297
+
298
+ /** Persist atomically, then refresh ownership only after a successful write. */
299
+ export async function saveLayerData(file, data, collection, layers, layering, explicitPolicy = false) {
300
+ if (layers) await assertLayerWriteDestination(layers, basename(file));
301
+ const persisted = layering ? writeLayerData(data, collection, layering, explicitPolicy) : data;
302
+ // The active identity layer file is always 0600: it is the personal write
303
+ // layer of an active AIWG_CONFIG_LAYERS configuration and may carry secrets.
304
+ // Without layering, `file` is whatever single directory the caller
305
+ // configured (which may be a shared, group-readable organisation checkout
306
+ // maintained directly, not a private identity directory): an existing file
307
+ // keeps its mode, while a newly created file starts private at 0600.
308
+ await writeConfigAtomic(file, JSON.stringify(persisted, null, 2) + '\n', layers
309
+ ? { userScope: true, rejectSymlinkParents: true }
310
+ : { newFileMode: 0o600, rejectSymlinkParents: true });
311
+ if (layering) {
312
+ layering.own = new Set(Object.keys(persisted[collection] || {}));
313
+ layering.ownFields = new Set(Object.keys(persisted).filter(field => field !== collection));
314
+ layering.ownPolicy = persisted.credentialPolicy;
315
+ layering.effectivePolicy = data.credentialPolicy;
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Resolve a profile's `extends` chain. Servers are concatenated base-first
321
+ * without duplicates. Per provider key, toolDeny accumulates across the chain
322
+ * and toolAllow is taken from the most-derived profile that sets it.
323
+ */
324
+ export function resolveProfileExtends(name, profiles, seen = []) {
325
+ const profile = profiles[name];
326
+ if (!profile) {
327
+ throw new Error(seen.length > 0
328
+ ? `Profile "${seen[seen.length - 1]}" extends "${name}", which is not defined in any configuration layer.`
329
+ : `Profile "${name}" not found.`);
330
+ }
331
+ if (seen.includes(name)) throw new Error(`Profile extends cycle: ${[...seen, name].join(' -> ')}`);
332
+ const bases = (profile.extends || []).map(base => resolveProfileExtends(base, profiles, [...seen, name]));
333
+
334
+ const servers = [];
335
+ const providerOverrides = {};
336
+ for (const source of [...bases, profile]) {
337
+ for (const server of source.servers || []) if (!servers.includes(server)) servers.push(server);
338
+ for (const [provider, override] of Object.entries(source.providerOverrides || {})) {
339
+ const previous = providerOverrides[provider] || {};
340
+ providerOverrides[provider] = {
341
+ ...previous,
342
+ ...(override.toolDeny ? { toolDeny: [...new Set([...(previous.toolDeny || []), ...override.toolDeny])] } : {}),
343
+ ...(override.toolAllow ? { toolAllow: [...override.toolAllow] } : {}),
344
+ };
345
+ }
346
+ }
347
+ return { ...profile, servers, providerOverrides };
348
+ }
@@ -0,0 +1,51 @@
1
+ export type McpCredentialPolicy = 'literal' | 'references' | 'none';
2
+
3
+ interface CredentialBearingServer {
4
+ name: string;
5
+ url?: string;
6
+ args?: string[];
7
+ env?: Record<string, string>;
8
+ headers?: Record<string, string>;
9
+ headerEnv?: Record<string, string>;
10
+ envFrom?: Record<string, string>;
11
+ auth?: { clientSecret?: string; tokenUrl?: string; redirectUri?: string; resource?: string };
12
+ oauth?: { clientSecret?: string; tokenUrl?: string; redirectUri?: string; resource?: string };
13
+ }
14
+
15
+ export const ENV_REFERENCE_SYNTAX: Readonly<Record<string, string | null>>;
16
+ export const CREDENTIAL_POLICIES: readonly McpCredentialPolicy[];
17
+ export function urlCarriesUserinfo(url?: string): boolean;
18
+ export function credentialLikeQueryParams(url?: string, options?: { includeReferences?: boolean }): string[];
19
+ export function credentialLikeFragmentParams(url?: string, options?: { includeReferences?: boolean }): string[];
20
+ export function isCredentialReferenceValue(value: unknown): boolean;
21
+ export function credentialLikeArgs(args?: string[], options?: { includeReferences?: boolean }): string[];
22
+ export function redactCredentialArgs(args?: string[]): string[] | undefined;
23
+ export function validateEnvReferenceName(name: string): void;
24
+ export function renderCredentialMaps(
25
+ server: CredentialBearingServer,
26
+ provider: string,
27
+ ): { env?: Record<string, string>; headers?: Record<string, string> };
28
+ export function credentialPolicyViolations(server: CredentialBearingServer, policy?: McpCredentialPolicy): string[];
29
+ export function assertCredentialPolicy(servers: CredentialBearingServer[], policy?: McpCredentialPolicy): void;
30
+ export function resolveCredentialPolicy(options?: {
31
+ flag?: string;
32
+ registryPolicy?: string;
33
+ floorPolicy?: string;
34
+ floorSource?: string;
35
+ env?: Record<string, string | undefined>;
36
+ }): McpCredentialPolicy;
37
+
38
+ export function stricterCredentialPolicy(
39
+ a: McpCredentialPolicy | undefined,
40
+ b: McpCredentialPolicy | undefined,
41
+ ): McpCredentialPolicy | undefined;
42
+
43
+ export function warnCredentialPolicyRelaxation(candidate: McpCredentialPolicy, floor: McpCredentialPolicy, source?: string): void;
44
+ export function warnDroppedOverlayPolicy(candidate: McpCredentialPolicy, floor: McpCredentialPolicy, source?: string): void;
45
+ export function resetCredentialPolicyWarnings(): void;
46
+
47
+ export const INSTALL_POLICY_EXEMPT_ENV: Readonly<Record<string, readonly string[]>>;
48
+ export function installPolicyCheckedServer(name: string, server: CredentialBearingServer): CredentialBearingServer;
49
+
50
+ export function redactCredentialText(value: string): string;
51
+ export function redactCredentialRecord<T>(value: T): T;