@geraldmaron/construct 1.1.0 → 1.2.0

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 (425) hide show
  1. package/README.md +30 -18
  2. package/bin/construct +392 -91
  3. package/bin/construct-postinstall.mjs +30 -2
  4. package/examples/distribution/README.md +42 -0
  5. package/examples/distribution/manifest.json +53 -0
  6. package/examples/distribution/sources/adr.md +84 -0
  7. package/examples/distribution/sources/deck-one-pager.md +65 -0
  8. package/examples/distribution/sources/prd-platform.md +161 -0
  9. package/examples/distribution/sources/research-brief.md +80 -0
  10. package/examples/distribution/sources/rfc-platform.md +82 -0
  11. package/examples/distribution/sources/runbook.md +103 -0
  12. package/examples/distribution/sources/strategy.md +88 -0
  13. package/lib/adapters-sync.mjs +67 -0
  14. package/lib/artifact-gate-notice.mjs +38 -0
  15. package/lib/artifact-manifest.mjs +112 -0
  16. package/lib/artifact-release-gate.mjs +171 -0
  17. package/lib/artifact-reviewers.mjs +68 -0
  18. package/lib/artifact-type-from-path.mjs +79 -0
  19. package/lib/audit-skills.mjs +4 -0
  20. package/lib/audit-specialists.mjs +285 -0
  21. package/lib/audit-trail.mjs +77 -10
  22. package/lib/auto-docs.mjs +87 -18
  23. package/lib/beads/drift.mjs +27 -6
  24. package/lib/beads-client.mjs +49 -121
  25. package/lib/beads-optimistic.mjs +5 -12
  26. package/lib/brand-fonts.mjs +93 -0
  27. package/lib/brand-prose.mjs +214 -0
  28. package/lib/brand-tokens.mjs +92 -0
  29. package/lib/bridges/copilot-proxy.mjs +13 -26
  30. package/lib/capability-ledger.mjs +156 -0
  31. package/lib/certification/artifact-fixtures.mjs +132 -0
  32. package/lib/certification/artifact-gates.mjs +63 -0
  33. package/lib/certification/artifact-provenance.mjs +97 -0
  34. package/lib/certification/canonical-scenarios.mjs +78 -0
  35. package/lib/certification/cli.mjs +191 -0
  36. package/lib/certification/dashboard-api.mjs +71 -0
  37. package/lib/certification/demo-parity.mjs +116 -0
  38. package/lib/certification/document-io-fixtures.mjs +246 -0
  39. package/lib/certification/document-workflow.mjs +97 -0
  40. package/lib/certification/eval-bridge.mjs +77 -0
  41. package/lib/certification/model-routing.mjs +149 -0
  42. package/lib/certification/prompt-budget.mjs +119 -0
  43. package/lib/certification/rc-gate.mjs +206 -0
  44. package/lib/certification/real-llm-scenarios.mjs +303 -0
  45. package/lib/certification/role-cards.mjs +113 -0
  46. package/lib/certification/role-overlays.mjs +117 -0
  47. package/lib/certification/run.mjs +122 -0
  48. package/lib/certification/runner.mjs +323 -0
  49. package/lib/certification/scenarios.mjs +51 -0
  50. package/lib/certification/skill-inventory.mjs +289 -0
  51. package/lib/certification/skill-scenarios.mjs +147 -0
  52. package/lib/certification/specialist-contracts.mjs +85 -0
  53. package/lib/certification/specialist-scenarios.mjs +175 -0
  54. package/lib/certification/stale-impact.mjs +146 -0
  55. package/lib/certification/status.mjs +252 -0
  56. package/lib/certification/store.mjs +77 -0
  57. package/lib/chat/cli.mjs +333 -0
  58. package/lib/chat/command-suggest.mjs +161 -0
  59. package/lib/chat/commands.mjs +215 -0
  60. package/lib/chat/config.mjs +142 -0
  61. package/lib/chat/context-compactor.mjs +250 -0
  62. package/lib/chat/context-continuation.mjs +253 -0
  63. package/lib/chat/continuation-source.mjs +58 -0
  64. package/lib/chat/demo-guide.mjs +61 -0
  65. package/lib/chat/design-tokens.mjs +91 -0
  66. package/lib/chat/desktop-binary.mjs +79 -0
  67. package/lib/chat/desktop-build.mjs +130 -0
  68. package/lib/chat/desktop-launcher.mjs +133 -0
  69. package/lib/chat/evidence.mjs +145 -0
  70. package/lib/chat/export.mjs +74 -0
  71. package/lib/chat/harness/driver.mjs +91 -0
  72. package/lib/chat/list-picker.mjs +112 -0
  73. package/lib/chat/model-picker.mjs +356 -0
  74. package/lib/chat/openrouter-fallback.mjs +151 -0
  75. package/lib/chat/permission-prompt.mjs +33 -0
  76. package/lib/chat/picker-catalog.mjs +45 -0
  77. package/lib/chat/policy-telemetry.mjs +34 -0
  78. package/lib/chat/present.mjs +245 -0
  79. package/lib/chat/session-context.mjs +39 -0
  80. package/lib/chat/session-persist.mjs +73 -0
  81. package/lib/chat/session-restore.mjs +71 -0
  82. package/lib/chat/session-settings.mjs +53 -0
  83. package/lib/chat/system-prompt.mjs +52 -0
  84. package/lib/chat/transparency.mjs +93 -0
  85. package/lib/chat/tui/color-scheme.mjs +42 -0
  86. package/lib/chat/tui/markdown.mjs +123 -0
  87. package/lib/chat/tui/presentation.mjs +100 -0
  88. package/lib/chat/tui/render.mjs +500 -0
  89. package/lib/chat/tui/turn-block.mjs +284 -0
  90. package/lib/chat/tui/turn-present.mjs +18 -0
  91. package/lib/chat/tui/turn-state.mjs +88 -0
  92. package/lib/chat/tui/usage.mjs +122 -0
  93. package/lib/chat/web-commands.mjs +146 -0
  94. package/lib/chat/web-launcher.mjs +63 -0
  95. package/lib/chat/web-picker-keys.mjs +46 -0
  96. package/lib/chat/web-session.mjs +159 -0
  97. package/lib/cli-commands.mjs +232 -11
  98. package/lib/comment-lint.mjs +6 -4
  99. package/lib/config/schema.mjs +36 -5
  100. package/lib/contract-schemas/decision.json +50 -0
  101. package/lib/contract-schemas/implementation.json +51 -0
  102. package/lib/contract-schemas/review-report.json +32 -0
  103. package/lib/contract-schemas/test-report.json +43 -0
  104. package/lib/contracts/construct-handoff.mjs +60 -0
  105. package/lib/contracts/validate.mjs +32 -3
  106. package/lib/contracts/violation-log.mjs +58 -5
  107. package/lib/dashboard-demo.mjs +71 -0
  108. package/lib/dashboard-static.mjs +19 -5
  109. package/lib/decisions/registry.mjs +5 -3
  110. package/lib/deck-export-pptx.mjs +1152 -0
  111. package/lib/demo-recording.mjs +142 -0
  112. package/lib/demo-script.mjs +114 -0
  113. package/lib/demo-surface.mjs +249 -0
  114. package/lib/demo.mjs +703 -0
  115. package/lib/diagram-export.mjs +192 -0
  116. package/lib/diagram.mjs +302 -0
  117. package/lib/docs-verify.mjs +25 -7
  118. package/lib/doctor/index.mjs +4 -2
  119. package/lib/doctor/project-adapters.mjs +44 -0
  120. package/lib/doctor/watchers/cx-budget.mjs +98 -0
  121. package/lib/doctor/watchers/graph-staleness.mjs +52 -0
  122. package/lib/doctor/watchers/process-pressure.mjs +4 -3
  123. package/lib/doctor/watchers/service-health.mjs +1 -1
  124. package/lib/document-export.mjs +360 -36
  125. package/lib/document-extract.mjs +88 -0
  126. package/lib/document-ingest.mjs +32 -18
  127. package/lib/embed/cli.mjs +2 -2
  128. package/lib/embed/daemon.mjs +18 -46
  129. package/lib/embed/semantic.mjs +5 -2
  130. package/lib/embed/worker.mjs +2 -2
  131. package/lib/embedded-contract/model-resolve.mjs +10 -9
  132. package/lib/env-config.mjs +26 -0
  133. package/lib/evals/dataset.mjs +137 -0
  134. package/lib/evals/gates.mjs +175 -0
  135. package/lib/graph/build-co-change.mjs +54 -0
  136. package/lib/graph/build-from-registry.mjs +136 -0
  137. package/lib/graph/build-import-graph.mjs +153 -0
  138. package/lib/graph/cli.mjs +110 -0
  139. package/lib/graph/impact-cli.mjs +89 -0
  140. package/lib/graph/impact.mjs +108 -0
  141. package/lib/graph/staleness.mjs +44 -0
  142. package/lib/graph/store.mjs +172 -0
  143. package/lib/handoffs/cleanup.mjs +1 -1
  144. package/lib/health-check.mjs +90 -76
  145. package/lib/hooks/artifact-release-gate.mjs +43 -0
  146. package/lib/hooks/audit-trail.mjs +14 -28
  147. package/lib/hooks/brand-prose-lint.mjs +38 -0
  148. package/lib/hooks/graph-impact-advisory.mjs +62 -0
  149. package/lib/hooks/session-optimize.mjs +84 -207
  150. package/lib/hooks/session-start.mjs +4 -5
  151. package/lib/host-disposition.mjs +7 -0
  152. package/lib/improvement/cli.mjs +189 -0
  153. package/lib/improvement/controller.mjs +137 -0
  154. package/lib/improvement/proposal.mjs +120 -0
  155. package/lib/improvement/specialist-loop.mjs +192 -0
  156. package/lib/improvement/store.mjs +89 -0
  157. package/lib/improvement/surface.mjs +219 -0
  158. package/lib/ingest-tooling.mjs +97 -0
  159. package/lib/init/doc-lanes.mjs +165 -0
  160. package/lib/init-docs.mjs +31 -178
  161. package/lib/init-unified.mjs +27 -97
  162. package/lib/init-update-guide.mjs +102 -0
  163. package/lib/init-update.mjs +32 -1
  164. package/lib/init.mjs +8 -0
  165. package/lib/install/desktop-binary-download.mjs +85 -0
  166. package/lib/install/legacy-global-cleanup.mjs +189 -0
  167. package/lib/intake/daemon.mjs +2 -0
  168. package/lib/intake/git-queue.mjs +9 -8
  169. package/lib/intake/queue.mjs +2 -1
  170. package/lib/intake/session-prelude.mjs +90 -1
  171. package/lib/libreoffice-export.mjs +97 -0
  172. package/lib/logging/rotate.mjs +9 -0
  173. package/lib/maintenance/docker-reclaim.mjs +206 -0
  174. package/lib/mcp/external-schema-cost.mjs +74 -0
  175. package/lib/mcp/server.mjs +84 -9
  176. package/lib/mcp/stdio-mcp-probe.mjs +188 -0
  177. package/lib/mcp/tool-budget.mjs +55 -4
  178. package/lib/mcp/tools/document.mjs +18 -4
  179. package/lib/mcp/tools/skills.mjs +32 -3
  180. package/lib/mcp/tools/workflow.mjs +13 -1
  181. package/lib/model-free-selector.mjs +18 -0
  182. package/lib/model-registry.mjs +13 -229
  183. package/lib/model-router.mjs +425 -93
  184. package/lib/models/behavior-matrix.mjs +289 -0
  185. package/lib/models/catalog.mjs +209 -0
  186. package/lib/models/execution-capability-profile.mjs +196 -0
  187. package/lib/models/execution-policy.mjs +307 -0
  188. package/lib/models/provider-poll.mjs +383 -0
  189. package/lib/npm-spawn-env.mjs +17 -0
  190. package/lib/ollama/installed-models.mjs +129 -0
  191. package/lib/oracle/actions.mjs +187 -0
  192. package/lib/oracle/artifact-gate.mjs +99 -0
  193. package/lib/oracle/cli.mjs +204 -0
  194. package/lib/oracle/daemon-entry.mjs +14 -0
  195. package/lib/oracle/dispatch.mjs +81 -0
  196. package/lib/oracle/execute.mjs +143 -0
  197. package/lib/oracle/gaps.mjs +76 -0
  198. package/lib/oracle/index.mjs +84 -0
  199. package/lib/oracle/issues.mjs +164 -0
  200. package/lib/oracle/org-graph.mjs +170 -0
  201. package/lib/oracle/policy.mjs +80 -0
  202. package/lib/oracle/read-model.mjs +398 -0
  203. package/lib/oracle/reconcile.mjs +191 -0
  204. package/lib/oracle/routing.mjs +89 -0
  205. package/lib/oracle/synthesize.mjs +384 -0
  206. package/lib/oracle/verdicts.mjs +51 -0
  207. package/lib/orchestration/worker.mjs +88 -27
  208. package/lib/orchestration-policy.mjs +50 -0
  209. package/lib/parity.mjs +96 -2
  210. package/lib/persona-sections.mjs +66 -0
  211. package/lib/playwright-demo.mjs +252 -0
  212. package/lib/project-init-shared.mjs +4 -1
  213. package/lib/prompt-composer.js +9 -4
  214. package/lib/prompt-validation-contract.mjs +24 -0
  215. package/lib/provider-capabilities.js +57 -11
  216. package/lib/providers/contract/adapters/confluence/index.mjs +181 -0
  217. package/lib/providers/contract/adapters/git/index.mjs +115 -0
  218. package/lib/providers/contract/adapters/github/index.mjs +166 -0
  219. package/lib/providers/contract/adapters/jira/index.mjs +187 -0
  220. package/lib/providers/contract/adapters/slack/index.mjs +175 -0
  221. package/lib/providers/contract/contract-tests.mjs +57 -0
  222. package/lib/providers/contract/errors.mjs +48 -0
  223. package/lib/providers/contract/interface.mjs +50 -0
  224. package/lib/providers/contract/registry.mjs +102 -0
  225. package/lib/providers/copilot-auth.mjs +297 -0
  226. package/lib/providers/credential-bootstrap.mjs +178 -0
  227. package/lib/providers/credential-catalog.mjs +46 -0
  228. package/lib/providers/credential-sources.mjs +63 -0
  229. package/lib/providers/creds.mjs +5 -2
  230. package/lib/providers/op-run.mjs +59 -0
  231. package/lib/providers/secret-resolver.mjs +159 -0
  232. package/lib/publish-template.mjs +163 -0
  233. package/lib/publish-tooling.mjs +119 -0
  234. package/lib/publish.mjs +305 -0
  235. package/lib/registry/cli.mjs +82 -0
  236. package/lib/registry/consolidation.mjs +147 -0
  237. package/lib/registry/generate-docs.mjs +75 -0
  238. package/lib/registry/skill-verification.mjs +57 -0
  239. package/lib/registry/surface-map.mjs +76 -0
  240. package/lib/registry/validate.mjs +135 -0
  241. package/lib/resources/budget.mjs +82 -0
  242. package/lib/resources/process-budget.mjs +45 -0
  243. package/lib/rules-delivery.mjs +9 -2
  244. package/lib/rules-read.mjs +26 -0
  245. package/lib/runtime-env.mjs +23 -0
  246. package/lib/runtime-pressure.mjs +51 -7
  247. package/lib/schema-infer.mjs +13 -2
  248. package/lib/server/chat-loop.mjs +622 -0
  249. package/lib/server/demo-preview.mjs +63 -0
  250. package/lib/server/index.mjs +229 -8
  251. package/lib/server/langfuse-login.mjs +3 -3
  252. package/lib/service-manager.mjs +61 -9
  253. package/lib/session-store.mjs +1 -1
  254. package/lib/setup.mjs +102 -2
  255. package/lib/specialists/prompt-schema.mjs +19 -10
  256. package/lib/specialists/roster.mjs +43 -0
  257. package/lib/specialists/scaffold.mjs +56 -0
  258. package/lib/storage/backend.mjs +6 -6
  259. package/lib/storage/embeddings-local.mjs +6 -3
  260. package/lib/storage/file-lock.mjs +18 -11
  261. package/lib/storage/hybrid-query.mjs +7 -4
  262. package/lib/storage/state-source.mjs +5 -5
  263. package/lib/storage/sync.mjs +2 -3
  264. package/lib/telemetry/rule-calls.mjs +52 -0
  265. package/lib/template-registry.mjs +2 -2
  266. package/lib/templates/visual-requirements.mjs +27 -51
  267. package/lib/test-corpus-inventory.mjs +313 -0
  268. package/lib/uninstall/uninstall.mjs +19 -7
  269. package/lib/update.mjs +12 -0
  270. package/lib/upgrade.mjs +14 -0
  271. package/lib/wireframe.mjs +20 -14
  272. package/lib/worker/run.mjs +17 -4
  273. package/lib/worker/trace.mjs +11 -3
  274. package/package.json +29 -13
  275. package/personas/construct.md +13 -13
  276. package/platforms/claude/settings.template.json +36 -0
  277. package/rules/common/patterns.md +1 -1
  278. package/rules/common/release-gates.md +4 -3
  279. package/scripts/sync-specialists.mjs +187 -60
  280. package/skills/devops/data-engineering.md +1 -1
  281. package/skills/docs/adr-workflow.md +1 -0
  282. package/skills/docs/backlog-proposal-workflow.md +1 -0
  283. package/skills/docs/codebase-research-workflow.md +40 -0
  284. package/skills/docs/customer-profile-workflow.md +1 -0
  285. package/skills/docs/document-ingest-workflow.md +1 -0
  286. package/skills/docs/evidence-ingest-workflow.md +1 -0
  287. package/skills/docs/init-docs.md +2 -2
  288. package/skills/docs/init-project.md +8 -3
  289. package/skills/docs/prd-workflow.md +24 -1
  290. package/skills/docs/prfaq-workflow.md +1 -0
  291. package/skills/docs/product-intelligence-workflow.md +1 -0
  292. package/skills/docs/product-signal-workflow.md +1 -0
  293. package/skills/docs/research-workflow.md +54 -37
  294. package/skills/docs/runbook-workflow.md +1 -0
  295. package/skills/docs/strategy-workflow.md +1 -0
  296. package/skills/docs/user-research-workflow.md +40 -0
  297. package/skills/operating/orchestration-reference.md +1 -1
  298. package/skills/roles/architect.md +5 -0
  299. package/skills/roles/operator.docs.md +4 -0
  300. package/skills/routing.md +4 -2
  301. package/specialists/artifact-manifest.json +480 -0
  302. package/specialists/artifact-manifest.schema.json +53 -0
  303. package/specialists/audit-enrichments.json +454 -0
  304. package/specialists/contracts.json +37 -25
  305. package/specialists/prompts/_shared/validation-contract.md +26 -0
  306. package/specialists/prompts/cx-accessibility.md +21 -2
  307. package/specialists/prompts/cx-ai-engineer.md +24 -1
  308. package/specialists/prompts/cx-architect.md +4 -1
  309. package/specialists/prompts/cx-business-strategist.md +23 -2
  310. package/specialists/prompts/cx-data-analyst.md +22 -1
  311. package/specialists/prompts/cx-data-engineer.md +23 -2
  312. package/specialists/prompts/cx-debugger.md +21 -2
  313. package/specialists/prompts/cx-designer.md +26 -3
  314. package/specialists/prompts/cx-devil-advocate.md +19 -2
  315. package/specialists/prompts/cx-docs-keeper.md +28 -1
  316. package/specialists/prompts/cx-engineer.md +22 -1
  317. package/specialists/prompts/cx-evaluator.md +18 -1
  318. package/specialists/prompts/cx-explorer.md +21 -2
  319. package/specialists/prompts/cx-legal-compliance.md +21 -2
  320. package/specialists/prompts/cx-operations.md +21 -2
  321. package/specialists/prompts/cx-oracle.md +94 -0
  322. package/specialists/prompts/cx-orchestrator.md +20 -1
  323. package/specialists/prompts/cx-platform-engineer.md +25 -2
  324. package/specialists/prompts/cx-product-manager.md +32 -1
  325. package/specialists/prompts/cx-qa.md +24 -2
  326. package/specialists/prompts/cx-rd-lead.md +23 -2
  327. package/specialists/prompts/cx-release-manager.md +23 -2
  328. package/specialists/prompts/cx-researcher.md +22 -2
  329. package/specialists/prompts/cx-reviewer.md +18 -1
  330. package/specialists/prompts/cx-security.md +22 -2
  331. package/specialists/prompts/cx-sre.md +30 -3
  332. package/specialists/prompts/cx-test-automation.md +7 -1
  333. package/specialists/prompts/cx-trace-reviewer.md +22 -3
  334. package/specialists/prompts/cx-ux-researcher.md +21 -2
  335. package/specialists/registry.json +52 -173
  336. package/specialists/tone-profiles.json +42 -0
  337. package/templates/demos/playwright/demo-recording.config.mjs +47 -0
  338. package/templates/demos/recordings/agentic-platforms-prd.json +19 -0
  339. package/templates/demos/scripts/agentic-platforms-prd.json +44 -0
  340. package/templates/demos/specs/_helpers/scroll-artifact.ts +89 -0
  341. package/templates/demos/tapes/agentic-platforms-prd.tape +49 -0
  342. package/templates/demos/tapes/resource-guard-rails.tape +49 -0
  343. package/templates/demos/vhs/construct-cockpit.json +24 -0
  344. package/templates/distribution/construct-brand.typ +446 -0
  345. package/templates/distribution/construct-decision.typ +38 -0
  346. package/templates/distribution/construct-deck.html +95 -0
  347. package/templates/distribution/construct-pdf.typ +38 -0
  348. package/templates/distribution/construct-prd.typ +38 -0
  349. package/templates/distribution/construct-research.typ +38 -0
  350. package/templates/distribution/construct-web.html +92 -0
  351. package/templates/distribution/fonts/Geist-Bold.ttf +0 -0
  352. package/templates/distribution/fonts/Geist-Medium.ttf +0 -0
  353. package/templates/distribution/fonts/Geist-Regular.ttf +0 -0
  354. package/templates/distribution/fonts/Geist-SemiBold.ttf +0 -0
  355. package/templates/distribution/fonts/GeistMono-Medium.ttf +0 -0
  356. package/templates/distribution/fonts/GeistMono-Regular.ttf +0 -0
  357. package/templates/distribution/fonts/GeistMono-SemiBold.ttf +0 -0
  358. package/templates/distribution/fonts/IBMPlexMono-Regular.otf +0 -0
  359. package/templates/distribution/fonts/JetBrainsMono-Medium.ttf +0 -0
  360. package/templates/distribution/fonts/JetBrainsMono-Regular.ttf +0 -0
  361. package/templates/distribution/fonts/JetBrainsMono-SemiBold.ttf +0 -0
  362. package/templates/distribution/fonts/PlusJakartaSans-Bold.ttf +0 -0
  363. package/templates/distribution/fonts/PlusJakartaSans-Medium.ttf +0 -0
  364. package/templates/distribution/fonts/PlusJakartaSans-Regular.ttf +0 -0
  365. package/templates/distribution/fonts/PlusJakartaSans-SemiBold.ttf +0 -0
  366. package/templates/distribution/fonts/README.md +36 -0
  367. package/templates/distribution/fonts/SpaceGrotesk-Variable.ttf +0 -0
  368. package/templates/distribution/fonts/handwritten/Caveat.ttf +0 -0
  369. package/templates/distribution/fonts/legacy/IBMPlexMono-Regular.otf +0 -0
  370. package/templates/distribution/fonts/legacy/Inter-Medium.otf +0 -0
  371. package/templates/distribution/fonts/legacy/Inter-Regular.otf +0 -0
  372. package/templates/distribution/fonts/legacy/Inter-SemiBold.otf +0 -0
  373. package/templates/distribution/fonts/legacy/InterDisplay-SemiBold.otf +0 -0
  374. package/templates/distribution/fonts/legacy/SourceSerif4-Regular.otf +0 -0
  375. package/templates/distribution/fonts/legacy/SourceSerif4-Semibold.otf +0 -0
  376. package/templates/distribution/icons/activity.svg +15 -0
  377. package/templates/distribution/icons/alert-triangle.svg +17 -0
  378. package/templates/distribution/icons/book-open.svg +16 -0
  379. package/templates/distribution/icons/bot.svg +20 -0
  380. package/templates/distribution/icons/brain.svg +22 -0
  381. package/templates/distribution/icons/circle-check.svg +16 -0
  382. package/templates/distribution/icons/clipboard-check.svg +17 -0
  383. package/templates/distribution/icons/cpu.svg +28 -0
  384. package/templates/distribution/icons/database.svg +17 -0
  385. package/templates/distribution/icons/eye.svg +16 -0
  386. package/templates/distribution/icons/file-text.svg +19 -0
  387. package/templates/distribution/icons/gauge.svg +16 -0
  388. package/templates/distribution/icons/git-branch.svg +17 -0
  389. package/templates/distribution/icons/key.svg +17 -0
  390. package/templates/distribution/icons/layers.svg +17 -0
  391. package/templates/distribution/icons/list-checks.svg +19 -0
  392. package/templates/distribution/icons/lock.svg +16 -0
  393. package/templates/distribution/icons/message-square.svg +15 -0
  394. package/templates/distribution/icons/network.svg +19 -0
  395. package/templates/distribution/icons/route.svg +17 -0
  396. package/templates/distribution/icons/scale.svg +19 -0
  397. package/templates/distribution/icons/search.svg +16 -0
  398. package/templates/distribution/icons/send.svg +16 -0
  399. package/templates/distribution/icons/server.svg +18 -0
  400. package/templates/distribution/icons/shield-check.svg +16 -0
  401. package/templates/distribution/icons/users.svg +18 -0
  402. package/templates/distribution/icons/webhook.svg +17 -0
  403. package/templates/distribution/icons/workflow.svg +17 -0
  404. package/templates/distribution/icons/wrench.svg +15 -0
  405. package/templates/distribution/run.mjs +18 -1
  406. package/templates/docs/adr.md +7 -0
  407. package/templates/docs/construct_guide.md +10 -10
  408. package/templates/docs/customer-profile.md +4 -0
  409. package/templates/docs/one-pager.md +4 -0
  410. package/templates/docs/postmortem.md +31 -0
  411. package/templates/docs/prd-platform.md +19 -0
  412. package/templates/docs/prd.md +15 -0
  413. package/templates/docs/prfaq.md +7 -0
  414. package/templates/docs/qa-strategy.md +103 -0
  415. package/templates/docs/research-brief.md +8 -0
  416. package/templates/docs/rfc-platform.md +12 -0
  417. package/templates/docs/test-plan.md +7 -0
  418. package/lib/ingest/chunker.mjs +0 -94
  419. package/lib/ingest/pipeline.mjs +0 -53
  420. package/lib/ingest/store.mjs +0 -82
  421. package/lib/mode-commands.mjs +0 -122
  422. package/lib/policy/unified-gates.mjs +0 -96
  423. package/lib/profiles/validate-custom.mjs +0 -114
  424. package/lib/services/telemetry-backend.mjs +0 -177
  425. package/lib/storage/fusion.mjs +0 -95
