@moretea-labs/forge 1.8.0 → 1.8.2

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 (695) hide show
  1. package/AGENTS.md +73 -7
  2. package/CHANGELOG.md +34 -0
  3. package/CLAUDE.md +17 -2
  4. package/CONTRIBUTING.md +3 -2
  5. package/README.en.md +24 -3
  6. package/README.md +10 -6
  7. package/README.zh-CN.md +5 -3
  8. package/THIRD_PARTY_NOTICES.md +1 -2
  9. package/adapters/chatgpt/browser-delivery-host.ts +7 -1
  10. package/adapters/chatgpt/browser-delivery-runtime.ts +531 -56
  11. package/adapters/chatgpt/controller-binding-store.ts +101 -22
  12. package/adapters/chatgpt/controller-round-host.ts +15 -25
  13. package/adapters/chatgpt/controller-round-settlement-store.ts +58 -4
  14. package/adapters/chatgpt/provider-delivery.ts +229 -2
  15. package/adapters/chatgpt/work-conversation-binding-store.ts +99 -54
  16. package/adapters/computer/chatgpt-extension-target.ts +708 -0
  17. package/adapters/computer/chrome-extension/background.js +541 -0
  18. package/adapters/computer/chrome-extension/content.js +402 -0
  19. package/adapters/computer/chrome-extension/core.js +24 -0
  20. package/adapters/computer/chrome-extension/manifest.json +32 -0
  21. package/adapters/computer/desktop-operator-discovery.ts +11 -31
  22. package/adapters/computer/desktop-operator-provider.ts +18 -6
  23. package/adapters/computer/interaction-target-authority.ts +178 -24
  24. package/adapters/mcp/access-mode.ts +18 -39
  25. package/adapters/mcp/auth.ts +2 -7
  26. package/adapters/mcp/instructions.ts +6 -5
  27. package/adapters/mcp/multi-repository.ts +10 -47
  28. package/adapters/mcp/oauth.ts +73 -0
  29. package/adapters/mcp/paths.ts +4 -1
  30. package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
  31. package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
  32. package/adapters/mcp/runtime-gateway/context-adapter.ts +490 -113
  33. package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +83 -0
  34. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +20 -5
  35. package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
  36. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +10 -27
  37. package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
  38. package/adapters/mcp/runtime-gateway/execution-tools.ts +14 -54
  39. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +2 -1
  40. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +132 -47
  41. package/adapters/mcp/runtime-gateway/process-tools.ts +173 -23
  42. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +16 -4
  43. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +69 -52
  44. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +4 -35
  45. package/adapters/mcp/runtime-gateway/result-adapter.ts +10 -4
  46. package/adapters/mcp/runtime-gateway/router.ts +19 -507
  47. package/adapters/mcp/runtime-gateway/routing-policy.ts +506 -0
  48. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +15 -66
  49. package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
  50. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +123 -178
  51. package/adapters/mcp/runtime-gateway/runtime-tools.ts +11 -5
  52. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +37 -1
  53. package/adapters/mcp/runtime-gateway/shared-adapter.ts +20 -0
  54. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +416 -270
  55. package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +140 -0
  56. package/adapters/mcp/runtime-gateway/work-adapter.ts +262 -3123
  57. package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
  58. package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
  59. package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +229 -0
  60. package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
  61. package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
  62. package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
  63. package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
  64. package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
  65. package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +320 -0
  66. package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
  67. package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +180 -0
  68. package/adapters/mcp/server.ts +91 -26
  69. package/adapters/mcp/setup.ts +5 -13
  70. package/adapters/mcp/tool-mapping/access-tools.ts +0 -6
  71. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +703 -3092
  72. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +5 -10
  73. package/adapters/mcp/tool-mapping/repository-tools.ts +170 -213
  74. package/adapters/mcp/toolset-names.ts +11 -37
  75. package/adapters/mcp/toolset.ts +19 -92
  76. package/{src/cli → adapters}/mcp/transports/AGENTS.md +5 -5
  77. package/{src/cli → adapters}/mcp/transports/CLAUDE.md +5 -5
  78. package/adapters/mcp/transports/http-observation.ts +377 -0
  79. package/adapters/mcp/transports/http-post-lifecycle.ts +19 -0
  80. package/adapters/mcp/transports/http.ts +200 -683
  81. package/adapters/mcp/transports/oauth-http.ts +322 -0
  82. package/adapters/mcp/transports/session-registry.ts +6 -0
  83. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +89 -5
  84. package/adapters/mcp/types.ts +0 -6
  85. package/assets/hooks/lib/session-state.sh +0 -38
  86. package/assets/hooks/post-bash.sh +1 -1
  87. package/assets/hooks/post-tool-observer.sh +0 -5
  88. package/assets/hooks/prompt-guard.sh +0 -55
  89. package/assets/partials/04-project-structure.partial.md +1 -1
  90. package/assets/partials/07-footer.partial.md +1 -1
  91. package/assets/partials-agents/02-operating-mode.partial.md +1 -1
  92. package/assets/partials-agents/08-deep-docs.partial.md +1 -1
  93. package/assets/plugin-registry.v1.json +6 -4
  94. package/assets/reference-configs/agentic-development-flow.md +26 -1
  95. package/assets/reference-configs/external-tooling.md +0 -76
  96. package/assets/reference-configs/global-working-rules.md +19 -0
  97. package/assets/reference-configs/release-deploy.md +1 -1
  98. package/assets/templates/helpers/architecture-queue.sh +1 -1
  99. package/assets/templates/helpers/check-agent-tooling.sh +1 -327
  100. package/assets/templates/helpers/check-task-workflow.sh +1 -1
  101. package/assets/templates/helpers/ensure-task-workflow.sh +0 -14
  102. package/assets/templates/helpers/migrate-project-template.sh +2 -2
  103. package/assets/workflow-contract.v1.json +0 -2
  104. package/docs/README.md +5 -2
  105. package/docs/architecture/CURRENT.md +205 -51
  106. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
  107. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
  108. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +42 -0
  109. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +2 -0
  110. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +19 -4
  111. package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
  112. package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
  113. package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +234 -0
  114. package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
  115. package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +74 -0
  116. package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +342 -0
  117. package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
  118. package/docs/architecture/decisions/20261009-exact-code-context-discovery.md +19 -0
  119. package/docs/architecture/decisions/20261009-generated-xcode-cache-retention.md +37 -0
  120. package/docs/architecture/decisions/20261011-autonomous-continuation-convergence.md +597 -0
  121. package/docs/architecture/decisions/20261017-edit-session-finalization-reconciliation.md +17 -0
  122. package/docs/architecture/decisions/20261019-retire-host-skills.md +16 -0
  123. package/docs/architecture/diagrams/agentic-dev-plugin-architecture.html +1 -1
  124. package/docs/architecture/domains/public-surface.md +3 -5
  125. package/docs/architecture/domains/verification.md +6 -5
  126. package/docs/architecture/domains/workflow-engine.md +6 -0
  127. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +3 -3
  128. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +4 -4
  129. package/docs/architecture/reviews/20261015-code-search-runtime-acceptance.md +75 -0
  130. package/docs/forge-plugin-management.md +16 -4
  131. package/docs/operations/controller-browser-plugin.md +23 -0
  132. package/docs/operations/controller-performance-and-502.md +17 -0
  133. package/docs/operations/controller-reliability-runbook.md +2 -2
  134. package/docs/operations/features.md +6 -7
  135. package/docs/operations/features.zh-CN.md +1 -1
  136. package/docs/operations/github-repository.md +4 -4
  137. package/docs/operations/github-semantic-release-projection.md +20 -0
  138. package/docs/operations/homebrew.md +2 -2
  139. package/docs/operations/mcp-tool-exposure.md +13 -52
  140. package/docs/operations/releasing.md +2 -0
  141. package/docs/operations/runtime-performance-diagnostics.md +8 -0
  142. package/docs/operations/standalone-disaster-recovery.md +15 -7
  143. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  144. package/docs/tutorials/03-first-repository-task.md +2 -0
  145. package/docs/wiki/Computer-and-Plugins.md +78 -0
  146. package/docs/wiki/Computer-and-Plugins.zh-CN.md +88 -0
  147. package/docs/wiki/Home.md +16 -2
  148. package/docs/wiki/Implementation.md +25 -13
  149. package/docs/wiki/Installation.md +28 -32
  150. package/docs/wiki/Integrations.md +4 -0
  151. package/docs/wiki/Quick-Start.md +35 -22
  152. package/docs/wiki/Releases-and-Upgrades.md +15 -25
  153. package/docs/wiki/_Sidebar.md +5 -1
  154. package/package.json +25 -25
  155. package/packages/kernel/cognition/api/index.ts +6 -0
  156. package/packages/kernel/cognition/application/activation-service.ts +451 -0
  157. package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
  158. package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
  159. package/packages/kernel/cognition/application/memory-service.ts +64 -0
  160. package/packages/kernel/cognition/domain/learning.ts +106 -0
  161. package/packages/kernel/cognition/domain/memory.ts +254 -0
  162. package/packages/kernel/controller/api/index.ts +1 -0
  163. package/packages/kernel/controller/application/continuation-service.ts +281 -0
  164. package/packages/kernel/controller/application/controller-round-service.ts +12 -0
  165. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +242 -21
  166. package/packages/kernel/controller/domain/controller-round.ts +4 -1
  167. package/packages/kernel/controller/domain/execution-quality.ts +3 -1
  168. package/packages/kernel/controller/infrastructure/controller-round-store.ts +689 -103
  169. package/packages/kernel/controller/infrastructure/controller-session-store.ts +348 -97
  170. package/packages/kernel/controller/ports/controller-host.ts +2 -0
  171. package/packages/kernel/identity/api/index.ts +10 -0
  172. package/packages/kernel/identity/domain/activity-projection.ts +43 -0
  173. package/packages/kernel/identity/domain/capability-broker.ts +60 -0
  174. package/packages/kernel/identity/domain/grant.ts +72 -0
  175. package/packages/kernel/identity/domain/owned-resource.ts +116 -0
  176. package/packages/kernel/identity/domain/user-request.ts +93 -0
  177. package/packages/kernel/identity/infrastructure/activity-projection-store.ts +87 -0
  178. package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
  179. package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
  180. package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
  181. package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
  182. package/packages/kernel/scheduler/api/index.ts +0 -2
  183. package/packages/kernel/scheduler/application/eligibility.ts +4 -1
  184. package/packages/kernel/scheduler/application/schedule-service.ts +3 -0
  185. package/packages/kernel/scheduler/application/settlement.ts +0 -15
  186. package/packages/kernel/scheduler/domain/schedule.ts +87 -0
  187. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +166 -143
  188. package/packages/kernel/work/api/index.ts +3 -2
  189. package/packages/kernel/work/application/work-service.ts +18 -42
  190. package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
  191. package/packages/kernel/work/domain/engineering-design.ts +10 -5
  192. package/packages/kernel/work/domain/engineering-profile.ts +8 -12
  193. package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
  194. package/packages/kernel/work/domain/execution-concurrency.ts +19 -26
  195. package/packages/kernel/work/domain/execution-snapshot.ts +0 -31
  196. package/packages/kernel/work/domain/implementation-review.ts +14 -331
  197. package/packages/kernel/work/domain/objective-graph.ts +119 -0
  198. package/packages/kernel/work/domain/project-engineering-contract.ts +23 -1
  199. package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
  200. package/packages/kernel/work/domain/state-machine.ts +34 -65
  201. package/packages/kernel/work/domain/types.ts +57 -51
  202. package/packages/kernel/work/infrastructure/work-contract-store.ts +847 -729
  203. package/packages/kernel/work/ports/work-contract-store.ts +5 -2
  204. package/packages/plugin-runtime/browser/provider-registry.ts +12 -36
  205. package/packages/plugin-runtime/browser/session-authority.ts +2 -1
  206. package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
  207. package/packages/plugin-runtime/computer/conversation-target.ts +153 -0
  208. package/packages/plugin-runtime/computer/index.ts +2 -0
  209. package/packages/plugin-runtime/computer/target-authority.ts +26 -1
  210. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +142 -18
  211. package/packages/protocols/computer/contract.ts +27 -1
  212. package/scripts/activate-source-baseline.ts +7 -9
  213. package/scripts/assemble-template.ts +11 -72
  214. package/scripts/canary-controller-semantic-wait.ts +12 -24
  215. package/scripts/check-mcp-compatibility.ts +94 -201
  216. package/scripts/check-package-identity.mjs +0 -5
  217. package/scripts/check-runtime-architecture.mjs +921 -239
  218. package/scripts/forge-local-recovery-helper.mjs +93 -15
  219. package/scripts/init-project.sh +0 -46
  220. package/scripts/install-ios-hid-toolchain.sh +60 -0
  221. package/scripts/install-standalone-recovery.ts +1 -1
  222. package/scripts/lib/project-init-lib.sh +6 -3
  223. package/scripts/migrate-windows-wsl-controller-home.ts +2 -0
  224. package/scripts/open-source-audit-allowlist.txt +1 -3
  225. package/scripts/run-bounded-command.mjs +5 -0
  226. package/scripts/run-governed-gate.ts +2 -1
  227. package/scripts/run-mobile-monitor-bridge.ts +0 -1
  228. package/scripts/smoke-control-plane-failure-domain.ts +94 -0
  229. package/scripts/smoke-ios-physical-device.ts +38 -10
  230. package/scripts/smoke-mcp-http-runtime.ts +12 -8
  231. package/scripts/smoke-mcp-tool-surface.ts +27 -41
  232. package/scripts/smoke-runtime-recovery.ts +12 -43
  233. package/scripts/smoke-schedule-engine.ts +23 -15
  234. package/scripts/smoke-unattended-transport-recovery.ts +223 -0
  235. package/scripts/smoke-user-request-continuation-restart.ts +232 -0
  236. package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +95 -0
  237. package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
  238. package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
  239. package/scripts/smoke-workflow-supervisor.ts +103 -0
  240. package/scripts/sync-generated-authority.mjs +0 -1
  241. package/scripts/verify-forge-runtime.sh +2 -1
  242. package/src/cli/agent-jobs/executor-health.ts +12 -35
  243. package/src/cli/agent-jobs/integration.ts +20 -51
  244. package/src/cli/agent-jobs/job-manager.ts +7 -3
  245. package/src/cli/chatgpt-browser/file-policy.ts +2 -2
  246. package/src/cli/commands/brain-assistant.ts +27 -13
  247. package/src/cli/commands/brain.ts +13 -15
  248. package/src/cli/commands/chatgpt.ts +1 -163
  249. package/src/cli/commands/doctor.ts +1 -143
  250. package/src/cli/commands/global-runtime.ts +3 -196
  251. package/src/cli/commands/init-hook.ts +6 -23
  252. package/src/cli/commands/init.ts +3 -252
  253. package/src/cli/commands/mcp.ts +0 -5
  254. package/src/cli/commands/prompt-guard-decision.ts +0 -2
  255. package/src/cli/commands/recovery.ts +137 -135
  256. package/src/cli/commands/repository.ts +17 -1
  257. package/src/cli/commands/runtime.ts +3 -3
  258. package/src/cli/commands/setup-execution.ts +41 -0
  259. package/src/cli/commands/setup-profile.ts +8 -0
  260. package/src/cli/controller/check-runner.ts +13 -2
  261. package/src/cli/controller/completion-backlog.ts +3 -3
  262. package/src/cli/controller/completion-orchestrator.ts +5 -44
  263. package/src/cli/controller/completion-target.ts +3 -72
  264. package/src/cli/controller/composite-operations.ts +1 -1
  265. package/src/cli/controller/context/instruction-context.ts +1 -1
  266. package/src/cli/controller/context/known-paths.ts +2 -2
  267. package/src/cli/controller/context/query-planning.ts +11 -4
  268. package/src/cli/controller/context/rule-resolution.ts +2 -2
  269. package/src/cli/controller/context/source-materializer.ts +83 -102
  270. package/src/cli/controller/context/structural-provider.ts +84 -0
  271. package/src/cli/controller/context/structural.ts +6 -6
  272. package/src/cli/controller/context/types.ts +8 -9
  273. package/src/cli/controller/context-pack.ts +107 -84
  274. package/src/cli/controller/execution-completion.ts +4 -7
  275. package/src/cli/controller/execution-policy.ts +36 -153
  276. package/src/cli/controller/governance.ts +2 -3
  277. package/src/cli/controller/issue-store.ts +4 -99
  278. package/src/cli/controller/launch-agents.ts +59 -18
  279. package/src/cli/controller/operational-plan.ts +25 -52
  280. package/src/cli/controller/runtime-config.ts +5 -1
  281. package/src/cli/controller/stuck-state-migration.ts +2 -3
  282. package/src/cli/controller/systemd-user.ts +42 -10
  283. package/src/cli/editing/edit-session.ts +18 -12
  284. package/src/cli/github/github.ts +4 -4
  285. package/src/cli/github/semantic-projection.ts +174 -0
  286. package/src/cli/hook/prompt-intents.ts +0 -9
  287. package/src/cli/index.ts +1 -43
  288. package/src/cli/local-bridge/connector-freshness.ts +1 -12
  289. package/src/cli/local-bridge/console-automations.ts +19 -14
  290. package/src/cli/local-bridge/console-view-models.ts +4 -13
  291. package/src/cli/local-bridge/facade-api.ts +64 -193
  292. package/src/cli/local-bridge/server.ts +24 -147
  293. package/src/cli/local-bridge/types.ts +0 -1
  294. package/src/cli/mcp/oauth-client-retirement.ts +74 -0
  295. package/src/cli/repositories/command-classifier.ts +0 -9
  296. package/src/cli/repositories/command-executor.ts +29 -23
  297. package/src/cli/repositories/command-scope.ts +37 -18
  298. package/src/cli/repositories/controller-home.ts +48 -0
  299. package/src/cli/repositories/ephemeral-workspace.ts +20 -30
  300. package/src/cli/repositories/git-command-executor.ts +2 -2
  301. package/src/cli/repositories/locks.ts +20 -0
  302. package/src/cli/repositories/registry.ts +79 -12
  303. package/src/cli/repositories/safe-patch.ts +2 -12
  304. package/src/cli/repositories/structured-git.ts +101 -5
  305. package/src/cli/repository/inspector.ts +185 -343
  306. package/src/cli/repository/session-cache.ts +92 -6
  307. package/src/effects/process-runner.ts +17 -1
  308. package/src/runtime/context/assistant-context.ts +20 -7
  309. package/src/runtime/context/assistant-work-context.ts +89 -27
  310. package/src/runtime/context/automatic-learning.ts +565 -0
  311. package/src/runtime/context/cognitive-attention.ts +173 -0
  312. package/src/runtime/context/cognitive-skill-canary.ts +158 -0
  313. package/src/runtime/context/context-closure.ts +46 -9
  314. package/src/runtime/context/context-pack-process.ts +103 -0
  315. package/src/runtime/context/context-plane.ts +1 -9
  316. package/src/runtime/context/direct-controller-learning.ts +298 -0
  317. package/src/runtime/context/generic-lsp-provider.ts +1 -1
  318. package/src/runtime/context/semantic-navigation-contract.ts +16 -0
  319. package/src/runtime/context/semantic-navigation.ts +7 -35
  320. package/src/runtime/context/typescript-navigation-process.ts +145 -0
  321. package/src/runtime/context/typescript-navigation.ts +89 -3
  322. package/src/runtime/control-plane/cleanup-artifact-retention.ts +215 -12
  323. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +9 -47
  324. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +34 -4
  325. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +29 -20
  326. package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
  327. package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
  328. package/src/runtime/control-plane/execution/retained-work-resume.ts +206 -199
  329. package/src/runtime/control-plane/execution/session-store.ts +31 -1
  330. package/src/runtime/control-plane/execution/validation.ts +6 -8
  331. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +13 -42
  332. package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
  333. package/src/runtime/control-plane/execution/work-completion-authority.ts +30 -85
  334. package/src/runtime/control-plane/execution/work-delivery-service.ts +616 -0
  335. package/src/runtime/control-plane/execution/work-evidence-policy.ts +12 -50
  336. package/src/runtime/control-plane/execution/work-execution-support.ts +50 -149
  337. package/src/runtime/control-plane/execution/work-handle-authority.ts +197 -110
  338. package/src/runtime/control-plane/execution/work-handle-store.ts +72 -44
  339. package/src/runtime/control-plane/execution/work-head-settlement.ts +53 -20
  340. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +52 -29
  341. package/src/runtime/control-plane/execution/work-operation-service.ts +60 -34
  342. package/src/runtime/control-plane/execution/work-preparation-service.ts +231 -267
  343. package/src/runtime/control-plane/execution/work-process-evidence.ts +2 -2
  344. package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
  345. package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
  346. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +201 -199
  347. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +33 -85
  348. package/src/runtime/control-plane/execution/work-verification-service.ts +406 -50
  349. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +6 -2
  350. package/src/runtime/control-plane/facade/capability-registry.ts +364 -37
  351. package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
  352. package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
  353. package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
  354. package/src/runtime/control-plane/facade/goal-workloop-access.ts +1 -20
  355. package/src/runtime/control-plane/facade/goal-workloop.ts +281 -1753
  356. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +12 -25
  357. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +256 -52
  358. package/src/runtime/control-plane/facade/index.ts +0 -1
  359. package/src/runtime/control-plane/facade/operation-digest.ts +12 -15
  360. package/src/runtime/control-plane/facade/plan-contract-store.ts +451 -991
  361. package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
  362. package/src/runtime/control-plane/facade/repository-work-admission.ts +12 -22
  363. package/src/runtime/control-plane/facade/requirement-authority.ts +102 -199
  364. package/src/runtime/control-plane/facade/requirement-board.ts +3 -3
  365. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +51 -14
  366. package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
  367. package/src/runtime/control-plane/facade/suggested-actions.ts +15 -18
  368. package/src/runtime/control-plane/facade/types.ts +49 -108
  369. package/src/runtime/control-plane/facade/work-continuation.ts +9 -9
  370. package/src/runtime/control-plane/generated-cache-retention.ts +206 -59
  371. package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
  372. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +1 -1
  373. package/src/runtime/control-plane/global-scheduler/maintenance.ts +256 -117
  374. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -12
  375. package/src/runtime/control-plane/global-scheduler/scheduler.ts +66 -40
  376. package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
  377. package/src/runtime/control-plane/governance/access-policy.ts +19 -2
  378. package/src/runtime/control-plane/governance/authorization.ts +20 -3
  379. package/src/runtime/control-plane/governance/external-effects.ts +1 -1
  380. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +63 -300
  381. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +29 -300
  382. package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
  383. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +5 -2
  384. package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -240
  385. package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
  386. package/src/runtime/control-plane/persistence/experience-store.ts +162 -29
  387. package/src/runtime/control-plane/persistence/requirement-store.ts +216 -74
  388. package/src/runtime/control-plane/persistence/sqlite-store.ts +113 -6
  389. package/src/runtime/control-plane/release-retention.ts +135 -47
  390. package/src/runtime/control-plane/repository-namespace-retention.ts +2 -2
  391. package/src/runtime/control-plane/runtime-cleanup.ts +295 -107
  392. package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
  393. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +19 -5
  394. package/src/runtime/control-plane/workspace/workspace-store.ts +169 -0
  395. package/src/runtime/diagnostics/incident-repair.ts +313 -146
  396. package/src/runtime/diagnostics/mcp-timing.ts +114 -0
  397. package/src/runtime/evidence/artifact-store.ts +158 -40
  398. package/src/runtime/evidence/evidence-store.ts +112 -8
  399. package/src/runtime/evidence/output-handle-index.ts +225 -0
  400. package/src/runtime/evidence/result-store.ts +158 -44
  401. package/src/runtime/evidence/sensitive-output.ts +22 -2
  402. package/src/runtime/execution/jobs/child-reference.ts +11 -0
  403. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -11
  404. package/src/runtime/execution/jobs/store.ts +3 -4
  405. package/src/runtime/execution/managed-workspace.ts +57 -0
  406. package/src/runtime/execution/process-runtime/check-facade.ts +1 -1
  407. package/src/runtime/execution/process-runtime/check-result-retention.ts +2 -2
  408. package/src/runtime/execution/process-runtime/check-result.ts +105 -1
  409. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +18 -8
  410. package/src/runtime/execution/process-runtime/command-facade.ts +82 -30
  411. package/src/runtime/execution/process-runtime/gc.ts +15 -2
  412. package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
  413. package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
  414. package/src/runtime/execution/process-runtime/index.ts +3 -0
  415. package/src/runtime/execution/process-runtime/lightweight-managed.ts +251 -72
  416. package/src/runtime/execution/process-runtime/persisted-check.ts +3 -3
  417. package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
  418. package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
  419. package/src/runtime/execution/process-runtime/resource-claims.ts +65 -61
  420. package/src/runtime/execution/process-runtime/runtime.ts +55 -18
  421. package/src/runtime/execution/process-runtime/store.ts +30 -2
  422. package/src/runtime/execution/process-runtime/types.ts +6 -1
  423. package/src/runtime/execution/thin-harness/execution-router.ts +16 -16
  424. package/src/runtime/execution/thin-harness/fast-executor.ts +2 -2
  425. package/src/runtime/execution/thin-harness/search-worker.ts +2 -2
  426. package/src/runtime/execution/workers/executor.ts +3 -8
  427. package/src/runtime/maintenance/cleanup.ts +41 -5
  428. package/src/runtime/platform/service-inventory.ts +130 -0
  429. package/src/runtime/platform/service-manager.ts +8 -0
  430. package/src/runtime/plugins/action-application.ts +97 -29
  431. package/src/runtime/plugins/app-store-connect-adapter.ts +290 -18
  432. package/src/runtime/plugins/browser-adapter.ts +1124 -333
  433. package/src/runtime/plugins/browser-automation-service.ts +23 -3
  434. package/src/runtime/plugins/browser-macos-bridge.ts +431 -137
  435. package/src/runtime/plugins/browser-manifest-surface.ts +47 -12
  436. package/src/runtime/plugins/browser-native-session-service.ts +63 -13
  437. package/src/runtime/plugins/browser-registration.ts +18 -0
  438. package/src/runtime/plugins/browser-runtime.ts +40 -36
  439. package/src/runtime/plugins/browser-session-authority.ts +2 -2
  440. package/src/runtime/plugins/browser-session-store.ts +46 -76
  441. package/src/runtime/plugins/capability-authorization-grants.ts +258 -62
  442. package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +228 -0
  443. package/src/runtime/plugins/computer-registration.ts +298 -20
  444. package/src/runtime/plugins/desktop-operator-external-policy.ts +36 -4
  445. package/src/runtime/plugins/desktop-operator-registration.ts +12 -4
  446. package/src/runtime/plugins/external-adapter.ts +42 -6
  447. package/src/runtime/plugins/external-registration.ts +21 -1
  448. package/src/runtime/plugins/github-adapter.ts +30 -3
  449. package/src/runtime/plugins/gmail-adapter.ts +189 -3
  450. package/src/runtime/plugins/google-calendar-adapter.ts +2 -2
  451. package/src/runtime/plugins/google-shared.ts +24 -12
  452. package/src/runtime/plugins/google-tasks-adapter.ts +2 -2
  453. package/src/runtime/plugins/ios/remote-xpc-hid.ts +70 -30
  454. package/src/runtime/plugins/ios-adapter.ts +46 -41
  455. package/src/runtime/plugins/ios-agent-device.ts +54 -4
  456. package/src/runtime/plugins/ios-device-ownership.ts +103 -0
  457. package/src/runtime/plugins/ios-physical-device.ts +497 -79
  458. package/src/runtime/plugins/lightweight-action.ts +208 -2
  459. package/src/runtime/plugins/local-system-adapter.ts +12 -100
  460. package/src/runtime/plugins/managed-process-adapter.ts +12 -5
  461. package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
  462. package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
  463. package/src/runtime/plugins/store.ts +682 -435
  464. package/src/runtime/plugins/types.ts +24 -0
  465. package/src/runtime/plugins/xiaohongshu-publish.ts +16 -0
  466. package/src/runtime/projections/git-status-sampler.ts +2 -19
  467. package/src/runtime/projections/invalidation.ts +7 -3
  468. package/src/runtime/projections/materialized-view.ts +80 -36
  469. package/src/runtime/recovery/actions.ts +4 -4
  470. package/src/runtime/recovery/capability-action-application.ts +46 -45
  471. package/src/runtime/recovery/lifecycle-inventory.ts +30 -40
  472. package/src/runtime/recovery/maintenance-executor.ts +374 -123
  473. package/src/runtime/recovery/probe.ts +2 -2
  474. package/src/runtime/recovery/store.ts +29 -11
  475. package/src/runtime/release/release-coordinator.ts +312 -0
  476. package/src/runtime/release/release-session.ts +716 -0
  477. package/src/runtime/release/release-state-migration.ts +25 -0
  478. package/src/runtime/resources/leases/store.ts +56 -25
  479. package/src/runtime/root/computer-composition.ts +191 -6
  480. package/src/runtime/root/controller-progression-composition.ts +254 -0
  481. package/src/runtime/root/controller-round-composition.ts +20 -1
  482. package/src/runtime/root/deployment-topology.ts +106 -0
  483. package/src/runtime/root/entry.ts +4 -3
  484. package/src/runtime/root/known-good-recovery.ts +184 -0
  485. package/src/runtime/root/local-bridge.ts +0 -1
  486. package/src/runtime/root/mcp-transport.ts +111 -18
  487. package/src/runtime/root/ownership.ts +261 -9
  488. package/src/runtime/root/package-connector-service.ts +227 -29
  489. package/src/runtime/root/package-runtime-release.ts +117 -30
  490. package/src/runtime/root/package-runtime-service.ts +24 -24
  491. package/src/runtime/root/release-execution-canary.ts +57 -12
  492. package/src/runtime/root/release-loader.ts +90 -8
  493. package/src/runtime/root/release-manifest.ts +80 -40
  494. package/src/runtime/root/release-materialize.ts +372 -106
  495. package/src/runtime/root/release-store.ts +395 -36
  496. package/src/runtime/root/runtime-lane.ts +191 -0
  497. package/src/runtime/root/runtime.ts +160 -29
  498. package/src/runtime/root/scheduled-controller-composition.ts +29 -3
  499. package/src/runtime/root/service-runner.ts +2 -2
  500. package/src/runtime/root/service.ts +126 -44
  501. package/src/runtime/root/status.ts +48 -0
  502. package/src/runtime/root/types.ts +32 -9
  503. package/src/runtime/root/workflow-supervisor-composition.ts +681 -0
  504. package/src/runtime/root/workflow-supervisor-runtime.ts +564 -0
  505. package/src/runtime/root/write-fence.ts +50 -2
  506. package/src/runtime/safe-tooling/google-credential-store.ts +67 -22
  507. package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
  508. package/src/runtime/safe-tooling/ios-development.ts +22 -8
  509. package/src/runtime/safe-tooling/job-summary.ts +1 -1
  510. package/src/runtime/safe-tooling/workspace-auth.ts +5 -2
  511. package/src/runtime/shared/atomic-text-file.ts +21 -0
  512. package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
  513. package/src/runtime/shared/json-files.ts +8 -3
  514. package/src/runtime/shared/process-environment.ts +20 -9
  515. package/src/runtime/shared/response-budget.ts +1 -3
  516. package/src/runtime/standalone-recovery/controller-home-migration.ts +16 -24
  517. package/src/runtime/standalone-recovery/core.ts +3792 -417
  518. package/src/runtime/standalone-recovery/entry.ts +507 -45
  519. package/src/runtime/standalone-recovery/installer.ts +262 -148
  520. package/src/runtime/standalone-recovery/mcp-server.ts +29 -193
  521. package/src/runtime/standalone-recovery/operation-lock.ts +12 -2
  522. package/src/runtime/standalone-recovery/performance.ts +54 -16
  523. package/src/runtime/standalone-recovery/release.ts +53 -5
  524. package/src/runtime/standalone-recovery/service-labels.ts +5 -2
  525. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
  526. package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
  527. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +11 -3
  528. package/src/runtime/workflow/schedules/engine.ts +166 -178
  529. package/src/runtime/workflow/schedules/pending-activation.ts +11 -4
  530. package/src/runtime/workflow/schedules/work-continuation.ts +95 -11
  531. package/src/runtime/workflows/runtime.ts +15 -19
  532. package/src/testing/test-governance.ts +5 -1
  533. package/supervisor/chatgpt-conversation.ts +23 -0
  534. package/supervisor/client.ts +218 -0
  535. package/supervisor/control-plane.ts +963 -0
  536. package/supervisor/entry.ts +15 -0
  537. package/supervisor/forge-validators.ts +147 -0
  538. package/supervisor/index.ts +5 -0
  539. package/supervisor/native-browser-adapter.ts +1151 -0
  540. package/supervisor/native-messaging/host.ts +58 -0
  541. package/supervisor/native-messaging/manifest.ts +26 -0
  542. package/supervisor/native-messaging/protocol.ts +27 -0
  543. package/supervisor/paths.ts +18 -0
  544. package/supervisor/protocol.ts +250 -0
  545. package/supervisor/server.ts +412 -0
  546. package/supervisor/store.ts +1870 -0
  547. package/supervisor/tsconfig.json +8 -0
  548. package/supervisor/types.ts +261 -0
  549. package/SKILL.md +0 -427
  550. package/adapters/chatgpt/controller-host.ts +0 -119
  551. package/adapters/mcp/controller-round-compatibility.ts +0 -184
  552. package/adapters/mcp/frozen-client-semantic-compatibility.ts +0 -292
  553. package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +0 -502
  554. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +0 -1
  555. package/adapters/mcp/toolset-selection.ts +0 -41
  556. package/agents/openai.yaml +0 -4
  557. package/assets/skill-hooks.json +0 -41
  558. package/assets/skill-version.json +0 -189
  559. package/assets/skills/claude-review/SKILL.md +0 -231
  560. package/assets/skills/codex-review/SKILL.md +0 -103
  561. package/assets/templates/helpers/check-skill-version.ts +0 -193
  562. package/packages/kernel/progression/api/index.ts +0 -2
  563. package/packages/kernel/progression/application/projector.ts +0 -211
  564. package/packages/kernel/progression/domain/types.ts +0 -116
  565. package/packages/kernel/progression/index.ts +0 -1
  566. package/packages/kernel/scheduler/application/continuation-service.ts +0 -212
  567. package/packages/kernel/scheduler/domain/continuation.ts +0 -38
  568. package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +0 -38
  569. package/packages/kernel/work/domain/admission-policy.ts +0 -34
  570. package/scripts/benchmark-background-check-overlap.ts +0 -228
  571. package/scripts/benchmark-computer-hotpath.ts +0 -255
  572. package/scripts/benchmark-controller-hotpath.ts +0 -212
  573. package/scripts/benchmark-controller-round2.ts +0 -191
  574. package/scripts/benchmark-ios-agent-device.ts +0 -511
  575. package/scripts/benchmark-mcp-host.sh +0 -117
  576. package/scripts/benchmark-mcp-transport.ts +0 -169
  577. package/scripts/benchmark-operational-memory-activation.ts +0 -235
  578. package/scripts/benchmark-operational-shadow.ts +0 -28
  579. package/scripts/benchmark-quality-harness.ts +0 -146
  580. package/scripts/benchmark-route-session-concurrency.ts +0 -915
  581. package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
  582. package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
  583. package/scripts/benchmark-thin-harness.ts +0 -251
  584. package/scripts/benchmark-typescript-navigation.ts +0 -186
  585. package/scripts/benchmark-work-efficiency.ts +0 -334
  586. package/scripts/build-controller-ui.ts +0 -28
  587. package/scripts/check-npm-release.sh +0 -32
  588. package/scripts/check-public-release-surface.sh +0 -19
  589. package/scripts/check-release-published.sh +0 -57
  590. package/scripts/check-release-readiness.sh +0 -37
  591. package/scripts/check-release-version.mjs +0 -74
  592. package/scripts/check-skill-version.ts +0 -5
  593. package/scripts/check-tarball-install-smoke.sh +0 -150
  594. package/scripts/ensure-codegraph.sh +0 -16
  595. package/scripts/package-source-archive.sh +0 -147
  596. package/scripts/public-release-files.txt +0 -39
  597. package/scripts/public-release.ts +0 -174
  598. package/scripts/publish-release-tarball.sh +0 -27
  599. package/scripts/route-nl-vs-ts-eval.ts +0 -679
  600. package/scripts/run-skill-hook.ts +0 -244
  601. package/scripts/stage-runtime-release.ts +0 -44
  602. package/scripts/sync-codex-installed-copies.sh +0 -182
  603. package/src/cli/commands/tools.ts +0 -167
  604. package/src/cli/controller/progress.ts +0 -656
  605. package/src/cli/controller/work-mode.ts +0 -181
  606. package/src/cli/local-bridge/console-assets.ts +0 -17
  607. package/src/cli/local-bridge/dashboard.ts +0 -17
  608. package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
  609. package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
  610. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
  611. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
  612. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
  613. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
  614. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
  615. package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
  616. package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
  617. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
  618. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
  619. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
  620. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
  621. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
  622. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
  623. package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
  624. package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
  625. package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
  626. package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
  627. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
  628. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
  629. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
  630. package/src/cli/local-bridge/frontend/src/styles.css +0 -28
  631. package/src/cli/local-bridge/frontend/src/types.ts +0 -15
  632. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
  633. package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
  634. package/src/cli/local-bridge/ui-dist/app.css +0 -1
  635. package/src/cli/local-bridge/ui-dist/app.js +0 -11228
  636. package/src/cli/mcp/access-mode.ts +0 -2
  637. package/src/cli/mcp/access-tools.ts +0 -2
  638. package/src/cli/mcp/audit.ts +0 -2
  639. package/src/cli/mcp/auth.ts +0 -2
  640. package/src/cli/mcp/instructions.ts +0 -2
  641. package/src/cli/mcp/legacy-context.ts +0 -2
  642. package/src/cli/mcp/legacy-tool-service.ts +0 -2
  643. package/src/cli/mcp/multi-repository.ts +0 -2
  644. package/src/cli/mcp/oauth.ts +0 -2
  645. package/src/cli/mcp/openai-secure-tunnel.ts +0 -2
  646. package/src/cli/mcp/paths.ts +0 -2
  647. package/src/cli/mcp/policy.ts +0 -2
  648. package/src/cli/mcp/post-finalize-work-attribution.ts +0 -2
  649. package/src/cli/mcp/proxy-env.ts +0 -2
  650. package/src/cli/mcp/readiness-projection.ts +0 -2
  651. package/src/cli/mcp/redaction.ts +0 -2
  652. package/src/cli/mcp/repo.ts +0 -2
  653. package/src/cli/mcp/repository-tools.ts +0 -2
  654. package/src/cli/mcp/server.ts +0 -2
  655. package/src/cli/mcp/setup.ts +0 -2
  656. package/src/cli/mcp/tool-capability-cache.ts +0 -2
  657. package/src/cli/mcp/tool-contract.ts +0 -2
  658. package/src/cli/mcp/tools.ts +0 -2
  659. package/src/cli/mcp/toolset-names.ts +0 -2
  660. package/src/cli/mcp/toolset-selection.ts +0 -2
  661. package/src/cli/mcp/toolset.ts +0 -2
  662. package/src/cli/mcp/transports/http.ts +0 -2
  663. package/src/cli/mcp/transports/session-registry.ts +0 -2
  664. package/src/cli/mcp/transports/stdio.ts +0 -2
  665. package/src/cli/mcp/types.ts +0 -2
  666. package/src/cli/mcp/umbrella-tools.ts +0 -2
  667. package/src/cli/tools/codegraph.ts +0 -695
  668. package/src/core/adoption/helper-wrapper-plan.ts +0 -159
  669. package/src/runtime/context/codegraph-cache-boundary.ts +0 -255
  670. package/src/runtime/context/codegraph-read-provider.ts +0 -611
  671. package/src/runtime/context/codegraph-sidecar.cjs +0 -248
  672. package/src/runtime/control-plane/codegraph-cache-retention.ts +0 -164
  673. package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +0 -169
  674. package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +0 -85
  675. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -655
  676. package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -2850
  677. package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
  678. package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -468
  679. package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -3
  680. package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -142
  681. package/src/runtime/control-plane/facade/work-implementation-review.ts +0 -2
  682. package/src/runtime/control-plane/facade/work-state-machine.ts +0 -2
  683. package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
  684. package/src/runtime/control-plane/routing/index.ts +0 -1
  685. package/src/runtime/control-plane/routing/route-policy.ts +0 -401
  686. package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
  687. package/src/runtime/gateway/mcp/execution-tools.ts +0 -2
  688. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +0 -2
  689. package/src/runtime/gateway/mcp/persisted-check-process.ts +0 -2
  690. package/src/runtime/gateway/mcp/process-tools.ts +0 -2
  691. package/src/runtime/gateway/mcp/router.ts +0 -2
  692. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +0 -2
  693. package/src/runtime/gateway/mcp/runtime-tools.ts +0 -2
  694. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +0 -2
  695. package/src/runtime/plugins/browser-provider-registry.ts +0 -2
