@moretea-labs/forge 1.7.2 → 1.8.1

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 (900) hide show
  1. package/AGENTS.md +121 -62
  2. package/CHANGELOG.md +40 -0
  3. package/CLAUDE.md +57 -62
  4. package/CONTRIBUTING.md +3 -2
  5. package/README.md +2 -2
  6. package/SKILL.md +0 -1
  7. package/THIRD_PARTY_NOTICES.md +5 -2
  8. package/adapters/browser/index.ts +2 -0
  9. package/adapters/chatgpt/browser-delivery-host.ts +77 -0
  10. package/adapters/chatgpt/browser-delivery-runtime.ts +1256 -0
  11. package/adapters/chatgpt/controller-binding-store.ts +134 -0
  12. package/adapters/chatgpt/controller-round-host.ts +68 -0
  13. package/adapters/chatgpt/controller-round-settlement-store.ts +99 -0
  14. package/adapters/chatgpt/provider-delivery.ts +340 -0
  15. package/adapters/chatgpt/work-conversation-binding-store.ts +218 -0
  16. package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
  17. package/adapters/computer/chatgpt-extension-target.ts +463 -0
  18. package/adapters/computer/chatgpt-target-router.ts +103 -0
  19. package/adapters/computer/chrome-extension/background.js +385 -0
  20. package/adapters/computer/chrome-extension/content.js +378 -0
  21. package/adapters/computer/chrome-extension/core.js +24 -0
  22. package/adapters/computer/chrome-extension/manifest.json +30 -0
  23. package/adapters/computer/desktop-operator-contract.ts +7 -0
  24. package/adapters/computer/desktop-operator-discovery.ts +181 -0
  25. package/adapters/computer/desktop-operator-negotiation.ts +174 -0
  26. package/adapters/computer/desktop-operator-provider.ts +312 -0
  27. package/adapters/computer/index.ts +1 -0
  28. package/adapters/computer/interaction-target-authority.ts +1068 -0
  29. package/adapters/controller-process/binding-store.ts +34 -0
  30. package/adapters/controller-process/controller-host.ts +26 -0
  31. package/adapters/mcp/access-mode.ts +132 -0
  32. package/adapters/mcp/audit.ts +32 -0
  33. package/adapters/mcp/auth.ts +406 -0
  34. package/adapters/mcp/instructions.ts +37 -0
  35. package/adapters/mcp/legacy-context.ts +18 -0
  36. package/adapters/mcp/multi-repository.ts +421 -0
  37. package/adapters/mcp/oauth.ts +464 -0
  38. package/adapters/mcp/paths.ts +143 -0
  39. package/adapters/mcp/policy.ts +232 -0
  40. package/adapters/mcp/proxy-env.ts +62 -0
  41. package/adapters/mcp/readiness-projection.ts +24 -0
  42. package/adapters/mcp/redaction.ts +21 -0
  43. package/adapters/mcp/repo.ts +24 -0
  44. package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
  45. package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +87 -0
  46. package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
  47. package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
  48. package/adapters/mcp/runtime-gateway/context-adapter.ts +1167 -0
  49. package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
  50. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +206 -0
  51. package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
  52. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +130 -0
  53. package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
  54. package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
  55. package/adapters/mcp/runtime-gateway/execution-tools.ts +173 -0
  56. package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
  57. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +412 -0
  58. package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
  59. package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
  60. package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
  61. package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
  62. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +380 -0
  63. package/adapters/mcp/runtime-gateway/process-tools.ts +457 -0
  64. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +88 -0
  65. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +285 -0
  66. package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
  67. package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
  68. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +79 -0
  69. package/adapters/mcp/runtime-gateway/result-adapter.ts +114 -0
  70. package/adapters/mcp/runtime-gateway/router.ts +242 -0
  71. package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
  72. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1200 -0
  73. package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
  74. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +574 -0
  75. package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
  76. package/adapters/mcp/runtime-gateway/runtime-tools.ts +78 -0
  77. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +187 -0
  78. package/adapters/mcp/runtime-gateway/shared-adapter.ts +45 -0
  79. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +962 -0
  80. package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
  81. package/adapters/mcp/runtime-gateway/work-adapter.ts +325 -0
  82. package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
  83. package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
  84. package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
  85. package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
  86. package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
  87. package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
  88. package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
  89. package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
  90. package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
  91. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
  92. package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
  93. package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
  94. package/adapters/mcp/server.ts +1092 -0
  95. package/adapters/mcp/setup.ts +789 -0
  96. package/adapters/mcp/tool-capability-cache.ts +87 -0
  97. package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
  98. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5139 -0
  99. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +145 -0
  100. package/adapters/mcp/tool-mapping/repository-tools.ts +1682 -0
  101. package/adapters/mcp/tool-mapping/tools.ts +9 -0
  102. package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
  103. package/adapters/mcp/toolset-names.ts +79 -0
  104. package/adapters/mcp/toolset-selection.ts +41 -0
  105. package/adapters/mcp/toolset.ts +337 -0
  106. package/adapters/mcp/transports/http-observation.ts +365 -0
  107. package/adapters/mcp/transports/http.ts +933 -0
  108. package/adapters/mcp/transports/oauth-http.ts +322 -0
  109. package/adapters/mcp/transports/session-registry.ts +519 -0
  110. package/adapters/mcp/transports/stdio.ts +8 -0
  111. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +223 -0
  112. package/adapters/mcp/types.ts +41 -0
  113. package/assets/AGENTS.md +2 -16
  114. package/assets/CLAUDE.md +2 -16
  115. package/assets/hooks/AGENTS.md +3 -14
  116. package/assets/hooks/CLAUDE.md +3 -14
  117. package/assets/hooks/hook-input.sh +9 -2
  118. package/assets/hooks/lib/workflow-state.sh +28 -10
  119. package/assets/hooks/prompt-guard.sh +63 -37
  120. package/assets/hooks/security-sentinel.sh +2 -1
  121. package/assets/hooks/session-start-context.sh +7 -5
  122. package/assets/hooks/stop-orchestrator.sh +13 -3
  123. package/assets/initializer-question-pack.v4.json +11 -7
  124. package/assets/partials/05-workflow.partial.md +18 -73
  125. package/assets/partials/07-footer.partial.md +1 -1
  126. package/assets/partials-agents/02-operating-mode.partial.md +1 -2
  127. package/assets/partials-agents/04-task-protocol.partial.md +14 -59
  128. package/assets/partials-agents/06-quality-safety.partial.md +11 -16
  129. package/assets/partials-agents/08-deep-docs.partial.md +1 -2
  130. package/assets/plan-map.json +11 -11
  131. package/assets/plugin-registry.v1.json +11 -3
  132. package/assets/plugin-trust-policy.v1.json +6 -0
  133. package/assets/recovery/ForgeRecovery.ps1 +100 -0
  134. package/assets/recovery/forge-wsl-rescue +271 -0
  135. package/assets/reference-configs/agentic-development-flow.md +25 -0
  136. package/assets/reference-configs/development-protocol.md +11 -15
  137. package/assets/reference-configs/document-generation.md +1 -1
  138. package/assets/reference-configs/global-working-rules.md +28 -1
  139. package/assets/reference-configs/harness-overview.md +22 -112
  140. package/assets/reference-configs/hook-operations.md +1 -1
  141. package/assets/reference-configs/sprint-contracts.md +2 -2
  142. package/assets/skill-version.json +2 -2
  143. package/assets/templates/helpers/architecture-event.ts +3 -34
  144. package/assets/templates/helpers/architecture-queue.sh +61 -10
  145. package/assets/templates/helpers/archive-workflow.sh +90 -180
  146. package/assets/templates/helpers/capability-config.ts +8 -11
  147. package/assets/templates/helpers/capability-resolver.ts +37 -19
  148. package/assets/templates/helpers/capture-plan.sh +2 -2
  149. package/assets/templates/helpers/check-architecture-sync.sh +1 -9
  150. package/assets/templates/helpers/check-task-sync.sh +82 -11
  151. package/assets/templates/helpers/check-task-workflow.sh +1 -3
  152. package/assets/templates/helpers/context-contract-sync.sh +1 -12
  153. package/assets/templates/helpers/contract-worktree.sh +26 -24
  154. package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
  155. package/assets/templates/helpers/inspect-project-state.ts +1 -1
  156. package/assets/templates/helpers/migrate-project-template.sh +1 -1
  157. package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
  158. package/assets/templates/helpers/new-plan.sh +2 -2
  159. package/assets/templates/helpers/plan-to-todo.sh +3 -31
  160. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  161. package/assets/templates/helpers/switch-plan.sh +1 -1
  162. package/assets/templates/helpers/workflow-contract.ts +1 -0
  163. package/assets/workflow-contract.v1.json +63 -171
  164. package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
  165. package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
  166. package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
  167. package/docs/architecture/CURRENT.md +233 -28
  168. package/docs/architecture/EVOLUTION.md +13 -0
  169. package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
  170. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
  171. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
  172. package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
  173. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
  174. package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
  175. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +37 -0
  176. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +311 -0
  177. package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
  178. package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
  179. package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
  180. package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
  181. package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
  182. package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
  183. package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
  184. package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
  185. package/docs/architecture/domains/verification.md +8 -0
  186. package/docs/architecture/domains/workflow-engine.md +7 -1
  187. package/docs/architecture/index.md +3 -3
  188. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
  189. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
  190. package/docs/forge-chatgpt-mcp-setup.md +5 -3
  191. package/docs/forge-plugin-management.md +20 -4
  192. package/docs/forge-runtime-self-healing-loop.md +2 -0
  193. package/docs/operations/benchmark-cross-version-freeze.md +38 -0
  194. package/docs/operations/controller-browser-plugin.md +3 -1
  195. package/docs/operations/controller-performance-and-502.md +17 -0
  196. package/docs/operations/controller-reliability-runbook.md +2 -2
  197. package/docs/operations/features.md +1 -1
  198. package/docs/operations/features.zh-CN.md +1 -1
  199. package/docs/operations/homebrew.md +2 -2
  200. package/docs/operations/mcp-tool-exposure.md +2 -0
  201. package/docs/operations/platform-support.md +16 -8
  202. package/docs/operations/platform-support.zh-CN.md +2 -2
  203. package/docs/operations/releasing.md +2 -0
  204. package/docs/operations/standalone-disaster-recovery.md +38 -5
  205. package/docs/operations/uu-remote-rescue.md +41 -0
  206. package/docs/operations/windows-wsl-host-rescue.md +133 -0
  207. package/docs/public-usage-guide.md +1 -1
  208. package/docs/public-usage-guide.zh-CN.md +1 -1
  209. package/docs/tutorials/01-install-and-start.md +1 -1
  210. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  211. package/docs/tutorials/02-connect-chatgpt.md +4 -4
  212. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
  213. package/docs/wiki/Implementation.md +1 -1
  214. package/package.json +46 -26
  215. package/packages/kernel/cognition/api/index.ts +6 -0
  216. package/packages/kernel/cognition/application/activation-service.ts +451 -0
  217. package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
  218. package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
  219. package/packages/kernel/cognition/application/memory-service.ts +64 -0
  220. package/packages/kernel/cognition/domain/learning.ts +106 -0
  221. package/packages/kernel/cognition/domain/memory.ts +254 -0
  222. package/packages/kernel/controller/api/index.ts +3 -0
  223. package/packages/kernel/controller/application/continuation-service.ts +281 -0
  224. package/packages/kernel/controller/application/controller-round-service.ts +41 -0
  225. package/packages/kernel/controller/application/controller-service.ts +30 -0
  226. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +529 -0
  227. package/packages/kernel/controller/domain/controller-round.ts +110 -0
  228. package/packages/kernel/controller/domain/execution-quality.ts +135 -0
  229. package/packages/kernel/controller/domain/types.ts +56 -0
  230. package/packages/kernel/controller/index.ts +1 -0
  231. package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
  232. package/packages/kernel/controller/infrastructure/controller-round-store.ts +1970 -0
  233. package/packages/kernel/controller/infrastructure/controller-session-store.ts +804 -0
  234. package/packages/kernel/controller/ports/controller-host.ts +18 -0
  235. package/packages/kernel/goal/api/index.ts +1 -0
  236. package/packages/kernel/goal/domain/types.ts +40 -0
  237. package/packages/kernel/goal/index.ts +1 -0
  238. package/packages/kernel/identity/api/index.ts +15 -0
  239. package/packages/kernel/identity/application/identity-service.ts +67 -0
  240. package/packages/kernel/identity/domain/activity-projection.ts +43 -0
  241. package/packages/kernel/identity/domain/capability-broker.ts +60 -0
  242. package/packages/kernel/identity/domain/grant.ts +72 -0
  243. package/packages/kernel/identity/domain/owned-resource.ts +116 -0
  244. package/packages/kernel/identity/domain/scope.ts +63 -0
  245. package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
  246. package/packages/kernel/identity/domain/types.ts +44 -0
  247. package/packages/kernel/identity/domain/user-request.ts +93 -0
  248. package/packages/kernel/identity/domain/workspace.ts +56 -0
  249. package/packages/kernel/identity/index.ts +1 -0
  250. package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
  251. package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
  252. package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
  253. package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
  254. package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
  255. package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
  256. package/packages/kernel/memory/api/index.ts +5 -0
  257. package/packages/kernel/memory/application/experience-service.ts +118 -0
  258. package/packages/kernel/memory/application/outcome-service.ts +77 -0
  259. package/packages/kernel/memory/domain/experience.ts +103 -0
  260. package/packages/kernel/memory/domain/operational-prior.ts +92 -0
  261. package/packages/kernel/memory/index.ts +1 -0
  262. package/packages/kernel/scheduler/api/index.ts +4 -0
  263. package/packages/kernel/scheduler/application/eligibility.ts +252 -0
  264. package/packages/kernel/scheduler/application/schedule-service.ts +27 -0
  265. package/packages/kernel/scheduler/application/settlement.ts +182 -0
  266. package/packages/kernel/scheduler/domain/schedule.ts +236 -0
  267. package/packages/kernel/scheduler/index.ts +1 -0
  268. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +805 -0
  269. package/packages/kernel/work/api/index.ts +16 -0
  270. package/packages/kernel/work/application/work-service.ts +123 -0
  271. package/packages/kernel/work/domain/check-receipt.ts +43 -0
  272. package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
  273. package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
  274. package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
  275. package/packages/kernel/work/domain/engineering-design.ts +286 -0
  276. package/packages/kernel/work/domain/engineering-profile.ts +238 -0
  277. package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
  278. package/packages/kernel/work/domain/execution-concurrency.ts +247 -0
  279. package/packages/kernel/work/domain/execution-snapshot.ts +2 -0
  280. package/packages/kernel/work/domain/implementation-review.ts +193 -0
  281. package/packages/kernel/work/domain/objective-graph.ts +119 -0
  282. package/packages/kernel/work/domain/project-engineering-contract.ts +224 -0
  283. package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
  284. package/{src/runtime/control-plane/facade/work-state-machine.ts → packages/kernel/work/domain/state-machine.ts} +37 -66
  285. package/packages/kernel/work/domain/types.ts +534 -0
  286. package/packages/kernel/work/index.ts +1 -0
  287. package/packages/kernel/work/infrastructure/work-contract-store.ts +1967 -0
  288. package/packages/kernel/work/ports/work-contract-store.ts +21 -0
  289. package/packages/plugin-runtime/browser/index.ts +3 -0
  290. package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
  291. package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
  292. package/packages/plugin-runtime/browser/session-authority.ts +62 -0
  293. package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
  294. package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
  295. package/packages/plugin-runtime/computer/index.ts +8 -0
  296. package/packages/plugin-runtime/computer/provider-error.ts +26 -0
  297. package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
  298. package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
  299. package/packages/plugin-runtime/computer/provider.ts +19 -0
  300. package/packages/plugin-runtime/computer/target-authority.ts +215 -0
  301. package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
  302. package/packages/plugin-runtime/external/index.ts +1 -0
  303. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +581 -0
  304. package/packages/plugin-runtime/index.ts +3 -0
  305. package/packages/protocols/browser/index.ts +1 -0
  306. package/packages/protocols/browser/session.ts +98 -0
  307. package/packages/protocols/computer/contract.ts +225 -0
  308. package/packages/protocols/computer/index.ts +1 -0
  309. package/packages/protocols/failure/contract.ts +158 -0
  310. package/packages/protocols/failure/index.ts +1 -0
  311. package/packages/protocols/handoff/index.ts +1 -0
  312. package/packages/protocols/handoff/status.ts +22 -0
  313. package/packages/protocols/mcp/execution-context.ts +11 -0
  314. package/packages/protocols/mcp/index.ts +2 -0
  315. package/packages/protocols/mcp/tool-contract.ts +42 -0
  316. package/packages/workflow-runtime/api/index.ts +2 -0
  317. package/packages/workflow-runtime/application/interpreter.ts +342 -0
  318. package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
  319. package/packages/workflow-runtime/index.ts +1 -0
  320. package/scripts/activate-source-baseline.ts +7 -9
  321. package/scripts/assemble-template.ts +1 -1
  322. package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
  323. package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
  324. package/scripts/canary-controller-semantic-wait.ts +183 -0
  325. package/scripts/canary-global-hook.sh +2 -2
  326. package/scripts/check-mcp-compatibility.ts +85 -7
  327. package/scripts/check-platform-support.mjs +28 -2
  328. package/scripts/check-public-docs.sh +1 -1
  329. package/scripts/check-repository-hygiene.mjs +92 -0
  330. package/scripts/check-runtime-architecture.mjs +1643 -127
  331. package/scripts/check-source-duplication.mjs +9 -1
  332. package/scripts/controller-ngrok-rotation.sh +5 -4
  333. package/scripts/create-project-dirs.sh +10 -74
  334. package/scripts/forge-local-recovery-helper.mjs +282 -0
  335. package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
  336. package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
  337. package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
  338. package/scripts/hook-shim.sh +2 -2
  339. package/scripts/init-project.sh +12 -70
  340. package/scripts/install-desktop-operator-registration.ts +4 -2
  341. package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
  342. package/scripts/install-standalone-recovery.ts +122 -10
  343. package/scripts/install-uu-remote-rescue-registration.ts +74 -0
  344. package/scripts/install-windows-host-recovery-registration.ts +22 -0
  345. package/scripts/lib/controller-home-env.sh +6 -3
  346. package/scripts/lib/project-init-lib.sh +42 -582
  347. package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
  348. package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
  349. package/scripts/open-source-audit-allowlist.txt +11 -1
  350. package/scripts/portability-bindings.mjs +70 -0
  351. package/scripts/run-bounded-command.mjs +60 -0
  352. package/scripts/run-bun-test-file.ts +22 -6
  353. package/scripts/run-governed-gate.ts +4 -2
  354. package/scripts/run-mobile-monitor-bridge.ts +0 -1
  355. package/scripts/smoke-control-plane-failure-domain.ts +94 -0
  356. package/scripts/smoke-mcp-http-runtime.ts +6 -1
  357. package/scripts/smoke-runtime-recovery.ts +13 -11
  358. package/scripts/smoke-schedule-engine.ts +24 -16
  359. package/scripts/smoke-unattended-transport-recovery.ts +223 -0
  360. package/scripts/smoke-user-request-continuation-restart.ts +232 -0
  361. package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
  362. package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
  363. package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
  364. package/scripts/smoke-workflow-supervisor.ts +103 -0
  365. package/scripts/test-governance.ts +51 -2
  366. package/scripts/verify-forge-runtime.sh +2 -1
  367. package/src/cli/agent-jobs/executor-health.ts +11 -34
  368. package/src/cli/agent-jobs/integration.ts +18 -49
  369. package/src/cli/agent-jobs/job-manager.ts +17 -4
  370. package/src/cli/chatgpt-browser/bind-server.ts +2 -1
  371. package/src/cli/chatgpt-browser/binding.ts +20 -10
  372. package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
  373. package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
  374. package/src/cli/chatgpt-browser/engine.ts +10 -11
  375. package/src/cli/chatgpt-browser/native-provider.ts +24 -83
  376. package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
  377. package/src/cli/chatgpt-browser/types.ts +1 -0
  378. package/src/cli/commands/bootstrap-control.ts +334 -0
  379. package/src/cli/commands/brain-assistant.ts +52 -0
  380. package/src/cli/commands/brain.ts +191 -40
  381. package/src/cli/commands/chatgpt.ts +32 -67
  382. package/src/cli/commands/computer.ts +437 -0
  383. package/src/cli/commands/doctor.ts +18 -1
  384. package/src/cli/commands/init-hook.ts +71 -61
  385. package/src/cli/commands/init.ts +3 -1
  386. package/src/cli/commands/mcp.ts +16 -10
  387. package/src/cli/commands/plugin.ts +38 -12
  388. package/src/cli/commands/recovery.ts +507 -80
  389. package/src/cli/commands/runtime.ts +12 -7
  390. package/src/cli/commands/setup-profile.ts +97 -39
  391. package/src/cli/commands/status.ts +2 -1
  392. package/src/cli/controller/check-runner.ts +500 -100
  393. package/src/cli/controller/completion-backlog.ts +3 -3
  394. package/src/cli/controller/completion-orchestrator.ts +4 -43
  395. package/src/cli/controller/completion-target.ts +3 -72
  396. package/src/cli/controller/context/source-materializer.ts +81 -100
  397. package/src/cli/controller/context/types.ts +7 -1
  398. package/src/cli/controller/context-pack.ts +28 -6
  399. package/src/cli/controller/execution-completion.ts +4 -7
  400. package/src/cli/controller/execution-policy.ts +36 -153
  401. package/src/cli/controller/governance.ts +2 -3
  402. package/src/cli/controller/issue-store.ts +4 -99
  403. package/src/cli/controller/launch-agents.ts +59 -18
  404. package/src/cli/controller/operational-plan.ts +24 -51
  405. package/src/cli/controller/runtime-config.ts +5 -1
  406. package/src/cli/controller/stuck-state-migration.ts +2 -3
  407. package/src/cli/controller/systemd-user.ts +148 -0
  408. package/src/cli/controller/types.ts +10 -70
  409. package/src/cli/controller/worklog.ts +67 -13
  410. package/src/cli/editing/edit-operation-contract.ts +92 -0
  411. package/src/cli/editing/edit-session.ts +290 -20
  412. package/src/cli/github/plugin.ts +17 -12
  413. package/src/cli/hook/route-registry.ts +3 -5
  414. package/src/cli/hook/runtime.ts +49 -3
  415. package/src/cli/index.ts +3 -0
  416. package/src/cli/local-bridge/connector-freshness.ts +2 -2
  417. package/src/cli/local-bridge/console-automations.ts +20 -15
  418. package/src/cli/local-bridge/console-view-models.ts +3 -12
  419. package/src/cli/local-bridge/facade-api.ts +105 -205
  420. package/src/cli/local-bridge/job-store.ts +1 -1
  421. package/src/cli/local-bridge/server.ts +48 -174
  422. package/src/cli/local-bridge/types.ts +0 -1
  423. package/src/cli/mcp/access-mode.ts +2 -132
  424. package/src/cli/mcp/access-tools.ts +2 -387
  425. package/src/cli/mcp/audit.ts +2 -32
  426. package/src/cli/mcp/auth.ts +2 -404
  427. package/src/cli/mcp/instructions.ts +2 -34
  428. package/src/cli/mcp/legacy-context.ts +2 -16
  429. package/src/cli/mcp/legacy-tool-service.ts +2 -5308
  430. package/src/cli/mcp/multi-repository.ts +2 -436
  431. package/src/cli/mcp/oauth-client-retirement.ts +74 -0
  432. package/src/cli/mcp/oauth.ts +2 -375
  433. package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
  434. package/src/cli/mcp/paths.ts +2 -140
  435. package/src/cli/mcp/policy.ts +2 -232
  436. package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
  437. package/src/cli/mcp/proxy-env.ts +2 -62
  438. package/src/cli/mcp/readiness-projection.ts +2 -24
  439. package/src/cli/mcp/redaction.ts +2 -21
  440. package/src/cli/mcp/repo.ts +2 -24
  441. package/src/cli/mcp/repository-tools.ts +2 -1613
  442. package/src/cli/mcp/server.ts +2 -951
  443. package/src/cli/mcp/setup.ts +2 -737
  444. package/src/cli/mcp/tool-capability-cache.ts +2 -87
  445. package/src/cli/mcp/tool-contract.ts +2 -14
  446. package/src/cli/mcp/tools.ts +2 -9
  447. package/src/cli/mcp/toolset-names.ts +2 -70
  448. package/src/cli/mcp/toolset-selection.ts +2 -41
  449. package/src/cli/mcp/toolset.ts +2 -336
  450. package/src/cli/mcp/transports/AGENTS.md +1 -10
  451. package/src/cli/mcp/transports/CLAUDE.md +1 -10
  452. package/src/cli/mcp/transports/http.ts +2 -1414
  453. package/src/cli/mcp/transports/session-registry.ts +2 -490
  454. package/src/cli/mcp/transports/stdio.ts +2 -8
  455. package/src/cli/mcp/types.ts +2 -41
  456. package/src/cli/mcp/umbrella-tools.ts +2 -117
  457. package/src/cli/repositories/command-executor.ts +91 -39
  458. package/src/cli/repositories/command-scope.ts +139 -4
  459. package/src/cli/repositories/controller-home.ts +167 -1
  460. package/src/cli/repositories/ephemeral-workspace.ts +20 -30
  461. package/src/cli/repositories/git-command-executor.ts +2 -2
  462. package/src/cli/repositories/identity.ts +13 -0
  463. package/src/cli/repositories/registry.ts +145 -60
  464. package/src/cli/repositories/repository-snapshot.ts +53 -7
  465. package/src/cli/repositories/runtime-storage.ts +27 -2
  466. package/src/cli/repositories/safe-patch.ts +34 -38
  467. package/src/cli/repositories/selected-path-actions.ts +22 -2
  468. package/src/cli/repositories/structured-git.ts +167 -27
  469. package/src/cli/repository/session-cache.ts +92 -6
  470. package/src/cli/runtime/helper-runner.ts +7 -0
  471. package/src/cli/runtime-invocation.ts +8 -1
  472. package/src/cli/tools/codegraph.ts +44 -25
  473. package/src/core/adoption/manifest-templates.ts +1 -1
  474. package/src/core/adoption/plan.ts +3 -26
  475. package/src/core/adoption/workflow-contract-plan.ts +17 -9
  476. package/src/effects/process-runner.ts +8 -14
  477. package/src/runtime/context/assistant-context.ts +167 -0
  478. package/src/runtime/context/assistant-work-context.ts +209 -0
  479. package/src/runtime/context/automatic-learning.ts +565 -0
  480. package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
  481. package/src/runtime/context/codegraph-read-provider.ts +55 -22
  482. package/src/runtime/context/codegraph-sidecar.cjs +8 -1
  483. package/src/runtime/context/cognitive-attention.ts +173 -0
  484. package/src/runtime/context/cognitive-skill-canary.ts +158 -0
  485. package/src/runtime/context/context-closure.ts +305 -0
  486. package/src/runtime/context/context-pack-process.ts +103 -0
  487. package/src/runtime/context/context-plane.ts +203 -0
  488. package/src/runtime/context/direct-controller-learning.ts +298 -0
  489. package/src/runtime/context/project-engineering-contract.ts +60 -0
  490. package/src/runtime/context/semantic-navigation-contract.ts +9 -0
  491. package/src/runtime/context/semantic-navigation.ts +6 -34
  492. package/src/runtime/context/typescript-navigation-process.ts +145 -0
  493. package/src/runtime/context/typescript-navigation.ts +89 -3
  494. package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
  495. package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
  496. package/src/runtime/control-plane/bootstrap/index.ts +5 -0
  497. package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
  498. package/src/runtime/control-plane/bootstrap/store.ts +21 -0
  499. package/src/runtime/control-plane/bootstrap/types.ts +87 -0
  500. package/src/runtime/control-plane/cleanup-artifact-retention.ts +531 -0
  501. package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
  502. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +282 -0
  503. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +380 -0
  504. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +113 -16
  505. package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
  506. package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
  507. package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
  508. package/src/runtime/control-plane/execution/retained-work-resume.ts +439 -102
  509. package/src/runtime/control-plane/execution/session-store.ts +31 -1
  510. package/src/runtime/control-plane/execution/validation.ts +34 -11
  511. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +87 -0
  512. package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
  513. package/src/runtime/control-plane/execution/work-completion-authority.ts +31 -44
  514. package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
  515. package/src/runtime/control-plane/execution/work-evidence-policy.ts +46 -53
  516. package/src/runtime/control-plane/execution/work-execution-support.ts +85 -164
  517. package/src/runtime/control-plane/execution/work-handle-authority.ts +349 -73
  518. package/src/runtime/control-plane/execution/work-handle-store.ts +55 -2
  519. package/src/runtime/control-plane/execution/work-head-settlement.ts +108 -0
  520. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +173 -28
  521. package/src/runtime/control-plane/execution/work-operation-service.ts +92 -40
  522. package/src/runtime/control-plane/execution/work-preparation-service.ts +145 -269
  523. package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
  524. package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
  525. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +543 -225
  526. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +59 -45
  527. package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
  528. package/src/runtime/control-plane/execution/work-verification-service.ts +662 -52
  529. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +205 -16
  530. package/src/runtime/control-plane/facade/capability-registry.ts +380 -44
  531. package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
  532. package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
  533. package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
  534. package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
  535. package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -22
  536. package/src/runtime/control-plane/facade/goal-workloop.ts +565 -1202
  537. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +145 -0
  538. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +266 -52
  539. package/src/runtime/control-plane/facade/index.ts +4 -3
  540. package/src/runtime/control-plane/facade/operation-digest.ts +34 -73
  541. package/src/runtime/control-plane/facade/plan-contract-store.ts +671 -245
  542. package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
  543. package/src/runtime/control-plane/facade/repository-work-admission.ts +31 -23
  544. package/src/runtime/control-plane/facade/requirement-authority.ts +121 -0
  545. package/src/runtime/control-plane/facade/requirement-board.ts +9 -4
  546. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +85 -0
  547. package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
  548. package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
  549. package/src/runtime/control-plane/facade/suggested-actions.ts +15 -15
  550. package/src/runtime/control-plane/facade/types.ts +218 -620
  551. package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
  552. package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
  553. package/src/runtime/control-plane/generated-cache-retention.ts +336 -19
  554. package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
  555. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +13 -2
  556. package/src/runtime/control-plane/global-scheduler/maintenance.ts +274 -84
  557. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
  558. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
  559. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -2
  560. package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
  561. package/src/runtime/control-plane/global-scheduler/scheduler.ts +227 -109
  562. package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
  563. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
  564. package/src/runtime/control-plane/governance/access-policy.ts +19 -2
  565. package/src/runtime/control-plane/governance/authorization.ts +20 -3
  566. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +160 -0
  567. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
  568. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +139 -670
  569. package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
  570. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +25 -6
  571. package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -194
  572. package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
  573. package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
  574. package/src/runtime/control-plane/persistence/experience-store.ts +302 -0
  575. package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
  576. package/src/runtime/control-plane/persistence/requirement-store.ts +230 -80
  577. package/src/runtime/control-plane/persistence/sqlite-store.ts +654 -78
  578. package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
  579. package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
  580. package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
  581. package/src/runtime/control-plane/release-retention.ts +154 -26
  582. package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
  583. package/src/runtime/control-plane/runtime-cleanup.ts +844 -38
  584. package/src/runtime/control-plane/runtime-generation.ts +6 -0
  585. package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
  586. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +479 -0
  587. package/src/runtime/control-plane/workspace/workspace-store.ts +249 -0
  588. package/src/runtime/diagnostics/incident-repair.ts +573 -0
  589. package/src/runtime/diagnostics/mcp-timing.ts +239 -6
  590. package/src/runtime/diagnostics/performance.ts +6 -14
  591. package/src/runtime/diagnostics/read-only-tool.ts +1 -1
  592. package/src/runtime/evidence/artifact-store.ts +216 -35
  593. package/src/runtime/evidence/evidence-store.ts +112 -8
  594. package/src/runtime/evidence/operational-shadow.ts +38 -0
  595. package/src/runtime/evidence/output-handle-index.ts +225 -0
  596. package/src/runtime/evidence/process-check-receipt.ts +5 -43
  597. package/src/runtime/evidence/result-store.ts +158 -44
  598. package/src/runtime/evidence/sensitive-output.ts +4 -4
  599. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
  600. package/src/runtime/execution/jobs/store.ts +124 -6
  601. package/src/runtime/execution/managed-workspace.ts +112 -5
  602. package/src/runtime/execution/process-runtime/canary.ts +19 -5
  603. package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
  604. package/src/runtime/execution/process-runtime/check-facade.ts +24 -10
  605. package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
  606. package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
  607. package/src/runtime/execution/process-runtime/check-result.ts +177 -2
  608. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +58 -9
  609. package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
  610. package/src/runtime/execution/process-runtime/command-facade.ts +117 -114
  611. package/src/runtime/execution/process-runtime/gc.ts +15 -2
  612. package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
  613. package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
  614. package/src/runtime/execution/process-runtime/index.ts +4 -0
  615. package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
  616. package/src/runtime/execution/process-runtime/lightweight-managed.ts +283 -73
  617. package/src/runtime/execution/process-runtime/persisted-check.ts +130 -23
  618. package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
  619. package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
  620. package/src/runtime/execution/process-runtime/resource-claims.ts +125 -64
  621. package/src/runtime/execution/process-runtime/runtime.ts +167 -17
  622. package/src/runtime/execution/process-runtime/store.ts +30 -2
  623. package/src/runtime/execution/process-runtime/types.ts +27 -0
  624. package/src/runtime/execution/workers/executor.ts +18 -19
  625. package/src/runtime/execution/workers/worker-entry.ts +1 -1
  626. package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
  627. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
  628. package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
  629. package/src/runtime/gateway/mcp/process-tools.ts +2 -289
  630. package/src/runtime/gateway/mcp/router.ts +2 -1125
  631. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
  632. package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6655
  633. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
  634. package/src/runtime/maintenance/cleanup.ts +38 -24
  635. package/src/runtime/platform/browser-product-discovery.ts +174 -0
  636. package/src/runtime/platform/computer-platform.ts +14 -0
  637. package/src/runtime/platform/executable-discovery.ts +54 -0
  638. package/src/runtime/platform/fetch-http-server.ts +90 -0
  639. package/src/runtime/platform/service-inventory.ts +130 -0
  640. package/src/runtime/platform/service-manager.ts +146 -0
  641. package/src/runtime/platform/windows-host-discovery.ts +118 -0
  642. package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
  643. package/src/runtime/plugins/action-application.ts +192 -0
  644. package/src/runtime/plugins/app-store-connect-adapter.ts +47 -35
  645. package/src/runtime/plugins/browser-adapter.ts +898 -135
  646. package/src/runtime/plugins/browser-automation-service.ts +33 -34
  647. package/src/runtime/plugins/browser-macos-bridge.ts +386 -46
  648. package/src/runtime/plugins/browser-manifest-surface.ts +75 -17
  649. package/src/runtime/plugins/browser-native-session-service.ts +64 -14
  650. package/src/runtime/plugins/browser-provider-registry.ts +2 -109
  651. package/src/runtime/plugins/browser-registration.ts +32 -0
  652. package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
  653. package/src/runtime/plugins/browser-runtime.ts +55 -10
  654. package/src/runtime/plugins/browser-session-authority.ts +101 -291
  655. package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
  656. package/src/runtime/plugins/browser-session-store.ts +243 -80
  657. package/src/runtime/plugins/browser-session-types.ts +2 -98
  658. package/src/runtime/plugins/capability-authorization-grants.ts +272 -59
  659. package/src/runtime/plugins/catalog-trust.ts +82 -0
  660. package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
  661. package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
  662. package/src/runtime/plugins/computer-registration.ts +1050 -0
  663. package/src/runtime/plugins/config-store.ts +119 -0
  664. package/src/runtime/plugins/desktop-operator-external-policy.ts +964 -0
  665. package/src/runtime/plugins/desktop-operator-registration.ts +123 -12
  666. package/src/runtime/plugins/errors.ts +8 -1
  667. package/src/runtime/plugins/external-adapter.ts +82 -323
  668. package/src/runtime/plugins/external-provider-policy.ts +48 -0
  669. package/src/runtime/plugins/external-registration.ts +30 -2
  670. package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
  671. package/src/runtime/plugins/external-unix-socket.ts +64 -193
  672. package/src/runtime/plugins/first-party-external-registration.ts +163 -0
  673. package/src/runtime/plugins/first-party-registry.ts +3 -1
  674. package/src/runtime/plugins/github-adapter.ts +1 -2
  675. package/src/runtime/plugins/gmail-adapter.ts +29 -8
  676. package/src/runtime/plugins/google-calendar-adapter.ts +8 -8
  677. package/src/runtime/plugins/google-shared.ts +45 -53
  678. package/src/runtime/plugins/google-tasks-adapter.ts +8 -8
  679. package/src/runtime/plugins/ios-adapter.ts +2 -3
  680. package/src/runtime/plugins/lightweight-action.ts +208 -2
  681. package/src/runtime/plugins/local-system-adapter.ts +192 -2
  682. package/src/runtime/plugins/macos-capability-broker.ts +15 -105
  683. package/src/runtime/plugins/managed-process-adapter.ts +12 -5
  684. package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
  685. package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
  686. package/src/runtime/plugins/resend-adapter.ts +15 -24
  687. package/src/runtime/plugins/store.ts +1003 -387
  688. package/src/runtime/plugins/types.ts +28 -1
  689. package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
  690. package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
  691. package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
  692. package/src/runtime/plugins/xiaohongshu-publish.ts +102 -430
  693. package/src/runtime/projections/controller-context.ts +12 -6
  694. package/src/runtime/projections/git-status-sampler.ts +73 -22
  695. package/src/runtime/projections/invalidation.ts +7 -3
  696. package/src/runtime/projections/materialized-view.ts +93 -43
  697. package/src/runtime/recovery/actions.ts +3 -3
  698. package/src/runtime/recovery/capability-action-application.ts +229 -0
  699. package/src/runtime/recovery/index.ts +3 -0
  700. package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
  701. package/src/runtime/recovery/local-jobs-repair.ts +14 -14
  702. package/src/runtime/recovery/maintenance-executor.ts +701 -168
  703. package/src/runtime/recovery/quarantine-retention.ts +256 -0
  704. package/src/runtime/recovery/store.ts +29 -11
  705. package/src/runtime/release/identity.ts +6 -0
  706. package/src/runtime/release/release-coordinator.ts +296 -0
  707. package/src/runtime/release/release-session.ts +716 -0
  708. package/src/runtime/release/release-state-migration.ts +25 -0
  709. package/src/runtime/resources/leases/store.ts +56 -25
  710. package/src/runtime/root/active-execution-power-assertion.ts +139 -0
  711. package/src/runtime/root/assistant-learning-loop.ts +45 -0
  712. package/src/runtime/root/browser-session-composition.ts +59 -0
  713. package/src/runtime/root/computer-composition.ts +376 -0
  714. package/src/runtime/root/computer-target-composition.ts +16 -0
  715. package/src/runtime/root/computer-target-persistence.ts +39 -0
  716. package/src/runtime/root/controller-progression-composition.ts +192 -0
  717. package/src/runtime/root/controller-round-composition.ts +126 -0
  718. package/src/runtime/root/controller-services.ts +2 -0
  719. package/src/runtime/root/deployment-topology.ts +106 -0
  720. package/src/runtime/root/entry.ts +8 -6
  721. package/src/runtime/root/gateway-adapter.ts +3 -3
  722. package/src/runtime/root/known-good-recovery.ts +184 -0
  723. package/src/runtime/root/local-bridge.ts +5 -3
  724. package/src/runtime/root/mcp-transport.ts +116 -23
  725. package/src/runtime/root/ownership.ts +261 -9
  726. package/src/runtime/root/package-connector-service.ts +212 -74
  727. package/src/runtime/root/package-runtime-release.ts +199 -33
  728. package/src/runtime/root/package-runtime-service.ts +77 -108
  729. package/src/runtime/root/release-execution-canary.ts +103 -0
  730. package/src/runtime/root/release-loader.ts +150 -0
  731. package/src/runtime/root/release-manifest.ts +185 -7
  732. package/src/runtime/root/release-materialize.ts +474 -31
  733. package/src/runtime/root/release-store.ts +425 -37
  734. package/src/runtime/root/runtime-lane.ts +191 -0
  735. package/src/runtime/root/runtime.ts +379 -32
  736. package/src/runtime/root/scheduled-controller-composition.ts +76 -0
  737. package/src/runtime/root/scheduler.ts +12 -4
  738. package/src/runtime/root/service-runner.ts +3 -3
  739. package/src/runtime/root/service.ts +73 -45
  740. package/src/runtime/root/status.ts +49 -0
  741. package/src/runtime/root/types.ts +55 -3
  742. package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
  743. package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
  744. package/src/runtime/root/write-fence.ts +50 -2
  745. package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
  746. package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
  747. package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
  748. package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
  749. package/src/runtime/shared/local-bridge-surface.ts +1 -1
  750. package/src/runtime/shared/process-environment.ts +36 -10
  751. package/src/runtime/shared/response-budget.ts +1 -3
  752. package/src/runtime/shared/storage-capacity.ts +68 -0
  753. package/src/runtime/standalone-recovery/controller-home-migration.ts +1108 -0
  754. package/src/runtime/standalone-recovery/core.ts +4609 -495
  755. package/src/runtime/standalone-recovery/entry.ts +642 -81
  756. package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
  757. package/src/runtime/standalone-recovery/installer.ts +399 -177
  758. package/src/runtime/standalone-recovery/mcp-server.ts +79 -0
  759. package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
  760. package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
  761. package/src/runtime/standalone-recovery/performance.ts +173 -0
  762. package/src/runtime/standalone-recovery/release.ts +58 -5
  763. package/src/runtime/standalone-recovery/service-labels.ts +5 -2
  764. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
  765. package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
  766. package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
  767. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +11 -4
  768. package/src/runtime/workflow/schedules/engine.ts +298 -484
  769. package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
  770. package/src/runtime/workflow/schedules/settlement.ts +2 -191
  771. package/src/runtime/workflow/schedules/store.ts +2 -607
  772. package/src/runtime/workflow/schedules/types.ts +2 -127
  773. package/src/runtime/workflow/schedules/work-continuation.ts +220 -16
  774. package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
  775. package/src/runtime/workflows/runtime.ts +482 -0
  776. package/src/testing/test-governance.ts +95 -30
  777. package/supervisor/chatgpt-conversation.ts +23 -0
  778. package/supervisor/client.ts +201 -0
  779. package/supervisor/control-plane.ts +868 -0
  780. package/supervisor/entry.ts +15 -0
  781. package/supervisor/forge-validators.ts +80 -0
  782. package/supervisor/index.ts +5 -0
  783. package/supervisor/native-browser-adapter.ts +786 -0
  784. package/supervisor/native-messaging/host.ts +39 -0
  785. package/supervisor/native-messaging/manifest.ts +26 -0
  786. package/supervisor/native-messaging/protocol.ts +27 -0
  787. package/supervisor/paths.ts +18 -0
  788. package/supervisor/protocol.ts +202 -0
  789. package/supervisor/server.ts +404 -0
  790. package/supervisor/store.ts +1451 -0
  791. package/supervisor/tsconfig.json +8 -0
  792. package/supervisor/types.ts +255 -0
  793. package/assets/templates/helpers/archive-architecture-request.sh +0 -244
  794. package/docs/architecture/history/README.md +0 -7
  795. package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
  796. package/docs/architecture/history/global-hook-runtime.md +0 -166
  797. package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
  798. package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  799. package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
  800. package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
  801. package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
  802. package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
  803. package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  804. package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  805. package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  806. package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  807. package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
  808. package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  809. package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  810. package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  811. package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  812. package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  813. package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  814. package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  815. package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  816. package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  817. package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
  818. package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
  819. package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
  820. package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  821. package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  822. package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  823. package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  824. package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
  825. package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
  826. package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
  827. package/docs/architecture/requests/archive/2026/root.md +0 -68
  828. package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
  829. package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
  830. package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
  831. package/scripts/archive-architecture-request.sh +0 -4
  832. package/scripts/benchmark-background-check-overlap.ts +0 -228
  833. package/scripts/benchmark-controller-hotpath.ts +0 -212
  834. package/scripts/benchmark-controller-round2.ts +0 -139
  835. package/scripts/benchmark-ios-agent-device.ts +0 -514
  836. package/scripts/benchmark-mcp-host.sh +0 -117
  837. package/scripts/benchmark-mcp-transport.ts +0 -170
  838. package/scripts/benchmark-quality-harness.ts +0 -146
  839. package/scripts/benchmark-route-session-concurrency.ts +0 -915
  840. package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
  841. package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
  842. package/scripts/benchmark-thin-harness.ts +0 -251
  843. package/scripts/benchmark-typescript-navigation.ts +0 -186
  844. package/scripts/benchmark-work-efficiency.ts +0 -334
  845. package/scripts/build-controller-ui.ts +0 -28
  846. package/scripts/check-npm-release.sh +0 -32
  847. package/scripts/check-public-release-surface.sh +0 -19
  848. package/scripts/check-release-published.sh +0 -57
  849. package/scripts/check-release-readiness.sh +0 -37
  850. package/scripts/check-release-version.mjs +0 -74
  851. package/scripts/check-tarball-install-smoke.sh +0 -67
  852. package/scripts/package-source-archive.sh +0 -147
  853. package/scripts/public-release-files.txt +0 -39
  854. package/scripts/public-release.ts +0 -174
  855. package/scripts/publish-release-tarball.sh +0 -27
  856. package/scripts/route-nl-vs-ts-eval.ts +0 -679
  857. package/scripts/run-skill-evals.ts +0 -1022
  858. package/scripts/stage-runtime-release.ts +0 -39
  859. package/src/cli/controller/progress.ts +0 -656
  860. package/src/cli/controller/work-mode.ts +0 -181
  861. package/src/cli/local-bridge/console-assets.ts +0 -17
  862. package/src/cli/local-bridge/dashboard.ts +0 -17
  863. package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
  864. package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
  865. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
  866. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
  867. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
  868. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
  869. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
  870. package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
  871. package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
  872. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
  873. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
  874. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
  875. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
  876. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
  877. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
  878. package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
  879. package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
  880. package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
  881. package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
  882. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
  883. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
  884. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
  885. package/src/cli/local-bridge/frontend/src/styles.css +0 -28
  886. package/src/cli/local-bridge/frontend/src/types.ts +0 -15
  887. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
  888. package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
  889. package/src/cli/local-bridge/ui-dist/app.css +0 -1
  890. package/src/cli/local-bridge/ui-dist/app.js +0 -11228
  891. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -385
  892. package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -1913
  893. package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
  894. package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -449
  895. package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -1118
  896. package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -93
  897. package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
  898. package/src/runtime/control-plane/routing/index.ts +0 -1
  899. package/src/runtime/control-plane/routing/route-policy.ts +0 -382
  900. package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
