@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,550 @@
1
+ /**
2
+ * Direct-worker onboarding/readiness (issue #2894, S1 Tier 0 direct handoff).
3
+ *
4
+ * For each configured worker this reports two separate facts that must never
5
+ * be merged:
6
+ * - "installation readiness": is the CLI installed, and is the installed
7
+ * version in the registered range;
8
+ * - "dispatch eligibility": what the resolver itself decides for this cli
9
+ * (worker_dispatch, per-cli enable, adapter binding, group/member
10
+ * qualification). When the caller supplies a `resolverSnapshot`, this calls
11
+ * the actual offline dispatch resolver (`resolveWorkerPlan` in resolver.ts)
12
+ * for every group member referencing the cli and reports its real
13
+ * member-specific refusals (missing installation/model evidence, policy
14
+ * denials, etc.) — never a re-derived approximation. Without a
15
+ * `resolverSnapshot` (for callers that have not resolved the wider model
16
+ * config/catalog), this falls back to a binding-only check through
17
+ * `resolveWorkerGroupInstallation` and says plainly that the result is
18
+ * binding-level only, not a dispatch verdict.
19
+ *
20
+ * Probing is opt-in. `diagnoseDirectWorkerReadiness` starts no process unless
21
+ * the caller supplies a `probe`; surfaces such as `aiwg doctor` report
22
+ * configuration from config and the registry by default and only probe behind
23
+ * an explicit flag. Immediately before each probe, the resolved executable
24
+ * and its version argv are announced through the caller's `announce`
25
+ * callback (synchronous, never buffered) so operators see exactly what is
26
+ * about to run before anything is spawned. The single probe implementation
27
+ * lives in the shared installation owner (`boundedVersionProbe` in
28
+ * worker-installation.mjs) with its credential-free environment allowlist and
29
+ * bounds; this module only maps its outcome onto readiness findings.
30
+ *
31
+ * Trust boundary: a version probe runs the executable named by project
32
+ * configuration (or the registry default) with only its registered
33
+ * `versionArgv`, a filtered credential-free environment, and an explicit
34
+ * cwd. That configuration is an execution trust boundary, not an executable
35
+ * allowlist — project configuration can select an arbitrary binary, and a
36
+ * `versionArgv` label cannot prove the selected program is side-effect free.
37
+ * Opting into `--probe-workers` trusts that executable (and, for bare names,
38
+ * PATH resolution) to the same extent any other locally invoked tool is
39
+ * trusted.
40
+ *
41
+ * The configured `adapter` binding is always resolved through the same
42
+ * public resolver used by installation and dispatch (`resolveWorkerGroupInstallation`
43
+ * / `resolveWorkerAdapter`): exact, case-sensitive namespace:id references,
44
+ * with implicit canonical `providers:<cli>` bindings for cli ids that are
45
+ * only referenced by a group member and have no explicit `parallelism.clis`
46
+ * entry. No alias normalization happens here — a binding this module accepts
47
+ * is a binding the public config validator also accepts, and vice versa.
48
+ *
49
+ * The shipped direct profiles are declarations, not live qualification: no
50
+ * finding ever calls a profile "qualified". Explicit operator approval uses `aiwg doctor --probe-workers --qualify`;
51
+ * a probe alone does not qualify a profile, and an installed, in-range CLI
52
+ * can still be refused by the resolver — the dispatch finding says so
53
+ * plainly with the resolver's own reason.
54
+ *
55
+ * Decision 17 (baseline simplification) dropped provider qualification
56
+ * runners, attestations and prescribed evidence files; this module therefore
57
+ * performs no live-qualification recording and requires no evidence files.
58
+ */
59
+ import { tmpdir } from 'node:os';
60
+ import { isAbsolute, resolve as resolvePath } from 'node:path';
61
+ import { boundedVersionProbe, normalizeWorkerVersionBanner, workerVersionProbeDefaults, workerVersionProbeEnvironment, workerVersionRangeMismatch, } from './worker-installation.mjs';
62
+ import { resolveWorkerGroupInstallation } from '../config/aiwg-config.js';
63
+ import { resolveWorkerPlan } from './resolver.js';
64
+ const SMOKE_NOTE = 'A version probe alone leaves profiles unqualified. To record direct CLI operator approval, run aiwg doctor --probe-workers --qualify; a version probe is not an authenticated smoke run (see #3005).';
65
+ function truncateBanner(banner, limit = 200) {
66
+ const singleLine = banner.replace(/[\r\n]+/g, '\\n');
67
+ return singleLine.length > limit ? `${singleLine.slice(0, limit)}…` : singleLine;
68
+ }
69
+ /**
70
+ * Resolve a configured executable against the project root before any probe
71
+ * changes its working directory. Bare command names (no path separator) are
72
+ * left untouched so PATH resolution still applies; already-absolute paths
73
+ * are left untouched; relative paths containing a separator are resolved
74
+ * against `projectRoot` so a project-relative executable is never looked up
75
+ * relative to the probe's tmpdir cwd.
76
+ */
77
+ export function resolveDirectWorkerExecutablePath(executable, projectRoot) {
78
+ if (isAbsolute(executable))
79
+ return executable;
80
+ if (!executable.includes('/') && !executable.includes('\\'))
81
+ return executable;
82
+ return resolvePath(projectRoot, executable);
83
+ }
84
+ /**
85
+ * Default live prober: the shared bounded probe with a credential-free
86
+ * environment and explicit cwd. ENOENT means not installed; every other
87
+ * spawn, timeout or output failure is a failed probe. Never launches a
88
+ * worker. Callers opt in by passing this (or their own prober); without a
89
+ * prober no process is started.
90
+ */
91
+ export async function probeDirectWorkerVersion(executable, versionArgv, options = {}) {
92
+ const timeoutMs = options.timeoutMs ?? workerVersionProbeDefaults.timeoutMs;
93
+ const maxBytes = options.maxBytes ?? workerVersionProbeDefaults.maxBytes;
94
+ const cwd = options.cwd ?? tmpdir();
95
+ try {
96
+ const { rawBanner } = await boundedVersionProbe(executable, versionArgv, workerVersionProbeEnvironment(), cwd, timeoutMs, maxBytes, undefined);
97
+ return { ok: true, rawBanner };
98
+ }
99
+ catch (error) {
100
+ return { ok: false, reason: error?.code === 'ENOENT' ? 'not-installed' : 'probe-failed' };
101
+ }
102
+ }
103
+ const ADAPTER_REFERENCE = /^(providers|workerAdapters):(.+)$/;
104
+ /**
105
+ * Resolve the configured binding by exact, case-sensitive namespace:id
106
+ * lookup — the same shape `resolveWorkerAdapter` (provider-policy.ts) uses
107
+ * for dispatch and `validateParallelismConfig` uses for config validation.
108
+ * No alias normalization happens for either namespace, so a binding this
109
+ * accepts is also a binding the public config validator accepts, and a
110
+ * custom `workerAdapters:<id>` reference resolves against
111
+ * `registry.workerAdapters`, never against the provider-alias registry.
112
+ *
113
+ * For the `providers` namespace this falls back from `workerAdapter` to
114
+ * `workerHealthAdapter` when no direct-dispatch binding is pinned yet — the
115
+ * install/version metadata is identical either way, and that is exactly
116
+ * what lets install readiness and dispatch eligibility disagree honestly
117
+ * (installed and in range, but the resolver still has no workerAdapter to
118
+ * dispatch through).
119
+ */
120
+ function resolveDirectBinding(registry, configuredAdapter, cli) {
121
+ let namespace;
122
+ let id;
123
+ if (configuredAdapter !== undefined) {
124
+ const match = ADAPTER_REFERENCE.exec(configuredAdapter.trim());
125
+ if (!match)
126
+ return { error: `Invalid worker adapter reference '${configuredAdapter}'` };
127
+ [, namespace, id] = match;
128
+ }
129
+ else {
130
+ namespace = 'providers';
131
+ id = cli;
132
+ }
133
+ if (namespace === 'providers') {
134
+ const entry = registry.providers[id];
135
+ const adapter = entry?.workerAdapter ?? entry?.workerHealthAdapter;
136
+ if (!adapter)
137
+ return { error: `No direct worker profile is registered for binding 'providers:${id}'` };
138
+ return { adapter, adapterRef: `providers:${id}` };
139
+ }
140
+ const adapter = registry.workerAdapters?.[id];
141
+ if (!adapter)
142
+ return { error: `Unknown worker adapter reference 'workerAdapters:${id}'` };
143
+ return { adapter, adapterRef: `workerAdapters:${id}` };
144
+ }
145
+ /** Every group member referencing this cli, including disabled ones (for context). */
146
+ function memberReferences(parallelism, cli) {
147
+ const refs = [];
148
+ for (const [groupId, group] of Object.entries(parallelism.groups ?? {})) {
149
+ for (const member of group?.members ?? []) {
150
+ if (member.cli !== cli)
151
+ continue;
152
+ refs.push({ groupId, memberId: member.id, member,
153
+ enabled: member.enabled !== false && parallelism.clis?.[cli]?.enabled !== false });
154
+ }
155
+ }
156
+ return refs.sort((a, b) => `${a.groupId}/${a.memberId}`.localeCompare(`${b.groupId}/${b.memberId}`));
157
+ }
158
+ function memberLabels(refs) {
159
+ return refs.map(ref => `${ref.groupId}/${ref.memberId}${ref.enabled ? '' : ' (disabled)'}`);
160
+ }
161
+ function describeConfigContext(members) {
162
+ return members.length ? `members: ${members.join(', ')}` : 'members: no group member references this cli';
163
+ }
164
+ /**
165
+ * Union of every cli id a readiness pass must consider: explicit
166
+ * `parallelism.clis` entries plus any cli id referenced only implicitly by a
167
+ * group member (canonical default binding, no explicit installation entry).
168
+ */
169
+ function allConfiguredCliIds(parallelism, cli) {
170
+ const ids = new Set(Object.keys(parallelism.clis ?? {}));
171
+ for (const group of Object.values(parallelism.groups ?? {})) {
172
+ for (const member of group?.members ?? [])
173
+ ids.add(member.cli);
174
+ }
175
+ return [...ids].filter(id => cli === undefined || id === cli).sort();
176
+ }
177
+ /**
178
+ * A cli is "required" only when an enabled group member referencing it also
179
+ * covers a role its group lists in `requiredRoles`. Every other configured
180
+ * cli is optional: installation failures stay advisory (never 'error', so
181
+ * they never flip doctor's exit code) until something actually requires it.
182
+ */
183
+ function isCliRequired(parallelism, cli) {
184
+ for (const group of Object.values(parallelism.groups ?? {})) {
185
+ const required = new Set(group?.requiredRoles ?? []);
186
+ if (!required.size)
187
+ continue;
188
+ for (const member of group?.members ?? []) {
189
+ if (member.cli !== cli || member.enabled === false)
190
+ continue;
191
+ if (parallelism.clis?.[cli]?.enabled === false)
192
+ continue;
193
+ if (member.roles.some(role => required.has(role)))
194
+ return true;
195
+ }
196
+ }
197
+ return false;
198
+ }
199
+ /** Cap a status at 'warn' for optional (non-required) clis so an unreferenced
200
+ * or not-yet-wired profile never flips doctor's exit code. */
201
+ function advisoryStatus(status, required) {
202
+ return required || status !== 'error' ? status : 'warn';
203
+ }
204
+ const offlineDirectContext = () => ({ estimator: 'doctor-offline-direct-context-v1', briefTokens: 0, inputTokens: 0, toolOutputAllowance: 0, reservedOutput: 0 });
205
+ /**
206
+ * Dispatch eligibility from the actual offline dispatch resolver
207
+ * (`resolveWorkerPlan`): called once per role for every group member
208
+ * referencing this cli, never re-derived. A resolvable binding alone is not
209
+ * reported as eligible — eligibility requires the resolver to actually place
210
+ * at least one referencing member in its eligible/alternatives set. Refusals
211
+ * are member-specific and name the resolver's own missing-evidence or policy
212
+ * diagnostics.
213
+ */
214
+ function resolveEligibilityFromDispatchResolver(resolverSnapshot, registry, cli, refs) {
215
+ const snapshot = {
216
+ config: resolverSnapshot.config, models: resolverSnapshot.models, modelDigests: resolverSnapshot.modelDigests,
217
+ catalog: resolverSnapshot.catalog, registry, installations: {},
218
+ };
219
+ const diagnostics = new Map();
220
+ const uncovered = [];
221
+ for (const ref of refs) {
222
+ if (!ref.enabled)
223
+ continue;
224
+ const matches = (tuple) => tuple.group === ref.groupId && tuple.member === ref.memberId;
225
+ const seen = [];
226
+ let eligibleHere = false;
227
+ for (const role of ref.member.roles) {
228
+ const plan = resolveWorkerPlan(snapshot, {
229
+ role: role, group: ref.groupId, member: ref.memberId,
230
+ now: resolverSnapshot.now, context: offlineDirectContext(),
231
+ });
232
+ if ([plan.selected, ...plan.alternatives].some(tuple => tuple && matches(tuple))) {
233
+ eligibleHere = true;
234
+ break;
235
+ }
236
+ const exclusion = plan.exclusions.find(matches);
237
+ for (const diagnostic of exclusion?.diagnostics ?? plan.diagnostics) {
238
+ // Only an exact member pointer is generalized; other config pointers
239
+ // remain distinct even when their diagnostic text happens to match.
240
+ const memberPointer = `/parallelism/groups/${ref.groupId}/members/${ref.memberId}`;
241
+ const pointer = diagnostic.pointer === memberPointer || diagnostic.pointer.startsWith(`${memberPointer}/`)
242
+ ? `/parallelism/groups/${ref.groupId}/members${diagnostic.pointer.slice(memberPointer.length)}`
243
+ : diagnostic.pointer;
244
+ seen.push({ ...diagnostic, pointer, members: [`${ref.groupId}/${ref.memberId}`],
245
+ roles: diagnostic.roles ?? [role] });
246
+ }
247
+ }
248
+ if (eligibleHere) {
249
+ return { eligibility: 'eligible',
250
+ detail: `Dispatch resolver accepts cli '${cli}' for group '${ref.groupId}' member '${ref.memberId}'. `
251
+ + `Dispatch eligibility: eligible. ${SMOKE_NOTE}` };
252
+ }
253
+ if (!seen.length)
254
+ uncovered.push(`${ref.groupId}/${ref.memberId}`);
255
+ for (const diagnostic of seen) {
256
+ const key = JSON.stringify([diagnostic.code, diagnostic.pointer, diagnostic.message,
257
+ diagnostic.blocking, diagnostic.origins]);
258
+ const shared = diagnostics.get(key);
259
+ if (shared) {
260
+ shared.members = [...new Set([...shared.members, ...diagnostic.members])];
261
+ shared.roles = [...new Set([...shared.roles, ...diagnostic.roles])];
262
+ }
263
+ else
264
+ diagnostics.set(key, diagnostic);
265
+ }
266
+ }
267
+ if (!diagnostics.size && !uncovered.length) {
268
+ return { eligibility: 'blocked',
269
+ detail: `Dispatch resolver has no enabled group member to evaluate for cli '${cli}'. `
270
+ + `Dispatch eligibility: blocked. ${SMOKE_NOTE}` };
271
+ }
272
+ const distinct = [...diagnostics.values()];
273
+ const reasons = distinct.map(diagnostic => `${diagnostic.code} ${diagnostic.pointer}: ${diagnostic.message}`
274
+ + ` [roles: ${diagnostic.roles.join(', ')}; members: ${diagnostic.members.join(', ')}]`);
275
+ if (uncovered.length)
276
+ reasons.push(`No qualified route [members: ${uncovered.join(', ')}]`);
277
+ return { eligibility: 'blocked', diagnostics: distinct,
278
+ detail: `Dispatch resolver refuses cli '${cli}': ${reasons.join('; ')}. `
279
+ + `Dispatch eligibility: blocked. ${SMOKE_NOTE}` };
280
+ }
281
+ /**
282
+ * Binding-only fallback used only when the caller has not supplied a
283
+ * `resolverSnapshot` (for example a direct unit-level call that never
284
+ * resolved the wider model config/catalog). States plainly that this is a
285
+ * binding-level result, not the dispatch resolver's verdict.
286
+ */
287
+ function resolveEligibilityFromBindingOnly(parallelism, registry, cli, installReady) {
288
+ try {
289
+ const resolved = resolveWorkerGroupInstallation(parallelism, cli, registry);
290
+ return { eligibility: 'eligible',
291
+ detail: `Resolver accepts cli '${cli}' (binding '${resolved.installation.adapter}'). Dispatch eligibility: eligible at binding level only — `
292
+ + `no resolver snapshot was supplied, so group/member qualification was not evaluated. `
293
+ + `Launch additionally requires current installation evidence and model observation under the dispatch resolver's gates. `
294
+ + `${SMOKE_NOTE}` };
295
+ }
296
+ catch (error) {
297
+ const reason = error instanceof Error ? error.message : String(error);
298
+ const readyNote = installReady
299
+ ? ' The CLI is installed with an in-range version, but dispatch stays refused for the reason above; '
300
+ + `an authenticated smoke run (see #3005) alone does not satisfy the resolver gates. ${SMOKE_NOTE}`
301
+ : ` ${SMOKE_NOTE}`;
302
+ return { eligibility: 'blocked',
303
+ detail: `Resolver refuses cli '${cli}': ${reason}. Dispatch eligibility: blocked.${readyNote}` };
304
+ }
305
+ }
306
+ function checkDispatchEligibility(parallelism, registry, cli, installReady, resolverSnapshot, refs) {
307
+ if (resolverSnapshot && refs.length)
308
+ return resolveEligibilityFromDispatchResolver(resolverSnapshot, registry, cli, refs);
309
+ return resolveEligibilityFromBindingOnly(parallelism, registry, cli, installReady);
310
+ }
311
+ /**
312
+ * Read-only readiness for every configured worker. Starts no process unless
313
+ * `probe` is supplied; without a prober each cli gets a config-only install
314
+ * finding plus its resolver eligibility. Never claims a profile is
315
+ * "qualified". Considers every explicit `parallelism.clis` entry plus any
316
+ * cli id a group member references implicitly (no explicit installation
317
+ * entry, canonical default binding).
318
+ */
319
+ export async function diagnoseDirectWorkerReadiness(input) {
320
+ const { parallelism, registry, probe, announce, timeoutMs = workerVersionProbeDefaults.timeoutMs, probeCwd = tmpdir(), configFile = '.aiwg/aiwg.config', projectRoot = process.cwd(), resolverSnapshot } = input;
321
+ const report = {
322
+ enabled: parallelism.worker_dispatch === true,
323
+ workersConfigured: allConfiguredCliIds(parallelism, input.cli).length > 0,
324
+ ready: true,
325
+ findings: [],
326
+ };
327
+ if (!report.enabled) {
328
+ report.findings.push({ cli: '*', kind: 'dispatch', profile: null, executable: null, installedVersion: null,
329
+ status: 'info', code: 'WORKER_DIRECT_DISABLED',
330
+ message: `Group dispatch is disabled (config ${configFile} key /parallelism/worker_dispatch); install/version checks do not apply and onboarding cannot proceed.`,
331
+ nextAction: 'enable-worker-dispatch-to-onboard',
332
+ configFile, configKey: '/parallelism/worker_dispatch',
333
+ readiness: null, eligibility: 'blocked',
334
+ eligibilityDetail: 'Dispatch eligibility: blocked — group dispatch is disabled.',
335
+ members: [] });
336
+ return report;
337
+ }
338
+ const cliIds = allConfiguredCliIds(parallelism, input.cli);
339
+ if (!cliIds.length) {
340
+ // No workers configured: no findings at all so doctor stays silent.
341
+ return report;
342
+ }
343
+ const pushInstall = (finding) => {
344
+ report.findings.push({ ...finding, kind: 'install', eligibility: null, eligibilityDetail: null });
345
+ if (finding.status === 'error' || finding.status === 'warn')
346
+ report.ready = false;
347
+ };
348
+ const pushDispatch = (cli, profile, status, code, eligibility, detail, configKey, members, nextAction, diagnostics) => {
349
+ report.findings.push({ cli, kind: 'dispatch', profile, executable: null, installedVersion: null,
350
+ status, code, message: `${detail} (config ${configFile} key ${configKey}; ${describeConfigContext(members)})`,
351
+ nextAction, configFile, configKey,
352
+ readiness: null, eligibility, eligibilityDetail: detail, members, ...(diagnostics ? { diagnostics } : {}) });
353
+ };
354
+ await Promise.all(cliIds.map(async (cli) => {
355
+ const configured = parallelism.clis?.[cli];
356
+ const refs = memberReferences(parallelism, cli);
357
+ const members = memberLabels(refs);
358
+ const required = isCliRequired(parallelism, cli);
359
+ const configKey = `/parallelism/clis/${cli}`;
360
+ if (configured?.enabled === false) {
361
+ pushInstall({ cli, profile: configured.invocationProfile ?? null,
362
+ executable: configured.executable ?? null, installedVersion: null,
363
+ status: 'info', code: 'WORKER_DIRECT_CLI_DISABLED',
364
+ message: `Dispatch is disabled for cli '${cli}' (config ${configFile} key ${configKey}/enabled); the version probe was skipped. Installation readiness: skipped. (${describeConfigContext(members)})`,
365
+ nextAction: 'enable-cli-to-onboard',
366
+ configFile, configKey, readiness: 'skipped', members });
367
+ const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
368
+ pushDispatch(cli, configured.invocationProfile ?? null, 'info', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'enable-cli-to-onboard', diagnostics);
369
+ return;
370
+ }
371
+ const resolved = resolveDirectBinding(registry, configured?.adapter, cli);
372
+ if ('error' in resolved) {
373
+ pushInstall({ cli, profile: configured?.invocationProfile ?? null, executable: null, installedVersion: null,
374
+ status: advisoryStatus(probe ? 'error' : 'warn', required), code: 'WORKER_DIRECT_NO_PROFILE',
375
+ message: `${resolved.error} (config ${configFile} key ${configKey}/adapter); the profile cannot be onboarded. Installation readiness: not-ready. (${describeConfigContext(members)})`,
376
+ nextAction: 'review-worker-configuration',
377
+ configFile, configKey: `${configKey}/adapter`, readiness: 'not-ready', members });
378
+ const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
379
+ pushDispatch(cli, configured?.invocationProfile ?? null, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'review-worker-configuration', diagnostics);
380
+ return;
381
+ }
382
+ const { adapter, adapterRef } = resolved;
383
+ if (adapter.invocation.kind !== 'process') {
384
+ pushInstall({ cli, profile: null, executable: null, installedVersion: null,
385
+ status: 'info', code: 'WORKER_DIRECT_HTTP',
386
+ message: `Cli '${cli}' binds '${adapterRef}' (config ${configFile} key ${configKey}/adapter), which uses an HTTP adapter; there is no CLI version probe — check endpoint health instead. Installation readiness: skipped. (${describeConfigContext(members)})`,
387
+ nextAction: 'check-endpoint-health',
388
+ configFile, configKey, readiness: 'skipped', members });
389
+ const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
390
+ pushDispatch(cli, null, eligibility === 'eligible' ? 'ok' : 'warn', eligibility === 'eligible' ? 'WORKER_DIRECT_DISPATCH_ELIGIBLE' : 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, eligibility === 'eligible' ? 'check-endpoint-health' : 'review-worker-configuration', diagnostics);
391
+ return;
392
+ }
393
+ const profileIds = Object.keys(adapter.invocationProfiles ?? {});
394
+ const profileRef = configured?.invocationProfile
395
+ ?? (profileIds.length === 1 ? profileIds[0] : undefined);
396
+ if (!profileRef || !adapter.invocationProfiles?.[profileRef]) {
397
+ pushInstall({ cli, profile: configured?.invocationProfile ?? null,
398
+ executable: null, installedVersion: null,
399
+ status: advisoryStatus('error', required), code: 'WORKER_DIRECT_PROFILE_UNKNOWN',
400
+ message: configured?.invocationProfile
401
+ ? `Configured invocation profile '${configured.invocationProfile}' is not registered for binding '${adapterRef}' (config ${configFile} key ${configKey}/invocationProfile). Installation readiness: not-ready. (${describeConfigContext(members)})`
402
+ : `Cli '${cli}' binding '${adapterRef}' has no single direct profile to check (config ${configFile} key ${configKey}); configure invocationProfile explicitly. Installation readiness: not-ready. (${describeConfigContext(members)})`,
403
+ nextAction: 'review-worker-configuration',
404
+ configFile, configKey, readiness: 'not-ready', members });
405
+ const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
406
+ pushDispatch(cli, configured?.invocationProfile ?? null, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'review-worker-configuration', diagnostics);
407
+ return;
408
+ }
409
+ const profile = adapter.invocationProfiles[profileRef];
410
+ const executable = resolveDirectWorkerExecutablePath(configured?.executable ?? adapter.invocation.executable, projectRoot);
411
+ if (!probe) {
412
+ pushInstall({ cli, profile: profileRef, executable, installedVersion: null,
413
+ status: 'info', code: 'WORKER_DIRECT_UNPROBED',
414
+ message: `Cli '${cli}' binds '${adapterRef}' profile '${profileRef}' (config ${configFile} key ${configKey}); version probe not run — no subprocess was started. Installation readiness: unprobed. Rerun with probes to check install/version. (${describeConfigContext(members)})`,
415
+ nextAction: 'rerun-with-probe-workers',
416
+ configFile, configKey, readiness: 'unprobed', members });
417
+ const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
418
+ pushDispatch(cli, profileRef, eligibility === 'eligible' ? 'ok' : 'warn', eligibility === 'eligible' ? 'WORKER_DIRECT_DISPATCH_ELIGIBLE' : 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, eligibility === 'eligible' ? 'rerun-with-probe-workers' : 'review-worker-configuration', diagnostics);
419
+ return;
420
+ }
421
+ announce?.(cli, executable, adapter.invocation.versionArgv);
422
+ let outcome;
423
+ try {
424
+ outcome = await probe(executable, adapter.invocation.versionArgv, { timeoutMs, cwd: probeCwd });
425
+ }
426
+ catch {
427
+ outcome = { ok: false, reason: 'probe-failed' };
428
+ }
429
+ if (!outcome.ok) {
430
+ const notInstalled = outcome.reason === 'not-installed';
431
+ pushInstall({ cli, profile: profileRef, executable, installedVersion: null,
432
+ status: advisoryStatus(notInstalled ? 'error' : 'warn', required),
433
+ code: notInstalled ? 'WORKER_DIRECT_NOT_INSTALLED' : 'WORKER_DIRECT_PROBE_FAILED',
434
+ message: notInstalled
435
+ ? `CLI '${executable}' is not installed (version probe failed to start; config ${configFile} key ${configKey}/executable); install it before onboarding cli '${cli}'. Installation readiness: not-ready. (${describeConfigContext(members)})`
436
+ : `Version probe for cli '${cli}' ('${executable}', config ${configFile} key ${configKey}) failed; retry once, then check PATH and execute permission. Installation readiness: not-ready. (${describeConfigContext(members)})`,
437
+ nextAction: notInstalled ? 'install-cli' : 'retry-version-probe',
438
+ configFile, configKey, readiness: 'not-ready', members });
439
+ const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
440
+ pushDispatch(cli, profileRef, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, notInstalled ? 'install-cli' : 'retry-version-probe', diagnostics);
441
+ return;
442
+ }
443
+ const rawBanner = outcome.rawBanner.trim();
444
+ let installedVersion;
445
+ try {
446
+ installedVersion = normalizeWorkerVersionBanner(rawBanner, adapter.invocation.versionPattern);
447
+ }
448
+ catch {
449
+ pushInstall({ cli, profile: profileRef, executable, installedVersion: null,
450
+ status: advisoryStatus('error', required), code: 'WORKER_DIRECT_BANNER_UNMATCHED',
451
+ message: `CLI '${executable}' banner ${JSON.stringify(truncateBanner(rawBanner))} does not match the registered version pattern (config ${configFile} key ${configKey}); the install is unexpected — reinstall a supported release. Installation readiness: not-ready. (${describeConfigContext(members)})`,
452
+ nextAction: 'reinstall-supported-cli',
453
+ configFile, configKey, readiness: 'not-ready', members });
454
+ const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
455
+ pushDispatch(cli, profileRef, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'reinstall-supported-cli', diagnostics);
456
+ return;
457
+ }
458
+ const mismatch = workerVersionRangeMismatch(installedVersion, [profile.supportedVersions, adapter.supportedVersions]);
459
+ if (mismatch) {
460
+ pushInstall({ cli, profile: profileRef, executable, installedVersion,
461
+ status: advisoryStatus('warn', required), code: 'WORKER_DIRECT_VERSION_OUT_OF_RANGE',
462
+ message: `CLI '${executable}' version ${installedVersion} is outside supported range '${mismatch}' for cli '${cli}' (profile '${profileRef}', config ${configFile} key ${configKey}); upgrade or pin a supported release. Installation readiness: not-ready. (${describeConfigContext(members)})`,
463
+ nextAction: 'upgrade-or-pin-cli',
464
+ configFile, configKey, readiness: 'not-ready', members });
465
+ const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, false, resolverSnapshot, refs);
466
+ pushDispatch(cli, profileRef, 'warn', 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'upgrade-or-pin-cli', diagnostics);
467
+ return;
468
+ }
469
+ pushInstall({ cli, profile: profileRef, executable, installedVersion,
470
+ status: 'ok', code: 'WORKER_DIRECT_READY',
471
+ message: `CLI '${executable}' version ${installedVersion} matches profile '${profileRef}' for cli '${cli}' (config ${configFile} key ${configKey}). Installation readiness: ready. ${SMOKE_NOTE} See the dispatch finding for whether the resolver would dispatch. (${describeConfigContext(members)})`,
472
+ nextAction: 'qualify-installed-cli',
473
+ configFile, configKey, readiness: 'ready', members });
474
+ const { eligibility, detail, diagnostics } = checkDispatchEligibility(parallelism, registry, cli, true, resolverSnapshot, refs);
475
+ pushDispatch(cli, profileRef, eligibility === 'eligible' ? 'ok' : 'warn', eligibility === 'eligible' ? 'WORKER_DIRECT_DISPATCH_ELIGIBLE' : 'WORKER_DIRECT_DISPATCH_BLOCKED', eligibility, detail, configKey, members, 'qualify-installed-cli', diagnostics);
476
+ }));
477
+ report.findings.sort((a, b) => a.cli.localeCompare(b.cli) || a.kind.localeCompare(b.kind));
478
+ return report;
479
+ }
480
+ /**
481
+ * Doctor wiring for the direct-worker section. With `probeWorkers` false this
482
+ * is config-only and starts no process; with true it runs the shared bounded
483
+ * probes, announcing each resolved executable through `announce` (if
484
+ * supplied) immediately before it is spawned — never a worker launch, model
485
+ * call or mutation, and never buffered behind the rest of the report. No
486
+ * workers configured (explicitly or implicitly through a group member) means
487
+ * no lines at all, so doctor output and exit code are unchanged there.
488
+ *
489
+ * `installFindings` carries the structured per-cli installation findings
490
+ * (`kind === 'install'`, one per configured cli, untruncated) backing
491
+ * `lines`, so machine-readable surfaces (`aiwg doctor --probe-workers
492
+ * --json`) can render one record per CLI without re-running the probes.
493
+ *
494
+ * Trust boundary: with `probeWorkers: true`, every probed executable is
495
+ * exactly the one named by project configuration (or the registry default)
496
+ * run with only its registered version arguments — opting in trusts that
497
+ * executable; a version probe cannot prove the program has no side effects.
498
+ */
499
+ export async function buildDirectWorkerDoctorSection(input) {
500
+ const { parallelism, registry, probeWorkers } = input;
501
+ const cliIds = allConfiguredCliIds(parallelism ?? {}, input.cli);
502
+ if (!cliIds.length)
503
+ return { lines: [], probed: false, workersConfigured: false, installFindings: [] };
504
+ const lines = [];
505
+ if (probeWorkers) {
506
+ lines.push({ name: 'Direct Worker Probes', status: 'info',
507
+ message: 'probe execution is opt-in (--probe-workers): running bounded `<executable> ...versionArgv` probes with a cleared environment and explicit cwd (no worker launch, no model call, no mutation). Trust boundary: each probe runs exactly the executable named by project configuration with only its version arguments — a version probe cannot prove that executable is side-effect free.' });
508
+ }
509
+ else {
510
+ lines.push({ name: 'Direct Worker', status: 'info',
511
+ message: 'direct-worker configuration only: no subprocess was started. Rerun with --probe-workers to run the bounded version probes' });
512
+ }
513
+ const report = await diagnoseDirectWorkerReadiness({
514
+ parallelism: parallelism,
515
+ registry,
516
+ cli: input.cli,
517
+ ...(probeWorkers ? { probe: input.probe ?? probeDirectWorkerVersion, ...(input.announce ? { announce: input.announce } : {}) } : {}),
518
+ ...(input.configFile ? { configFile: input.configFile } : {}),
519
+ ...(input.projectRoot ? { projectRoot: input.projectRoot } : {}),
520
+ ...(input.resolverSnapshot ? { resolverSnapshot: input.resolverSnapshot } : {}),
521
+ });
522
+ for (const finding of report.findings.slice(0, 15)) {
523
+ const name = finding.cli === '*' ? 'Direct Worker'
524
+ : finding.kind === 'dispatch' ? `Direct Worker Dispatch (${finding.cli})` : `Direct Worker (${finding.cli})`;
525
+ lines.push({ name, status: finding.status, message: `${finding.code}: ${finding.message} [next: ${finding.nextAction}]` });
526
+ }
527
+ if (report.findings.length > 15) {
528
+ lines.push({ name: 'Direct Worker', status: 'warn',
529
+ message: `${report.findings.length - 15} additional findings; narrow the cli set in parallelism.clis` });
530
+ }
531
+ if (probeWorkers && !report.findings.some(finding => finding.code === 'WORKER_DIRECT_READY')) {
532
+ lines.push({ name: 'Direct Worker', status: 'info',
533
+ message: `no profile is onboarded; ${SMOKE_NOTE}` });
534
+ }
535
+ return {
536
+ lines,
537
+ probed: probeWorkers,
538
+ workersConfigured: true,
539
+ // Disabled dispatch skips probes but still needs one record per configured
540
+ // CLI in machine-readable output. Keep the existing text findings intact.
541
+ installFindings: report.enabled
542
+ ? report.findings.filter(finding => finding.kind === 'install')
543
+ : cliIds.map(cli => ({
544
+ ...report.findings[0], cli, kind: 'install',
545
+ configKey: `/parallelism/clis/${cli}`, readiness: 'skipped',
546
+ eligibility: null, eligibilityDetail: null,
547
+ })),
548
+ };
549
+ }
550
+ //# sourceMappingURL=worker-direct-readiness.js.map