@@ -1,18 +1,32 @@
1
+ import { spawnSync } from 'child_process';
1
2
  import { createHash, randomUUID } from 'crypto';
3
+ import { existsSync } from 'fs';
2
4
  import { createServer, type IncomingMessage, type ServerResponse } from 'http';
3
5
  import { basename, join, resolve } from 'path';
4
6
  import { pathToFileURL } from 'url';
5
7
  import { readMcpServiceOAuthPassphrase } from '../../../adapters/mcp/auth';
6
8
  import { FORGE_VERSION } from '../../version';
7
9
  import type { Tool } from '@modelcontextprotocol/server';
8
- import { RecoveryMcpSessionServer } from './mcp-server';
10
+ import { RecoveryMcpServer } from './mcp-server';
9
11
  import {
10
12
  activateRuntimeRelease,
13
+ activatePinnedRuntimeRelease,
14
+ bootAndVerifyConfiguredRuntimeReleaseSessionCandidate,
15
+ cancelConfiguredRuntimeReleaseSession,
16
+ cutoverConfiguredRuntimeReleaseSession,
11
17
  assertRecoveryMutationIdentity,
12
18
  attestKnownGood,
19
+ configuredRuntimeReleaseSourceState,
20
+ measureConfiguredRuntimePerformance,
21
+ RECOVERY_INTERNAL_PERFORMANCE_COMMAND,
13
22
  diagnose,
14
23
  gatewayToken,
24
+ listRecoveryHistory,
15
25
  listReleases,
26
+ pinRuntimeRelease,
27
+ prepareConfiguredRuntimeReleaseSession,
28
+ promoteConfiguredRuntimeReleaseSessionKnownGood,
29
+ rollbackConfiguredRuntimeReleaseSession,
16
30
  loadRecoveryConfig,
17
31
  loadWatchdogState,
18
32
  saveWatchdogState,
@@ -22,12 +36,13 @@ import {
22
36
  repairPublicTunnel,
23
37
  restartPrimaryConnector,
24
38
  restartPrimaryRuntime,
25
- restartRecoveryWatchdog,
26
39
  stageAndActivateConfiguredRuntimeRelease,
40
+ unpinRuntimeRelease,
27
41
  rollbackPrevious,
28
42
  secureEqual,
29
43
  runtimeStatus,
30
44
  verifyStableRuntime,
45
+ verifyConfiguredRuntimeReleaseSessionStaticGates,
31
46
  watchdogTick,
32
47
  type WatchdogState,
33
48
  type RecoveryConfig,
@@ -41,11 +56,73 @@ import {
41
56
  } from './watchdog-heartbeat';
42
57
  import {
43
58
  RECOVERY_RELEASE_ROLE_CANARY_ARG,
59
+ readCurrentRecoveryRelease,
60
+ recoveryGatewayIsDraining,
44
61
  writeRecoveryRuntimeIdentity,
45
62
  type RecoveryRuntimeIdentity,
46
63
  type RecoveryRuntimeRole,
47
64
  } from './release';
48
65
  import { RECOVERY_MUTATION_IDENTITY_CONTRACT, RECOVERY_MUTATION_IDENTITY_FIELDS } from './mutation-identity-contract';
66
+ import { readReleaseSession } from '../release/release-session';
67
+ import { readRuntimeOwner } from '../root/ownership';
68
+ import { observeRuntimeStatus } from '../root/status';
69
+ import { migrateReleaseDurableState } from '../release/release-state-migration';
70
+ import {
71
+ advanceConfiguredRuntimeRelease,
72
+ advanceConfiguredRuntimeReleaseStep,
73
+ decideConfiguredRuntimeReleaseReconciliation,
74
+ type RuntimeReleaseProvider,
75
+ } from '../release/release-coordinator';
76
+ import { runBoundedChild } from '../shared/bounded-child-supervisor';
77
+ import { runtimeAuthorityFreeEnvironment } from '../shared/process-environment';
78
+
79
+ const RECOVERY_RUNTIME_RELEASE_PROVIDER: RuntimeReleaseProvider<RecoveryConfig> = {
80
+ prepare: (config, requestId) => prepareConfiguredRuntimeReleaseSession(config, {}, requestId),
81
+ verifyStatic: (config, sessionId, requestId) => verifyConfiguredRuntimeReleaseSessionStaticGates(config, sessionId, requestId),
82
+ verifyCandidate: (config, sessionId, requestId) => bootAndVerifyConfiguredRuntimeReleaseSessionCandidate(config, sessionId, requestId),
83
+ cutover: (config, sessionId, requestId) => cutoverConfiguredRuntimeReleaseSession(config, sessionId, requestId),
84
+ promoteKnownGood: (config, sessionId, requestId) => promoteConfiguredRuntimeReleaseSessionKnownGood(config, sessionId, {}, requestId),
85
+ };
86
+
87
+ const RECOVERY_INTERNAL_RELEASE_RECONCILE_COMMAND = '__reconcile-runtime-release-step';
88
+ const RECOVERY_AUTOMATIC_RELEASE_INTERVAL_MS = 15_000;
89
+ /**
90
+ * Upper bound for retrying one non-converging release step. A stuck release must
91
+ * still be retried, but never at a cadence that can starve the daemon that owns it.
92
+ */
93
+ const RECOVERY_AUTOMATIC_RELEASE_FAILURE_BACKOFF_MAX_MS = 15 * 60_000;
94
+ const RECOVERY_AUTOMATIC_RELEASE_STEP_TIMEOUT_MS = 15 * 60_000;
95
+
96
+ /**
97
+ * A CLI launched from the primary Runtime's Process Runtime cannot own its
98
+ * cutover: stopping Stable A cancels that very CLI before the release
99
+ * transaction can settle. The persistent Recovery daemon/MCP is independent.
100
+ * Compare the serving Runtime's existing process owner with the actual OS
101
+ * parent chain, not a caller-supplied scope or model-provided permission flag.
102
+ */
103
+ function assertIndependentReleaseCliOwner(controllerHome: string): void {
104
+ const runtime = observeRuntimeStatus(controllerHome);
105
+ const owner = runtime.running ? readRuntimeOwner(controllerHome) : undefined;
106
+ if (!owner) return;
107
+ if (process.platform === 'win32') throw new Error('RECOVERY_CUTOVER_PROCESS_ANCESTRY_UNVERIFIED');
108
+ let pid = process.ppid;
109
+ const visited = new Set<number>();
110
+ for (let depth = 0; depth < 32 && pid > 1; depth += 1) {
111
+ if (pid === owner.pid) {
112
+ throw new Error('RECOVERY_CUTOVER_REQUIRES_INDEPENDENT_OWNER: invoke the Recovery daemon/MCP, not a command owned by the primary Runtime');
113
+ }
114
+ if (visited.has(pid)) throw new Error('RECOVERY_CUTOVER_PROCESS_ANCESTRY_UNVERIFIED');
115
+ visited.add(pid);
116
+ const inspected = spawnSync('ps', ['-o', 'ppid=', '-p', String(pid)], {
117
+ encoding: 'utf8', timeout: 2_000, maxBuffer: 4 * 1024,
118
+ });
119
+ if (inspected.status !== 0) throw new Error('RECOVERY_CUTOVER_PROCESS_ANCESTRY_UNVERIFIED');
120
+ const parent = Number.parseInt(inspected.stdout.trim(), 10);
121
+ if (!Number.isInteger(parent) || parent < 1) throw new Error('RECOVERY_CUTOVER_PROCESS_ANCESTRY_UNVERIFIED');
122
+ pid = parent;
123
+ }
124
+ if (pid > 1) throw new Error('RECOVERY_CUTOVER_PROCESS_ANCESTRY_UNVERIFIED');
125
+ }
49
126
 
50
127
  function option(name: string): string | undefined {
51
128
  const index = process.argv.indexOf(name);
@@ -64,6 +141,7 @@ import { runRecoveryControllerHomeMigrationWorker, scheduleRecoveryControllerHom
64
141
 
65
142
  export const RECOVERY_CLI_COMMANDS = [
66
143
  'status',
144
+ 'daemon',
67
145
  'verify',
68
146
  'verify-external',
69
147
  'list-releases',
@@ -74,6 +152,15 @@ export const RECOVERY_CLI_COMMANDS = [
74
152
  'recover-primary-runtime',
75
153
  'activate-runtime-release',
76
154
  'stage-and-activate-runtime-release',
155
+ 'release-session-status',
156
+ 'release-session-advance',
157
+ 'release-session-prepare',
158
+ 'release-session-static-verify',
159
+ 'release-session-candidate-verify',
160
+ 'release-session-cutover',
161
+ 'release-session-cancel',
162
+ 'release-session-rollback',
163
+ 'release-session-known-good',
77
164
  'migrate-controller-home-worker',
78
165
  'restart-public-tunnel',
79
166
  'diagnose',
@@ -94,6 +181,10 @@ export function recoveryRuntimeRoleFromExecutable(executable = process.execPath)
94
181
  async function cli(): Promise<void> {
95
182
  const command = process.argv.find((value, index) => index >= 2 && !value.startsWith('-') && process.argv[index - 1] !== '--controller-home') ?? 'status';
96
183
  const config = loadRecoveryConfig(controllerHome(), option('--config'));
184
+ // Internal durable state has one current schema. Every Recovery entrypoint,
185
+ // including gateway/watchdog startup, crosses the same migration boundary as
186
+ // Canonical Runtime before reading or mutating release semantics.
187
+ migrateReleaseDurableState(config.controllerHome);
97
188
  const executableRole = recoveryRuntimeRoleFromExecutable();
98
189
  if (executableRole) {
99
190
  if (command !== executableRole) {
@@ -107,8 +198,53 @@ async function cli(): Promise<void> {
107
198
  else await startWatchdog(config);
108
199
  return;
109
200
  }
201
+ if (command === RECOVERY_INTERNAL_PERFORMANCE_COMMAND) {
202
+ try {
203
+ output({ schemaVersion: 1, ok: true, evidence: await measureConfiguredRuntimePerformance(config) });
204
+ } catch (error) {
205
+ const detail = error instanceof Error ? error.message : String(error);
206
+ output({
207
+ schemaVersion: 1,
208
+ ok: false,
209
+ error: /^RECOVERY_PERFORMANCE_(?:UNKNOWN|REJECTED):/.test(detail)
210
+ ? detail
211
+ : 'RECOVERY_PERFORMANCE_UNKNOWN: isolated sampler failed',
212
+ });
213
+ }
214
+ return;
215
+ }
216
+ if (command === RECOVERY_INTERNAL_RELEASE_RECONCILE_COMMAND) {
217
+ try {
218
+ const decision = decideConfiguredRuntimeReleaseReconciliation(
219
+ config.controllerHome,
220
+ () => configuredRuntimeReleaseSourceState(config),
221
+ );
222
+ if (!decision.required) {
223
+ output({ schemaVersion: 1, ok: true, attempted: false, noOp: true, decision });
224
+ return;
225
+ }
226
+ const result = await advanceConfiguredRuntimeReleaseStep(
227
+ config,
228
+ RECOVERY_RUNTIME_RELEASE_PROVIDER,
229
+ `recovery-auto-release:${process.pid}:${Date.now()}`,
230
+ decision,
231
+ );
232
+ output({ schemaVersion: 1, ok: result.ok, attempted: result.attempted, decision, result });
233
+ } catch (error) {
234
+ output({ schemaVersion: 1, ok: false, attempted: false, error: error instanceof Error ? error.message : String(error) });
235
+ }
236
+ return;
237
+ }
110
238
  switch (command) {
111
239
  case 'status': output(await runtimeStatus(config)); return;
240
+ case 'daemon': {
241
+ if (process.argv.includes(RECOVERY_RELEASE_ROLE_CANARY_ARG)) {
242
+ output({ status: 'ok', role: 'daemon', executable: basename(process.execPath) });
243
+ return;
244
+ }
245
+ await startRecoveryDaemon(config);
246
+ return;
247
+ }
112
248
  case 'verify': output(await verifyStableRuntime(config)); return;
113
249
  case 'verify-external': {
114
250
  const verified = await verifyStableRuntime(config);
@@ -117,11 +253,12 @@ async function cli(): Promise<void> {
117
253
  }
118
254
  case 'list-releases': output(await listReleases(config)); return;
119
255
  case 'attest-known-good': output(await attestKnownGood(config)); return;
120
- case 'rollback-previous': output(await rollbackPrevious(config)); return;
121
- case 'restart-primary-runtime': output(await restartPrimaryRuntime(config)); return;
122
- case 'restart-primary-connector': output(await restartPrimaryConnector(config)); return;
123
- case 'recover-primary-runtime': output(await recoverPrimaryRuntime(config)); return;
256
+ case 'rollback-previous': assertIndependentReleaseCliOwner(config.controllerHome); output(await rollbackPrevious(config)); return;
257
+ case 'restart-primary-runtime': assertIndependentReleaseCliOwner(config.controllerHome); output(await restartPrimaryRuntime(config, { forceHealthyRestart: true })); return;
258
+ case 'restart-primary-connector': output(await restartPrimaryConnector(config, { forceHealthyRestart: true, requestId: `recovery-cli:${process.pid}:${Date.now()}` })); return;
259
+ case 'recover-primary-runtime': assertIndependentReleaseCliOwner(config.controllerHome); output(await recoverPrimaryRuntime(config)); return;
124
260
  case 'activate-runtime-release': {
261
+ assertIndependentReleaseCliOwner(config.controllerHome);
125
262
  const releasePath = option('--release-manifest') ?? option('--release-path');
126
263
  const expectedActiveReleaseId = option('--expected-active-release');
127
264
  const expectedAuthorityRevisionRaw = option('--expected-authority-revision');
@@ -136,7 +273,57 @@ async function cli(): Promise<void> {
136
273
  }));
137
274
  return;
138
275
  }
139
- case 'stage-and-activate-runtime-release': output(await stageAndActivateConfiguredRuntimeRelease(config, {}, `recovery-cli:${process.pid}:${Date.now()}`)); return;
276
+ case 'stage-and-activate-runtime-release': assertIndependentReleaseCliOwner(config.controllerHome); output(await stageAndActivateConfiguredRuntimeRelease(config, {}, `recovery-cli:${process.pid}:${Date.now()}`)); return;
277
+ case 'release-session-status': {
278
+ const sessionId = option('--session-id');
279
+ if (!sessionId) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
280
+ output(readReleaseSession(config.controllerHome, sessionId) ?? null);
281
+ return;
282
+ }
283
+ case 'release-session-advance': {
284
+ assertIndependentReleaseCliOwner(config.controllerHome);
285
+ output(await advanceConfiguredRuntimeRelease(config, RECOVERY_RUNTIME_RELEASE_PROVIDER, `recovery-cli:${process.pid}:${Date.now()}`));
286
+ return;
287
+ }
288
+ case 'release-session-prepare': output(await prepareConfiguredRuntimeReleaseSession(config, {}, `recovery-cli:${process.pid}:${Date.now()}`)); return;
289
+ case 'release-session-static-verify': {
290
+ const sessionId = option('--session-id');
291
+ if (!sessionId) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
292
+ output(await verifyConfiguredRuntimeReleaseSessionStaticGates(config, sessionId, `recovery-cli:${process.pid}:${Date.now()}`));
293
+ return;
294
+ }
295
+ case 'release-session-candidate-verify': {
296
+ const sessionId = option('--session-id');
297
+ if (!sessionId) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
298
+ output(await bootAndVerifyConfiguredRuntimeReleaseSessionCandidate(config, sessionId, `recovery-cli:${process.pid}:${Date.now()}`));
299
+ return;
300
+ }
301
+ case 'release-session-cutover': {
302
+ assertIndependentReleaseCliOwner(config.controllerHome);
303
+ const sessionId = option('--session-id');
304
+ if (!sessionId) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
305
+ output(await cutoverConfiguredRuntimeReleaseSession(config, sessionId, `recovery-cli:${process.pid}:${Date.now()}`));
306
+ return;
307
+ }
308
+ case 'release-session-cancel': {
309
+ const sessionId = option('--session-id');
310
+ if (!sessionId) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
311
+ output(await cancelConfiguredRuntimeReleaseSession(config, sessionId, `recovery-cli:${process.pid}:${Date.now()}`));
312
+ return;
313
+ }
314
+ case 'release-session-rollback': {
315
+ assertIndependentReleaseCliOwner(config.controllerHome);
316
+ const sessionId = option('--session-id');
317
+ if (!sessionId) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
318
+ output(await rollbackConfiguredRuntimeReleaseSession(config, sessionId, {}, `recovery-cli:${process.pid}:${Date.now()}`));
319
+ return;
320
+ }
321
+ case 'release-session-known-good': {
322
+ const sessionId = option('--session-id');
323
+ if (!sessionId) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
324
+ output(await promoteConfiguredRuntimeReleaseSessionKnownGood(config, sessionId, {}, `recovery-cli:${process.pid}:${Date.now()}`));
325
+ return;
326
+ }
140
327
  case 'migrate-controller-home-worker': {
141
328
  const canonicalSourceRoot = option('--canonical-source-root');
142
329
  const expectedSourceRevision = option('--expected-source-revision');
@@ -178,8 +365,8 @@ export function resetWatchdogStateForRecoveryRelease(
178
365
  };
179
366
  }
180
367
 
181
- async function startWatchdog(config: RecoveryConfig): Promise<void> {
182
- const runtimeIdentity = writeRecoveryRuntimeIdentity(config.controllerHome, 'watchdog');
368
+ async function startWatchdog(config: RecoveryConfig, daemonIdentity?: RecoveryRuntimeIdentity): Promise<void> {
369
+ const runtimeIdentity = daemonIdentity ?? writeRecoveryRuntimeIdentity(config.controllerHome, 'watchdog');
183
370
  let heartbeat: RecoveryWatchdogHeartbeat = createRecoveryWatchdogHeartbeat(runtimeIdentity);
184
371
  const persistHeartbeat = (patch: Partial<RecoveryWatchdogHeartbeat> = {}) => {
185
372
  heartbeat = writeRecoveryWatchdogHeartbeat(config.controllerHome, { ...heartbeat, ...patch });
@@ -286,16 +473,29 @@ function mutationInputSchema(
286
473
 
287
474
  export const RECOVERY_TOOLS = [
288
475
  { name: 'runtime_status', description: 'Read canonical Runtime ownership, readiness, endpoint, release observation, and exact Recovery machine identity.', inputSchema: { type: 'object', additionalProperties: false } },
476
+ { name: 'list_recovery_history', description: 'Read a bounded sanitized tail of canonical standalone Recovery audit events.', inputSchema: { type: 'object', properties: { limit: { type: 'integer', minimum: 1, maximum: 50 } }, additionalProperties: false } },
289
477
  { name: 'list_releases', description: 'Read active, previous, and known-good whole-Runtime release evidence.', inputSchema: { type: 'object', additionalProperties: false } },
290
478
  { name: 'verify_stable_runtime', description: 'Run independent stable runtime verification.', inputSchema: { type: 'object', additionalProperties: false } },
291
479
  { name: 'verify_external_runtime', description: 'Verify the external primary MCP endpoint.', inputSchema: { type: 'object', additionalProperties: false } },
292
480
  { name: 'attest_known_good', description: 'Record the active release as known-good only after full independent verification succeeds.', inputSchema: mutationInputSchema() },
293
481
  { name: 'rollback_previous', description: 'While Canonical Runtime is stopped, atomically restore its attested previous whole-Runtime release and SQLite backup.', inputSchema: mutationInputSchema() },
294
- { name: 'restart_primary_runtime', description: 'Restart the installed canonical Forge Runtime service only after exact Recovery machine identity matches.', inputSchema: mutationInputSchema() },
295
- { name: 'restart_primary_connector', description: 'Restart the explicitly configured primary OAuth/Connector service only after exact Recovery machine identity and local Canonical Runtime verification succeed.', inputSchema: mutationInputSchema() },
482
+ { name: 'restart_primary_runtime', description: 'Explicitly restart the installed canonical Forge Runtime service, including when currently healthy, then require whole-Runtime verification after exact Recovery machine identity matches.', inputSchema: mutationInputSchema() },
483
+ { name: 'restart_primary_connector', description: 'Explicitly restart the configured primary OAuth/Connector service, including when endpoint probes pass, only after exact Recovery machine identity and local Canonical Runtime verification succeed; verify the listener and public endpoint again afterward.', inputSchema: mutationInputSchema() },
296
484
  { name: 'recover_primary_runtime', description: 'Stop the canonical Runtime, restore the attested previous whole-Runtime release and SQLite backup, restart it, and require verification.', inputSchema: mutationInputSchema() },
297
485
  { name: 'activate_runtime_release', description: 'Activate an already staged immutable Runtime release only if machine identity and caller-observed active release/authority revision are still current. Reverse activation of current.previous is rejected; use rollback_previous/recover_primary_runtime instead.', inputSchema: mutationInputSchema({ release_path: { type: 'string', minLength: 8, maxLength: 1024, description: 'Absolute path to the staged immutable Runtime release directory.' }, expected_active_release_id: { type: 'string', minLength: 1, maxLength: 256 }, expected_authority_revision: { type: 'integer', minimum: 1 } }, ['release_path', 'expected_active_release_id', 'expected_authority_revision']) },
298
- { name: 'stage_and_activate_runtime_release', description: 'Build one immutable Runtime release from the fixed Recovery-configured source root, then activate it transactionally with rollback protection. No arbitrary source path is accepted.', inputSchema: mutationInputSchema() },
486
+ { name: 'pin_runtime_release', description: 'Pin one extant legacy/home-bound immutable Runtime release so retention preserves it for explicit Runtime-only recovery activation. Portable source candidates are rejected and must use ReleaseSession.', inputSchema: mutationInputSchema({ release_path: { type: 'string', minLength: 8, maxLength: 1024, description: 'Absolute path to the immutable Runtime release directory or manifest.' } }, ['release_path']) },
487
+ { name: 'unpin_runtime_release', description: 'Remove the explicit stable Runtime retention pin without deleting or activating any release.', inputSchema: mutationInputSchema() },
488
+ { name: 'activate_pinned_runtime_release', description: 'Activate the explicitly pinned legacy/home-bound Runtime release without restoring an older SQLite backup; portable source candidates are rejected and must use ReleaseSession.', inputSchema: mutationInputSchema({ expected_active_release_id: { type: 'string', minLength: 1, maxLength: 256 }, expected_authority_revision: { type: 'integer', minimum: 1 } }, ['expected_active_release_id', 'expected_authority_revision']) },
489
+ { name: 'stage_and_activate_runtime_release', description: 'Compatibility alias: freeze the fixed configured source, create isolated Candidate B, and build one portable immutable Runtime release into a durable ReleaseSession. It no longer activates Stable A.', inputSchema: mutationInputSchema() },
490
+ { name: 'release_session_status', description: 'Read one durable ReleaseSession and its exact Stable A/Candidate B phase and evidence.', inputSchema: { type: 'object', properties: { session_id: { type: 'string', minLength: 8, maxLength: 120 } }, required: ['session_id'], additionalProperties: false } },
491
+ { name: 'advance_runtime_release_session', description: 'Run the single active normal Runtime ReleaseSession autonomously through all immediately executable phases until known-good or a genuine provider/safety boundary. ReleaseSession remains the sole durable progression authority and no Work is created per phase.', inputSchema: mutationInputSchema() },
492
+ { name: 'prepare_runtime_release_session', description: 'Freeze configured source and Stable A authority, create isolated Candidate B, and build a portable byte-identifiable Runtime artifact without stopping Stable A.', inputSchema: mutationInputSchema() },
493
+ { name: 'verify_runtime_release_session_static', description: 'Run canonical static gates on the frozen source revision and advance only that exact ReleaseSession.', inputSchema: mutationInputSchema({ session_id: { type: 'string', minLength: 8, maxLength: 120 } }, ['session_id']) },
494
+ { name: 'verify_runtime_release_session_candidate', description: 'Boot Candidate B in its isolated ControllerHome/service/port, run whole-Runtime and Recovery restart canaries, and mark the session cutover-eligible while Stable A stays active.', inputSchema: mutationInputSchema({ session_id: { type: 'string', minLength: 8, maxLength: 120 } }, ['session_id']) },
495
+ { name: 'cutover_runtime_release_session', description: 'Perform the single fenced cutover attempt for a cutover-eligible ReleaseSession using the byte-identical verified Candidate B artifact. Failed cutover restores exact Stable A and terminalizes without retry.', inputSchema: mutationInputSchema({ session_id: { type: 'string', minLength: 8, maxLength: 120 } }, ['session_id']) },
496
+ { name: 'cancel_runtime_release_session', description: 'Retire a superseded or rejected Candidate B before cutover, terminalizing the ReleaseSession with the existing failed state and leaving Stable A unchanged.', inputSchema: mutationInputSchema({ session_id: { type: 'string', minLength: 8, maxLength: 120 } }, ['session_id']) },
497
+ { name: 'rollback_runtime_release_session', description: 'Abort the exact in-flight ReleaseSession activation transaction and restore its frozen Stable A whole-Runtime release plus SQLite backup.', inputSchema: mutationInputSchema({ session_id: { type: 'string', minLength: 8, maxLength: 120 } }, ['session_id']) },
498
+ { name: 'promote_runtime_release_session_known_good', description: 'After committed cutover soak, require full verification and performance observation, create a recoverable release+SQLite+service bundle, and terminalize the ReleaseSession known-good.', inputSchema: mutationInputSchema({ session_id: { type: 'string', minLength: 8, maxLength: 120 } }, ['session_id']) },
299
499
  { name: 'migrate_controller_home', description: 'Schedule a Linux-only standalone Recovery transaction that relocates this Forge installation to the stable user-level Controller Home, reinstalls immutable Runtime/Connector/Recovery owners, verifies them, and rolls back on failure.', inputSchema: mutationInputSchema({ canonical_source_root: { type: 'string', minLength: 1, maxLength: 1024 }, expected_source_revision: { type: 'string', minLength: 7, maxLength: 80 } }, ['canonical_source_root', 'expected_source_revision']) },
300
500
  { name: 'restart_public_tunnel', description: 'Restart the explicitly configured public tunnel only after exact Recovery machine identity and local runtime verification succeeds and the external endpoint is unavailable.', inputSchema: mutationInputSchema() },
301
501
  { name: 'reconnect_primary_connector', description: 'Check canonical Runtime Gateway and primary MCP reconnection readiness without publishing a release.', inputSchema: { type: 'object', additionalProperties: false } },
@@ -591,11 +791,18 @@ function assertRecoveryGatewayMutationIdentity(config: RecoveryConfig, args: Rec
591
791
  export async function dispatchRecoveryTool(config: RecoveryConfig, name: string, args: Record<string, unknown>): Promise<unknown> {
592
792
  switch (name) {
593
793
  case 'runtime_status': return runtimeStatus(config);
794
+ case 'list_recovery_history': return listRecoveryHistory(config, typeof args.limit === 'number' ? args.limit : 20);
594
795
  case 'list_releases': return listReleases(config);
595
796
  case 'verify_stable_runtime': return verifyStableRuntime(config);
596
797
  case 'verify_external_runtime': {
597
798
  const verified = await verifyStableRuntime(config);
598
- return { ok: verified.probes.external_mcp_http?.ok === true, external: verified.probes.external_mcp_http, mcp: verified.probes.mcp_initialize };
799
+ const externalConfigured = Boolean(config.publicMcpUrl);
800
+ return {
801
+ ok: externalConfigured ? verified.probes.external_mcp_http?.ok === true : verified.ok,
802
+ externalConfigured,
803
+ external: verified.probes.external_mcp_http,
804
+ mcp: verified.probes.mcp_initialize,
805
+ };
599
806
  }
600
807
  case 'attest_known_good': {
601
808
  if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
@@ -610,12 +817,12 @@ export async function dispatchRecoveryTool(config: RecoveryConfig, name: string,
610
817
  case 'restart_primary_runtime': {
611
818
  if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
612
819
  assertRecoveryGatewayMutationIdentity(config, args);
613
- return mutationResponse(config, await restartPrimaryRuntime(config));
820
+ return mutationResponse(config, await restartPrimaryRuntime(config, { forceHealthyRestart: true }));
614
821
  }
615
822
  case 'restart_primary_connector': {
616
823
  if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
617
824
  assertRecoveryGatewayMutationIdentity(config, args);
618
- return mutationResponse(config, await restartPrimaryConnector(config, { requestId: `recovery-gateway:${args.request_id}` }));
825
+ return mutationResponse(config, await restartPrimaryConnector(config, { requestId: `recovery-gateway:${args.request_id}`, forceHealthyRestart: true }));
619
826
  }
620
827
  case 'recover_primary_runtime': {
621
828
  if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
@@ -636,11 +843,85 @@ export async function dispatchRecoveryTool(config: RecoveryConfig, name: string,
636
843
  expectedAuthorityRevision: Number(args.expected_authority_revision),
637
844
  }));
638
845
  }
846
+ case 'pin_runtime_release': {
847
+ if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
848
+ assertRecoveryGatewayMutationIdentity(config, args);
849
+ if (typeof args.release_path !== 'string' || !args.release_path.trim()) throw new Error('RECOVERY_RELEASE_PATH_REQUIRED');
850
+ const releasePath = args.release_path.trim();
851
+ const manifestPath = basename(releasePath) === 'manifest.json' ? releasePath : join(releasePath, 'manifest.json');
852
+ return mutationResponse(config, await pinRuntimeRelease(config, manifestPath, `recovery-gateway:${args.request_id}`));
853
+ }
854
+ case 'unpin_runtime_release': {
855
+ if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
856
+ assertRecoveryGatewayMutationIdentity(config, args);
857
+ return mutationResponse(config, await unpinRuntimeRelease(config, `recovery-gateway:${args.request_id}`));
858
+ }
859
+ case 'activate_pinned_runtime_release': {
860
+ if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
861
+ assertRecoveryGatewayMutationIdentity(config, args);
862
+ if (typeof args.expected_active_release_id !== 'string' || !args.expected_active_release_id.trim()) throw new Error('RECOVERY_EXPECTED_ACTIVE_RELEASE_REQUIRED');
863
+ if (!Number.isInteger(args.expected_authority_revision) || Number(args.expected_authority_revision) < 1) throw new Error('RECOVERY_EXPECTED_AUTHORITY_REVISION_REQUIRED');
864
+ return mutationResponse(config, await activatePinnedRuntimeRelease(config, {}, {
865
+ requestId: `recovery-gateway:${args.request_id}`,
866
+ expectedActiveReleaseId: args.expected_active_release_id.trim(),
867
+ expectedAuthorityRevision: Number(args.expected_authority_revision),
868
+ }));
869
+ }
639
870
  case 'stage_and_activate_runtime_release': {
640
871
  if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
641
872
  assertRecoveryGatewayMutationIdentity(config, args);
642
873
  return mutationResponse(config, await stageAndActivateConfiguredRuntimeRelease(config, {}, `recovery-gateway:${args.request_id}`));
643
874
  }
875
+ case 'release_session_status': {
876
+ if (typeof args.session_id !== 'string' || !args.session_id.trim()) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
877
+ return readReleaseSession(config.controllerHome, args.session_id.trim()) ?? null;
878
+ }
879
+ case 'advance_runtime_release_session': {
880
+ if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
881
+ assertRecoveryGatewayMutationIdentity(config, args);
882
+ return mutationResponse(config, await advanceConfiguredRuntimeRelease(config, RECOVERY_RUNTIME_RELEASE_PROVIDER, `recovery-gateway:${args.request_id}`));
883
+ }
884
+ case 'prepare_runtime_release_session': {
885
+ if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
886
+ assertRecoveryGatewayMutationIdentity(config, args);
887
+ return mutationResponse(config, await prepareConfiguredRuntimeReleaseSession(config, {}, `recovery-gateway:${args.request_id}`));
888
+ }
889
+ case 'verify_runtime_release_session_static': {
890
+ if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
891
+ assertRecoveryGatewayMutationIdentity(config, args);
892
+ if (typeof args.session_id !== 'string' || !args.session_id.trim()) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
893
+ return mutationResponse(config, await verifyConfiguredRuntimeReleaseSessionStaticGates(config, args.session_id.trim(), `recovery-gateway:${args.request_id}`));
894
+ }
895
+ case 'verify_runtime_release_session_candidate': {
896
+ if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
897
+ assertRecoveryGatewayMutationIdentity(config, args);
898
+ if (typeof args.session_id !== 'string' || !args.session_id.trim()) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
899
+ return mutationResponse(config, await bootAndVerifyConfiguredRuntimeReleaseSessionCandidate(config, args.session_id.trim(), `recovery-gateway:${args.request_id}`));
900
+ }
901
+ case 'cutover_runtime_release_session': {
902
+ if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
903
+ assertRecoveryGatewayMutationIdentity(config, args);
904
+ if (typeof args.session_id !== 'string' || !args.session_id.trim()) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
905
+ return mutationResponse(config, await cutoverConfiguredRuntimeReleaseSession(config, args.session_id.trim(), `recovery-gateway:${args.request_id}`));
906
+ }
907
+ case 'cancel_runtime_release_session': {
908
+ if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
909
+ assertRecoveryGatewayMutationIdentity(config, args);
910
+ if (typeof args.session_id !== 'string' || !args.session_id.trim()) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
911
+ return mutationResponse(config, await cancelConfiguredRuntimeReleaseSession(config, args.session_id.trim(), `recovery-gateway:${args.request_id}`));
912
+ }
913
+ case 'rollback_runtime_release_session': {
914
+ if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
915
+ assertRecoveryGatewayMutationIdentity(config, args);
916
+ if (typeof args.session_id !== 'string' || !args.session_id.trim()) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
917
+ return mutationResponse(config, await rollbackConfiguredRuntimeReleaseSession(config, args.session_id.trim(), {}, `recovery-gateway:${args.request_id}`));
918
+ }
919
+ case 'promote_runtime_release_session_known_good': {
920
+ if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
921
+ assertRecoveryGatewayMutationIdentity(config, args);
922
+ if (typeof args.session_id !== 'string' || !args.session_id.trim()) throw new Error('RECOVERY_RELEASE_SESSION_ID_REQUIRED');
923
+ return mutationResponse(config, await promoteConfiguredRuntimeReleaseSessionKnownGood(config, args.session_id.trim(), {}, `recovery-gateway:${args.request_id}`));
924
+ }
644
925
  case 'migrate_controller_home': {
645
926
  const migrationRequestId = requestId(args.request_id);
646
927
  if (!migrationRequestId) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
@@ -663,13 +944,185 @@ export async function dispatchRecoveryTool(config: RecoveryConfig, name: string,
663
944
  }
664
945
  }
665
946
 
666
- async function startGateway(config: RecoveryConfig): Promise<void> {
947
+ /**
948
+ * Stable identity of the release step the daemon is currently driving. A step
949
+ * that keeps failing without progressing (same session, phase, and revision)
950
+ * must not fork a full Recovery executable every interval forever.
951
+ */
952
+ function releaseReconciliationFingerprint(decision: { action?: unknown; session?: { sessionId?: unknown; phase?: unknown; revision?: unknown } }): string {
953
+ return [
954
+ typeof decision.action === 'string' ? decision.action : 'none',
955
+ typeof decision.session?.sessionId === 'string' ? decision.session.sessionId : 'none',
956
+ typeof decision.session?.phase === 'string' ? decision.session.phase : 'none',
957
+ typeof decision.session?.revision === 'number' ? String(decision.session.revision) : 'none',
958
+ ].join(':');
959
+ }
960
+
961
+ /**
962
+ * Retry schedule for the automatic release driver. A step that fails without
963
+ * progressing (same fingerprint) backs off exponentially up to a bounded cap so a
964
+ * non-converging release degrades into a slow retry instead of a
965
+ * full-executable fork storm that starves this daemon, the Recovery gateway it
966
+ * serves, and every tunnel behind it. Progress, or a new session/step, resets the
967
+ * schedule; a successful step restores the base interval.
968
+ */
969
+ export function nextReleaseReconcileBackoff(
970
+ prior: { fingerprint?: string; consecutiveFailures: number },
971
+ failed: boolean,
972
+ fingerprint: string | undefined,
973
+ ): { fingerprint?: string; consecutiveFailures: number; delayMs: number } {
974
+ if (!failed) {
975
+ return { fingerprint: undefined, consecutiveFailures: 0, delayMs: RECOVERY_AUTOMATIC_RELEASE_INTERVAL_MS };
976
+ }
977
+ const nextFingerprint = fingerprint ?? 'unknown';
978
+ const consecutiveFailures = nextFingerprint === prior.fingerprint ? Math.max(1, prior.consecutiveFailures) + 1 : 1;
979
+ return {
980
+ fingerprint: nextFingerprint,
981
+ consecutiveFailures,
982
+ delayMs: Math.min(RECOVERY_AUTOMATIC_RELEASE_INTERVAL_MS * 2 ** (consecutiveFailures - 1), RECOVERY_AUTOMATIC_RELEASE_FAILURE_BACKOFF_MAX_MS),
983
+ };
984
+ }
985
+
986
+ async function runAutomaticReleaseReconciliationStep(
987
+ config: RecoveryConfig,
988
+ observed: { fingerprint?: string } = {},
989
+ selectedDecision?: ReturnType<typeof decideConfiguredRuntimeReleaseReconciliation>,
990
+ ): Promise<void> {
991
+ // Most daemon ticks are no-ops. Decide that in the resident process first so
992
+ // a healthy/current source does not fork a complete Recovery executable every
993
+ // fifteen seconds merely to rediscover the same result. The short-lived child
994
+ // remains the mutation boundary whenever a durable release action is needed.
995
+ const decision = selectedDecision ?? decideConfiguredRuntimeReleaseReconciliation(
996
+ config.controllerHome,
997
+ () => configuredRuntimeReleaseSourceState(config),
998
+ );
999
+ observed.fingerprint = decision.required ? releaseReconciliationFingerprint(decision) : undefined;
1000
+ if (!decision.required) return;
1001
+
1002
+ const release = readCurrentRecoveryRelease(config.controllerHome);
1003
+ if (!release) throw new Error('RECOVERY_AUTOMATIC_RELEASE_CURRENT_RECOVERY_UNKNOWN');
1004
+ const executable = join(release.releasePath, 'forge-recovery');
1005
+ if (!existsSync(executable)) throw new Error('RECOVERY_AUTOMATIC_RELEASE_EXECUTABLE_UNAVAILABLE');
1006
+ const result = await runBoundedChild(
1007
+ executable,
1008
+ [RECOVERY_INTERNAL_RELEASE_RECONCILE_COMMAND, '--controller-home', config.controllerHome],
1009
+ {
1010
+ timeoutMs: RECOVERY_AUTOMATIC_RELEASE_STEP_TIMEOUT_MS,
1011
+ maxOutputBytes: 64 * 1024,
1012
+ forwardSignals: false,
1013
+ env: runtimeAuthorityFreeEnvironment(process.env),
1014
+ },
1015
+ );
1016
+ if (result.status !== 0 || result.failureCode || result.timedOut) {
1017
+ const detail = result.failureCode ?? result.error ?? (result.stderr.trim() || `exit=${result.status}`);
1018
+ throw new Error(`RECOVERY_AUTOMATIC_RELEASE_STEP_FAILED: ${detail.slice(0, 500)}`);
1019
+ }
1020
+ let envelope: unknown;
1021
+ try { envelope = JSON.parse(result.stdout); }
1022
+ catch { throw new Error('RECOVERY_AUTOMATIC_RELEASE_PROTOCOL_INVALID'); }
1023
+ if (!envelope || typeof envelope !== 'object' || Array.isArray(envelope)) throw new Error('RECOVERY_AUTOMATIC_RELEASE_PROTOCOL_INVALID');
1024
+ const parsed = envelope as {
1025
+ ok?: unknown;
1026
+ attempted?: unknown;
1027
+ error?: unknown;
1028
+ decision?: { reason?: unknown; action?: unknown };
1029
+ result?: { detail?: unknown };
1030
+ };
1031
+ if (parsed.ok !== true) {
1032
+ const detail = typeof parsed.error === 'string'
1033
+ ? parsed.error
1034
+ : typeof parsed.result?.detail === 'string'
1035
+ ? parsed.result.detail
1036
+ : 'automatic release reconciliation failed';
1037
+ throw new Error(`RECOVERY_AUTOMATIC_RELEASE_STEP_FAILED: ${detail.slice(0, 500)}`);
1038
+ }
1039
+ // An idle-window timeout is a legitimate no-cutover outcome, but not
1040
+ // forward progress. Feed it into the existing phase/revision backoff so
1041
+ // repeated drains cannot monopolize the Connector every 15 seconds.
1042
+ if (parsed.decision?.action === 'cutover'
1043
+ && typeof parsed.result?.detail === 'string'
1044
+ && parsed.result.detail.startsWith('RELEASE_SESSION_CUTOVER_DEFERRED_FOR_MCP_ACTIVITY')) {
1045
+ throw new Error(`RECOVERY_AUTOMATIC_RELEASE_CUTOVER_DEFERRED: ${parsed.result.detail.slice(0, 500)}`);
1046
+ }
1047
+ if (parsed.decision?.action) {
1048
+ process.stdout.write(JSON.stringify({
1049
+ at: new Date().toISOString(),
1050
+ action: 'automatic_release_reconcile',
1051
+ reason: parsed.decision?.reason,
1052
+ releaseAction: parsed.decision?.action,
1053
+ }) + '\n');
1054
+ }
1055
+ }
1056
+
1057
+ async function startAutomaticReleaseReconciliation(config: RecoveryConfig): Promise<never> {
1058
+ let lastFailedFingerprint: string | undefined;
1059
+ let consecutiveFailures = 0;
1060
+ for (;;) {
1061
+ const observed: { fingerprint?: string } = {};
1062
+ let failure: string | undefined;
1063
+ try {
1064
+ const decision = decideConfiguredRuntimeReleaseReconciliation(
1065
+ config.controllerHome,
1066
+ () => configuredRuntimeReleaseSourceState(config),
1067
+ );
1068
+ observed.fingerprint = decision.required ? releaseReconciliationFingerprint(decision) : undefined;
1069
+ // ReleaseSession cutover owns the one Connector drain/quietness fence.
1070
+ // A read-only busy pre-gate here would prevent that drain from ever
1071
+ // starting under sustained MCP traffic, starving the verified candidate.
1072
+ if (decision.required) await runAutomaticReleaseReconciliationStep(config, observed, decision);
1073
+ } catch (error) {
1074
+ failure = error instanceof Error ? error.message : String(error);
1075
+ process.stderr.write(`automatic release reconciliation failed: ${failure}\n`);
1076
+ }
1077
+ const backoff = nextReleaseReconcileBackoff(
1078
+ { fingerprint: lastFailedFingerprint, consecutiveFailures },
1079
+ Boolean(failure),
1080
+ observed.fingerprint,
1081
+ );
1082
+ lastFailedFingerprint = backoff.fingerprint;
1083
+ consecutiveFailures = backoff.consecutiveFailures;
1084
+ if (failure && backoff.delayMs > RECOVERY_AUTOMATIC_RELEASE_INTERVAL_MS) {
1085
+ process.stderr.write(
1086
+ `automatic release reconciliation backing off ${Math.round(backoff.delayMs / 1000)}s after ${backoff.consecutiveFailures} consecutive failures of ${backoff.fingerprint}\n`,
1087
+ );
1088
+ }
1089
+ await new Promise((resolveSleep) => setTimeout(resolveSleep, backoff.delayMs));
1090
+ }
1091
+ }
1092
+
1093
+ async function startRecoveryDaemon(config: RecoveryConfig): Promise<void> {
1094
+ const runtimeIdentity = writeRecoveryRuntimeIdentity(config.controllerHome, 'daemon');
1095
+
1096
+ // The Recovery gateway is the control plane used to repair every other
1097
+ // Recovery subsystem. Bind it before starting background work. A reconcile
1098
+ // or watchdog tick may perform expensive synchronous setup before its first
1099
+ // await, so invoking those loops first can leave a live daemon process with
1100
+ // no listening gateway.
1101
+ const backgroundTimer = setTimeout(() => {
1102
+ void startAutomaticReleaseReconciliation(config).catch((error) => {
1103
+ process.stderr.write(`Recovery release driver failed: ${error instanceof Error ? error.message : String(error)}\n`);
1104
+ process.exit(1);
1105
+ });
1106
+ if (config.installProfile === 'self-healing') {
1107
+ void startWatchdog(config, runtimeIdentity).catch((error) => {
1108
+ process.stderr.write(`Recovery monitor failed: ${error instanceof Error ? error.message : String(error)}\n`);
1109
+ process.exit(1);
1110
+ });
1111
+ }
1112
+ }, 0);
1113
+ backgroundTimer.unref?.();
1114
+
1115
+ await startGateway(config, runtimeIdentity);
1116
+ }
1117
+
1118
+ async function startGateway(config: RecoveryConfig, daemonIdentity?: RecoveryRuntimeIdentity): Promise<void> {
667
1119
  const gateway = config.gateway;
668
1120
  if (!gateway || gateway.host !== '127.0.0.1' || !Number.isInteger(gateway.port) || gateway.port < 1024 || gateway.port > 65535) {
669
1121
  throw new Error('RECOVERY_GATEWAY_CONFIG_INVALID');
670
1122
  }
671
- let runtimeIdentity: RecoveryRuntimeIdentity | undefined;
672
- let watchdogRestartInFlight = false;
1123
+ let runtimeIdentity: RecoveryRuntimeIdentity | undefined = daemonIdentity;
1124
+ let activeMcpRequests = 0;
1125
+ let latestMcpActivityAt: number | undefined;
673
1126
  const recentMutations = new Map<string, number[]>();
674
1127
  const oauthCodes = new Map<string, PendingOAuthCode>();
675
1128
  const oauthClients = new Map<string, OAuthClient>();
@@ -678,10 +1131,10 @@ async function startGateway(config: RecoveryConfig): Promise<void> {
678
1131
  securitySchemes: TOOL_SECURITY_SCHEMES,
679
1132
  _meta: { securitySchemes: TOOL_SECURITY_SCHEMES },
680
1133
  })) as unknown as Tool[];
681
- const recoveryMcp = new RecoveryMcpSessionServer({
1134
+ const recoveryMcp = new RecoveryMcpServer({
682
1135
  tools: recoveryTools,
683
1136
  dispatchTool: async (name, args, context) => {
684
- if (name === 'attest_known_good' || name === 'rollback_previous' || name === 'restart_primary_runtime' || name === 'restart_primary_connector' || name === 'recover_primary_runtime' || name === 'activate_runtime_release' || name === 'stage_and_activate_runtime_release' || name === 'migrate_controller_home' || name === 'restart_public_tunnel') {
1137
+ if (name === 'attest_known_good' || name === 'rollback_previous' || name === 'restart_primary_runtime' || name === 'restart_primary_connector' || name === 'recover_primary_runtime' || name === 'activate_runtime_release' || name === 'pin_runtime_release' || name === 'unpin_runtime_release' || name === 'activate_pinned_runtime_release' || name === 'stage_and_activate_runtime_release' || name === 'prepare_runtime_release_session' || name === 'verify_runtime_release_session_static' || name === 'verify_runtime_release_session_candidate' || name === 'cutover_runtime_release_session' || name === 'promote_runtime_release_session_known_good' || name === 'migrate_controller_home' || name === 'restart_public_tunnel') {
685
1138
  const now = Date.now();
686
1139
  const window = (recentMutations.get(context.remoteAddress) ?? []).filter((at) => now - at < 60_000);
687
1140
  if (window.length >= 3) throw new Error('Recovery mutation rate limit exceeded.');
@@ -699,17 +1152,20 @@ async function startGateway(config: RecoveryConfig): Promise<void> {
699
1152
  return;
700
1153
  }
701
1154
  if (request.method === 'GET' && matchesAnyPath(request.url, ['/health', '/recovery/health'])) {
702
- const watchdog = observeRecoveryWatchdogHealth(config.controllerHome);
1155
+ const watchdog = observeRecoveryWatchdogHealth(
1156
+ config.controllerHome,
1157
+ config.installProfile === 'self-healing',
1158
+ );
703
1159
  json(response, 200, {
704
1160
  status: watchdog.ok ? 'ok' : 'degraded',
705
1161
  service: 'forge-standalone-recovery',
706
1162
  watchdog: {
707
1163
  ok: watchdog.ok,
708
1164
  detail: watchdog.detail,
709
- pulseAgeMs: watchdog.pulseAgeMs,
710
- tickAgeMs: watchdog.tickAgeMs,
711
- releaseRevision: watchdog.runtimeIdentity?.releaseRevision,
712
- pid: watchdog.runtimeIdentity?.pid,
1165
+ pulseAgeMs: 'pulseAgeMs' in watchdog ? watchdog.pulseAgeMs : undefined,
1166
+ tickAgeMs: 'tickAgeMs' in watchdog ? watchdog.tickAgeMs : undefined,
1167
+ releaseRevision: 'runtimeIdentity' in watchdog ? watchdog.runtimeIdentity?.releaseRevision : runtimeIdentity?.releaseRevision,
1168
+ pid: 'runtimeIdentity' in watchdog ? watchdog.runtimeIdentity?.pid : runtimeIdentity?.pid,
713
1169
  },
714
1170
  version: FORGE_VERSION,
715
1171
  ...(runtimeIdentity ? {
@@ -718,6 +1174,11 @@ async function startGateway(config: RecoveryConfig): Promise<void> {
718
1174
  sourceCommit: runtimeIdentity.sourceCommit,
719
1175
  manifestSha256: runtimeIdentity.manifestSha256,
720
1176
  } : {}),
1177
+ requests: {
1178
+ draining: recoveryGatewayIsDraining(config.controllerHome),
1179
+ activeMcpRequests,
1180
+ ...(latestMcpActivityAt !== undefined ? { latestMcpActivityAgeMs: Math.max(0, Date.now() - latestMcpActivityAt) } : {}),
1181
+ },
721
1182
  });
722
1183
  return;
723
1184
  }
@@ -854,34 +1315,35 @@ async function startGateway(config: RecoveryConfig): Promise<void> {
854
1315
  const mcpRequest = classifyRecoveryMcpRequest(request, gatewayToken(config));
855
1316
  if (mcpRequest === 'not_mcp' || mcpRequest === 'method_not_supported') { json(response, 404, { error: 'NOT_FOUND' }); return; }
856
1317
  if (mcpRequest === 'auth_required') { response.setHeader('www-authenticate', recoveryWwwAuthenticate(request, config)); json(response, 401, recoveryUnauthorizedBody()); return; }
1318
+ if (recoveryGatewayIsDraining(config.controllerHome)) {
1319
+ response.setHeader('retry-after', '1');
1320
+ json(response, 503, { error: 'RECOVERY_DRAINING' });
1321
+ return;
1322
+ }
857
1323
  let body: unknown;
858
1324
  if (request.method === 'POST') {
859
1325
  if (!/^application\/json(?:\s*;|$)/i.test(String(request.headers['content-type'] ?? ''))) { json(response, 415, { error: 'RECOVERY_CONTENT_TYPE_REQUIRED' }); return; }
860
1326
  try { body = JSON.parse(await readBody(request)); } catch { json(response, 400, rpcError(null, -32700, 'Invalid JSON.')); return; }
861
1327
  }
862
- await recoveryMcp.handle(request, response, body);
863
- });
864
- await new Promise<void>((resolveListen, reject) => { server.once('error', reject); server.listen(gateway.port, gateway.host, () => resolveListen()); });
865
- runtimeIdentity = writeRecoveryRuntimeIdentity(config.controllerHome, 'gateway');
866
- const superviseWatchdog = async () => {
867
- if (!runtimeIdentity || watchdogRestartInFlight) return;
868
- const watchdog = observeRecoveryWatchdogHealth(config.controllerHome);
869
- if (watchdog.ok) return;
870
- watchdogRestartInFlight = true;
1328
+ activeMcpRequests += 1;
1329
+ latestMcpActivityAt = Date.now();
1330
+ let settled = false;
1331
+ const settle = () => {
1332
+ if (settled) return;
1333
+ settled = true;
1334
+ activeMcpRequests = Math.max(0, activeMcpRequests - 1);
1335
+ latestMcpActivityAt = Date.now();
1336
+ };
1337
+ response.once('finish', settle);
1338
+ response.once('close', settle);
871
1339
  try {
872
- const recovery = await restartRecoveryWatchdog(config);
873
- auditGateway({
874
- watchdog_supervision: recovery.ok ? 'recovered' : 'failed',
875
- detail: recovery.detail,
876
- attempted: recovery.attempted,
877
- serviceTarget: recovery.serviceTarget,
878
- });
1340
+ await recoveryMcp.handle(request, response, body);
879
1341
  } finally {
880
- watchdogRestartInFlight = false;
1342
+ if (response.writableEnded || response.destroyed) settle();
881
1343
  }
882
- };
883
- const watchdogSupervisor = setInterval(() => { void superviseWatchdog(); }, 15_000);
884
- watchdogSupervisor.unref?.();
1344
+ });
1345
+ await new Promise<void>((resolveListen, reject) => { server.once('error', reject); server.listen(gateway.port, gateway.host, () => resolveListen()); });
1346
+ runtimeIdentity ??= writeRecoveryRuntimeIdentity(config.controllerHome, 'gateway');
885
1347
  process.stdout.write(JSON.stringify({ status: 'ready', host: gateway.host, port: gateway.port, runtimeIdentity }) + '\n');
886
1348
  }
887
1349