@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
+ * Decision-offload checkpoint execution and offline adoption reporting.
3
+ *
4
+ * Opportunity records describe only checkpoints that called this contract.
5
+ * Generic activity-log prose is deliberately not interpreted as a precise
6
+ * opportunity denominator. Completed-call records remain in ledger.ts; the
7
+ * shared correlation id joins the two append-only journals.
8
+ */
9
+ import { randomUUID } from 'node:crypto';
10
+ import { constants as fsConstants, createReadStream } from 'node:fs';
11
+ import { lstat, mkdir, open } from 'node:fs/promises';
12
+ import { createInterface } from 'node:readline';
13
+ import path from 'node:path';
14
+ import { projectAiwgPath, projectAiwgWritePath } from '../config/project-artifacts.js';
15
+ import { resolveDecisionEnablement } from '../config/decision-config.js';
16
+ import { readAiwgConfig } from '../config/aiwg-config.js';
17
+ import { resolveActiveProvider } from '../cli/provider-resolution.js';
18
+ import { askDecision, DecisionAskUsageError } from './ask.js';
19
+ import { inspectJevCredential } from './jev-credentials.js';
20
+ import { inspectDecisionOffloadActivation } from './offload-activation.js';
21
+ import { ATTRIBUTION_FIELD_PATTERN, decisionLedgerHistory, DECISION_LEDGER_RECORD_TIMEOUT_MS, recordDecisionAsk, resolveDecisionSession, } from './ledger.js';
22
+ export const DECISION_OPPORTUNITY_SCHEMA = 'aiwg-decision-opportunity/v1';
23
+ export const DECISION_OFFLOAD_SNAPSHOT_SCHEMA = 'aiwg-decision-offload-snapshot/v1';
24
+ const OPPORTUNITY_FILE = 'opportunities.jsonl';
25
+ const DIR_MODE = 0o700;
26
+ const FILE_MODE = 0o600;
27
+ const MAX_OPPORTUNITY_RECORD_BYTES = 4000;
28
+ export const DECISION_OPPORTUNITY_RECORD_TIMEOUT_MS = DECISION_LEDGER_RECORD_TIMEOUT_MS;
29
+ const TIMED_OUT = Symbol('decision-offload-timeout');
30
+ export const DECISION_OFFLOAD_CHECKPOINT_COVERAGE = [
31
+ 'address-issues:worker-pick', 'address-issues:failure-triage', 'address-issues:review-verdict',
32
+ 'address-issues:minor-triage', 'aiwg-mission:worker-pick', 'aiwg-mission:failure-triage',
33
+ 'subagent-scoping:worker-pick', 'subagent-scoping:failure-triage',
34
+ ].map(caller => ({ caller, coverage: 'instruction-only' }));
35
+ export const DECISION_OFFLOAD_EXECUTABLE_COVERAGE = [
36
+ { entrypoint: 'aiwg decision ask', transport: 'cli', coverage: 'shared-contract-generic' },
37
+ { entrypoint: 'decision-ask', transport: 'mcp', coverage: 'shared-contract-generic' },
38
+ ];
39
+ function safeCaller(caller) {
40
+ if (!ATTRIBUTION_FIELD_PATTERN.test(caller))
41
+ throw new DecisionAskUsageError('invalid-caller', 'checkpoint caller must be a valid attribution tag');
42
+ return caller;
43
+ }
44
+ export function decisionOffloadCategory(caller) {
45
+ if (!caller)
46
+ return 'unattributed';
47
+ const prefix = caller.split(':', 1)[0].toLowerCase();
48
+ if (['diagnosis', 'diagnostic', 'control'].includes(prefix))
49
+ return 'diagnostic';
50
+ if (['setup', 'verify'].includes(prefix))
51
+ return 'setup';
52
+ if (['benchmark', 'bench', 'qualification', 'eval'].includes(prefix))
53
+ return 'benchmark';
54
+ return 'organic';
55
+ }
56
+ async function providerFor(options) {
57
+ const work = (async () => {
58
+ const resolve = options.resolveProvider ?? resolveActiveProvider;
59
+ const resolution = await resolve({ cwd: options.projectRoot, explicitProvider: options.explicitProvider, env: options.env ?? process.env });
60
+ return resolution.provider ?? 'unknown';
61
+ })().catch(() => 'unknown');
62
+ const outcome = await bounded(work, options.providerTimeoutMs ?? DECISION_OPPORTUNITY_RECORD_TIMEOUT_MS);
63
+ return outcome === TIMED_OUT ? 'unknown' : outcome;
64
+ }
65
+ async function bounded(work, timeoutMs) {
66
+ let timer;
67
+ const timeout = new Promise(resolve => {
68
+ timer = setTimeout(() => resolve(TIMED_OUT), timeoutMs);
69
+ // Keep this timer referenced: it is the only liveness guarantee when an
70
+ // injected resolver or filesystem promise never settles in a standalone
71
+ // CLI process. Unref would let Node exit before the ask can run.
72
+ });
73
+ try {
74
+ const outcome = await Promise.race([work, timeout]);
75
+ if (outcome === TIMED_OUT)
76
+ work.catch(() => undefined);
77
+ return outcome;
78
+ }
79
+ finally {
80
+ if (timer)
81
+ clearTimeout(timer);
82
+ }
83
+ }
84
+ async function boundedAppend(append, record, projectRoot, timeoutMs, warn) {
85
+ let work;
86
+ try {
87
+ work = Promise.resolve(append(record, projectRoot, warn));
88
+ }
89
+ catch (error) {
90
+ warn(`warning: failed to record decision opportunity: ${error instanceof Error ? error.message : String(error)}`);
91
+ return false;
92
+ }
93
+ const outcome = await bounded(work.catch(error => {
94
+ warn(`warning: failed to record decision opportunity: ${error instanceof Error ? error.message : String(error)}`);
95
+ return false;
96
+ }), timeoutMs);
97
+ if (outcome === TIMED_OUT) {
98
+ warn(`warning: decision opportunity recording exceeded ${timeoutMs}ms; completion unknown`);
99
+ return null;
100
+ }
101
+ return outcome;
102
+ }
103
+ function recordFor(options, fields, correlationId, provider) {
104
+ const session = resolveDecisionSession(options.explicitSessionId, options.env ?? process.env);
105
+ const caller = safeCaller(options.caller);
106
+ return {
107
+ schema: DECISION_OPPORTUNITY_SCHEMA,
108
+ correlationId,
109
+ timestamp: new Date(options.now?.() ?? Date.now()).toISOString(),
110
+ provider,
111
+ ...session,
112
+ transport: options.transport,
113
+ caller,
114
+ category: options.category ?? decisionOffloadCategory(caller),
115
+ ...fields,
116
+ };
117
+ }
118
+ const RECORD_TIMESTAMP_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.\d{3}Z$/;
119
+ function isStrictTimestamp(value) {
120
+ if (typeof value !== 'string' || !RECORD_TIMESTAMP_PATTERN.test(value))
121
+ return false;
122
+ const parsed = new Date(value);
123
+ return Number.isFinite(parsed.getTime()) && parsed.toISOString() === value;
124
+ }
125
+ function validOpportunity(value) {
126
+ if (!value || typeof value !== 'object' || Array.isArray(value))
127
+ return false;
128
+ const v = value;
129
+ return v.schema === DECISION_OPPORTUNITY_SCHEMA
130
+ && typeof v.correlationId === 'string' && v.correlationId.length > 0
131
+ && isStrictTimestamp(v.timestamp)
132
+ && ['started', 'completed', 'skipped'].includes(String(v.phase))
133
+ && typeof v.provider === 'string'
134
+ && typeof v.sessionId === 'string' && typeof v.sessionSource === 'string'
135
+ && ['cli', 'mcp', 'internal'].includes(String(v.transport))
136
+ && typeof v.caller === 'string' && ATTRIBUTION_FIELD_PATTERN.test(v.caller)
137
+ && ['organic', 'diagnostic', 'setup', 'benchmark', 'unattributed'].includes(String(v.category))
138
+ && typeof v.eligible === 'boolean' && typeof v.attempted === 'boolean'
139
+ && ['answered', 'fallback', 'skipped', 'unknown'].includes(String(v.outcome))
140
+ && typeof v.reason === 'string'
141
+ && (typeof v.ledgerRecorded === 'boolean' || v.ledgerRecorded === null);
142
+ }
143
+ async function writeFully(handle, buffer) {
144
+ let offset = 0;
145
+ while (offset < buffer.length) {
146
+ const { bytesWritten } = await handle.write(buffer.subarray(offset));
147
+ if (!Number.isFinite(bytesWritten) || bytesWritten <= 0)
148
+ throw new Error('decision opportunity: write made no progress');
149
+ offset += bytesWritten;
150
+ }
151
+ }
152
+ export async function appendDecisionOpportunity(record, projectRoot, warn = message => process.stderr.write(`${message}\n`)) {
153
+ if (!validOpportunity(record))
154
+ return false;
155
+ const line = `${JSON.stringify(record)}\n`;
156
+ if (Buffer.byteLength(line, 'utf8') > MAX_OPPORTUNITY_RECORD_BYTES) {
157
+ warn(`warning: decision opportunity exceeds the ${MAX_OPPORTUNITY_RECORD_BYTES}-byte size cap and was not written`);
158
+ return false;
159
+ }
160
+ let handle;
161
+ try {
162
+ const directory = projectAiwgWritePath(projectRoot, 'decision');
163
+ const file = path.join(directory, OPPORTUNITY_FILE);
164
+ await mkdir(directory, { recursive: true, mode: DIR_MODE });
165
+ const dirStat = await lstat(directory);
166
+ if (!dirStat.isDirectory() || dirStat.isSymbolicLink() || (dirStat.mode & 0o077) !== 0)
167
+ throw new Error('unsafe opportunity directory');
168
+ try {
169
+ const fileStat = await lstat(file);
170
+ if (!fileStat.isFile() || fileStat.isSymbolicLink() || fileStat.nlink !== 1 || (fileStat.mode & 0o077) !== 0)
171
+ throw new Error('unsafe opportunity file');
172
+ }
173
+ catch (error) {
174
+ if (error.code !== 'ENOENT')
175
+ throw error;
176
+ }
177
+ handle = await open(file, fsConstants.O_APPEND | fsConstants.O_CREAT | fsConstants.O_WRONLY | (fsConstants.O_NOFOLLOW ?? 0), FILE_MODE);
178
+ const opened = await handle.stat();
179
+ if (!opened.isFile() || opened.nlink !== 1 || (opened.mode & 0o077) !== 0)
180
+ throw new Error('unsafe opened opportunity file');
181
+ await writeFully(handle, Buffer.from(line, 'utf8'));
182
+ return true;
183
+ }
184
+ catch (error) {
185
+ warn(`warning: failed to record decision opportunity: ${error instanceof Error ? error.message : String(error)}`);
186
+ return false;
187
+ }
188
+ finally {
189
+ await handle?.close().catch(() => undefined);
190
+ }
191
+ }
192
+ export async function runDecisionOffloadCheckpoint(input, options) {
193
+ const correlationId = (options.id ?? randomUUID)();
194
+ const provider = await providerFor(options);
195
+ const append = options.appendOpportunity ?? appendDecisionOpportunity;
196
+ let warned = false;
197
+ let pendingOpportunityWarning;
198
+ const warn = (message) => {
199
+ if (warned)
200
+ return;
201
+ warned = true;
202
+ (options.warn ?? (value => process.stderr.write(`${value}\n`)))(message);
203
+ };
204
+ const queueOpportunityWarning = (message) => { pendingOpportunityWarning ??= message; };
205
+ const flushOpportunityWarning = () => {
206
+ if (pendingOpportunityWarning)
207
+ warn(pendingOpportunityWarning);
208
+ };
209
+ const eligibility = options.eligibility ?? { eligible: true };
210
+ const appendBounded = (record, appendWarn) => boundedAppend(append, record, options.projectRoot, options.opportunityTimeoutMs ?? DECISION_OPPORTUNITY_RECORD_TIMEOUT_MS, appendWarn);
211
+ if (!eligibility.eligible) {
212
+ const opportunity = recordFor(options, { phase: 'skipped', eligible: false, attempted: false, outcome: 'skipped', reason: eligibility.reason ?? 'other-exclusion', ledgerRecorded: null }, correlationId, provider);
213
+ return { outcome: 'skipped', result: null, opportunity, opportunityRecorded: await appendBounded(opportunity, warn) };
214
+ }
215
+ // Persist the eligible, attempted checkpoint before invoking the backend.
216
+ // If the process dies in the call, the offline snapshot retains an unknown
217
+ // attempt rather than incorrectly reporting that no opportunity existed.
218
+ const started = recordFor(options, {
219
+ phase: 'started', eligible: true, attempted: true, outcome: 'unknown',
220
+ reason: 'attempt-started', ledgerRecorded: null,
221
+ }, correlationId, provider);
222
+ const attemptRecorded = await appendBounded(started, queueOpportunityWarning);
223
+ let result;
224
+ try {
225
+ const ask = options.ask ?? askDecision;
226
+ result = await ask(input, options.askRuntime ?? { cwd: options.projectRoot, env: options.env ?? process.env });
227
+ }
228
+ catch (error) {
229
+ const usage = error instanceof DecisionAskUsageError;
230
+ const opportunity = recordFor(options, {
231
+ phase: 'completed',
232
+ eligible: true, attempted: !usage, outcome: usage ? 'skipped' : 'unknown',
233
+ reason: usage ? 'usage-rejected' : 'execution-unknown', ledgerRecorded: false,
234
+ }, correlationId, provider);
235
+ const opportunityRecorded = await appendBounded(opportunity, queueOpportunityWarning);
236
+ flushOpportunityWarning();
237
+ if (usage)
238
+ throw error;
239
+ return { outcome: 'unknown', result: null, opportunity, attemptRecorded, opportunityRecorded, error };
240
+ }
241
+ let ledger = null;
242
+ try {
243
+ ledger = await (options.recordAsk ?? recordDecisionAsk)(input, result, {
244
+ ...options,
245
+ warn,
246
+ caller: options.ledgerCaller === undefined ? safeCaller(options.caller) : options.ledgerCaller,
247
+ id: () => correlationId,
248
+ });
249
+ }
250
+ catch (error) {
251
+ warn(`warning: failed to record decision ledger: ${error instanceof Error ? error.message : String(error)}`);
252
+ }
253
+ const opportunity = recordFor(options, {
254
+ phase: 'completed',
255
+ eligible: true, attempted: result.reason !== 'not-configured', outcome: result.status,
256
+ reason: result.reason, ledgerRecorded: ledger ? true : null,
257
+ }, correlationId, provider);
258
+ const opportunityRecorded = await appendBounded(opportunity, queueOpportunityWarning);
259
+ flushOpportunityWarning();
260
+ return { outcome: result.status, result, opportunity, attemptRecorded, opportunityRecorded };
261
+ }
262
+ /** Collapse append-only started/completed events to one current state per checkpoint. */
263
+ function collapseOpportunities(records) {
264
+ const byId = new Map();
265
+ for (const record of records)
266
+ byId.set(record.correlationId, record);
267
+ return [...byId.values()];
268
+ }
269
+ async function readOpportunities(projectRoot) {
270
+ const file = projectAiwgPath(projectRoot, 'decision', OPPORTUNITY_FILE);
271
+ const records = [];
272
+ let invalid = 0;
273
+ try {
274
+ const input = createReadStream(file, { encoding: 'utf8' });
275
+ for await (const line of createInterface({ input, crlfDelay: Infinity })) {
276
+ if (!line.trim())
277
+ continue;
278
+ try {
279
+ const value = JSON.parse(line);
280
+ if (validOpportunity(value))
281
+ records.push(value);
282
+ else
283
+ invalid += 1;
284
+ }
285
+ catch {
286
+ invalid += 1;
287
+ }
288
+ }
289
+ return { records, invalid, status: 'available' };
290
+ }
291
+ catch (error) {
292
+ if (error.code === 'ENOENT')
293
+ return { records, invalid, status: 'absent' };
294
+ return { records, invalid: invalid + 1, status: 'unreadable' };
295
+ }
296
+ }
297
+ const ACTIVITY_LINE_PATTERN = /^##\s+\[(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})\]\s+[a-z]+\s+\|\s+(.+?)\s*$/;
298
+ const ORCHESTRATION_ACTIVITY_PATTERN = /\b(?:worker[- ]dispatch|dispatch(?:ed|ing)?\s+(?:a\s+)?worker|address-issues(?:\s+cycle)?|orchestration\s+worker)\b/i;
299
+ async function readOrchestrationActivity(projectRoot, since) {
300
+ const root = projectAiwgPath(projectRoot);
301
+ const candidates = [path.join(root, 'activity.log'), path.join(root, 'activity-log', 'activity.log')];
302
+ for (const candidate of candidates) {
303
+ try {
304
+ let recognizable = 0;
305
+ let valid = 0;
306
+ const input = createReadStream(candidate, { encoding: 'utf8' });
307
+ for await (const line of createInterface({ input, crlfDelay: Infinity })) {
308
+ const match = ACTIVITY_LINE_PATTERN.exec(line);
309
+ if (!match)
310
+ continue;
311
+ const timestamp = Date.parse(`${match[1]}T${match[2]}:00.000Z`);
312
+ if (!Number.isFinite(timestamp) || timestamp < since.getTime())
313
+ continue;
314
+ valid += 1;
315
+ if (ORCHESTRATION_ACTIVITY_PATTERN.test(match[3]))
316
+ recognizable += 1;
317
+ }
318
+ return { status: 'available', source: path.relative(projectRoot, candidate), valid, recognizable };
319
+ }
320
+ catch (error) {
321
+ if (error.code === 'ENOENT')
322
+ continue;
323
+ return { status: 'unreadable', source: path.relative(projectRoot, candidate), valid: 0, recognizable: 0 };
324
+ }
325
+ }
326
+ return { status: 'absent', source: null, valid: 0, recognizable: 0 };
327
+ }
328
+ function filterScope(records, sessionId, since) {
329
+ return records.filter(record => sessionId ? record.sessionId === sessionId : Date.parse(record.timestamp) >= since.getTime());
330
+ }
331
+ function completedCalls(records) {
332
+ const fallbackReasons = {};
333
+ let answered = 0;
334
+ let fallback = 0;
335
+ let llmTokensAvoided = 0;
336
+ let jevTokens = 0;
337
+ for (const record of records) {
338
+ if (record.status === 'answered')
339
+ answered += 1;
340
+ else {
341
+ fallback += 1;
342
+ const reason = record.fallbackReason ?? 'unknown';
343
+ fallbackReasons[reason] = (fallbackReasons[reason] ?? 0) + 1;
344
+ }
345
+ llmTokensAvoided += record.llm_tokens_avoided ?? record.estimatedLlmTokensAvoided;
346
+ jevTokens += record.jev_tokens ?? (record.usage?.inputTokens ?? 0) + (record.usage?.outputTokens ?? 0);
347
+ }
348
+ return { completed: records.length, answered, fallback, fallbackReasons, llmTokensAvoided, jevTokens };
349
+ }
350
+ function opportunityCounts(records) {
351
+ const reasons = {};
352
+ for (const record of records)
353
+ reasons[record.reason] = (reasons[record.reason] ?? 0) + 1;
354
+ return {
355
+ observed: records.length,
356
+ eligible: records.filter(record => record.eligible).length,
357
+ attempted: records.filter(record => record.attempted).length,
358
+ answered: records.filter(record => record.outcome === 'answered').length,
359
+ fallback: records.filter(record => record.outcome === 'fallback').length,
360
+ skipped: records.filter(record => record.outcome === 'skipped').length,
361
+ unknown: records.filter(record => record.outcome === 'unknown').length,
362
+ usageRejected: records.filter(record => record.reason === 'usage-rejected').length,
363
+ ledgerRecordingUnknown: records.filter(record => record.ledgerRecorded === null && record.outcome !== 'skipped').length,
364
+ reasons,
365
+ };
366
+ }
367
+ function activationEvidence(input) {
368
+ return { ...(input?.ruleSourceAvailable !== undefined ? { ruleSourceAvailable: input.ruleSourceAvailable } : {}), addon: input?.addon ?? 'unknown', ruleDelivery: input?.ruleDelivery ?? 'unknown',
369
+ startupAnchor: input?.startupAnchor ?? 'unknown', nativeActivation: input?.nativeActivation ?? 'not-claimed',
370
+ ...(input?.source ? { source: input.source } : {}) };
371
+ }
372
+ function askReadinessState(configured, latest) {
373
+ if (configured === null)
374
+ return 'unknown';
375
+ if (!configured)
376
+ return 'disabled';
377
+ if (latest?.outcome === 'answered')
378
+ return 'reachable';
379
+ if (latest?.outcome === 'fallback' && latest.attempted)
380
+ return 'fallback';
381
+ if (latest?.outcome === 'unknown')
382
+ return 'unknown';
383
+ return 'configured-unprobed';
384
+ }
385
+ export async function buildDecisionOffloadSnapshot(options) {
386
+ const projectRoot = path.resolve(options.projectRoot);
387
+ const env = options.env ?? process.env;
388
+ const now = options.now ?? new Date();
389
+ let projectConfig = null;
390
+ let configReadable = true;
391
+ try {
392
+ projectConfig = await readAiwgConfig(projectRoot);
393
+ }
394
+ catch {
395
+ configReadable = false;
396
+ }
397
+ const configuredThresholds = projectConfig?.decision?.offload_report;
398
+ const windowHours = options.windowHours ?? configuredThresholds?.window_hours ?? 24;
399
+ const since = new Date(now.getTime() - windowHours * 60 * 60 * 1000);
400
+ const detectedSession = resolveDecisionSession(options.sessionId, env);
401
+ const sessionId = options.sessionId ?? (detectedSession.sessionId === 'unknown' ? undefined : detectedSession.sessionId);
402
+ const thresholds = {
403
+ minimumEligible: configuredThresholds?.minimum_eligible ?? 1,
404
+ minimumAttemptRate: configuredThresholds?.minimum_attempt_rate ?? 0.5,
405
+ repeatedFallbacks: configuredThresholds?.repeated_fallbacks ?? 3,
406
+ ...options.thresholds,
407
+ };
408
+ let enablement = null;
409
+ try {
410
+ enablement = resolveDecisionEnablement({ cwd: projectRoot, env });
411
+ }
412
+ catch {
413
+ configReadable = false;
414
+ }
415
+ let credential;
416
+ try {
417
+ credential = await inspectJevCredential({ cwd: projectRoot, env });
418
+ }
419
+ catch {
420
+ credential = { configured: null, enabled: null, source: 'unknown' };
421
+ }
422
+ const [opportunitiesRead, ledger, activity] = await Promise.all([
423
+ readOpportunities(projectRoot),
424
+ decisionLedgerHistory(projectRoot),
425
+ readOrchestrationActivity(projectRoot, since),
426
+ ]);
427
+ const opportunityEvents = filterScope(opportunitiesRead.records, sessionId, since);
428
+ const opportunities = collapseOpportunities(opportunityEvents);
429
+ const calls = filterScope(ledger.records, sessionId, since);
430
+ const organicOpportunities = opportunities.filter(record => record.category === 'organic');
431
+ const diagnosticOpportunities = opportunities.filter(record => record.category !== 'organic');
432
+ const organic = opportunityCounts(organicOpportunities);
433
+ const diagnostics = opportunityCounts(diagnosticOpportunities);
434
+ const opportunityIds = new Set(opportunities.filter(record => record.attempted).map(record => record.correlationId));
435
+ const callIds = new Set(calls.map(record => record.id));
436
+ const organicOpportunityIds = new Set(organicOpportunities.filter(record => record.attempted).map(record => record.correlationId));
437
+ const organicCallIds = new Set(calls.filter(record => decisionOffloadCategory(record.caller) === 'organic').map(record => record.id));
438
+ const latestObserved = [...opportunities].sort((a, b) => a.timestamp.localeCompare(b.timestamp)).at(-1);
439
+ const latestTransportEvidence = [...opportunities].filter(record => record.attempted)
440
+ .sort((a, b) => a.timestamp.localeCompare(b.timestamp)).at(-1);
441
+ const askConfigured = credential.configured === null || credential.enabled === null
442
+ ? null : credential.configured && credential.enabled;
443
+ const evaluateState = configReadable && enablement
444
+ ? (enablement.enabled ? 'configured-unprobed' : 'disabled') : 'unknown';
445
+ let inspectedActivation;
446
+ if (!options.activationEvidence) {
447
+ try {
448
+ const inspected = await inspectDecisionOffloadActivation({ cwd: projectRoot, frameworkRoot: options.frameworkRoot, env });
449
+ inspectedActivation = {
450
+ addon: inspected.installed === true ? 'installed' : inspected.installed === false ? 'missing' : 'unknown',
451
+ ruleSourceAvailable: inspected.ruleSourcePresent,
452
+ ruleDelivery: inspected.ruleDelivery,
453
+ startupAnchor: inspected.startupAnchorPresent === true ? 'present'
454
+ : inspected.startupAnchorPresent === false ? 'missing' : 'unknown',
455
+ nativeActivation: inspected.nativeActivation,
456
+ source: 'local-configuration-and-startup-pointer; source availability is not delivery',
457
+ };
458
+ }
459
+ catch { /* unavailable inspection remains unknown, never a loading claim */ }
460
+ }
461
+ const activation = activationEvidence(options.activationEvidence ?? inspectedActivation);
462
+ const organicCalls = calls.filter(record => decisionOffloadCategory(record.caller) === 'organic');
463
+ const activityCandidate = activity.recognizable > 0 && organicCalls.length === 0;
464
+ const activitySuppressionReason = !activityCandidate ? null
465
+ : activation.addon === 'unknown' ? 'activation-unknown'
466
+ : activation.addon === 'missing' ? 'addon-missing'
467
+ : askConfigured === null ? 'ask-readiness-unknown'
468
+ : askConfigured === false ? 'ask-disabled-or-unconfigured'
469
+ : null;
470
+ const activityGap = activityCandidate && activitySuppressionReason === null;
471
+ const driftReasons = [];
472
+ const attemptedWithoutLedger = [...opportunityIds].filter(id => !callIds.has(id)).length;
473
+ const organicAttemptedWithoutLedger = [...organicOpportunityIds].filter(id => !organicCallIds.has(id)).length;
474
+ if (!configReadable || opportunitiesRead.status === 'unreadable' || opportunitiesRead.invalid > 0 || organic.unknown > 0)
475
+ driftReasons.push('observation-unknown');
476
+ if (organic.ledgerRecordingUnknown > 0 || ledger.skipped > 0)
477
+ driftReasons.push('recording-failure-or-unknown');
478
+ if (organicAttemptedWithoutLedger > 0)
479
+ driftReasons.push('attempted-without-completed-ledger');
480
+ if (organic.usageRejected > 0)
481
+ driftReasons.push('usage-rejection');
482
+ if (activation.addon === 'missing')
483
+ driftReasons.push('missing-addon');
484
+ else if (activation.addon === 'unknown')
485
+ driftReasons.push('activation-evidence-unknown');
486
+ if (activation.ruleDelivery === 'missing')
487
+ driftReasons.push('missing-rule-delivery');
488
+ if (activation.startupAnchor === 'missing')
489
+ driftReasons.push('missing-activation-anchor');
490
+ if (askConfigured === false)
491
+ driftReasons.push('disabled-or-unconfigured');
492
+ else if (askConfigured === null)
493
+ driftReasons.push('ask-readiness-unknown');
494
+ if (organic.eligible > 0 && organic.eligible >= thresholds.minimumEligible) {
495
+ const rate = organic.eligible ? organic.attempted / organic.eligible : 0;
496
+ if (organic.attempted === 0)
497
+ driftReasons.push('observed-eligible-zero-attempts');
498
+ else if (rate < thresholds.minimumAttemptRate)
499
+ driftReasons.push('observed-eligible-low-attempt-rate');
500
+ }
501
+ if (organic.fallback >= thresholds.repeatedFallbacks)
502
+ driftReasons.push('repeated-fallback');
503
+ const unknown = !configReadable || askConfigured === null || activation.addon === 'unknown' || activation.startupAnchor === 'unknown'
504
+ || opportunitiesRead.status === 'unreadable' || opportunitiesRead.invalid > 0
505
+ || organic.unknown > 0 || organic.ledgerRecordingUnknown > 0 || ledger.skipped > 0 || organicAttemptedWithoutLedger > 0;
506
+ const driftStatus = unknown ? 'unknown' : organic.eligible === 0 ? 'not-applicable' : driftReasons.length ? 'advisory' : 'ok';
507
+ return {
508
+ schema: DECISION_OFFLOAD_SNAPSHOT_SCHEMA,
509
+ scope: { projectRoot, mode: sessionId ? 'session' : 'window', sessionId: sessionId ?? null,
510
+ since: sessionId ? null : since.toISOString(), windowHours: sessionId ? null : windowHours, generatedAt: now.toISOString() },
511
+ readiness: {
512
+ ask: { state: askReadinessState(askConfigured, latestTransportEvidence), configured: credential.configured,
513
+ enabled: credential.enabled, source: credential.source, probed: false },
514
+ evaluate: { state: evaluateState, enabled: enablement?.enabled ?? null,
515
+ source: configReadable && enablement ? enablement.source : 'unknown', probed: false },
516
+ observed: latestObserved ? {
517
+ timestamp: latestObserved.timestamp, transport: latestObserved.transport,
518
+ category: latestObserved.category, outcome: latestObserved.outcome,
519
+ reason: latestObserved.reason,
520
+ } : null,
521
+ },
522
+ activation,
523
+ coverage: { executable: DECISION_OFFLOAD_EXECUTABLE_COVERAGE,
524
+ instructionOnly: DECISION_OFFLOAD_CHECKPOINT_COVERAGE, unsupportedObservation: 'unknown' },
525
+ opportunities: { organic, excluded: diagnostics, journalEvents: opportunityEvents.length, invalidRecords: opportunitiesRead.invalid,
526
+ journal: opportunitiesRead.status },
527
+ calls: { organic: completedCalls(organicCalls),
528
+ excluded: completedCalls(calls.filter(record => decisionOffloadCategory(record.caller) !== 'organic')), invalidRecords: ledger.skipped },
529
+ activityHint: {
530
+ status: activityGap ? 'advisory' : 'none',
531
+ reason: activityGap ? 'orchestration-without-organic-completed-calls' : null,
532
+ suppressionReason: activitySuppressionReason,
533
+ recognizableOrchestrationEvents: activity.recognizable,
534
+ eligibleCount: 'unknown',
535
+ confidence: 'low',
536
+ scope: 'recent-window',
537
+ source: activity.source,
538
+ journal: activity.status,
539
+ note: 'Generic activity is a low-confidence signal only; it is not a measured eligible-opportunity denominator.',
540
+ },
541
+ correlation: { matched: [...opportunityIds].filter(id => callIds.has(id)).length,
542
+ attemptedWithoutLedger,
543
+ organicAttemptedWithoutLedger,
544
+ ledgerWithoutOpportunity: [...callIds].filter(id => !opportunityIds.has(id)).length },
545
+ drift: { status: driftStatus, advisory: driftStatus === 'advisory', reasons: [...new Set(driftReasons)], thresholds,
546
+ thresholdSource: options.thresholds ? 'options' : configuredThresholds ? 'project' : 'default',
547
+ note: organic.eligible === 0 ? 'No observed eligible checkpoints; this is not evidence of healthy adoption.' : null },
548
+ };
549
+ }
550
+ //# sourceMappingURL=offload.js.map
@@ -110,9 +110,7 @@ export async function runDecisionLoadHarness(options) {
110
110
  const { manifest, clock, driver, sample } = options;
111
111
  const arrivals = decisionLoadArrivals(manifest, options.windowSeconds);
112
112
  const start = clock.now();
113
- const threadCpu = process.threadCpuUsage?.bind(process);
114
- const cpuStart = threadCpu?.();
115
- const wallStart = process.hrtime.bigint();
113
+ const cpuStart = process.threadCpuUsage();
116
114
  let rss = process.memoryUsage().rss;
117
115
  let heap = process.memoryUsage().heapUsed;
118
116
  const sampleMemory = () => {
@@ -170,8 +168,8 @@ export async function runDecisionLoadHarness(options) {
170
168
  await clock.advance(drainStep);
171
169
  const results = await all;
172
170
  sampleMemory();
173
- const threadUsage = cpuStart && threadCpu ? threadCpu(cpuStart) : undefined;
174
- const cpuMicros = threadUsage ? threadUsage.user + threadUsage.system : Number(process.hrtime.bigint() - wallStart) / 1000;
171
+ const threadUsage = process.threadCpuUsage(cpuStart);
172
+ const cpuMicros = threadUsage.user + threadUsage.system;
175
173
  const virtualDurationMs = Math.max(1, clock.now() - start);
176
174
  const shedReasons = {};
177
175
  let attempts = 0;
@@ -219,7 +217,7 @@ export async function runDecisionLoadHarness(options) {
219
217
  heapUsedMiB: Number((heap / 1_048_576).toFixed(1)),
220
218
  residentMemoryMiB: Number((rss / 1_048_576).toFixed(1)),
221
219
  cpuPercentOneCore: Number(((cpuMicros / 1000) / virtualDurationMs * 100).toFixed(3)),
222
- cpuSource: threadUsage ? 'thread' : 'wall-clock-upper-bound',
220
+ cpuSource: 'thread',
223
221
  },
224
222
  };
225
223
  }
@@ -0,0 +1,28 @@
1
+ /** Planning assumptions, not a vendor price quote or measured caller usage. */
2
+ export const DEFAULT_DECISION_SAVINGS_WEIGHTS = {
3
+ caller_model_class: 'reasoning',
4
+ reasoning_tokens: 1024,
5
+ output_tokens: 128,
6
+ llm_per_mtok: 15,
7
+ jev_per_mtok: 0.2,
8
+ };
9
+ export function decisionSavingsWeights(ask) {
10
+ const modelClass = ask?.savings?.caller_model_class ?? 'reasoning';
11
+ return {
12
+ ...DEFAULT_DECISION_SAVINGS_WEIGHTS,
13
+ ...ask?.cost,
14
+ caller_model_class: modelClass,
15
+ reasoning_tokens: ask?.savings?.reasoning_tokens ?? (modelClass === 'reasoning' ? 1024 : 256),
16
+ output_tokens: ask?.savings?.output_tokens ?? 128,
17
+ };
18
+ }
19
+ export function estimateDecisionSavings(inputTokens, answered, jevTokens, weights) {
20
+ const llmTokens = answered ? inputTokens + weights.reasoning_tokens + weights.output_tokens : 0;
21
+ return {
22
+ llm_tokens_avoided: llmTokens,
23
+ jev_tokens: jevTokens,
24
+ cost_weighted_net_usd: (llmTokens * weights.llm_per_mtok - jevTokens * weights.jev_per_mtok) / 1_000_000,
25
+ estimationMethod: { version: 'aiwg-decision-savings/v2', input_tokens: inputTokens, ...weights },
26
+ };
27
+ }
28
+ //# sourceMappingURL=savings.js.map
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url';
5
5
  import Ajv2020 from 'ajv/dist/2020.js';
6
6
  import addFormats from 'ajv-formats';
7
7
  import { canonicalJson } from '../security/artifact-trust.js';
8
- import { admitEntry, EntryAdmissionError } from './entry.js';
8
+ import { admissionClockStart, admitEntry, elapsedAdmissionMs, EntryAdmissionError } from './entry.js';
9
9
  import { DECISION_API_VERSION, DECISION_API_VERSION_STRUCTURED } from './types.js';
10
10
  const schemaFiles = {
11
11
  DecisionBinding: 'DecisionBinding.schema.json',
@@ -83,10 +83,13 @@ export function preprocessingLineageSchemaErrors(value) {
83
83
  return validate(value) ? null : ajvMessage(validate.errors);
84
84
  }
85
85
  export function artifactDigest(value) {
86
- const started = performance.now();
86
+ // Per-thread CPU time, not wall-clock: see entry.ts `admissionClockStart`
87
+ // for why a `performance.now()` budget here would be tripped by host
88
+ // scheduling contention rather than actual digesting cost.
89
+ const started = admissionClockStart();
87
90
  admitEntry(value);
88
91
  const canonical = canonicalJson(value);
89
- if (performance.now() - started > 1000) {
92
+ if (elapsedAdmissionMs(started) > 1000) {
90
93
  throw new EntryAdmissionError('time-budget', { bytes: Buffer.byteLength(canonical), entries: 0 });
91
94
  }
92
95
  return `sha256:${createHash('sha256').update(canonical).digest('hex')}`;