@@ -0,0 +1,196 @@
1
+ /**
2
+ * lib/models/execution-capability-profile.mjs — the single resolved capability
3
+ * record (construct-6zga.1.8).
4
+ *
5
+ * Consolidates the four formerly-scattered capability producers into one
6
+ * serializable, versioned record that chat (resolveCapabilityTier) and specialist
7
+ * prompt composition (resolveModelOperatingProfile via lib/prompt-composer.js) both
8
+ * consume. It is keyed by provider, requested/resolved model, adapter protocol, and
9
+ * observation time. Every capability declares an evidence source
10
+ * (provider_metadata | live_probe | operator_override | compatibility_fallback |
11
+ * unknown) and a confidence, so a reader can tell measured truth from a name/size
12
+ * guess. Unknowns compile to conservative defaults and set a degraded flag for
13
+ * telemetry.
14
+ *
15
+ * Provenance, not behavior change: this bead unifies the source of truth and
16
+ * exposes measured evidence (provider metadata, the Ollama agentic-coherence
17
+ * probe). The derived capabilityTier / operatingProfile still equal the existing
18
+ * name/size heuristics (tagged compatibility_fallback) so live behavior is
19
+ * preserved; construct-6zga.1.2 (capability-adaptive policy) is where measured
20
+ * evidence will supersede the heuristic. Compatibility heuristics stay isolated in
21
+ * model-router's resolveCapabilityTier / resolveModelOperatingProfile and are
22
+ * documented there as the temporary fallback.
23
+ *
24
+ * Reference shape: schemas/execution-capability-profile.schema.json.
25
+ */
26
+ import { readFileSync } from 'node:fs';
27
+ import { join } from 'node:path';
28
+ import { homedir } from 'node:os';
29
+
30
+ import { resolveProviderCapabilitiesSync } from '../provider-capabilities.js';
31
+ import { getModelVerdict } from '../ollama/capability-store.mjs';
32
+ import { isLocalModel } from '../mcp/tool-budget.mjs';
33
+ import { transportForProviderGroup, classifyCapabilityClass } from './behavior-matrix.mjs';
34
+ import { resolveCapabilityTier, resolveModelOperatingProfile } from '../model-router.mjs';
35
+
36
+ export const EXECUTION_PROFILE_SCHEMA_VERSION = 1;
37
+
38
+ export const PROFILE_EVIDENCE_SOURCES = Object.freeze([
39
+ 'provider_metadata',
40
+ 'live_probe',
41
+ 'operator_override',
42
+ 'compatibility_fallback',
43
+ 'unknown',
44
+ ]);
45
+
46
+ const CONFIDENCE_LEVELS = Object.freeze(['high', 'medium', 'low', 'none']);
47
+
48
+ const OVERRIDES_FILENAME = 'capability-overrides.json';
49
+
50
+ function field(value, source, confidence) {
51
+ return Object.freeze({ value, source, confidence });
52
+ }
53
+
54
+ export function providerGroupForModel(model) {
55
+ const seg = String(model || '').split('/')[0];
56
+ return seg || 'unknown';
57
+ }
58
+
59
+ // Adapter protocol mirrors the dispatch in apps/chat/engine/ai-sdk-agent.mjs:
60
+ // Anthropic uses its native messages API, OpenAI and Copilot use chat completions,
61
+ // and every other provider goes through the OpenAI-compatible shim.
62
+
63
+ function adapterProtocolForGroup(group) {
64
+ if (group === 'anthropic') return 'anthropic-messages';
65
+ if (group === 'openai' || group === 'github-copilot') return 'openai-chat-completions';
66
+ return 'openai-compatible';
67
+ }
68
+
69
+ // Mirrors resolveAdapterKey in lib/provider-capabilities.js: a model that maps to a
70
+ // vendor adapter carries that vendor's declared metadata; anything else falls to the
71
+ // generic default, which is a guess, not provider metadata.
72
+
73
+ function hasVendorAdapter(modelId) {
74
+ const id = String(modelId || '').toLowerCase();
75
+ if (/^anthropic\//.test(id) || /^openrouter\/anthropic\//.test(id)) return true;
76
+ if (/^google\//.test(id) || /^openrouter\/google\//.test(id)) return true;
77
+ if (/^openai\//.test(id) || /^openrouter\/openai\//.test(id) || /^github-copilot\//.test(id)) return true;
78
+ if (/^deepseek\//.test(id) || /^openrouter\/deepseek\//.test(id)) return true;
79
+ return false;
80
+ }
81
+
82
+ function loadOverridesFile(homeDir) {
83
+ try {
84
+ const raw = JSON.parse(readFileSync(join(homeDir, '.cx', OVERRIDES_FILENAME), 'utf8'));
85
+ if (raw && typeof raw === 'object' && raw.models && typeof raw.models === 'object') return raw.models;
86
+ } catch { /* absent or malformed — no overrides */ }
87
+ return {};
88
+ }
89
+
90
+ // The Ollama agentic-coherence store is keyed by the native model id (no ollama/
91
+ // prefix), the same transform bin/construct uses before isKnownCollapsed.
92
+
93
+ function localVerdict(model) {
94
+ if (!isLocalModel(model)) return null;
95
+ const native = String(model).replace(/^ollama\//, '');
96
+ return getModelVerdict(native);
97
+ }
98
+
99
+ /**
100
+ * Resolve the execution capability profile for a model. Synchronous and
101
+ * side-effect free: it reads cached/declared evidence only — live probing is the
102
+ * opt-in job of lib/models/behavior-matrix.mjs, never this resolver.
103
+ */
104
+ export function resolveExecutionCapabilityProfile({
105
+ model,
106
+ resolvedModel = null,
107
+ envValues = {},
108
+ overrides = null,
109
+ homeDir = homedir(),
110
+ now = () => new Date().toISOString(),
111
+ } = {}) {
112
+ const requestedModel = model || null;
113
+ const group = providerGroupForModel(model);
114
+ const transport = transportForProviderGroup(group);
115
+ const overrideMap = (overrides ?? loadOverridesFile(homeDir))[requestedModel] || {};
116
+
117
+ const vendor = hasVendorAdapter(model);
118
+ const providerCaps = resolveProviderCapabilitiesSync(model);
119
+ const capsSource = vendor ? 'provider_metadata' : 'compatibility_fallback';
120
+ const capsConfidence = vendor ? 'medium' : 'low';
121
+
122
+ const verdict = localVerdict(model);
123
+ const coherent = verdict ? verdict.coherent === true : null;
124
+
125
+ const measured = {
126
+ toolsKnown: verdict ? verdict.calledTool === true : false,
127
+ tools: verdict ? verdict.calledTool === true : false,
128
+ coherent,
129
+ };
130
+ const capabilityClass = classifyCapabilityClass({ transport, measured });
131
+
132
+ const tierFallback = resolveCapabilityTier({ model });
133
+ const operatingProfileFallback = resolveModelOperatingProfile({ envValues, selectedModel: model });
134
+
135
+ const apply = (key, fallbackField) => {
136
+ if (Object.prototype.hasOwnProperty.call(overrideMap, key)) {
137
+ return field(overrideMap[key], 'operator_override', 'high');
138
+ }
139
+ return fallbackField;
140
+ };
141
+
142
+ const capabilities = {
143
+ contextWindow: apply('contextWindow', field(providerCaps.maxContextWindow ?? null, capsSource, capsConfidence)),
144
+ structuredOutput: apply('structuredOutput', field(providerCaps.structuredOutput === true, capsSource, capsConfidence)),
145
+ cacheControl: apply('cacheControl', field(providerCaps.cacheControl === true, capsSource, capsConfidence)),
146
+ tokenRatio: apply('tokenRatio', field(providerCaps.tokenRatio ?? null, capsSource, capsConfidence)),
147
+ agenticCoherence: apply('agenticCoherence', verdict
148
+ ? field(verdict.coherent ? 'COHERENT' : 'COLLAPSED', 'live_probe', 'medium')
149
+ : field(null, 'unknown', 'none')),
150
+ capabilityTier: apply('capabilityTier', field(tierFallback, 'compatibility_fallback', 'low')),
151
+ operatingProfileId: apply('operatingProfileId', field(operatingProfileFallback.id, 'compatibility_fallback', 'low')),
152
+ };
153
+
154
+ const evidenceSources = [...new Set(Object.values(capabilities).map((f) => f.source))];
155
+ const hasMeasured = evidenceSources.some((s) => s === 'provider_metadata' || s === 'live_probe' || s === 'operator_override');
156
+ const degraded = capabilityClass === 'unknown' || !hasMeasured;
157
+
158
+ return Object.freeze({
159
+ schemaVersion: EXECUTION_PROFILE_SCHEMA_VERSION,
160
+ key: Object.freeze({
161
+ provider: group,
162
+ requestedModel,
163
+ resolvedModel: resolvedModel || requestedModel,
164
+ adapterProtocol: adapterProtocolForGroup(group),
165
+ observedAt: now(),
166
+ }),
167
+ capabilityClass,
168
+ transport,
169
+ capabilities: Object.freeze(capabilities),
170
+ degraded,
171
+ evidenceSources: Object.freeze(evidenceSources),
172
+ });
173
+ }
174
+
175
+ export function capabilityTierFromProfile(profile) {
176
+ return profile?.capabilities?.capabilityTier?.value ?? 'full';
177
+ }
178
+
179
+ export function operatingProfileIdFromProfile(profile) {
180
+ return profile?.capabilities?.operatingProfileId?.value ?? 'balanced';
181
+ }
182
+
183
+ export function validateExecutionCapabilityProfile(profile) {
184
+ const errors = [];
185
+ if (!profile || typeof profile !== 'object') return { valid: false, errors: ['profile is not an object'] };
186
+ if (profile.schemaVersion !== EXECUTION_PROFILE_SCHEMA_VERSION) errors.push(`schemaVersion must be ${EXECUTION_PROFILE_SCHEMA_VERSION}`);
187
+ if (!profile.key || typeof profile.key !== 'object') errors.push('key missing');
188
+ if (typeof profile.degraded !== 'boolean') errors.push('degraded must be boolean');
189
+ if (!Array.isArray(profile.evidenceSources)) errors.push('evidenceSources must be an array');
190
+ for (const [name, f] of Object.entries(profile.capabilities || {})) {
191
+ if (!f || typeof f !== 'object') { errors.push(`capabilities.${name} not a field`); continue; }
192
+ if (!PROFILE_EVIDENCE_SOURCES.includes(f.source)) errors.push(`capabilities.${name}.source invalid: ${f.source}`);
193
+ if (!CONFIDENCE_LEVELS.includes(f.confidence)) errors.push(`capabilities.${name}.confidence invalid: ${f.confidence}`);
194
+ }
195
+ return { valid: errors.length === 0, errors };
196
+ }
@@ -0,0 +1,307 @@
1
+ /**
2
+ * lib/models/execution-policy.mjs — the immutable per-turn execution policy
3
+ * compiler (construct-6zga.1.2).
4
+ *
5
+ * Compiles one frozen, traceable policy from an ExecutionCapabilityProfile
6
+ * (construct-6zga.1.8) plus the turn's intent, task risk, and evidence
7
+ * requirement. The policy is the single record the owned loop reads for prompt
8
+ * section budgets, tool-schema and tool-group limits, tool-iteration caps, the
9
+ * evidence-first requirement, output budget, visible-thinking policy, the
10
+ * continuation/compaction trigger, and caching eligibility.
11
+ *
12
+ * Capability-driven, never name-driven: every control branches on the profile's
13
+ * measured capability class, transport, and capability values plus the
14
+ * normalized turn inputs — never on a provider or model-name string
15
+ * (construct-6zga.1.2 AC4). A missing, unknown, or degraded profile collapses to
16
+ * the conservative envelope and emits degraded-mode telemetry (AC3). The record
17
+ * is frozen and JSON-serializable, and carries the profile key and turn inputs it
18
+ * was derived from so a reader can re-verify it (AC1). Reference shape:
19
+ * schemas/execution-policy.schema.json.
20
+ */
21
+ import { MODEL_OPERATING_PROFILES } from '../model-router.mjs';
22
+ import { capabilityTierFromProfile, operatingProfileIdFromProfile } from './execution-capability-profile.mjs';
23
+
24
+ export const EXECUTION_POLICY_SCHEMA_VERSION = 1;
25
+
26
+ export const POLICY_INTENTS = Object.freeze([
27
+ 'repository-summary',
28
+ 'code-change',
29
+ 'research',
30
+ 'tool-failure',
31
+ 'general',
32
+ ]);
33
+
34
+ export const POLICY_RISK_LEVELS = Object.freeze(['low', 'medium', 'high']);
35
+
36
+ export const POLICY_EVIDENCE_REQUIREMENTS = Object.freeze(['none', 'preferred', 'required']);
37
+
38
+ export const POLICY_TOOL_GROUPS = Object.freeze(['read', 'search', 'edit', 'shell', 'construct', 'heavy-mcp']);
39
+
40
+ export const POLICY_THINKING_MODES = Object.freeze(['hidden', 'summary', 'full']);
41
+
42
+ const KNOWN_CAPABILITY_CLASSES = Object.freeze(new Set([
43
+ 'hosted-direct',
44
+ 'hosted-routed',
45
+ 'local-capable',
46
+ 'local-constrained',
47
+ 'unknown',
48
+ ]));
49
+
50
+ // The capability class fixes the base execution envelope: how many tool schemas
51
+ // a model holds without overload, how many tool iterations it sustains
52
+ // coherently, how much output to budget, and whether visible reasoning helps or
53
+ // (for small local models that echo it back) hurts. Hosted-direct is the most
54
+ // capable; unknown is the most conservative. Turn intent and risk adjust these.
55
+
56
+ const CLASS_ENVELOPE = Object.freeze({
57
+ 'hosted-direct': Object.freeze({ toolTier: 'rich', maxToolIterations: 16, outputTokenBudget: 8000, visibleThinking: 'summary' }),
58
+ 'hosted-routed': Object.freeze({ toolTier: 'rich', maxToolIterations: 14, outputTokenBudget: 6000, visibleThinking: 'summary' }),
59
+ 'local-capable': Object.freeze({ toolTier: 'standard', maxToolIterations: 10, outputTokenBudget: 4000, visibleThinking: 'hidden' }),
60
+ 'local-constrained': Object.freeze({ toolTier: 'minimal', maxToolIterations: 6, outputTokenBudget: 2000, visibleThinking: 'hidden' }),
61
+ unknown: Object.freeze({ toolTier: 'minimal', maxToolIterations: 4, outputTokenBudget: 1500, visibleThinking: 'hidden' }),
62
+ });
63
+
64
+ const TOOL_TIER_SCHEMA_CAP = Object.freeze({ rich: 32, standard: 16, minimal: 8 });
65
+
66
+ const TOOL_TIER_GROUPS = Object.freeze({
67
+ rich: Object.freeze(['read', 'search', 'edit', 'shell', 'construct', 'heavy-mcp']),
68
+ standard: Object.freeze(['read', 'search', 'edit', 'construct']),
69
+ minimal: Object.freeze(['read', 'search', 'construct']),
70
+ });
71
+
72
+ const INTENT_EVIDENCE_FIRST = Object.freeze(new Set(['repository-summary', 'research', 'code-change']));
73
+ const INTENT_CITATIONS_REQUIRED = Object.freeze(new Set(['repository-summary', 'research']));
74
+
75
+ function clampInt(value, min, max) {
76
+ const n = Number.isFinite(value) ? Math.floor(value) : min;
77
+ return Math.max(min, Math.min(max, n));
78
+ }
79
+
80
+ function oneOf(allowed, value, fallback) {
81
+ return allowed.includes(value) ? value : fallback;
82
+ }
83
+
84
+ // The chat turn overlay (lib/chat/transparency.mjs) speaks the orchestration
85
+ // vocabulary (INTENT_CLASSES, risk flags, externalResearch). These helpers map it
86
+ // onto the compiler's stable input enums so the compiler stays decoupled from
87
+ // routing changes. A turn that follows a failed tool result is the recovery case
88
+ // the loop signals with toolFailure.
89
+
90
+ export function normalizePolicyIntent({ intent = null, assumptionsBlocked = false, toolFailure = false } = {}) {
91
+ if (toolFailure === true) return 'tool-failure';
92
+ const i = String(intent || '').toLowerCase();
93
+ if (assumptionsBlocked === true && (i === '' || i === 'research' || i === 'investigation')) return 'repository-summary';
94
+ if (i === 'investigation') return 'repository-summary';
95
+ if (i === 'implementation' || i === 'fix') return 'code-change';
96
+ if (i === 'research' || i === 'evaluation') return 'research';
97
+ return 'general';
98
+ }
99
+
100
+ export function normalizePolicyRisk(riskFlags = {}) {
101
+ const f = riskFlags && typeof riskFlags === 'object' ? riskFlags : {};
102
+ if (f.security === true || f.dataIntegrity === true) return 'high';
103
+ if (f.architecture === true || f.ai === true) return 'medium';
104
+ return 'low';
105
+ }
106
+
107
+ export function normalizeEvidenceRequirement({ externalResearch = null, assumptionsBlocked = false } = {}) {
108
+ if (externalResearch?.required === true || assumptionsBlocked === true) return 'required';
109
+ return 'none';
110
+ }
111
+
112
+ /**
113
+ * Compile the immutable per-turn execution policy. Branches only on the profile's
114
+ * capability class, transport, and capability values plus the normalized intent,
115
+ * risk, and evidence requirement — never on a model or provider name (AC4).
116
+ */
117
+ export function compileExecutionPolicy({
118
+ profile = null,
119
+ intent = 'general',
120
+ risk = 'low',
121
+ evidenceRequirement = 'none',
122
+ } = {}) {
123
+ const safeProfile = profile && typeof profile === 'object' ? profile : null;
124
+ const capabilityClass = KNOWN_CAPABILITY_CLASSES.has(safeProfile?.capabilityClass)
125
+ ? safeProfile.capabilityClass
126
+ : 'unknown';
127
+ const transport = typeof safeProfile?.transport === 'string' ? safeProfile.transport : 'unknown';
128
+
129
+ const normIntent = oneOf(POLICY_INTENTS, intent, 'general');
130
+ const normRisk = oneOf(POLICY_RISK_LEVELS, risk, 'low');
131
+ const normEvidence = oneOf(POLICY_EVIDENCE_REQUIREMENTS, evidenceRequirement, 'none');
132
+
133
+ const degradeReasons = [];
134
+ if (!safeProfile) degradeReasons.push('profile-missing');
135
+ if (capabilityClass === 'unknown') degradeReasons.push('capability-class-unknown');
136
+ if (safeProfile?.degraded === true) degradeReasons.push('profile-degraded');
137
+ const degraded = degradeReasons.length > 0;
138
+
139
+ const envelope = CLASS_ENVELOPE[capabilityClass];
140
+
141
+ // A degraded or unmeasured record must not unlock rich tool budgets, so it
142
+ // collapses to the minimal tier regardless of its nominal class.
143
+
144
+ const effectiveToolTier = degraded ? 'minimal' : envelope.toolTier;
145
+
146
+ let maxToolSchemas = TOOL_TIER_SCHEMA_CAP[effectiveToolTier];
147
+ let maxToolIterations = envelope.maxToolIterations;
148
+ if (normIntent === 'code-change') maxToolIterations += 4;
149
+ else if (normIntent === 'research') maxToolIterations += 2;
150
+ else if (normIntent === 'tool-failure') {
151
+ maxToolIterations = clampInt(maxToolIterations / 2, 2, maxToolIterations);
152
+ maxToolSchemas = Math.min(maxToolSchemas, 6);
153
+ }
154
+
155
+ const groups = new Set(TOOL_TIER_GROUPS[effectiveToolTier]);
156
+ if (normIntent === 'repository-summary' || normIntent === 'research') {
157
+ groups.delete('edit');
158
+ groups.delete('shell');
159
+ }
160
+ if (normIntent === 'tool-failure') groups.delete('heavy-mcp');
161
+ const allowedToolGroups = POLICY_TOOL_GROUPS.filter((g) => groups.has(g));
162
+
163
+ const evidenceFirst = degraded
164
+ || normRisk === 'high'
165
+ || normEvidence !== 'none'
166
+ || INTENT_EVIDENCE_FIRST.has(normIntent);
167
+ const citationsRequired = normEvidence === 'required'
168
+ || normRisk === 'high'
169
+ || INTENT_CITATIONS_REQUIRED.has(normIntent);
170
+
171
+ let visibleThinking = degraded ? 'hidden' : envelope.visibleThinking;
172
+ if (!degraded && normRisk === 'high' && visibleThinking === 'hidden') visibleThinking = 'summary';
173
+
174
+ const cacheCapable = safeProfile?.capabilities?.cacheControl?.value === true;
175
+ const cachingEligible = cacheCapable && !degraded;
176
+
177
+ const contextWindow = Number(safeProfile?.capabilities?.contextWindow?.value);
178
+ const hasContextWindow = Number.isFinite(contextWindow) && contextWindow > 0;
179
+ const compactionTriggerRatio = degraded ? 0.5 : 0.75;
180
+ const compactionTriggerTokens = hasContextWindow ? Math.floor(contextWindow * compactionTriggerRatio) : null;
181
+
182
+ const resolvedOperatingProfileId = operatingProfileIdFromProfile(safeProfile);
183
+ const operatingProfileId = MODEL_OPERATING_PROFILES[resolvedOperatingProfileId] ? resolvedOperatingProfileId : 'balanced';
184
+ const op = MODEL_OPERATING_PROFILES[operatingProfileId];
185
+
186
+ const policy = {
187
+ schemaVersion: EXECUTION_POLICY_SCHEMA_VERSION,
188
+ source: Object.freeze({
189
+ profileSchemaVersion: safeProfile?.schemaVersion ?? null,
190
+ profileKey: safeProfile?.key ? Object.freeze({ ...safeProfile.key }) : null,
191
+ capabilityClass,
192
+ transport,
193
+ intent: normIntent,
194
+ risk: normRisk,
195
+ evidenceRequirement: normEvidence,
196
+ evidenceSources: Object.freeze([...(Array.isArray(safeProfile?.evidenceSources) ? safeProfile.evidenceSources : [])]),
197
+ }),
198
+ prompt: Object.freeze({
199
+ systemPromptTier: capabilityTierFromProfile(safeProfile),
200
+ operatingProfileId,
201
+ sectionBudgets: Object.freeze({
202
+ maxPromptTokens: op.maxPromptTokens,
203
+ learnedPatternsTokens: op.learnedPatternsTokens,
204
+ taskPacketTokens: op.taskPacketTokens,
205
+ contextDigestTokens: op.contextDigestTokens,
206
+ hostConstraintsTokens: op.hostConstraintsTokens,
207
+ roleFlavorTokens: op.roleFlavorTokens,
208
+ }),
209
+ retrievalFirst: op.retrievalFirst === true,
210
+ preferCompressedGuidance: op.preferCompressedRoleGuidance === true,
211
+ }),
212
+ tools: Object.freeze({
213
+ maxToolSchemas,
214
+ allowedToolGroups: Object.freeze(allowedToolGroups),
215
+ maxToolIterations,
216
+ }),
217
+ evidence: Object.freeze({
218
+ evidenceFirst,
219
+ citationsRequired,
220
+ }),
221
+ output: Object.freeze({
222
+ outputTokenBudget: envelope.outputTokenBudget,
223
+ visibleThinking,
224
+ }),
225
+ continuation: Object.freeze({
226
+ compactionTriggerRatio,
227
+ compactionTriggerTokens,
228
+ }),
229
+ caching: Object.freeze({
230
+ eligible: cachingEligible,
231
+ scope: cachingEligible ? 'stable-prefix' : 'none',
232
+ }),
233
+ degradedMode: degraded,
234
+ telemetry: Object.freeze({
235
+ degraded,
236
+ reasons: Object.freeze(degradeReasons),
237
+ }),
238
+ };
239
+ return Object.freeze(policy);
240
+ }
241
+
242
+ /**
243
+ * Compile a policy directly from a chat turn overlay (lib/chat/transparency.mjs),
244
+ * normalizing its routing vocabulary into the compiler's input enums first.
245
+ */
246
+ export function compilePolicyFromOverlay({ profile = null, overlay = null, toolFailure = false } = {}) {
247
+ const intent = normalizePolicyIntent({
248
+ intent: overlay?.intent,
249
+ assumptionsBlocked: overlay?.assumptionsBlocked === true,
250
+ toolFailure,
251
+ });
252
+ const risk = normalizePolicyRisk(overlay?.riskFlags);
253
+ const evidenceRequirement = normalizeEvidenceRequirement({
254
+ externalResearch: overlay?.externalResearch,
255
+ assumptionsBlocked: overlay?.assumptionsBlocked === true,
256
+ });
257
+ return compileExecutionPolicy({ profile, intent, risk, evidenceRequirement });
258
+ }
259
+
260
+ /**
261
+ * Hand-rolled validator (no ajv — Construct stays dependency-free at startup).
262
+ * Returns { valid, errors } against schemas/execution-policy.schema.json.
263
+ */
264
+ export function validateExecutionPolicy(policy) {
265
+ const errors = [];
266
+ if (!policy || typeof policy !== 'object') return { valid: false, errors: ['policy is not an object'] };
267
+ if (policy.schemaVersion !== EXECUTION_POLICY_SCHEMA_VERSION) errors.push(`schemaVersion must be ${EXECUTION_POLICY_SCHEMA_VERSION}`);
268
+
269
+ const s = policy.source;
270
+ if (!s || typeof s !== 'object') errors.push('source missing');
271
+ else {
272
+ if (!KNOWN_CAPABILITY_CLASSES.has(s.capabilityClass)) errors.push(`source.capabilityClass invalid: ${s.capabilityClass}`);
273
+ if (!POLICY_INTENTS.includes(s.intent)) errors.push(`source.intent invalid: ${s.intent}`);
274
+ if (!POLICY_RISK_LEVELS.includes(s.risk)) errors.push(`source.risk invalid: ${s.risk}`);
275
+ if (!POLICY_EVIDENCE_REQUIREMENTS.includes(s.evidenceRequirement)) errors.push(`source.evidenceRequirement invalid: ${s.evidenceRequirement}`);
276
+ }
277
+
278
+ const t = policy.tools;
279
+ if (!t || typeof t !== 'object') errors.push('tools missing');
280
+ else {
281
+ if (!Number.isInteger(t.maxToolSchemas) || t.maxToolSchemas < 0) errors.push('tools.maxToolSchemas invalid');
282
+ if (!Number.isInteger(t.maxToolIterations) || t.maxToolIterations < 1) errors.push('tools.maxToolIterations invalid');
283
+ if (!Array.isArray(t.allowedToolGroups) || t.allowedToolGroups.some((g) => !POLICY_TOOL_GROUPS.includes(g))) errors.push('tools.allowedToolGroups invalid');
284
+ }
285
+
286
+ const o = policy.output;
287
+ if (!o || typeof o !== 'object') errors.push('output missing');
288
+ else {
289
+ if (!Number.isInteger(o.outputTokenBudget) || o.outputTokenBudget < 1) errors.push('output.outputTokenBudget invalid');
290
+ if (!POLICY_THINKING_MODES.includes(o.visibleThinking)) errors.push(`output.visibleThinking invalid: ${o.visibleThinking}`);
291
+ }
292
+
293
+ const e = policy.evidence;
294
+ if (!e || typeof e !== 'object') errors.push('evidence missing');
295
+ else {
296
+ if (typeof e.evidenceFirst !== 'boolean') errors.push('evidence.evidenceFirst must be boolean');
297
+ if (typeof e.citationsRequired !== 'boolean') errors.push('evidence.citationsRequired must be boolean');
298
+ }
299
+
300
+ if (!policy.caching || typeof policy.caching !== 'object') errors.push('caching missing');
301
+ else if (typeof policy.caching.eligible !== 'boolean') errors.push('caching.eligible must be boolean');
302
+
303
+ if (typeof policy.degradedMode !== 'boolean') errors.push('degradedMode must be boolean');
304
+ if (!policy.telemetry || typeof policy.telemetry !== 'object' || typeof policy.telemetry.degraded !== 'boolean') errors.push('telemetry.degraded must be boolean');
305
+
306
+ return { valid: errors.length === 0, errors };
307
+ }