@@ -1,5 +1,6 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import { createRequire } from 'node:module';
2
- import { copyFileSync, existsSync, mkdirSync, renameSync, unlinkSync } from 'fs';
3
+ import { closeSync, copyFileSync, existsSync, mkdirSync, openSync, readSync, renameSync, statSync, unlinkSync } from 'fs';
3
4
  import { dirname, join } from 'path';
4
5
  import { durableControllerHome } from '../../../cli/repositories/controller-home';
5
6
 
@@ -15,6 +16,8 @@ interface SqliteStatement {
15
16
  get(...parameters: unknown[]): unknown;
16
17
  all(...parameters: unknown[]): unknown[];
17
18
  run(...parameters: unknown[]): unknown;
19
+ /** Bun statements retain native resources until finalized; Node's StatementSync does not expose this method. */
20
+ finalize?(): void;
18
21
  }
19
22
 
20
23
  export interface SqliteDatabase {
@@ -59,10 +62,114 @@ export interface ControlPlaneDatabaseInspection {
59
62
  orphanRecordCount: number;
60
63
  }
61
64
 
65
+ /**
66
+ * Ephemeral database-wide change fence. SQLite data_version changes whenever a
67
+ * different connection commits to the same database, including domain tables
68
+ * that do not participate in control_plane_audit.
69
+ */
70
+ export interface ControlPlaneDatabaseChangeObserver {
71
+ path: string;
72
+ dataVersion(): number;
73
+ close(): void;
74
+ }
75
+
76
+ const CONTROL_PLANE_DATABASE_HASH_CHUNK_BYTES = 1024 * 1024;
77
+
78
+ /** Full snapshot identity without allocating the whole SQLite file. */
79
+ export function sha256ControlPlaneDatabaseFile(path: string): string {
80
+ const descriptor = openSync(path, 'r');
81
+ const hash = createHash('sha256');
82
+ const buffer = Buffer.allocUnsafe(CONTROL_PLANE_DATABASE_HASH_CHUNK_BYTES);
83
+ try {
84
+ for (;;) {
85
+ const bytesRead = readSync(descriptor, buffer, 0, buffer.length, null);
86
+ if (bytesRead === 0) break;
87
+ hash.update(buffer.subarray(0, bytesRead));
88
+ }
89
+ return hash.digest('hex');
90
+ } finally {
91
+ closeSync(descriptor);
92
+ }
93
+ }
94
+
95
+ export const CONTROL_PLANE_SQLITE_MAINTENANCE_POLICY_VERSION = 'control-plane-sqlite-maintenance-v1' as const;
96
+ export const DEFAULT_CONTROL_PLANE_SQLITE_VACUUM_MIN_RECLAIMABLE_BYTES = 64 * 1024 * 1024;
97
+ export const DEFAULT_CONTROL_PLANE_SQLITE_VACUUM_MIN_RECLAIMABLE_RATIO = 0.20;
98
+
99
+ export interface ControlPlaneDatabaseMaintenanceOptions {
100
+ minimumReclaimableBytes?: number;
101
+ minimumReclaimableRatio?: number;
102
+ allowVacuum?: boolean;
103
+ }
104
+
105
+ export interface ControlPlaneDatabaseMaintenanceReport {
106
+ policyVersion: typeof CONTROL_PLANE_SQLITE_MAINTENANCE_POLICY_VERSION;
107
+ path: string;
108
+ checkpointMode: 'passive';
109
+ checkpointed: boolean;
110
+ pageSize: number;
111
+ pageCountBefore: number;
112
+ freePageCountBefore: number;
113
+ reclaimableBytesBefore: number;
114
+ reclaimableRatioBefore: number;
115
+ vacuumEligible: boolean;
116
+ vacuumAttempted: boolean;
117
+ vacuumed: boolean;
118
+ pageCountAfter: number;
119
+ freePageCountAfter: number;
120
+ reclaimableBytesAfter: number;
121
+ reclaimableRatioAfter: number;
122
+ reclaimedBytes: number;
123
+ skippedReason?: 'database_missing' | 'below_threshold' | 'vacuum_disabled' | 'database_busy';
124
+ }
125
+
62
126
  export const CONTROL_PLANE_SCHEMA_VERSION = 1;
63
127
  const DATABASE_FILE = 'control-plane.sqlite';
64
128
  const require = createRequire(import.meta.url);
65
129
 
130
+ interface ReusableReadDatabase {
131
+ database: SqliteDatabase;
132
+ fileIdentity: string;
133
+ }
134
+
135
+ const reusableReadDatabasePaths = new Set<string>();
136
+ const reusableReadDatabases = new Map<string, ReusableReadDatabase>();
137
+
138
+ function databaseFileIdentity(path: string): string | undefined {
139
+ try {
140
+ const stat = statSync(path);
141
+ // Content changes must not invalidate a WAL reader. Identity changes only
142
+ // when the live database file itself is replaced.
143
+ return stat.ino
144
+ ? `${stat.dev}:${stat.ino}`
145
+ : `${stat.dev}:birth:${stat.birthtimeMs}`;
146
+ } catch {
147
+ return undefined;
148
+ }
149
+ }
150
+
151
+ function closeReusableReadDatabase(path: string): void {
152
+ const cached = reusableReadDatabases.get(path);
153
+ if (!cached) return;
154
+ reusableReadDatabases.delete(path);
155
+ cached.database.close();
156
+ }
157
+
158
+ /**
159
+ * Opt in only for the long-lived Canonical Runtime. CLI, tests, and short-lived
160
+ * workers retain one-open-per-operation semantics so process exit and temporary
161
+ * Controller Home cleanup never depend on hidden SQLite handles.
162
+ */
163
+ export function enableControlPlaneReadConnectionReuse(controllerHome: string): void {
164
+ reusableReadDatabasePaths.add(controlPlaneDatabasePath(controllerHome));
165
+ }
166
+
167
+ export function disableControlPlaneReadConnectionReuse(controllerHome: string): void {
168
+ const path = controlPlaneDatabasePath(controllerHome);
169
+ reusableReadDatabasePaths.delete(path);
170
+ closeReusableReadDatabase(path);
171
+ }
172
+
66
173
  function now(): string {
67
174
  return new Date().toISOString();
68
175
  }
@@ -98,9 +205,28 @@ function scalar(row: unknown): unknown {
98
205
  return Object.values(row as Record<string, unknown>)[0];
99
206
  }
100
207
 
208
+ /**
209
+ * Keep native statement lifetime inside the SQLite adapter. Bun retains a
210
+ * Windows file handle for each unfinalized statement even after db.close(),
211
+ * while Node's StatementSync has no explicit finalizer. The optional call
212
+ * gives both runtimes identical Controller Home release semantics.
213
+ */
214
+ function withSqliteStatement<T>(
215
+ database: SqliteDatabase,
216
+ sql: string,
217
+ operation: (statement: SqliteStatement) => T,
218
+ ): T {
219
+ const statement = database.prepare(sql);
220
+ try {
221
+ return operation(statement);
222
+ } finally {
223
+ statement.finalize?.();
224
+ }
225
+ }
226
+
101
227
  function assertDatabaseIntegrity(database: SqliteDatabase, path: string): void {
102
228
  try {
103
- const result = scalar(database.prepare('PRAGMA quick_check').get());
229
+ const result = withSqliteStatement(database, 'PRAGMA quick_check', (statement) => scalar(statement.get()));
104
230
  if (result !== 'ok') throw new Error(String(result ?? 'missing quick_check result'));
105
231
  } catch (error) {
106
232
  const message = error instanceof Error ? error.message : String(error);
@@ -110,13 +236,21 @@ function assertDatabaseIntegrity(database: SqliteDatabase, path: string): void {
110
236
  }
111
237
 
112
238
  function tableExists(database: SqliteDatabase, table: string): boolean {
113
- const row = database.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(table) as { name?: string } | undefined;
239
+ const row = withSqliteStatement(
240
+ database,
241
+ "SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?",
242
+ (statement) => statement.get(table) as { name?: string } | undefined,
243
+ );
114
244
  return row?.name === table;
115
245
  }
116
246
 
117
247
  function currentSchemaVersion(database: SqliteDatabase): number | undefined {
118
248
  if (!tableExists(database, 'control_plane_schema')) return undefined;
119
- const value = scalar(database.prepare('SELECT MAX(version) AS version FROM control_plane_schema').get());
249
+ const value = withSqliteStatement(
250
+ database,
251
+ 'SELECT MAX(version) AS version FROM control_plane_schema',
252
+ (statement) => scalar(statement.get()),
253
+ );
120
254
  return typeof value === 'number' && Number.isInteger(value) ? value : undefined;
121
255
  }
122
256
 
@@ -163,7 +297,11 @@ function initializeSchema(database: SqliteDatabase): void {
163
297
  CREATE INDEX IF NOT EXISTS control_plane_audit_lookup
164
298
  ON control_plane_audit (namespace, scope, record_key, revision);
165
299
  `);
166
- database.prepare('INSERT OR IGNORE INTO control_plane_schema (version, applied_at) VALUES (?, ?)').run(CONTROL_PLANE_SCHEMA_VERSION, now());
300
+ withSqliteStatement(
301
+ database,
302
+ 'INSERT OR IGNORE INTO control_plane_schema (version, applied_at) VALUES (?, ?)',
303
+ (statement) => statement.run(CONTROL_PLANE_SCHEMA_VERSION, now()),
304
+ );
167
305
  }
168
306
 
169
307
  export function controlPlaneDatabasePath(controllerHome: string): string {
@@ -186,9 +324,10 @@ function openDatabase(controllerHome: string): SqliteDatabase {
186
324
  // inspectOpenDatabase(), which is the authoritative integrity boundary.
187
325
  // Keep schema fail-closed on every open, but reserve the full integrity scan
188
326
  // for those lifecycle/inspection boundaries.
327
+ let schemaVersion: number | undefined;
189
328
  if (existed) {
190
329
  try {
191
- assertSupportedSchema(database, path);
330
+ schemaVersion = assertSupportedSchema(database, path);
192
331
  } catch (error) {
193
332
  const busy = sqliteBusyError(path, error);
194
333
  if (busy) throw busy;
@@ -198,8 +337,14 @@ function openDatabase(controllerHome: string): SqliteDatabase {
198
337
  }
199
338
  }
200
339
  database.exec('PRAGMA journal_mode = WAL;');
201
- initializeSchema(database);
202
- assertSupportedSchema(database, path, true);
340
+ // Schema DDL is bootstrap work, not transaction work. Existing supported
341
+ // databases have already crossed the fail-closed schema boundary above;
342
+ // rerunning CREATE INDEX/TABLE + INSERT OR IGNORE on every write only burns
343
+ // allocations/SQLite preparation and then validates the same schema twice.
344
+ if (schemaVersion === undefined) {
345
+ initializeSchema(database);
346
+ assertSupportedSchema(database, path, true);
347
+ }
203
348
  return database;
204
349
  } catch (error) {
205
350
  database.close();
@@ -238,6 +383,68 @@ function openDatabaseForRead(controllerHome: string): SqliteDatabase {
238
383
  return openDatabase(controllerHome);
239
384
  }
240
385
 
386
+ export function openControlPlaneDatabaseChangeObserver(controllerHome: string): ControlPlaneDatabaseChangeObserver {
387
+ const path = controlPlaneDatabasePath(controllerHome);
388
+ const database = openDatabaseForRead(controllerHome);
389
+ let closed = false;
390
+ const dataVersion = () => {
391
+ if (closed) throw new Error('CONTROL_PLANE_DATABASE_CHANGE_OBSERVER_CLOSED');
392
+ return integerPragma(database, 'PRAGMA data_version', 'data_version');
393
+ };
394
+ // Prime the connection before returning it so the first caller observation
395
+ // is relative to an established SQLite connection, not connection startup.
396
+ dataVersion();
397
+ return {
398
+ path,
399
+ dataVersion,
400
+ close() {
401
+ if (closed) return;
402
+ closed = true;
403
+ database.close();
404
+ },
405
+ };
406
+ }
407
+
408
+ function withDatabaseForRead<T>(controllerHome: string, operation: (database: SqliteDatabase) => T): T {
409
+ const path = controlPlaneDatabasePath(controllerHome);
410
+ if (!reusableReadDatabasePaths.has(path)) {
411
+ const database = openDatabaseForRead(controllerHome);
412
+ try {
413
+ return operation(database);
414
+ } finally {
415
+ database.close();
416
+ }
417
+ }
418
+
419
+ const liveIdentity = databaseFileIdentity(path);
420
+ const cached = reusableReadDatabases.get(path);
421
+ if (cached && liveIdentity && cached.fileIdentity === liveIdentity) {
422
+ return operation(cached.database);
423
+ }
424
+ if (cached) closeReusableReadDatabase(path);
425
+
426
+ const database = openDatabaseForRead(controllerHome);
427
+ const openedIdentity = databaseFileIdentity(path);
428
+ if (!openedIdentity) {
429
+ database.close();
430
+ throw new Error(`CONTROL_PLANE_SQLITE_MISSING: ${path}`);
431
+ }
432
+ reusableReadDatabases.set(path, { database, fileIdentity: openedIdentity });
433
+ try {
434
+ return operation(database);
435
+ } catch (error) {
436
+ // Do not retain a reader that failed while evaluating a query/schema. The
437
+ // next operation gets a clean connection and repeats the fail-closed checks.
438
+ closeReusableReadDatabase(path);
439
+ throw error;
440
+ }
441
+ }
442
+
443
+ /** Read-only snapshot boundary for domain stores that need several indexed lookups in one hot-path operation. */
444
+ export function withControlPlaneReadDatabase<T>(controllerHome: string, operation: (database: SqliteDatabase) => T): T {
445
+ return withDatabaseForRead(controllerHome, operation);
446
+ }
447
+
241
448
  function rowToRecord<T>(row: StoredRecordRow): ControlPlaneRecord<T> {
242
449
  let value: T;
243
450
  try {
@@ -258,11 +465,11 @@ function rowToRecord<T>(row: StoredRecordRow): ControlPlaneRecord<T> {
258
465
  }
259
466
 
260
467
  function selectRecord<T>(database: SqliteDatabase, namespace: string, scope: string, key: string): ControlPlaneRecord<T> | undefined {
261
- const row = database.prepare(`
262
- SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
263
- FROM control_plane_records
264
- WHERE namespace = ? AND scope = ? AND record_key = ?
265
- `).get(namespace, scope, key) as StoredRecordRow | undefined;
468
+ const row = withSqliteStatement(database, `
469
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
470
+ FROM control_plane_records
471
+ WHERE namespace = ? AND scope = ? AND record_key = ?
472
+ `, (statement) => statement.get(namespace, scope, key) as StoredRecordRow | undefined);
266
473
  return row ? rowToRecord<T>(row) : undefined;
267
474
  }
268
475
 
@@ -272,22 +479,30 @@ function writeRecord<T>(
272
479
  existing?: ControlPlaneRecord<T>,
273
480
  ): ControlPlaneRecord<T> {
274
481
  const at = now();
275
- const revision = (existing?.revision ?? 0) + 1;
482
+ const previousAuditRevision = Number(withSqliteStatement(database, `
483
+ SELECT MAX(revision) AS revision FROM control_plane_audit
484
+ WHERE namespace = ? AND scope = ? AND record_key = ?
485
+ `, (statement) => scalar(statement.get(input.namespace, input.scope, input.key))) ?? 0);
486
+ const revision = existing
487
+ ? existing.revision + 1
488
+ : Math.max(0, Number.isSafeInteger(previousAuditRevision) ? previousAuditRevision : 0) + 1;
276
489
  const createdAt = existing?.createdAt ?? at;
277
- database.prepare(`
278
- INSERT INTO control_plane_records (
279
- namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
280
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
281
- ON CONFLICT(namespace, scope, record_key) DO UPDATE SET
282
- schema_version = excluded.schema_version,
283
- revision = excluded.revision,
284
- payload = excluded.payload,
285
- updated_at = excluded.updated_at
286
- `).run(input.namespace, input.scope, input.key, input.schemaVersion, revision, JSON.stringify(input.value), createdAt, at);
287
- database.prepare(`
288
- INSERT INTO control_plane_audit (namespace, scope, record_key, action, revision, occurred_at)
289
- VALUES (?, ?, ?, ?, ?, ?)
290
- `).run(input.namespace, input.scope, input.key, input.action, revision, at);
490
+ withSqliteStatement(database, `
491
+ INSERT INTO control_plane_records (
492
+ namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
493
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
494
+ ON CONFLICT(namespace, scope, record_key) DO UPDATE SET
495
+ schema_version = excluded.schema_version,
496
+ revision = excluded.revision,
497
+ payload = excluded.payload,
498
+ updated_at = excluded.updated_at
499
+ `, (statement) => statement.run(
500
+ input.namespace, input.scope, input.key, input.schemaVersion, revision, JSON.stringify(input.value), createdAt, at,
501
+ ));
502
+ withSqliteStatement(database, `
503
+ INSERT INTO control_plane_audit (namespace, scope, record_key, action, revision, occurred_at)
504
+ VALUES (?, ?, ?, ?, ?, ?)
505
+ `, (statement) => statement.run(input.namespace, input.scope, input.key, input.action, revision, at));
291
506
  return {
292
507
  namespace: input.namespace,
293
508
  scope: input.scope,
@@ -352,12 +567,24 @@ export function withControlPlaneTransaction<T>(controllerHome: string, operation
352
567
  }
353
568
 
354
569
  export function readControlPlaneRecord<T>(controllerHome: string, namespace: string, scope: string, key: string): ControlPlaneRecord<T> | undefined {
355
- const database = openDatabaseForRead(controllerHome);
356
- try {
357
- return selectRecord<T>(database, namespace, scope, key);
358
- } finally {
359
- database.close();
360
- }
570
+ return withDatabaseForRead(controllerHome, (database) =>
571
+ selectRecord<T>(database, namespace, scope, key));
572
+ }
573
+
574
+ export function listControlPlaneRecordKeys(
575
+ controllerHome: string,
576
+ input: { namespace: string; scope: string; limit?: number },
577
+ ): string[] {
578
+ const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 5_000), 5_000));
579
+ return withDatabaseForRead(controllerHome, (database) =>
580
+ withSqliteStatement(database, `
581
+ SELECT record_key
582
+ FROM control_plane_records
583
+ WHERE namespace = ? AND scope = ?
584
+ ORDER BY record_key ASC
585
+ LIMIT ?
586
+ `, (statement) => (statement.all(input.namespace, input.scope, limit) as Array<{ record_key: string }>)
587
+ .map((row) => row.record_key)));
361
588
  }
362
589
 
363
590
  export function writeControlPlaneRecord<T>(
@@ -376,6 +603,72 @@ export function writeControlPlaneRecord<T>(
376
603
  writeControlPlaneRecordWithinTransaction(database, input));
377
604
  }
378
605
 
606
+ export function deleteControlPlaneRecordWithinTransaction(
607
+ database: SqliteDatabase,
608
+ input: { namespace: string; scope: string; key: string; action?: string; expectedRevision?: number },
609
+ ): boolean {
610
+ const existing = selectRecord(database, input.namespace, input.scope, input.key);
611
+ if (input.expectedRevision !== undefined && existing?.revision !== input.expectedRevision) {
612
+ throw new ControlPlaneConflictError(input.namespace, input.scope, input.key, input.expectedRevision, existing?.revision);
613
+ }
614
+ if (!existing) return false;
615
+ const revision = existing.revision + 1;
616
+ const at = now();
617
+ withSqliteStatement(database, `
618
+ DELETE FROM control_plane_records
619
+ WHERE namespace = ? AND scope = ? AND record_key = ?
620
+ `, (statement) => statement.run(input.namespace, input.scope, input.key));
621
+ withSqliteStatement(database, `
622
+ INSERT INTO control_plane_audit (namespace, scope, record_key, action, revision, occurred_at)
623
+ VALUES (?, ?, ?, ?, ?, ?)
624
+ `, (statement) => statement.run(input.namespace, input.scope, input.key, input.action ?? 'delete', revision, at));
625
+ return true;
626
+ }
627
+
628
+ export function deleteControlPlaneRecord(
629
+ controllerHome: string,
630
+ input: { namespace: string; scope: string; key: string; action?: string; expectedRevision?: number },
631
+ ): boolean {
632
+ return withControlPlaneTransaction(controllerHome, (database) =>
633
+ deleteControlPlaneRecordWithinTransaction(database, input));
634
+ }
635
+
636
+ export function deleteControlPlaneRecordsWithinTransaction(
637
+ database: SqliteDatabase,
638
+ input: { namespace: string; scope: string; action?: string },
639
+ ): number {
640
+ const rows = withSqliteStatement(database, `
641
+ SELECT record_key, revision
642
+ FROM control_plane_records
643
+ WHERE namespace = ? AND scope = ?
644
+ ORDER BY record_key ASC
645
+ `, (statement) => statement.all(input.namespace, input.scope) as Array<{ record_key: string; revision: number }>);
646
+ if (rows.length === 0) return 0;
647
+
648
+ const at = now();
649
+ withSqliteStatement(database, `
650
+ DELETE FROM control_plane_records
651
+ WHERE namespace = ? AND scope = ? AND record_key = ?
652
+ `, (remove) => withSqliteStatement(database, `
653
+ INSERT INTO control_plane_audit (namespace, scope, record_key, action, revision, occurred_at)
654
+ VALUES (?, ?, ?, ?, ?, ?)
655
+ `, (audit) => {
656
+ for (const row of rows) {
657
+ remove.run(input.namespace, input.scope, row.record_key);
658
+ audit.run(input.namespace, input.scope, row.record_key, input.action ?? 'delete_scope', row.revision + 1, at);
659
+ }
660
+ }));
661
+ return rows.length;
662
+ }
663
+
664
+ export function deleteControlPlaneRecords(
665
+ controllerHome: string,
666
+ input: { namespace: string; scope: string; action?: string },
667
+ ): number {
668
+ return withControlPlaneTransaction(controllerHome, (database) =>
669
+ deleteControlPlaneRecordsWithinTransaction(database, input));
670
+ }
671
+
379
672
  export class ControlPlaneConflictError extends Error {
380
673
  readonly expectedRevision: number | null;
381
674
  readonly actualRevision: number | undefined;
@@ -388,32 +681,162 @@ export class ControlPlaneConflictError extends Error {
388
681
  }
389
682
  }
390
683
 
684
+ export function listControlPlaneRecordsWithinTransaction<T>(
685
+ database: SqliteDatabase,
686
+ input: { namespace: string; scope?: string; limit?: number },
687
+ ): ControlPlaneRecord<T>[] {
688
+ const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 1000), 5000));
689
+ const rows = input.scope === undefined
690
+ ? withSqliteStatement(database, `
691
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
692
+ FROM control_plane_records
693
+ WHERE namespace = ?
694
+ ORDER BY updated_at ASC, record_key ASC
695
+ LIMIT ?
696
+ `, (statement) => statement.all(input.namespace, limit))
697
+ : withSqliteStatement(database, `
698
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
699
+ FROM control_plane_records
700
+ WHERE namespace = ? AND scope = ?
701
+ ORDER BY updated_at ASC, record_key ASC
702
+ LIMIT ?
703
+ `, (statement) => statement.all(input.namespace, input.scope, limit));
704
+ return (rows as StoredRecordRow[]).map((row) => rowToRecord<T>(row));
705
+ }
706
+
391
707
  export function listControlPlaneRecords<T>(
392
708
  controllerHome: string,
393
709
  input: { namespace: string; scope?: string; limit?: number },
394
710
  ): ControlPlaneRecord<T>[] {
395
- const database = openDatabaseForRead(controllerHome);
396
- try {
397
- const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 1000), 5000));
398
- const rows = input.scope === undefined
399
- ? database.prepare(`
400
- SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
401
- FROM control_plane_records
402
- WHERE namespace = ?
403
- ORDER BY updated_at ASC, record_key ASC
404
- LIMIT ?
405
- `).all(input.namespace, limit)
406
- : database.prepare(`
407
- SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
408
- FROM control_plane_records
409
- WHERE namespace = ? AND scope = ?
410
- ORDER BY updated_at ASC, record_key ASC
411
- LIMIT ?
412
- `).all(input.namespace, input.scope, limit);
711
+ return withDatabaseForRead(controllerHome, (database) =>
712
+ listControlPlaneRecordsWithinTransaction<T>(database, input));
713
+ }
714
+
715
+ /** Exact stable-key lookup across scopes. This is a read of the existing Control Plane authority, not a second id registry. */
716
+ export function findControlPlaneRecordsByKey<T>(
717
+ controllerHome: string,
718
+ input: { namespace: string; key: string; limit?: number },
719
+ ): ControlPlaneRecord<T>[] {
720
+ const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 2), 100));
721
+ return withDatabaseForRead(controllerHome, (database) => {
722
+ const rows = withSqliteStatement(database, `
723
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
724
+ FROM control_plane_records
725
+ WHERE namespace = ? AND record_key = ?
726
+ ORDER BY scope ASC
727
+ LIMIT ?
728
+ `, (statement) => statement.all(input.namespace, input.key, limit));
413
729
  return (rows as StoredRecordRow[]).map((row) => rowToRecord<T>(row));
414
- } finally {
415
- database.close();
730
+ });
731
+ }
732
+
733
+ /**
734
+ * Candidate-only read optimization for top-level JSON text fields. This never
735
+ * establishes domain authority: missing/null/non-text values remain candidates,
736
+ * and callers must still run their canonical semantic validation.
737
+ */
738
+ export interface PayloadTextExclusion {
739
+ namespace: string;
740
+ field: string;
741
+ excludedValues: readonly string[];
742
+ }
743
+
744
+ function payloadTextExclusionPredicate(input: PayloadTextExclusion): string {
745
+ const field = input.field.trim();
746
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(field)) {
747
+ throw new Error(`CONTROL_PLANE_JSON_FIELD_INVALID: ${input.field}`);
416
748
  }
749
+ const values = [...new Set(input.excludedValues.map((value) => value.trim()).filter(Boolean))].sort();
750
+ if (values.length > 32) throw new Error('CONTROL_PLANE_JSON_EXCLUSION_LIMIT_EXCEEDED');
751
+ // These literals are escaped here, never supplied as SQL by a domain caller.
752
+ // SQLite must see identical literals in the query and partial-index predicate.
753
+ const literal = (value: string) => `'${value.replaceAll("'", "''")}'`;
754
+ const namespace = `namespace = ${literal(input.namespace)}`;
755
+ if (values.length === 0) return namespace;
756
+ const extraction = `json_extract(payload, ${literal(`$.${field}`)})`;
757
+ return `${namespace} AND (${extraction} IS NULL OR ${extraction} NOT IN (${values.map(literal).join(', ')}))`;
758
+ }
759
+
760
+ function payloadTextExclusionIndexName(input: PayloadTextExclusion, predicate = payloadTextExclusionPredicate(input)): string {
761
+ const prefix = `control_plane_candidate_${createHash('sha256').update(`${input.namespace}/${input.field}`).digest('hex').slice(0, 16)}_`;
762
+ return `${prefix}${createHash('sha256').update(predicate).digest('hex').slice(0, 16)}`;
763
+ }
764
+
765
+ /** Explicit startup DDL. JSON remains authority; SQLite maintains the derived index in every write transaction. */
766
+ export function initializeControlPlanePayloadTextExclusionIndex(
767
+ controllerHome: string,
768
+ input: PayloadTextExclusion,
769
+ ): void {
770
+ const predicate = payloadTextExclusionPredicate(input);
771
+ const prefix = `control_plane_candidate_${createHash('sha256').update(`${input.namespace}/${input.field}`).digest('hex').slice(0, 16)}_`;
772
+ const name = payloadTextExclusionIndexName(input, predicate);
773
+ withControlPlaneTransaction(controllerHome, (database) => {
774
+ database.exec(`CREATE INDEX IF NOT EXISTS ${name} ON control_plane_records (scope, updated_at, record_key) WHERE ${predicate}`);
775
+ // Retire obsolete predicates for this domain field in the same transaction.
776
+ const old = withSqliteStatement(database, "SELECT name FROM sqlite_master WHERE type = 'index' AND substr(name, 1, ?) = ?",
777
+ (statement) => statement.all(prefix.length, prefix)) as Array<{ name: string }>;
778
+ for (const index of old) {
779
+ if (index.name !== name && /^control_plane_candidate_[a-f0-9]{16}_[a-f0-9]{16}$/.test(index.name)) {
780
+ database.exec(`DROP INDEX ${index.name}`);
781
+ }
782
+ }
783
+ });
784
+ }
785
+
786
+ export function listControlPlaneRecordsExcludingPayloadTextValues<T>(
787
+ controllerHome: string,
788
+ input: PayloadTextExclusion & { scope: string; limit?: number },
789
+ ): ControlPlaneRecord<T>[] {
790
+ const predicate = payloadTextExclusionPredicate(input);
791
+ const candidateIndex = payloadTextExclusionIndexName(input, predicate);
792
+ const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 1_000), 5_000));
793
+ return withDatabaseForRead(controllerHome, (database) => {
794
+ const candidateIndexExists = withSqliteStatement(database,
795
+ "SELECT 1 AS present FROM sqlite_master WHERE type = 'index' AND name = ? LIMIT 1",
796
+ (statement) => statement.get(candidateIndex) != null);
797
+ const indexHint = candidateIndexExists ? ` INDEXED BY ${candidateIndex}` : '';
798
+ const rows = withSqliteStatement(database, `
799
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
800
+ FROM control_plane_records${indexHint}
801
+ WHERE ${predicate} AND scope = ?
802
+ ORDER BY updated_at ASC, record_key ASC
803
+ LIMIT ?
804
+ `, (statement) => statement.all(input.scope, limit));
805
+ return (rows as StoredRecordRow[]).map((row) => rowToRecord<T>(row));
806
+ });
807
+ }
808
+
809
+ /**
810
+ * Internal correctness surface for authorities that must observe every durable
811
+ * fact in a namespace/scope. User-facing and diagnostic list APIs should keep
812
+ * using listControlPlaneRecords so their output remains bounded.
813
+ */
814
+ export function listAllControlPlaneRecordsWithinTransaction<T>(
815
+ database: SqliteDatabase,
816
+ input: { namespace: string; scope?: string },
817
+ ): ControlPlaneRecord<T>[] {
818
+ const rows = input.scope === undefined
819
+ ? withSqliteStatement(database, `
820
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
821
+ FROM control_plane_records
822
+ WHERE namespace = ?
823
+ ORDER BY updated_at ASC, record_key ASC
824
+ `, (statement) => statement.all(input.namespace))
825
+ : withSqliteStatement(database, `
826
+ SELECT namespace, scope, record_key, schema_version, revision, payload, created_at, updated_at
827
+ FROM control_plane_records
828
+ WHERE namespace = ? AND scope = ?
829
+ ORDER BY updated_at ASC, record_key ASC
830
+ `, (statement) => statement.all(input.namespace, input.scope));
831
+ return (rows as StoredRecordRow[]).map((row) => rowToRecord<T>(row));
832
+ }
833
+
834
+ export function listAllControlPlaneRecords<T>(
835
+ controllerHome: string,
836
+ input: { namespace: string; scope?: string },
837
+ ): ControlPlaneRecord<T>[] {
838
+ return withDatabaseForRead(controllerHome, (database) =>
839
+ listAllControlPlaneRecordsWithinTransaction<T>(database, input));
417
840
  }
418
841
 
419
842
  function inspectOpenDatabase(database: SqliteDatabase, path: string): ControlPlaneDatabaseInspection {
@@ -422,34 +845,42 @@ function inspectOpenDatabase(database: SqliteDatabase, path: string): ControlPla
422
845
  if (!tableExists(database, 'control_plane_records') || !tableExists(database, 'control_plane_audit')) {
423
846
  throw new Error(`CONTROL_PLANE_BACKUP_INVALID: ${path}: required tables are missing`);
424
847
  }
425
- const recordCount = Number(scalar(database.prepare('SELECT COUNT(*) AS count FROM control_plane_records').get()) ?? 0);
426
- const auditEventCount = Number(scalar(database.prepare('SELECT COUNT(*) AS count FROM control_plane_audit').get()) ?? 0);
427
- const orphanRecordCount = Number(scalar(database.prepare(`
428
- SELECT COUNT(*) AS count
429
- FROM control_plane_records record
430
- WHERE (
431
- SELECT COUNT(DISTINCT audit.revision)
432
- FROM control_plane_audit audit
433
- WHERE audit.namespace = record.namespace
434
- AND audit.scope = record.scope
435
- AND audit.record_key = record.record_key
436
- AND audit.revision BETWEEN 1 AND record.revision
437
- ) != record.revision
438
- OR COALESCE((
439
- SELECT MIN(audit.revision)
440
- FROM control_plane_audit audit
441
- WHERE audit.namespace = record.namespace
442
- AND audit.scope = record.scope
443
- AND audit.record_key = record.record_key
444
- ), 0) != 1
445
- OR COALESCE((
446
- SELECT MAX(audit.revision)
848
+ const recordCount = Number(withSqliteStatement(
849
+ database,
850
+ 'SELECT COUNT(*) AS count FROM control_plane_records',
851
+ (statement) => scalar(statement.get()),
852
+ ) ?? 0);
853
+ const auditEventCount = Number(withSqliteStatement(
854
+ database,
855
+ 'SELECT COUNT(*) AS count FROM control_plane_audit',
856
+ (statement) => scalar(statement.get()),
857
+ ) ?? 0);
858
+ const orphanRecordCount = Number(withSqliteStatement(database, `
859
+ SELECT COUNT(*) AS count
860
+ FROM control_plane_records record
861
+ WHERE (
862
+ SELECT COUNT(DISTINCT audit.revision)
447
863
  FROM control_plane_audit audit
448
864
  WHERE audit.namespace = record.namespace
449
865
  AND audit.scope = record.scope
450
866
  AND audit.record_key = record.record_key
451
- ), 0) != record.revision
452
- `).get()) ?? 0);
867
+ AND audit.revision BETWEEN 1 AND record.revision
868
+ ) != record.revision
869
+ OR COALESCE((
870
+ SELECT MIN(audit.revision)
871
+ FROM control_plane_audit audit
872
+ WHERE audit.namespace = record.namespace
873
+ AND audit.scope = record.scope
874
+ AND audit.record_key = record.record_key
875
+ ), 0) != 1
876
+ OR COALESCE((
877
+ SELECT MAX(audit.revision)
878
+ FROM control_plane_audit audit
879
+ WHERE audit.namespace = record.namespace
880
+ AND audit.scope = record.scope
881
+ AND audit.record_key = record.record_key
882
+ ), 0) != record.revision
883
+ `, (statement) => scalar(statement.get())) ?? 0);
453
884
  if (orphanRecordCount > 0) {
454
885
  throw new Error(`CONTROL_PLANE_AUDIT_CONTINUITY_INVALID: ${path}: discontinuous_records=${orphanRecordCount}`);
455
886
  }
@@ -476,6 +907,147 @@ export function inspectControlPlaneDatabaseFile(path: string): ControlPlaneDatab
476
907
  }
477
908
  }
478
909
 
910
+ interface ControlPlaneDatabasePageSnapshot {
911
+ pageSize: number;
912
+ pageCount: number;
913
+ freePageCount: number;
914
+ reclaimableBytes: number;
915
+ reclaimableRatio: number;
916
+ }
917
+
918
+ function integerPragma(database: SqliteDatabase, sql: string, label: string): number {
919
+ const value = Number(withSqliteStatement(database, sql, (statement) => scalar(statement.get())));
920
+ if (!Number.isFinite(value) || value < 0) throw new Error(`CONTROL_PLANE_SQLITE_PRAGMA_INVALID: ${label}`);
921
+ return Math.floor(value);
922
+ }
923
+
924
+ function controlPlaneDatabasePageSnapshot(database: SqliteDatabase): ControlPlaneDatabasePageSnapshot {
925
+ const pageSize = integerPragma(database, 'PRAGMA page_size', 'page_size');
926
+ const pageCount = integerPragma(database, 'PRAGMA page_count', 'page_count');
927
+ const freePageCount = integerPragma(database, 'PRAGMA freelist_count', 'freelist_count');
928
+ const reclaimableBytes = freePageCount * pageSize;
929
+ return {
930
+ pageSize,
931
+ pageCount,
932
+ freePageCount,
933
+ reclaimableBytes,
934
+ reclaimableRatio: pageCount > 0 ? freePageCount / pageCount : 0,
935
+ };
936
+ }
937
+
938
+ /**
939
+ * Physical SQLite maintenance only. Domain lifecycles own row deletion; this
940
+ * boundary never decides semantic terminality or removes canonical records.
941
+ * PASSIVE checkpointing is non-blocking, and VACUUM is attempted only when
942
+ * already-reclaimable free pages exceed both configured thresholds. A live
943
+ * writer causes a fail-closed busy skip instead of lock stealing or corruption
944
+ * recovery.
945
+ */
946
+ export function maintainControlPlaneDatabase(
947
+ controllerHome: string,
948
+ options: ControlPlaneDatabaseMaintenanceOptions = {},
949
+ ): ControlPlaneDatabaseMaintenanceReport {
950
+ const path = controlPlaneDatabasePath(controllerHome);
951
+ const report: ControlPlaneDatabaseMaintenanceReport = {
952
+ policyVersion: CONTROL_PLANE_SQLITE_MAINTENANCE_POLICY_VERSION,
953
+ path,
954
+ checkpointMode: 'passive',
955
+ checkpointed: false,
956
+ pageSize: 0,
957
+ pageCountBefore: 0,
958
+ freePageCountBefore: 0,
959
+ reclaimableBytesBefore: 0,
960
+ reclaimableRatioBefore: 0,
961
+ vacuumEligible: false,
962
+ vacuumAttempted: false,
963
+ vacuumed: false,
964
+ pageCountAfter: 0,
965
+ freePageCountAfter: 0,
966
+ reclaimableBytesAfter: 0,
967
+ reclaimableRatioAfter: 0,
968
+ reclaimedBytes: 0,
969
+ };
970
+ if (!existsSync(path)) {
971
+ report.skippedReason = 'database_missing';
972
+ return report;
973
+ }
974
+
975
+ let database: SqliteDatabase;
976
+ try {
977
+ database = openRawDatabase(path);
978
+ } catch (error) {
979
+ if ((error instanceof Error ? error.message : String(error)).startsWith('CONTROL_PLANE_SQLITE_BUSY:')) {
980
+ report.skippedReason = 'database_busy';
981
+ return report;
982
+ }
983
+ throw error;
984
+ }
985
+ try {
986
+ database.exec('PRAGMA busy_timeout = 0; PRAGMA foreign_keys = ON;');
987
+ assertSupportedSchema(database, path, true);
988
+ try {
989
+ database.exec('PRAGMA wal_checkpoint(PASSIVE);');
990
+ report.checkpointed = true;
991
+ } catch (error) {
992
+ const busy = sqliteBusyError(path, error);
993
+ if (busy) {
994
+ report.skippedReason = 'database_busy';
995
+ return report;
996
+ }
997
+ throw error;
998
+ }
999
+
1000
+ const before = controlPlaneDatabasePageSnapshot(database);
1001
+ report.pageSize = before.pageSize;
1002
+ report.pageCountBefore = before.pageCount;
1003
+ report.freePageCountBefore = before.freePageCount;
1004
+ report.reclaimableBytesBefore = before.reclaimableBytes;
1005
+ report.reclaimableRatioBefore = before.reclaimableRatio;
1006
+ report.pageCountAfter = before.pageCount;
1007
+ report.freePageCountAfter = before.freePageCount;
1008
+ report.reclaimableBytesAfter = before.reclaimableBytes;
1009
+ report.reclaimableRatioAfter = before.reclaimableRatio;
1010
+
1011
+ const minimumReclaimableBytes = Math.max(0, Math.floor(options.minimumReclaimableBytes ?? DEFAULT_CONTROL_PLANE_SQLITE_VACUUM_MIN_RECLAIMABLE_BYTES));
1012
+ const configuredRatio = Number(options.minimumReclaimableRatio ?? DEFAULT_CONTROL_PLANE_SQLITE_VACUUM_MIN_RECLAIMABLE_RATIO);
1013
+ const minimumReclaimableRatio = Number.isFinite(configuredRatio) ? Math.min(1, Math.max(0, configuredRatio)) : DEFAULT_CONTROL_PLANE_SQLITE_VACUUM_MIN_RECLAIMABLE_RATIO;
1014
+ report.vacuumEligible = before.freePageCount > 0
1015
+ && before.reclaimableBytes >= minimumReclaimableBytes
1016
+ && before.reclaimableRatio >= minimumReclaimableRatio;
1017
+ if (!report.vacuumEligible) {
1018
+ report.skippedReason = 'below_threshold';
1019
+ return report;
1020
+ }
1021
+ if (options.allowVacuum === false) {
1022
+ report.skippedReason = 'vacuum_disabled';
1023
+ return report;
1024
+ }
1025
+
1026
+ report.vacuumAttempted = true;
1027
+ try {
1028
+ database.exec('VACUUM;');
1029
+ } catch (error) {
1030
+ const busy = sqliteBusyError(path, error);
1031
+ if (busy) {
1032
+ report.skippedReason = 'database_busy';
1033
+ return report;
1034
+ }
1035
+ throw error;
1036
+ }
1037
+ assertDatabaseIntegrity(database, path);
1038
+ const after = controlPlaneDatabasePageSnapshot(database);
1039
+ report.vacuumed = true;
1040
+ report.pageCountAfter = after.pageCount;
1041
+ report.freePageCountAfter = after.freePageCount;
1042
+ report.reclaimableBytesAfter = after.reclaimableBytes;
1043
+ report.reclaimableRatioAfter = after.reclaimableRatio;
1044
+ report.reclaimedBytes = Math.max(0, (before.pageCount - after.pageCount) * before.pageSize);
1045
+ return report;
1046
+ } finally {
1047
+ database.close();
1048
+ }
1049
+ }
1050
+
479
1051
  export function backupControlPlaneDatabase(controllerHome: string, destinationPath: string): ControlPlaneDatabaseInspection {
480
1052
  const destination = join(destinationPath);
481
1053
  if (existsSync(destination)) throw new Error(`CONTROL_PLANE_BACKUP_EXISTS: ${destination}`);
@@ -497,6 +1069,10 @@ export function backupControlPlaneDatabase(controllerHome: string, destinationPa
497
1069
  export function restoreControlPlaneDatabase(controllerHome: string, backupPath: string): ControlPlaneDatabaseInspection {
498
1070
  if (!existsSync(backupPath)) throw new Error(`CONTROL_PLANE_BACKUP_MISSING: ${backupPath}`);
499
1071
  const target = controlPlaneDatabasePath(controllerHome);
1072
+ // A reusable Runtime reader refers to the current inode, not the pathname.
1073
+ // Close it before replacing the live authority; reuse remains enabled and the
1074
+ // next read opens the verified replacement.
1075
+ closeReusableReadDatabase(target);
500
1076
  const staging = `${target}.restore-${process.pid}-${Date.now()}`;
501
1077
  copyFileSync(backupPath, staging);
502
1078
  try {