@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,576 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import * as fs from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath, pathToFileURL } from 'node:url';
6
+ import { findPackageRoot } from '../cli/find-package-root.js';
7
+ import { projectAiwgPath, projectControlPath } from '../config/project-artifacts.js';
8
+ import { getUserStateDir } from '../config/user-state.js';
9
+ import { collectGraphIndexFiles } from './index-files.js';
10
+ import { collectIndexStatus } from './index-status.js';
11
+ import { getFortemiCoreSyncStatus, syncFortemiCoreIndex } from './fortemi-core-sync.js';
12
+ import { acquireDirectoryLock, DirectoryLockContentionError } from './prebuilt-build-lock.js';
13
+ import { resolveImportExclusions } from '../sessions/import-exclusions.js';
14
+ import { resolveImportLimits } from '../sessions/import-limits.js';
15
+ import { BUILTIN_GRAPH_CONFIGS, GRAPH_CONFIGS, INDEX_EXTRACTOR_VERSION, INDEX_VERSION, getGraphIndexDir, graphBuildOrder, loadGlobalGraphConfigs, loadUserGraphConfigs, resolveGraphBackendType, resolveGraphScanDir, } from './types.js';
16
+ function readJson(file, tolerateMalformed = true) {
17
+ try {
18
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
19
+ }
20
+ catch (error) {
21
+ if (error.code === 'ENOENT' || (tolerateMalformed && error instanceof SyntaxError))
22
+ return undefined;
23
+ throw error;
24
+ }
25
+ }
26
+ function writeJson(file, value) {
27
+ const temporary = `${file}.${process.pid}.tmp`;
28
+ fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
29
+ fs.renameSync(temporary, file);
30
+ }
31
+ /** The registry is process-global; reload it per workspace to avoid cross-project leakage. */
32
+ function loadWorkspaceGraphs(workspace) {
33
+ for (const name of Object.keys(GRAPH_CONFIGS))
34
+ delete GRAPH_CONFIGS[name];
35
+ Object.assign(GRAPH_CONFIGS, structuredClone(BUILTIN_GRAPH_CONFIGS));
36
+ loadUserGraphConfigs(workspace);
37
+ loadGlobalGraphConfigs();
38
+ }
39
+ function workspacePaths(options) {
40
+ const cwd = options.cwd ?? process.cwd();
41
+ const found = new Map();
42
+ const add = (directory) => {
43
+ const canonical = fs.realpathSync(directory);
44
+ if (!fs.statSync(canonical).isDirectory())
45
+ throw new Error(`workspace is not a directory: ${directory}`);
46
+ const aliases = found.get(canonical) ?? [canonical];
47
+ if (!aliases.includes(directory))
48
+ aliases.push(directory);
49
+ found.set(canonical, aliases);
50
+ };
51
+ for (const workspace of options.workspaces ?? [])
52
+ add(path.resolve(cwd, workspace));
53
+ const roots = typeof options.root === 'string' ? [options.root] : options.root ?? [];
54
+ if (roots.length) {
55
+ const excluded = new Set(['node_modules', '.git', '.aiwg', '.cache', '.worktrees', 'worktrees', 'aiwg-wt', '.aiwg-worktrees']);
56
+ // Track ancestors rather than all visits so other logical routes retain their aliases.
57
+ const ancestors = new Set();
58
+ const scan = (directory, depth) => {
59
+ const canonical = fs.realpathSync(directory);
60
+ if (ancestors.has(canonical))
61
+ return;
62
+ ancestors.add(canonical);
63
+ if (fs.existsSync(path.join(directory, '.aiwg')) && fs.statSync(path.join(directory, '.aiwg')).isDirectory())
64
+ add(directory);
65
+ if (depth < 4) {
66
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
67
+ if (excluded.has(entry.name))
68
+ continue;
69
+ const child = path.join(directory, entry.name);
70
+ if (entry.isSymbolicLink()) {
71
+ try {
72
+ if (!fs.statSync(child).isDirectory())
73
+ continue;
74
+ }
75
+ catch (error) {
76
+ if (['ENOENT', 'ENOTDIR', 'ELOOP'].includes(error.code ?? ''))
77
+ continue;
78
+ throw error;
79
+ }
80
+ }
81
+ else if (!entry.isDirectory())
82
+ continue;
83
+ scan(child, depth + 1);
84
+ }
85
+ }
86
+ ancestors.delete(canonical);
87
+ };
88
+ for (const root of roots)
89
+ scan(path.resolve(cwd, root), 0);
90
+ }
91
+ if (!roots.length && !options.workspaces?.length) {
92
+ let current = fs.realpathSync(cwd);
93
+ while (!fs.existsSync(path.join(current, '.aiwg')) && !fs.existsSync(path.join(current, '.git'))) {
94
+ const parent = path.dirname(current);
95
+ if (parent === current) {
96
+ current = cwd;
97
+ break;
98
+ }
99
+ current = parent;
100
+ }
101
+ add(current);
102
+ // Keep the physical project boundary when cwd itself links into a subdirectory.
103
+ const canonical = fs.realpathSync(current);
104
+ for (let logical = path.resolve(cwd);; logical = path.dirname(logical)) {
105
+ if (fs.realpathSync(logical) === canonical) {
106
+ add(logical);
107
+ break;
108
+ }
109
+ if (path.dirname(logical) === logical)
110
+ break;
111
+ }
112
+ }
113
+ return new Map([...found.keys()].sort().map(workspace => [workspace, found.get(workspace).sort()]));
114
+ }
115
+ export function planIndexRefresh(options) {
116
+ return planWorkspaceRefresh(options, workspacePaths(options));
117
+ }
118
+ function planWorkspaceRefresh(options, workspaces) {
119
+ const saved = { ...GRAPH_CONFIGS };
120
+ try {
121
+ const jobs = [];
122
+ for (const workspace of workspaces.keys()) {
123
+ loadWorkspaceGraphs(workspace);
124
+ const graphs = collectIndexStatus(workspace).graphs
125
+ .sort((a, b) => graphBuildOrder(a.name, GRAPH_CONFIGS[a.name]) - graphBuildOrder(b.name, GRAPH_CONFIGS[b.name]) || a.name.localeCompare(b.name));
126
+ if (!options.only || options.only === 'graphs') {
127
+ for (const graph of graphs) {
128
+ if (graph.built || graph.defaultBuild || graph.origin === 'registered') {
129
+ jobs.push({ kind: 'graph', workspace, graph: graph.name, status: 'planned', wallMs: 0 });
130
+ }
131
+ }
132
+ }
133
+ if (!options.only || options.only === 'caches') {
134
+ for (const graph of graphs) {
135
+ // Refresh only local, opted-in caches; never create a new cache from a packaged prebuilt.
136
+ if (fs.existsSync(getFortemiCoreSyncStatus(workspace, graph.name).location)) {
137
+ jobs.push({ kind: 'cache', workspace, graph: graph.name, status: 'planned', wallMs: 0 });
138
+ }
139
+ }
140
+ }
141
+ if ((!options.only || options.only === 'sessions') && fs.existsSync(path.join(workspace, '.aiwg/sessions/catalog.sqlite'))) {
142
+ jobs.push({ kind: 'sessions', workspace, status: 'planned', wallMs: 0, noManifest: 0 });
143
+ }
144
+ for (const kind of ['runtime', 'issues']) {
145
+ if (options.only && options.only !== kind)
146
+ continue;
147
+ if (kind === 'issues' && !fs.existsSync(projectAiwgPath(workspace, 'issues')))
148
+ continue;
149
+ jobs.push({ kind, workspace, status: 'planned', wallMs: 0 });
150
+ }
151
+ }
152
+ if (!options.only || options.only === 'models') {
153
+ jobs.push({ kind: 'models', scope: 'host', status: 'planned', wallMs: 0 });
154
+ }
155
+ return jobs;
156
+ }
157
+ finally {
158
+ for (const name of Object.keys(GRAPH_CONFIGS))
159
+ delete GRAPH_CONFIGS[name];
160
+ Object.assign(GRAPH_CONFIGS, saved);
161
+ }
162
+ }
163
+ /** Use the dispatch sampler and evaluator even when worker dispatch is disabled. */
164
+ export async function refreshResourceGate(workspace, live) {
165
+ const packageRoot = findPackageRoot(path.dirname(fileURLToPath(import.meta.url)));
166
+ if (!packageRoot)
167
+ throw new Error('cannot locate the resource preflight module');
168
+ const moduleUrl = pathToFileURL(path.join(packageRoot, 'tools/scripts/resource-preflight.mjs')).href;
169
+ const preflight = await import(moduleUrl);
170
+ const config = readJson(projectControlPath(workspace, 'aiwg.config'), false);
171
+ const thresholds = preflight.resolveDispatchThresholds({
172
+ buildDispatch: config?.build?.resource_preflight?.dispatch,
173
+ admissionHostResources: config?.parallelism?.admission?.hostResources,
174
+ overlay: { max_load_ratio: 0.8 },
175
+ });
176
+ const result = preflight.evaluateDispatchGate(live ?? preflight.readLiveHostResources({ paths: [workspace] }), thresholds);
177
+ return { ok: result.ok, reason: result.failures.map((failure) => failure.reason).join('; ') || undefined };
178
+ }
179
+ async function graphFingerprint(workspace, graph) {
180
+ const files = await collectGraphIndexFiles(workspace, graph);
181
+ // Source import resolution also depends on the compiler/package configuration.
182
+ if (graph === 'source') {
183
+ for (const file of ['tsconfig.json', 'package.json']) {
184
+ if (fs.existsSync(path.join(workspace, file)))
185
+ files.push(path.join(workspace, file));
186
+ }
187
+ }
188
+ const stats = [...new Set(files)].sort().map(file => {
189
+ const stat = fs.statSync(file);
190
+ return [file, stat.mtimeMs, stat.ctimeMs, stat.size];
191
+ });
192
+ return createHash('sha256').update(JSON.stringify([
193
+ INDEX_VERSION, INDEX_EXTRACTOR_VERSION, GRAPH_CONFIGS[graph], resolveGraphBackendType(graph), stats,
194
+ ])).digest('hex');
195
+ }
196
+ async function refreshWorkspaceRuntime(workspace, full, runtime) {
197
+ const directory = projectAiwgPath(workspace, 'smiths', 'toolsmith');
198
+ const output = path.join(directory, 'runtime.json');
199
+ const infoPath = path.join(directory, 'runtime-info.md');
200
+ const loaded = readJson(output);
201
+ const previous = Array.isArray(loaded?.tools) ? loaded : undefined;
202
+ if (!full && previous?.inputFingerprint === runtime.catalog.inputFingerprint
203
+ && previous.generated === runtime.catalog.generated && previous.environment?.workingDir === workspace
204
+ && fs.existsSync(infoPath))
205
+ return;
206
+ let diskFreeGb = 0;
207
+ try {
208
+ const disk = fs.statfsSync(workspace);
209
+ diskFreeGb = Math.round(disk.bavail * disk.bsize / (1024 ** 3) * 10) / 10;
210
+ }
211
+ catch { /* Match discovery's unavailable-resource fallback. */ }
212
+ const previousIds = new Set((previous?.tools ?? []).map(tool => tool.id));
213
+ const currentIds = new Set(runtime.catalog.tools.map(tool => tool.id));
214
+ const catalog = {
215
+ ...runtime.catalog,
216
+ environment: { ...runtime.catalog.environment, workingDir: workspace },
217
+ resources: { ...runtime.catalog.resources, diskFreeGb },
218
+ delta: {
219
+ added: [...currentIds].filter(id => !previousIds.has(id)),
220
+ removed: [...previousIds].filter(id => !currentIds.has(id)),
221
+ },
222
+ };
223
+ const info = runtime.info.split('\n').map(line => {
224
+ if (line.startsWith('- **Working Directory**:'))
225
+ return `- **Working Directory**: ${workspace}`;
226
+ if (line.startsWith('- **Disk Free**:'))
227
+ return `- **Disk Free**: ${diskFreeGb} GB`;
228
+ if (line.startsWith('- **Catalog**:'))
229
+ return `- **Catalog**: ${output}`;
230
+ return line;
231
+ }).join('\n');
232
+ fs.mkdirSync(directory, { recursive: true });
233
+ writeJson(output, catalog);
234
+ fs.writeFileSync(infoPath, info, { mode: 0o600 });
235
+ }
236
+ async function executeJob(job, options, aliases, runtime) {
237
+ const workspace = job.workspace ?? process.env.HOME ?? homedir();
238
+ if (job.kind === 'graph') {
239
+ const graph = job.graph;
240
+ const config = GRAPH_CONFIGS[graph];
241
+ if (!config.scanDirs.some(dir => fs.existsSync(resolveGraphScanDir(workspace, dir)))) {
242
+ job.status = 'skipped';
243
+ job.reason = 'scan directories missing';
244
+ return;
245
+ }
246
+ const directory = getGraphIndexDir(workspace, graph);
247
+ const markerPath = path.join(directory, 'refresh-state.json');
248
+ const marker = readJson(markerPath);
249
+ const metadata = readJson(path.join(directory, 'metadata.json'));
250
+ const fingerprint = await graphFingerprint(workspace, graph);
251
+ const outputs = ['metadata.json', 'dependencies.json', 'tags.json', 'stats.json'];
252
+ if (resolveGraphBackendType(graph) === 'sqlite')
253
+ outputs.push('graph.db');
254
+ if (!options.full && marker?.fingerprint === fingerprint && metadata?.builtAt === marker.builtAt
255
+ && outputs.every(file => fs.existsSync(path.join(directory, file)))) {
256
+ job.status = 'skipped';
257
+ job.reason = 'unchanged';
258
+ return;
259
+ }
260
+ const { buildIndex } = await import('./index-builder.js');
261
+ await buildIndex(workspace, { graph, force: Boolean(options.full), explicit: false, quiet: true });
262
+ const built = readJson(path.join(directory, 'metadata.json'));
263
+ if (!built)
264
+ throw new Error(`graph did not produce metadata: ${graph}`);
265
+ writeJson(markerPath, { fingerprint, builtAt: built.builtAt });
266
+ }
267
+ else if (job.kind === 'cache') {
268
+ const status = getFortemiCoreSyncStatus(workspace, job.graph);
269
+ if (!options.full && status.built && !status.stale && !status.sourceIndexNewer) {
270
+ job.status = 'skipped';
271
+ job.reason = 'unchanged';
272
+ return;
273
+ }
274
+ syncFortemiCoreIndex(workspace, { graph: job.graph });
275
+ }
276
+ else if (job.kind === 'runtime' || job.kind === 'issues') {
277
+ const directory = job.kind === 'runtime'
278
+ ? projectAiwgPath(workspace, 'smiths', 'toolsmith') : projectAiwgPath(workspace, 'issues');
279
+ const output = path.join(directory, job.kind === 'runtime' ? 'runtime.json' : 'index/issues.index.json');
280
+ const before = outputStamp(output);
281
+ if (job.kind === 'runtime') {
282
+ await refreshWorkspaceRuntime(workspace, Boolean(options.full), await runtime());
283
+ }
284
+ else {
285
+ const { LocalIssueProviderCore } = await import('../issues/local.js');
286
+ await new LocalIssueProviderCore(directory).rebuildIssueIndex({ force: Boolean(options.full) });
287
+ }
288
+ if (!options.full && before !== undefined && before === outputStamp(output)) {
289
+ job.status = 'skipped';
290
+ job.reason = 'unchanged';
291
+ return;
292
+ }
293
+ }
294
+ else if (job.kind === 'models') {
295
+ const { getFrameworkRoot } = await import('../channel/manager.mjs');
296
+ const { collectProviderInventory } = await import('../providers/provider-inventory.js');
297
+ const { resolveDynamicModelCatalog } = await import('../models/model-discovery.js');
298
+ const catalog = await resolveDynamicModelCatalog({
299
+ aiwgRoot: await getFrameworkRoot(), inventory: await collectProviderInventory(workspace),
300
+ homeDir: process.env.HOME, allowNetwork: true, forceRefresh: Boolean(options.full),
301
+ omp: { cwd: workspace }, grokBuild: { cwd: workspace, env: process.env },
302
+ });
303
+ if (catalog.discovery?.source === 'cache') {
304
+ job.status = 'skipped';
305
+ job.reason = 'unchanged';
306
+ return;
307
+ }
308
+ }
309
+ else {
310
+ const { refreshSessionCatalog } = await import('../sessions/refresh.js');
311
+ const result = await refreshSessionCatalog(workspace, Boolean(options.full), options.lockWaitMs ?? 0, options.signal, aliases, {
312
+ maxRecordBytes: options.maxRecordBytes, maxSourceBytes: options.maxSourceBytes,
313
+ ...(options.exclude?.length ? { exclude: options.exclude } : {}),
314
+ });
315
+ if (typeof result === 'string') {
316
+ job.status = 'skipped';
317
+ job.reason = result;
318
+ job.noManifest = result === 'no authorized discovery manifest' ? 1 : 0;
319
+ return;
320
+ }
321
+ if (result) {
322
+ const receipts = Array.isArray(result) ? result : [result];
323
+ if (Array.isArray(result))
324
+ job.sessionImports = result;
325
+ else
326
+ job.sessionImport = result;
327
+ if (receipts.some(receipt => receipt.run.status === 'partial')) {
328
+ const rejected = receipts.reduce((sum, receipt) => sum + receipt.totals.rejected, 0);
329
+ const pending = receipts.reduce((sum, receipt) => sum + receipt.totals.pending, 0);
330
+ job.reason = `session import partial: ${rejected} rejected, ${pending} pending`;
331
+ }
332
+ }
333
+ }
334
+ job.status = 'ok';
335
+ }
336
+ function outputStamp(file) {
337
+ try {
338
+ const stat = fs.statSync(file, { bigint: true });
339
+ return [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(':');
340
+ }
341
+ catch (error) {
342
+ if (error.code === 'ENOENT')
343
+ return undefined;
344
+ throw error;
345
+ }
346
+ }
347
+ function jobKey(job, aliases) {
348
+ if (job.scope === 'host')
349
+ return JSON.stringify([job.kind, 'host', job.graph ?? null]);
350
+ return JSON.stringify([job.kind, job.workspace, job.graph ?? null,
351
+ ...(job.kind === 'sessions' && aliases.length > 1 ? [aliases] : [])]);
352
+ }
353
+ export async function refreshIndexes(options = {}, dependencies = {}) {
354
+ const start = Date.now();
355
+ const workspaces = workspacePaths(options);
356
+ const jobs = planWorkspaceRefresh(options, workspaces);
357
+ const report = {
358
+ contractVersion: '1.0.0', command: 'index.refresh', status: 'ok',
359
+ mode: options.full ? 'full' : 'incremental', jobs, messages: [],
360
+ totals: { jobs: jobs.length, ok: 0, skipped: 0, deferred: 0, errors: 0, noManifest: 0, excluded: 0, wallMs: 0 },
361
+ };
362
+ const finish = () => {
363
+ for (const job of jobs.filter(job => job.noManifest)) {
364
+ const command = `aiwg sessions discover --workspace '${job.workspace.replace(/'/g, "'\\''")}'`;
365
+ report.messages.push(`Session refresh skipped for ${job.workspace}: no authorized discovery manifest. Run \`${command}\` or configure sessions.discovery_manifests in .aiwg/aiwg.config.`);
366
+ }
367
+ report.totals = {
368
+ jobs: jobs.length,
369
+ ok: jobs.filter(job => job.status === 'ok').length,
370
+ skipped: jobs.filter(job => job.status === 'skipped').length,
371
+ deferred: jobs.filter(job => job.status === 'deferred').length,
372
+ errors: jobs.filter(job => job.status === 'error').length,
373
+ noManifest: jobs.reduce((sum, job) => sum + (job.noManifest ?? 0), 0),
374
+ excluded: jobs.reduce((sum, job) => sum + (job.sessionImports ?? (job.sessionImport ? [job.sessionImport] : []))
375
+ .reduce((total, receipt) => total + (receipt.totals.excluded ?? 0), 0), 0),
376
+ wallMs: Date.now() - start,
377
+ };
378
+ return report;
379
+ };
380
+ if (options.dryRun)
381
+ return finish();
382
+ const stateDir = getUserStateDir();
383
+ let release;
384
+ try {
385
+ release = await acquireDirectoryLock(path.join(stateDir, 'index-refresh.lock'), {
386
+ timeoutMs: options.lockWaitMs ?? 0, reclaimDeadOwner: true,
387
+ });
388
+ }
389
+ catch (error) {
390
+ if (!(error instanceof DirectoryLockContentionError))
391
+ throw error;
392
+ report.status = 'skipped';
393
+ report.reason = 'locked';
394
+ for (const job of jobs) {
395
+ job.status = 'skipped';
396
+ job.reason = 'locked';
397
+ }
398
+ return finish();
399
+ }
400
+ const saved = { ...GRAPH_CONFIGS };
401
+ try {
402
+ const checkpointPath = path.join(stateDir, 'index-refresh-checkpoint.json');
403
+ const keys = jobs.map(job => jobKey(job, workspaces.get(job.workspace ?? '') ?? []));
404
+ const previous = readJson(checkpointPath);
405
+ const checkpoint = previous?.mode === report.mode
406
+ && Array.isArray(previous.completed) && JSON.stringify(previous.jobs) === JSON.stringify(keys)
407
+ ? previous : { runId: randomUUID(), mode: report.mode, jobs: keys, completed: [] };
408
+ writeJson(checkpointPath, checkpoint);
409
+ let sqliteAvailable;
410
+ let runtime;
411
+ const discoverRuntime = () => runtime ??= (async () => {
412
+ const { RuntimeDiscovery } = await import('../smiths/toolsmith/runtime-discovery.mjs');
413
+ const directory = path.join(stateDir, 'index-refresh-runtime');
414
+ const catalog = await new RuntimeDiscovery(directory, { cwd: process.env.HOME ?? homedir(), quiet: true })
415
+ .discover({ force: Boolean(options.full) });
416
+ return { catalog, info: fs.readFileSync(path.join(directory, 'runtime-info.md'), 'utf8') };
417
+ })();
418
+ for (const job of jobs) {
419
+ options.signal?.throwIfAborted();
420
+ const aliases = workspaces.get(job.workspace ?? '') ?? [];
421
+ const key = jobKey(job, aliases);
422
+ if (checkpoint.completed.includes(key)) {
423
+ job.status = 'skipped';
424
+ job.reason = 'checkpoint';
425
+ continue;
426
+ }
427
+ const jobStart = Date.now();
428
+ try {
429
+ const gateWorkspace = job.workspace ?? workspaces.keys().next().value ?? options.cwd ?? process.cwd();
430
+ const gate = await (dependencies.gate ?? refreshResourceGate)(gateWorkspace);
431
+ if (!gate.ok) {
432
+ job.status = 'deferred';
433
+ job.reason = gate.reason;
434
+ report.status = 'deferred';
435
+ report.reason = gate.reason;
436
+ break;
437
+ }
438
+ if (job.workspace)
439
+ loadWorkspaceGraphs(job.workspace);
440
+ if (job.kind === 'cache' && jobs.some(other => other.kind === 'graph'
441
+ && other.workspace === job.workspace && other.graph === job.graph && other.status === 'error')) {
442
+ job.status = 'skipped';
443
+ job.reason = 'graph failed';
444
+ }
445
+ else if (job.kind === 'sessions') {
446
+ sqliteAvailable ??= await (dependencies.sessionsAvailable ?? (async () => {
447
+ const { sessionRefreshAvailable } = await import('../sessions/refresh.js');
448
+ return sessionRefreshAvailable();
449
+ }))();
450
+ if (!sqliteAvailable) {
451
+ const message = 'Session refresh skipped: run `aiwg features install sqlite`.';
452
+ if (!report.messages.includes(message))
453
+ report.messages.push(message);
454
+ job.status = 'skipped';
455
+ job.reason = 'sqlite unavailable';
456
+ }
457
+ else
458
+ await executeJob(job, options, aliases, discoverRuntime);
459
+ }
460
+ else
461
+ await executeJob(job, options, aliases, discoverRuntime);
462
+ }
463
+ catch (error) {
464
+ job.status = 'error';
465
+ job.error = error instanceof Error ? error.message : String(error);
466
+ }
467
+ finally {
468
+ job.wallMs = Date.now() - jobStart;
469
+ }
470
+ if (job.status !== 'error' && job.reason !== 'graph failed'
471
+ && job.reason !== 'no authorized discovery manifest')
472
+ checkpoint.completed.push(key);
473
+ writeJson(checkpointPath, checkpoint);
474
+ dependencies.afterJob?.(job, checkpoint.completed.length);
475
+ }
476
+ options.signal?.throwIfAborted();
477
+ const deferred = jobs.some(job => job.status === 'deferred');
478
+ if (!deferred)
479
+ fs.rmSync(checkpointPath, { force: true });
480
+ const errors = jobs.filter(job => job.status === 'error').length;
481
+ if (errors)
482
+ report.status = errors === jobs.length ? 'error' : 'partial';
483
+ else if (!deferred) {
484
+ if (jobs.some(job => job.sessionImport?.run.status === 'partial'
485
+ || job.sessionImports?.some(receipt => receipt.run.status === 'partial')
486
+ || job.reason === 'sqlite unavailable' || job.reason?.startsWith('no authorized')))
487
+ report.status = 'partial';
488
+ }
489
+ return finish();
490
+ }
491
+ finally {
492
+ for (const name of Object.keys(GRAPH_CONFIGS))
493
+ delete GRAPH_CONFIGS[name];
494
+ Object.assign(GRAPH_CONFIGS, saved);
495
+ await release();
496
+ }
497
+ }
498
+ export async function runIndexRefresh(args, cwd = process.cwd(), signal) {
499
+ const options = { cwd, signal, workspaces: [], root: [] };
500
+ let json = args.includes('--json');
501
+ try {
502
+ for (let i = 0; i < args.length; i++) {
503
+ const flag = args[i];
504
+ if (flag === '--full')
505
+ options.full = true;
506
+ else if (flag === '--dry-run')
507
+ options.dryRun = true;
508
+ else if (flag === '--json')
509
+ json = true;
510
+ else if (['--workspace', '--root', '--only', '--lock-wait-ms', '--max-record-bytes', '--max-source-bytes', '--exclude'].includes(flag)) {
511
+ const value = args[++i];
512
+ if (!value || value.startsWith('--'))
513
+ throw new Error(`${flag} requires a value`);
514
+ if (flag === '--workspace')
515
+ options.workspaces.push(value);
516
+ else if (flag === '--root')
517
+ options.root.push(value);
518
+ else if (flag === '--max-record-bytes')
519
+ options.maxRecordBytes = value;
520
+ else if (flag === '--max-source-bytes')
521
+ options.maxSourceBytes = value;
522
+ else if (flag === '--exclude')
523
+ (options.exclude ??= []).push(value);
524
+ else if (flag === '--only') {
525
+ if (value !== 'graphs' && value !== 'sessions' && value !== 'caches'
526
+ && value !== 'runtime' && value !== 'models' && value !== 'issues') {
527
+ throw new Error('--only must be graphs, sessions, caches, runtime, models, or issues');
528
+ }
529
+ options.only = value;
530
+ }
531
+ else {
532
+ if (!/^\d+$/.test(value) || !Number.isSafeInteger(Number(value)))
533
+ throw new Error('--lock-wait-ms must be a non-negative integer');
534
+ options.lockWaitMs = Number(value);
535
+ }
536
+ }
537
+ else
538
+ throw new Error(`unknown refresh option: ${flag}`);
539
+ }
540
+ resolveImportLimits(undefined, { maxRecordBytes: options.maxRecordBytes, maxSourceBytes: options.maxSourceBytes });
541
+ resolveImportExclusions(undefined, options.exclude);
542
+ const report = await refreshIndexes(options);
543
+ if (json)
544
+ console.log(JSON.stringify(report, null, 2));
545
+ else {
546
+ console.log(`Index refresh: ${report.status} (${report.mode})${report.reason ? `: ${report.reason}` : ''}`);
547
+ for (const job of report.jobs)
548
+ console.log(` ${job.status}: ${job.kind} ${job.scope === 'host' ? '[host]' : job.workspace}${job.graph ? ` [${job.graph}]` : ''}${job.error || job.reason ? `: ${job.error ?? job.reason}` : ''}`);
549
+ for (const job of report.jobs) {
550
+ for (const receipt of job.sessionImports ?? (job.sessionImport ? [job.sessionImport] : [])) {
551
+ for (const source of receipt.run.sources) {
552
+ if (source.status === 'excluded')
553
+ console.log(` excluded: ${source.sourceId} ${source.exclusion?.kind} ${source.exclusion?.entry}`);
554
+ }
555
+ }
556
+ }
557
+ console.log(` Excluded session sources: ${report.totals.excluded}`);
558
+ for (const message of report.messages)
559
+ console.log(message);
560
+ }
561
+ if (report.totals.errors)
562
+ process.exitCode = 1;
563
+ return report;
564
+ }
565
+ catch (error) {
566
+ const report = {
567
+ contractVersion: '1.0.0', command: 'index.refresh', status: 'error', mode: options.full ? 'full' : 'incremental',
568
+ jobs: [], messages: [error instanceof Error ? error.message : String(error)],
569
+ totals: { jobs: 0, ok: 0, skipped: 0, deferred: 0, errors: 1, noManifest: 0, excluded: 0, wallMs: 0 },
570
+ };
571
+ console.log(json ? JSON.stringify(report, null, 2) : `Index refresh: error: ${report.messages[0]}`);
572
+ process.exitCode = 1;
573
+ return report;
574
+ }
575
+ }
576
+ //# sourceMappingURL=index-refresh.js.map
@@ -0,0 +1,9 @@
1
+ /** Artifact worker uses the exact caller module: source never silently loads stale dist. */
2
+ import { parentPort, workerData } from 'node:worker_threads';
3
+ const module = workerData.moduleUrl.endsWith('.ts')
4
+ ? await (await import('tsx/esm/api')).tsImport(workerData.moduleUrl, import.meta.url)
5
+ : await import(workerData.moduleUrl);
6
+ parentPort.on('message', ({ index, job }) => {
7
+ try { parentPort.postMessage({ index, result: module.prepareIndexArtifact(job) }); }
8
+ catch (error) { parentPort.postMessage({ index, error: `${job.relativePath}: ${error.message}` }); }
9
+ });
@@ -1,17 +1,20 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
+ import { hostname } from 'node:os';
5
+ export class DirectoryLockContentionError extends Error {
6
+ }
4
7
  /**
5
8
  * Acquire a cross-process directory lock and return an ownership-safe release.
6
9
  *
7
- * The lock deliberately fails after a bounded wait instead of deleting a lock
8
- * that may belong to a slow but healthy package build.
10
+ * Package builds fail after a bounded wait. Maintenance can opt into recovery
11
+ * of owners proven dead on the same host; healthy or unknown owners are retained.
9
12
  */
10
13
  export async function acquireDirectoryLock(lockPath, options = {}) {
11
14
  const timeoutMs = options.timeoutMs ?? 180_000;
12
15
  const pollMs = options.pollMs ?? 100;
13
16
  const deadline = Date.now() + timeoutMs;
14
- const token = `${process.pid}:${randomUUID()}`;
17
+ const token = `${process.pid}:${randomUUID()}:${hostname()}`;
15
18
  const ownerPath = path.join(lockPath, 'owner');
16
19
  await mkdir(path.dirname(lockPath), { recursive: true });
17
20
  while (true) {
@@ -29,8 +32,10 @@ export async function acquireDirectoryLock(lockPath, options = {}) {
29
32
  catch (error) {
30
33
  if (error.code !== 'EEXIST')
31
34
  throw error;
35
+ if (options.reclaimDeadOwner && await reclaimDeadLock(lockPath))
36
+ continue;
32
37
  if (Date.now() >= deadline) {
33
- throw new Error(`timed out waiting for build lock: ${lockPath}`);
38
+ throw new DirectoryLockContentionError(`timed out waiting for build lock: ${lockPath}`);
34
39
  }
35
40
  await new Promise((resolve) => setTimeout(resolve, pollMs));
36
41
  }
@@ -46,4 +51,42 @@ export async function acquireDirectoryLock(lockPath, options = {}) {
46
51
  await rm(lockPath, { recursive: true, force: true });
47
52
  };
48
53
  }
54
+ /** Serialize stale-owner recovery so two waiters cannot remove a new lease. */
55
+ async function reclaimDeadLock(lockPath) {
56
+ let release;
57
+ try {
58
+ release = await acquireDirectoryLock(`${lockPath}.recovery`, { timeoutMs: 0, reclaimDeadOwner: true });
59
+ }
60
+ catch (error) {
61
+ if (error instanceof DirectoryLockContentionError)
62
+ return false;
63
+ throw error;
64
+ }
65
+ try {
66
+ let owner;
67
+ try {
68
+ owner = (await readFile(path.join(lockPath, 'owner'), 'utf8')).trim();
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ const [pidText, , host] = owner.split(':');
74
+ const pid = Number(pidText);
75
+ if (host !== hostname() || !Number.isSafeInteger(pid) || pid <= 0)
76
+ return false;
77
+ try {
78
+ process.kill(pid, 0);
79
+ return false;
80
+ }
81
+ catch (error) {
82
+ if (error.code !== 'ESRCH')
83
+ return false;
84
+ }
85
+ await rm(lockPath, { recursive: true, force: true });
86
+ return true;
87
+ }
88
+ finally {
89
+ await release();
90
+ }
91
+ }
49
92
  //# sourceMappingURL=prebuilt-build-lock.js.map