@moretea-labs/forge 1.6.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/AGENTS.md +4 -0
  2. package/CHANGELOG.md +39 -0
  3. package/CLAUDE.md +4 -0
  4. package/README.md +2 -2
  5. package/README.zh-CN.md +1 -1
  6. package/SKILL.md +2 -2
  7. package/THIRD_PARTY_NOTICES.md +1 -1
  8. package/agents/openai.yaml +1 -1
  9. package/assets/hooks/hook-input.sh +11 -8
  10. package/assets/hooks/lib/session-state.sh +10 -7
  11. package/assets/hooks/lib/workflow-state.sh +13 -177
  12. package/assets/hooks/post-tool-observer.sh +8 -2
  13. package/assets/hooks/prompt-guard.sh +5 -81
  14. package/assets/hooks/session-start-context.sh +56 -1
  15. package/assets/partials/05-workflow.partial.md +1 -1
  16. package/assets/partials-agents/02-operating-mode.partial.md +2 -0
  17. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  18. package/assets/plugin-registry.v1.json +3 -3
  19. package/assets/reference-configs/document-generation.md +1 -1
  20. package/assets/reference-configs/harness-overview.md +1 -1
  21. package/assets/reference-configs/sprint-contracts.md +3 -3
  22. package/assets/skill-version.json +2 -2
  23. package/assets/templates/contract.template.md +2 -7
  24. package/assets/templates/helpers/capture-plan.sh +2 -2
  25. package/assets/templates/helpers/check-architecture-sync.sh +17 -0
  26. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  27. package/assets/templates/helpers/contract-worktree.sh +1 -21
  28. package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
  29. package/assets/templates/helpers/inspect-project-state.ts +8 -6
  30. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  31. package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
  32. package/assets/templates/helpers/new-plan.sh +1 -1
  33. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  34. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  35. package/assets/templates/helpers/verify-contract.sh +1 -111
  36. package/assets/templates/helpers/verify-sprint.sh +3 -180
  37. package/assets/templates/helpers/workflow-contract.ts +5 -1
  38. package/assets/templates/plan.template.md +1 -1
  39. package/assets/templates/review.template.md +9 -34
  40. package/assets/workflow-contract.v1.json +10 -19
  41. package/docs/README.md +10 -10
  42. package/docs/architecture/CURRENT.md +178 -0
  43. package/docs/architecture/EVOLUTION.md +42 -0
  44. package/docs/architecture/current/README.md +4 -156
  45. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  46. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  47. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  48. package/docs/architecture/history/README.md +7 -0
  49. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  50. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  51. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  52. package/docs/architecture/history.md +3 -34
  53. package/docs/architecture/index.md +15 -98
  54. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  55. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  56. package/docs/architecture/versions/1.6.md +38 -0
  57. package/docs/architecture/versions/1.7.md +41 -0
  58. package/docs/forge-chatgpt-browser-engine.md +2 -2
  59. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  60. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  61. package/docs/forge-github-issue-launcher.md +1 -1
  62. package/docs/forge-plugin-management.md +1 -1
  63. package/docs/forge-runtime-self-healing-loop.md +1 -1
  64. package/docs/operations/controller-browser-plugin.md +26 -6
  65. package/docs/operations/controller-performance-and-502.md +143 -0
  66. package/docs/operations/controller-reliability-runbook.md +1 -1
  67. package/docs/operations/features.md +2 -2
  68. package/docs/operations/github-repository.md +1 -1
  69. package/docs/operations/homebrew.md +2 -2
  70. package/docs/operations/mcp-tool-exposure.md +5 -3
  71. package/docs/wiki/Architecture.md +6 -6
  72. package/docs/wiki/Implementation.md +2 -2
  73. package/docs/wiki/Integrations.md +2 -2
  74. package/docs/wiki/Work-Lifecycle.md +1 -1
  75. package/package.json +33 -11
  76. package/scripts/activate-source-baseline.ts +17 -13
  77. package/scripts/architecture-event.ts +2 -820
  78. package/scripts/architecture-queue.sh +2 -645
  79. package/scripts/archive-architecture-request.sh +2 -242
  80. package/scripts/archive-workflow.sh +1 -291
  81. package/scripts/benchmark-background-check-overlap.ts +228 -0
  82. package/scripts/benchmark-mcp-host.sh +117 -0
  83. package/scripts/benchmark-mcp-transport.ts +170 -0
  84. package/scripts/benchmark-quality-harness.ts +146 -0
  85. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  86. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  87. package/scripts/benchmark-typescript-navigation.ts +186 -0
  88. package/scripts/benchmark-work-efficiency.ts +334 -0
  89. package/scripts/capability-config.ts +2 -392
  90. package/scripts/capability-resolver.ts +2 -503
  91. package/scripts/capture-plan.sh +1 -332
  92. package/scripts/check-agent-tooling.sh +2 -1558
  93. package/scripts/check-architecture-sync.sh +2 -371
  94. package/scripts/check-brain-manifest.sh +2 -253
  95. package/scripts/check-context-files.sh +2 -47
  96. package/scripts/check-deploy-sql-order.sh +2 -115
  97. package/scripts/check-mcp-compatibility.ts +14 -0
  98. package/scripts/check-runtime-architecture.mjs +349 -79
  99. package/scripts/check-skill-version.ts +3 -191
  100. package/scripts/check-source-duplication.mjs +61 -0
  101. package/scripts/check-stable-baseline.ts +92 -0
  102. package/scripts/check-task-sync.sh +2 -63
  103. package/scripts/check-task-workflow.sh +2 -1000
  104. package/scripts/codex-handoff-resume.sh +2 -267
  105. package/scripts/context-contract-sync.sh +2 -514
  106. package/scripts/contract-run.ts +2 -463
  107. package/scripts/contract-worktree.sh +1 -722
  108. package/scripts/ensure-task-workflow.sh +1 -1225
  109. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  110. package/scripts/inspect-project-state.ts +4 -279
  111. package/scripts/install-standalone-recovery.ts +24 -0
  112. package/scripts/lib/project-init-lib.sh +22 -46
  113. package/scripts/lib/run-forge-helper.sh +19 -0
  114. package/scripts/lib/run-forge-helper.ts +26 -0
  115. package/scripts/migrate-project-template.sh +1 -1189
  116. package/scripts/migrate-workflow-docs.ts +4 -412
  117. package/scripts/new-plan.sh +1 -180
  118. package/scripts/new-spec.sh +1 -47
  119. package/scripts/new-sprint.sh +1 -46
  120. package/scripts/plan-to-todo.sh +1 -796
  121. package/scripts/prepare-codex-handoff.sh +2 -201
  122. package/scripts/prepare-handoff.sh +1 -78
  123. package/scripts/refresh-current-status.sh +2 -467
  124. package/scripts/run-governed-gate.ts +5 -1
  125. package/scripts/select-agent-context-blocks.sh +2 -64
  126. package/scripts/ship-worktrees.sh +1 -558
  127. package/scripts/smoke-ios-physical-device.ts +4 -2
  128. package/scripts/smoke-runtime-recovery.ts +1 -35
  129. package/scripts/smoke-schedule-engine.ts +1 -20
  130. package/scripts/sprint-backlog.sh +1 -834
  131. package/scripts/stage-runtime-release.ts +2 -1
  132. package/scripts/summarize-failures.sh +2 -123
  133. package/scripts/switch-plan.sh +1 -150
  134. package/scripts/sync-brain-docs.sh +2 -379
  135. package/scripts/sync-generated-authority.mjs +175 -0
  136. package/scripts/verify-contract.sh +2 -703
  137. package/scripts/verify-forge-runtime.sh +0 -1
  138. package/scripts/verify-sprint.sh +1 -677
  139. package/scripts/workflow-contract.ts +4 -205
  140. package/scripts/workstream-sync.sh +2 -261
  141. package/src/cli/agent-jobs/executor-health.ts +1 -1
  142. package/src/cli/commands/init-hook.ts +3 -3
  143. package/src/cli/commands/mcp.ts +9 -5
  144. package/src/cli/commands/plugin.ts +12 -3
  145. package/src/cli/commands/recovery.ts +25 -3
  146. package/src/cli/commands/runtime.ts +55 -3
  147. package/src/cli/commands/security.ts +12 -1
  148. package/src/cli/commands/setup-profile.ts +31 -21
  149. package/src/cli/controller/check-runner.ts +113 -5
  150. package/src/cli/controller/composite-result.ts +1 -1
  151. package/src/cli/controller/context/focus.ts +51 -0
  152. package/src/cli/controller/context/instruction-context.ts +114 -0
  153. package/src/cli/controller/context/known-paths.ts +76 -0
  154. package/src/cli/controller/context/query-planning.ts +110 -0
  155. package/src/cli/controller/context/rule-resolution.ts +532 -0
  156. package/src/cli/controller/context/source-materializer.ts +278 -0
  157. package/src/cli/controller/context/structural.ts +16 -0
  158. package/src/cli/controller/context/types.ts +286 -0
  159. package/src/cli/controller/context-pack.ts +828 -487
  160. package/src/cli/controller/launch-agents.ts +77 -2
  161. package/src/cli/controller/operational-plan.ts +2 -2
  162. package/src/cli/controller/work-mode.ts +6 -4
  163. package/src/cli/editing/edit-session.ts +60 -7
  164. package/src/cli/github/contracts.ts +16 -0
  165. package/src/cli/github/github.ts +135 -16
  166. package/src/cli/github/session.ts +2 -1
  167. package/src/cli/installer/managed-entries.ts +15 -1
  168. package/src/cli/local-bridge/console-automations.ts +54 -78
  169. package/src/cli/local-bridge/console-view-models.ts +1 -35
  170. package/src/cli/local-bridge/facade-api.ts +37 -264
  171. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  172. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
  173. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  174. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
  175. package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
  176. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  177. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
  178. package/src/cli/local-bridge/frontend/src/styles.css +1 -1
  179. package/src/cli/local-bridge/frontend/src/types.ts +5 -8
  180. package/src/cli/local-bridge/job-store.ts +1 -490
  181. package/src/cli/local-bridge/server.ts +2 -515
  182. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  183. package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
  184. package/src/cli/mcp/auth.ts +5 -5
  185. package/src/cli/mcp/instructions.ts +3 -2
  186. package/src/cli/mcp/legacy-tool-service.ts +10 -222
  187. package/src/cli/mcp/multi-repository.ts +16 -5
  188. package/src/cli/mcp/oauth.ts +175 -26
  189. package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
  190. package/src/cli/mcp/repository-tools.ts +282 -146
  191. package/src/cli/mcp/server.ts +586 -116
  192. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  193. package/src/cli/mcp/tool-contract.ts +14 -0
  194. package/src/cli/mcp/toolset-selection.ts +1 -1
  195. package/src/cli/mcp/toolset.ts +44 -7
  196. package/src/cli/mcp/transports/http.ts +124 -14
  197. package/src/cli/mcp/transports/session-registry.ts +43 -5
  198. package/src/cli/mcp/types.ts +3 -3
  199. package/src/cli/repositories/command-classifier.ts +103 -21
  200. package/src/cli/repositories/command-executor.ts +78 -526
  201. package/src/cli/repositories/command-process.ts +108 -0
  202. package/src/cli/repositories/command-scope.ts +99 -41
  203. package/src/cli/repositories/controller-home.ts +7 -8
  204. package/src/cli/repositories/git-command-executor.ts +2 -0
  205. package/src/cli/repositories/locks.ts +53 -16
  206. package/src/cli/repositories/repository-snapshot.ts +124 -0
  207. package/src/cli/repositories/selected-path-actions.ts +17 -9
  208. package/src/cli/repositories/structured-git.ts +118 -6
  209. package/src/cli/repository/inspector.ts +563 -65
  210. package/src/cli/repository/session-cache.ts +58 -17
  211. package/src/cli/v81-entry.ts +1 -4
  212. package/src/effects/fs-transaction.ts +85 -17
  213. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  214. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  215. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  216. package/src/runtime/context/lsp-client.ts +283 -0
  217. package/src/runtime/context/semantic-navigation-contract.ts +61 -0
  218. package/src/runtime/context/semantic-navigation.ts +223 -0
  219. package/src/runtime/context/swift-navigation.ts +565 -0
  220. package/src/runtime/context/typescript-navigation.ts +226 -0
  221. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  222. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
  223. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  224. package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
  225. package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
  226. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  227. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  228. package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
  229. package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
  230. package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
  231. package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
  232. package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
  233. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  234. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  235. package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
  236. package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
  237. package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
  238. package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
  239. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  240. package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
  241. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
  242. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
  243. package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
  244. package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
  245. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
  246. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  247. package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
  248. package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
  249. package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
  250. package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
  251. package/src/runtime/control-plane/facade/index.ts +3 -0
  252. package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
  253. package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
  254. package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
  255. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  256. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  257. package/src/runtime/control-plane/facade/types.ts +90 -10
  258. package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
  259. package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
  260. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  261. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  262. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  263. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  264. package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
  265. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  266. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  267. package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
  268. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  269. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  270. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  271. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  272. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  273. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  274. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  275. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  276. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  277. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  278. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
  279. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
  280. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
  281. package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
  282. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  283. package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
  284. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  285. package/src/runtime/control-plane/release-retention.ts +62 -2
  286. package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
  287. package/src/runtime/control-plane/routing/route-policy.ts +24 -21
  288. package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
  289. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  290. package/src/runtime/diagnostics/mcp-timing.ts +13 -0
  291. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  292. package/src/runtime/diagnostics/performance.ts +24 -1
  293. package/src/runtime/evidence/event-ledger.ts +1 -1
  294. package/src/runtime/evidence/process-check-execution.ts +18 -0
  295. package/src/runtime/execution/jobs/child-reference.ts +6 -2
  296. package/src/runtime/execution/jobs/store.ts +58 -4
  297. package/src/runtime/execution/jobs/types.ts +1 -1
  298. package/src/runtime/execution/managed-workspace.ts +203 -1
  299. package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
  300. package/src/runtime/execution/process-runtime/check-result.ts +88 -0
  301. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
  302. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  303. package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
  304. package/src/runtime/execution/process-runtime/gc.ts +38 -6
  305. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
  306. package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
  307. package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
  308. package/src/runtime/execution/process-runtime/runtime.ts +53 -3
  309. package/src/runtime/execution/process-runtime/store.ts +1 -0
  310. package/src/runtime/execution/process-runtime/types.ts +4 -12
  311. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  312. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  313. package/src/runtime/execution/work-activity.ts +21 -0
  314. package/src/runtime/execution/workers/executor.ts +18 -61
  315. package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
  316. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
  317. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  318. package/src/runtime/gateway/mcp/router.ts +186 -84
  319. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
  320. package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
  321. package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
  322. package/src/runtime/plugins/browser-adapter.ts +2662 -829
  323. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  324. package/src/runtime/plugins/browser-handoff-host.ts +2 -25
  325. package/src/runtime/plugins/browser-handoff.ts +110 -5
  326. package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
  327. package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
  328. package/src/runtime/plugins/browser-native-session-service.ts +222 -0
  329. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  330. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  331. package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
  332. package/src/runtime/plugins/browser-runtime.ts +193 -0
  333. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  334. package/src/runtime/plugins/browser-session-store.ts +121 -0
  335. package/src/runtime/plugins/browser-session-types.ts +98 -0
  336. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  337. package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
  338. package/src/runtime/plugins/external-adapter.ts +358 -3
  339. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  340. package/src/runtime/plugins/first-party-registry.ts +2 -0
  341. package/src/runtime/plugins/github-adapter.ts +125 -0
  342. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  343. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  344. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  345. package/src/runtime/plugins/ios-adapter.ts +106 -16
  346. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  347. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  348. package/src/runtime/plugins/lightweight-action.ts +153 -0
  349. package/src/runtime/plugins/local-system-adapter.ts +123 -6
  350. package/src/runtime/plugins/macos-capability-broker.ts +47 -9
  351. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  352. package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
  353. package/src/runtime/plugins/resend-adapter.ts +1 -1
  354. package/src/runtime/plugins/store.ts +330 -33
  355. package/src/runtime/plugins/types.ts +38 -3
  356. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  357. package/src/runtime/projections/git-status-sampler.ts +16 -16
  358. package/src/runtime/projections/materialized-view.ts +28 -10
  359. package/src/runtime/recovery/maintenance-executor.ts +426 -25
  360. package/src/runtime/root/mcp-transport.ts +180 -10
  361. package/src/runtime/root/package-connector-service.ts +230 -10
  362. package/src/runtime/root/package-runtime-release.ts +143 -17
  363. package/src/runtime/root/package-runtime-service.ts +461 -22
  364. package/src/runtime/root/release-manifest.ts +144 -18
  365. package/src/runtime/root/release-materialize.ts +125 -32
  366. package/src/runtime/root/release-store.ts +48 -11
  367. package/src/runtime/root/runtime.ts +23 -5
  368. package/src/runtime/root/service.ts +100 -29
  369. package/src/runtime/root/types.ts +12 -0
  370. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  371. package/src/runtime/shared/trusted-executable.ts +62 -0
  372. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  373. package/src/runtime/standalone-recovery/core.ts +1079 -240
  374. package/src/runtime/standalone-recovery/entry.ts +100 -24
  375. package/src/runtime/standalone-recovery/installer.ts +20 -4
  376. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  377. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  378. package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
  379. package/src/runtime/workflow/schedules/engine.ts +364 -202
  380. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  381. package/src/runtime/workflow/schedules/settlement.ts +64 -17
  382. package/src/runtime/workflow/schedules/store.ts +209 -19
  383. package/src/runtime/workflow/schedules/types.ts +2 -4
  384. package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
  385. package/src/testing/test-governance.ts +8 -40
  386. package/assets/reference-configs/heartbeat-triage.md +0 -57
  387. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  388. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  389. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  390. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  391. package/docs/architecture/current/architecture-invariants.md +0 -331
  392. package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
  393. package/docs/architecture/current/codegraph-context-provider.md +0 -238
  394. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  395. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  396. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
  397. package/docs/architecture/current/entity-model.md +0 -612
  398. package/docs/architecture/current/external-plugin-governance.md +0 -396
  399. package/docs/architecture/current/failure-recovery.md +0 -447
  400. package/docs/architecture/current/governance.md +0 -213
  401. package/docs/architecture/current/human-interaction-plane.md +0 -87
  402. package/docs/architecture/current/implementation-status.md +0 -128
  403. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  404. package/docs/architecture/current/legacy-governance.md +0 -58
  405. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  406. package/docs/architecture/current/migration-roadmap.md +0 -105
  407. package/docs/architecture/current/multi-repository-execution.md +0 -371
  408. package/docs/architecture/current/operations-runbook.md +0 -45
  409. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  410. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  411. package/docs/architecture/current/repository-access-modes.md +0 -102
  412. package/docs/architecture/current/route-policy.md +0 -15
  413. package/docs/architecture/current/route-session-concurrency.md +0 -85
  414. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  415. package/docs/architecture/current/runtime-directory-map.md +0 -49
  416. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  417. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  418. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  419. package/docs/architecture/current/system-overview.md +0 -169
  420. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  421. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  422. package/docs/architecture/current/thin-harness-v1.md +0 -321
  423. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  424. package/docs/forge-autonomous-goal-loop.md +0 -9
  425. package/docs/forge-provider-configuration.md +0 -7
  426. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
  427. package/docs/operations/gmail-assistant-routines.md +0 -186
  428. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  429. package/docs/operations/provider-configuration.md +0 -31
  430. package/scripts/harness-trace-grade.sh +0 -150
  431. package/scripts/heartbeat-triage.sh +0 -361
  432. package/scripts/maintenance-triage.sh +0 -167
  433. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  434. package/src/cli/repositories/goal-registry.ts +0 -239
  435. package/src/runtime/assistant/action-proposals.ts +0 -279
  436. package/src/runtime/assistant/intent.ts +0 -395
  437. package/src/runtime/assistant/model-provider.ts +0 -460
  438. package/src/runtime/assistant/openapi.ts +0 -223
  439. package/src/runtime/assistant/policy.ts +0 -89
  440. package/src/runtime/assistant/readiness.ts +0 -258
  441. package/src/runtime/assistant/routine-runtime.ts +0 -477
  442. package/src/runtime/assistant/schedule-binding.ts +0 -171
  443. package/src/runtime/assistant/standing-grants.ts +0 -375
  444. package/src/runtime/assistant/store.ts +0 -243
  445. package/src/runtime/assistant/types.ts +0 -135
  446. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  447. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  448. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  449. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  450. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  451. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  452. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  453. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  454. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  455. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  456. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  457. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  458. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  459. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  460. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  461. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  462. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  463. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  464. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  465. package/src/runtime/workflow/findings/store.ts +0 -204
  466. package/src/runtime/workflow/findings/types.ts +0 -58
  467. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  468. package/src/runtime/workflow/portfolio/store.ts +0 -185
  469. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -1,4 +1,4 @@
1
- import { readFileSync } from 'fs';
1
+ import { existsSync, readFileSync, realpathSync } from 'fs';
2
2
  import { resolve } from 'path';
3
3
  import { CONTROL_PLANE_SCHEMA_VERSION } from '../control-plane/persistence/sqlite-store';
4
4
  import type { RuntimeReleaseManifest } from './types';
@@ -8,6 +8,82 @@ function requireString(value: unknown, field: string): string {
8
8
  return value.trim();
9
9
  }
10
10
 
11
+ function canonicalExistingPathIdentity(path: string): string {
12
+ const resolved = resolve(path);
13
+ return existsSync(resolved) ? realpathSync(resolved) : resolved;
14
+ }
15
+
16
+ function requireSha256Identity(value: unknown, field: string): string {
17
+ const identity = requireString(value, field);
18
+ if (!/^sha256:[a-f0-9]{64}$/i.test(identity)) {
19
+ throw new Error(`RELEASE_MANIFEST_INVALID: ${field} must be sha256:<64 hex>`);
20
+ }
21
+ return identity;
22
+ }
23
+
24
+ type RuntimeComponentPair<KEntry extends keyof RuntimeReleaseManifest, KIdentity extends keyof RuntimeReleaseManifest> = Pick<
25
+ RuntimeReleaseManifest,
26
+ KEntry | KIdentity
27
+ >;
28
+
29
+ function optionalRuntimeComponent<
30
+ KEntry extends keyof RuntimeReleaseManifest,
31
+ KIdentity extends keyof RuntimeReleaseManifest,
32
+ >(input: {
33
+ value: Record<string, unknown>;
34
+ entryField: KEntry;
35
+ identityField: KIdentity;
36
+ canonicalEntry: RuntimeReleaseManifest[KEntry];
37
+ }): RuntimeComponentPair<KEntry, KIdentity> | undefined {
38
+ const rawEntry = input.value[input.entryField as string];
39
+ const rawIdentity = input.value[input.identityField as string];
40
+ if (rawEntry === undefined && rawIdentity === undefined) return undefined;
41
+ const entry = requireString(rawEntry, input.entryField as string);
42
+ if (entry !== input.canonicalEntry) {
43
+ throw new Error(`RELEASE_MANIFEST_INVALID: ${String(input.entryField)} must be ${String(input.canonicalEntry)}, got ${entry}`);
44
+ }
45
+ const identity = requireSha256Identity(rawIdentity, input.identityField as string);
46
+ return {
47
+ [input.entryField]: input.canonicalEntry,
48
+ [input.identityField]: identity,
49
+ } as RuntimeComponentPair<KEntry, KIdentity>;
50
+ }
51
+
52
+ export const COMPILED_RUNTIME_RELEASE_COMPONENT_FIELDS = [
53
+ 'diagnosticEntrypoint', 'diagnosticArtifactIdentity',
54
+ 'browserNodeBridgeEntrypoint', 'browserNodeBridgeArtifactIdentity',
55
+ 'browserHandoffEntrypoint', 'browserHandoffArtifactIdentity',
56
+ 'processRunnerEntrypoint', 'processRunnerArtifactIdentity',
57
+ 'checkRunnerEntrypoint', 'checkRunnerArtifactIdentity',
58
+ 'pluginActionSidecarEntrypoint', 'pluginActionSidecarArtifactIdentity',
59
+ 'externalPluginProbeEntrypoint', 'externalPluginProbeArtifactIdentity',
60
+ 'codeGraphNodeEntrypoint', 'codeGraphNodeArtifactIdentity',
61
+ 'codeGraphSidecarEntrypoint', 'codeGraphSidecarArtifactIdentity',
62
+ 'codeGraphLibraryRoot', 'codeGraphLibraryArtifactIdentity',
63
+ 'packageRoot', 'packageArtifactIdentity',
64
+ 'controllerUiRoot', 'controllerUiArtifactIdentity',
65
+ ] as const satisfies readonly (keyof RuntimeReleaseManifest)[];
66
+
67
+ /**
68
+ * Compiled self-host candidates have a closed execution surface. Package
69
+ * launcher releases are a separate source-backed release form and therefore
70
+ * intentionally do not satisfy this contract.
71
+ */
72
+ export type CompleteCompiledRuntimeReleaseManifest = RuntimeReleaseManifest & Required<Pick<
73
+ RuntimeReleaseManifest,
74
+ typeof COMPILED_RUNTIME_RELEASE_COMPONENT_FIELDS[number]
75
+ >>;
76
+
77
+ export function requireCompleteCompiledRuntimeReleaseManifest(
78
+ manifest: RuntimeReleaseManifest,
79
+ ): asserts manifest is CompleteCompiledRuntimeReleaseManifest {
80
+ for (const field of COMPILED_RUNTIME_RELEASE_COMPONENT_FIELDS) {
81
+ if (manifest[field] === undefined) {
82
+ throw new Error(`RUNTIME_RELEASE_COMPILED_COMPONENT_MISSING: ${field}`);
83
+ }
84
+ }
85
+ }
86
+
11
87
  export function loadRuntimeReleaseManifest(
12
88
  path: string,
13
89
  expectedControllerHome: string,
@@ -31,7 +107,7 @@ export function loadRuntimeReleaseManifest(
31
107
  throw new Error('RELEASE_MANIFEST_INVALID: configurationSchemaVersion must be 1');
32
108
  }
33
109
  const controllerHome = resolve(requireString(value.controllerHome, 'controllerHome'));
34
- if (controllerHome !== resolve(expectedControllerHome)) {
110
+ if (canonicalExistingPathIdentity(controllerHome) !== canonicalExistingPathIdentity(expectedControllerHome)) {
35
111
  throw new Error('RELEASE_MANIFEST_CONTROLLER_HOME_MISMATCH');
36
112
  }
37
113
  const compatibility = value.databaseSchemaCompatibility as Record<string, unknown> | undefined;
@@ -45,18 +121,60 @@ export function loadRuntimeReleaseManifest(
45
121
  `RELEASE_DATABASE_SCHEMA_INCOMPATIBLE: runtime=${CONTROL_PLANE_SCHEMA_VERSION} supported=${minimum}-${maximum}`,
46
122
  );
47
123
  }
48
- let diagnosticEntrypoint: 'forge-cli' | undefined;
49
- let diagnosticArtifactIdentity: string | undefined;
50
- if (value.diagnosticEntrypoint !== undefined) {
51
- const diagnosticEntry = requireString(value.diagnosticEntrypoint, 'diagnosticEntrypoint');
52
- if (diagnosticEntry !== 'forge-cli') {
53
- throw new Error(`RELEASE_MANIFEST_INVALID: diagnosticEntrypoint must be forge-cli, got ${diagnosticEntry}`);
54
- }
55
- diagnosticEntrypoint = 'forge-cli';
56
- diagnosticArtifactIdentity = requireString(value.diagnosticArtifactIdentity, 'diagnosticArtifactIdentity');
57
- } else if (value.diagnosticArtifactIdentity !== undefined) {
58
- throw new Error('RELEASE_MANIFEST_INVALID: diagnosticArtifactIdentity requires diagnosticEntrypoint');
59
- }
124
+ const diagnostic = optionalRuntimeComponent({
125
+ value,
126
+ entryField: 'diagnosticEntrypoint',
127
+ identityField: 'diagnosticArtifactIdentity',
128
+ canonicalEntry: 'forge-cli',
129
+ });
130
+ const browserNodeBridge = optionalRuntimeComponent({
131
+ value,
132
+ entryField: 'browserNodeBridgeEntrypoint',
133
+ identityField: 'browserNodeBridgeArtifactIdentity',
134
+ canonicalEntry: 'browser-node-bridge-host.js',
135
+ });
136
+ const browserHandoff = optionalRuntimeComponent({
137
+ value,
138
+ entryField: 'browserHandoffEntrypoint',
139
+ identityField: 'browserHandoffArtifactIdentity',
140
+ canonicalEntry: 'browser-handoff-host.js',
141
+ });
142
+ const processRunner = optionalRuntimeComponent({
143
+ value,
144
+ entryField: 'processRunnerEntrypoint',
145
+ identityField: 'processRunnerArtifactIdentity',
146
+ canonicalEntry: 'process-runner.js',
147
+ });
148
+ const checkRunner = optionalRuntimeComponent({
149
+ value,
150
+ entryField: 'checkRunnerEntrypoint',
151
+ identityField: 'checkRunnerArtifactIdentity',
152
+ canonicalEntry: 'forge-check-runner',
153
+ });
154
+ const pluginActionSidecar = optionalRuntimeComponent({
155
+ value,
156
+ entryField: 'pluginActionSidecarEntrypoint',
157
+ identityField: 'pluginActionSidecarArtifactIdentity',
158
+ canonicalEntry: 'forge-plugin-action-sidecar',
159
+ });
160
+ const externalPluginProbe = optionalRuntimeComponent({
161
+ value,
162
+ entryField: 'externalPluginProbeEntrypoint',
163
+ identityField: 'externalPluginProbeArtifactIdentity',
164
+ canonicalEntry: 'external-unix-socket-probe.cjs',
165
+ });
166
+ const packageSnapshot = optionalRuntimeComponent({
167
+ value,
168
+ entryField: 'packageRoot',
169
+ identityField: 'packageArtifactIdentity',
170
+ canonicalEntry: 'package',
171
+ });
172
+ const controllerUi = optionalRuntimeComponent({
173
+ value,
174
+ entryField: 'controllerUiRoot',
175
+ identityField: 'controllerUiArtifactIdentity',
176
+ canonicalEntry: 'ui-dist',
177
+ });
60
178
  const browserAutomationHelperFields = [
61
179
  value.browserAutomationHelperEntrypoint,
62
180
  value.browserAutomationHelperArtifactIdentity,
@@ -107,11 +225,11 @@ export function loadRuntimeReleaseManifest(
107
225
  }
108
226
  codeGraphRuntime = {
109
227
  codeGraphNodeEntrypoint: 'codegraph-node',
110
- codeGraphNodeArtifactIdentity: requireString(value.codeGraphNodeArtifactIdentity, 'codeGraphNodeArtifactIdentity'),
228
+ codeGraphNodeArtifactIdentity: requireSha256Identity(value.codeGraphNodeArtifactIdentity, 'codeGraphNodeArtifactIdentity'),
111
229
  codeGraphSidecarEntrypoint: 'codegraph-sidecar.cjs',
112
- codeGraphSidecarArtifactIdentity: requireString(value.codeGraphSidecarArtifactIdentity, 'codeGraphSidecarArtifactIdentity'),
230
+ codeGraphSidecarArtifactIdentity: requireSha256Identity(value.codeGraphSidecarArtifactIdentity, 'codeGraphSidecarArtifactIdentity'),
113
231
  codeGraphLibraryRoot: 'codegraph-lib',
114
- codeGraphLibraryArtifactIdentity: requireString(value.codeGraphLibraryArtifactIdentity, 'codeGraphLibraryArtifactIdentity'),
232
+ codeGraphLibraryArtifactIdentity: requireSha256Identity(value.codeGraphLibraryArtifactIdentity, 'codeGraphLibraryArtifactIdentity'),
115
233
  };
116
234
  }
117
235
 
@@ -131,9 +249,17 @@ export function loadRuntimeReleaseManifest(
131
249
  releaseId: requireString(value.releaseId, 'releaseId'),
132
250
  artifactIdentity: requireString(value.artifactIdentity, 'artifactIdentity'),
133
251
  entrypoint: 'forge-runtime',
134
- ...(diagnosticEntrypoint ? { diagnosticEntrypoint, diagnosticArtifactIdentity } : {}),
252
+ ...(diagnostic ?? {}),
253
+ ...(browserNodeBridge ?? {}),
254
+ ...(browserHandoff ?? {}),
255
+ ...(processRunner ?? {}),
256
+ ...(checkRunner ?? {}),
257
+ ...(pluginActionSidecar ?? {}),
258
+ ...(externalPluginProbe ?? {}),
135
259
  ...(browserAutomationHelper ?? {}),
136
260
  ...(codeGraphRuntime ?? {}),
261
+ ...(packageSnapshot ?? {}),
262
+ ...(controllerUi ?? {}),
137
263
  arguments: argumentsValue as string[],
138
264
  configurationSchemaVersion: 1,
139
265
  controllerHome,
@@ -5,6 +5,8 @@ import { dirname, join, relative, resolve } from 'path';
5
5
  import { runProcess } from '../../effects/process-runner';
6
6
  import { resolveBunExecutable } from '../shared/process-environment';
7
7
  import { CONTROL_PLANE_SCHEMA_VERSION } from '../control-plane/persistence/sqlite-store';
8
+ import { loadRuntimeReleaseManifest, requireCompleteCompiledRuntimeReleaseManifest } from './release-manifest';
9
+ import { packageRuntimeFileIndex, stagePackageRuntimeSnapshot } from './package-runtime-release';
8
10
 
9
11
  /**
10
12
  * Stage one immutable Forge Runtime release below Controller Home. The staged
@@ -24,6 +26,7 @@ export interface MacOSRuntimeCodeSigning {
24
26
  }
25
27
 
26
28
  export interface StagedRuntimeRelease {
29
+ controllerHome: string;
27
30
  releasePath: string;
28
31
  manifestPath: string;
29
32
  releaseId: string;
@@ -33,10 +36,12 @@ export interface StagedRuntimeRelease {
33
36
  browserHandoffArtifactIdentity?: string;
34
37
  processRunnerArtifactIdentity?: string;
35
38
  checkRunnerArtifactIdentity?: string;
39
+ pluginActionSidecarArtifactIdentity?: string;
36
40
  externalPluginProbeArtifactIdentity?: string;
37
41
  codeGraphNodeArtifactIdentity?: string;
38
42
  codeGraphSidecarArtifactIdentity?: string;
39
43
  codeGraphLibraryArtifactIdentity?: string;
44
+ packageArtifactIdentity?: string;
40
45
  controllerUiArtifactIdentity?: string;
41
46
  macosCodeSigning?: MacOSRuntimeCodeSigning;
42
47
  manifestSha256: string;
@@ -308,6 +313,13 @@ export function stageRuntimeReleaseFromCandidateSource(input: {
308
313
  let rawManifest: Record<string, unknown>;
309
314
  try { rawManifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as Record<string, unknown>; }
310
315
  catch (error) { throw new Error(`RUNTIME_RELEASE_CANDIDATE_MANIFEST_INVALID: ${error instanceof Error ? error.message : String(error)}`); }
316
+ const manifest = loadRuntimeReleaseManifest(manifestPath, controllerHome);
317
+ requireCompleteCompiledRuntimeReleaseManifest(manifest);
318
+ if (manifest.releaseId !== receipt.releaseId
319
+ || manifest.artifactIdentity !== receipt.artifactIdentity
320
+ || manifest.sourceCommit !== receipt.sourceCommit) {
321
+ throw new Error('RUNTIME_RELEASE_CANDIDATE_MANIFEST_RECEIPT_MISMATCH');
322
+ }
311
323
  const platform = dependencies.platform ?? process.platform;
312
324
  const macosCodeSigning = parseMacOSRuntimeCodeSigning(rawManifest.macosCodeSigning, 'RUNTIME_RELEASE_CANDIDATE_SIGNING_INVALID');
313
325
  if (platform === 'darwin') {
@@ -321,15 +333,30 @@ export function stageRuntimeReleaseFromCandidateSource(input: {
321
333
  }
322
334
  }
323
335
 
324
- return {
336
+ const staged: StagedRuntimeRelease = {
337
+ controllerHome,
325
338
  releasePath,
326
339
  manifestPath,
327
340
  releaseId: receipt.releaseId,
328
341
  artifactIdentity: receipt.artifactIdentity,
342
+ diagnosticArtifactIdentity: manifest.diagnosticArtifactIdentity,
343
+ browserNodeBridgeArtifactIdentity: manifest.browserNodeBridgeArtifactIdentity,
344
+ browserHandoffArtifactIdentity: manifest.browserHandoffArtifactIdentity,
345
+ processRunnerArtifactIdentity: manifest.processRunnerArtifactIdentity,
346
+ checkRunnerArtifactIdentity: manifest.checkRunnerArtifactIdentity,
347
+ pluginActionSidecarArtifactIdentity: manifest.pluginActionSidecarArtifactIdentity,
348
+ externalPluginProbeArtifactIdentity: manifest.externalPluginProbeArtifactIdentity,
349
+ codeGraphNodeArtifactIdentity: manifest.codeGraphNodeArtifactIdentity,
350
+ codeGraphSidecarArtifactIdentity: manifest.codeGraphSidecarArtifactIdentity,
351
+ codeGraphLibraryArtifactIdentity: manifest.codeGraphLibraryArtifactIdentity,
352
+ packageArtifactIdentity: manifest.packageArtifactIdentity,
353
+ controllerUiArtifactIdentity: manifest.controllerUiArtifactIdentity,
329
354
  ...(macosCodeSigning ? { macosCodeSigning } : {}),
330
355
  manifestSha256: receipt.manifestSha256,
331
356
  sourceCommit: receipt.sourceCommit,
332
357
  };
358
+ assertRuntimeReleaseFiles(staged, dependencies);
359
+ return staged;
333
360
  }
334
361
 
335
362
  function defaultCompileBinary(input: { sourceRoot: string; outputPath: string; entryPath?: string }): { ok: boolean; stderr?: string; stdout?: string; error?: string } {
@@ -491,6 +518,19 @@ export function stageRuntimeRelease(input: {
491
518
  chmodSync(checkRunnerPath, 0o700);
492
519
  const checkRunnerArtifactIdentity = `sha256:${sha256(checkRunnerPath)}`;
493
520
 
521
+ const pluginActionSidecarEntrypoint = 'forge-plugin-action-sidecar' as const;
522
+ const pluginActionSidecarPath = join(staging, pluginActionSidecarEntrypoint);
523
+ const pluginActionSidecarCompile = compileBinary({
524
+ sourceRoot,
525
+ outputPath: pluginActionSidecarPath,
526
+ entryPath: join(sourceRoot, 'src/runtime/plugins/plugin-action-sidecar.ts'),
527
+ });
528
+ if (!pluginActionSidecarCompile.ok) {
529
+ throw new Error(`RUNTIME_RELEASE_PLUGIN_ACTION_SIDECAR_BUILD_FAILED: ${pluginActionSidecarCompile.stderr || pluginActionSidecarCompile.stdout || pluginActionSidecarCompile.error}`.slice(0, 2_000));
530
+ }
531
+ chmodSync(pluginActionSidecarPath, 0o700);
532
+ const pluginActionSidecarArtifactIdentity = `sha256:${sha256(pluginActionSidecarPath)}`;
533
+
494
534
  const externalPluginProbeEntrypoint = 'external-unix-socket-probe.cjs' as const;
495
535
  const sourceExternalPluginProbePath = join(sourceRoot, 'src', 'runtime', 'plugins', externalPluginProbeEntrypoint);
496
536
  if (!existsSync(sourceExternalPluginProbePath)) {
@@ -533,6 +573,16 @@ export function stageRuntimeRelease(input: {
533
573
  cpSync(sourceControllerUiPath, controllerUiPath, { recursive: true, force: false });
534
574
  const controllerUiArtifactIdentity = `sha256:${sha256Directory(controllerUiPath)}`;
535
575
 
576
+ // The persistent OAuth/Connector is source-backed even when the primary
577
+ // Runtime itself is compiled. Co-locate one immutable package snapshot in
578
+ // the same release so standalone Recovery can bind the Connector to the
579
+ // exact active release instead of retaining an older package release.
580
+ const packageRoot = 'package' as const;
581
+ const packagePath = join(staging, packageRoot);
582
+ const packageRecords = packageRuntimeFileIndex(sourceRoot);
583
+ stagePackageRuntimeSnapshot(sourceRoot, packagePath, packageRecords);
584
+ const packageArtifactIdentity = `sha256:${sha256Directory(packagePath)}`;
585
+
536
586
  const manifest = {
537
587
  schemaVersion: 1,
538
588
  releaseId,
@@ -549,6 +599,8 @@ export function stageRuntimeRelease(input: {
549
599
  processRunnerArtifactIdentity,
550
600
  checkRunnerEntrypoint,
551
601
  checkRunnerArtifactIdentity,
602
+ pluginActionSidecarEntrypoint,
603
+ pluginActionSidecarArtifactIdentity,
552
604
  externalPluginProbeEntrypoint,
553
605
  externalPluginProbeArtifactIdentity,
554
606
  codeGraphNodeEntrypoint,
@@ -557,6 +609,8 @@ export function stageRuntimeRelease(input: {
557
609
  codeGraphSidecarArtifactIdentity,
558
610
  codeGraphLibraryRoot,
559
611
  codeGraphLibraryArtifactIdentity,
612
+ packageRoot,
613
+ packageArtifactIdentity,
560
614
  controllerUiRoot,
561
615
  controllerUiArtifactIdentity,
562
616
  arguments: [],
@@ -576,6 +630,7 @@ export function stageRuntimeRelease(input: {
576
630
  writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 });
577
631
  renameSync(staging, releasePath);
578
632
  return {
633
+ controllerHome: resolve(input.controllerHome),
579
634
  releasePath,
580
635
  manifestPath: join(releasePath, 'manifest.json'),
581
636
  releaseId,
@@ -586,10 +641,12 @@ export function stageRuntimeRelease(input: {
586
641
  browserHandoffArtifactIdentity,
587
642
  processRunnerArtifactIdentity,
588
643
  checkRunnerArtifactIdentity,
644
+ pluginActionSidecarArtifactIdentity,
589
645
  externalPluginProbeArtifactIdentity,
590
646
  codeGraphNodeArtifactIdentity,
591
647
  codeGraphSidecarArtifactIdentity,
592
648
  codeGraphLibraryArtifactIdentity,
649
+ packageArtifactIdentity,
593
650
  controllerUiArtifactIdentity,
594
651
  manifestSha256: createHash('sha256').update(`${JSON.stringify(manifest, null, 2)}\n`).digest('hex'),
595
652
  sourceCommit,
@@ -601,13 +658,53 @@ export function stageRuntimeRelease(input: {
601
658
  }
602
659
 
603
660
  export function assertRuntimeReleaseFiles(release: StagedRuntimeRelease, dependencies: { platform?: NodeJS.Platform; inspectMacOSRuntime?: (executable: string) => MacOSRuntimeCodeSigning } = {}): void {
661
+ const assertRegularFile = (path: string, missingCode: string): void => {
662
+ if (!existsSync(path)) throw new Error(`${missingCode}: ${path}`);
663
+ const status = lstatSync(path);
664
+ if (status.isSymbolicLink() || !status.isFile()) throw new Error(`RUNTIME_RELEASE_PATH_NOT_REGULAR_FILE: ${path}`);
665
+ };
666
+ const assertExecutable = (path: string): void => {
667
+ if ((statSync(path).mode & 0o111) === 0) throw new Error(`RUNTIME_RELEASE_ENTRYPOINT_NOT_EXECUTABLE: ${path}`);
668
+ };
669
+ const assertFileIdentity = (path: string, expected: string): void => {
670
+ const actual = `sha256:${sha256(path)}`;
671
+ if (actual !== expected) throw new Error(`RUNTIME_RELEASE_ARTIFACT_IDENTITY_MISMATCH: ${path}`);
672
+ };
673
+ const assertDirectoryIdentity = (path: string, expected: string): void => {
674
+ if (!existsSync(path)) throw new Error(`RUNTIME_RELEASE_DIRECTORY_MISSING: ${path}`);
675
+ const status = lstatSync(path);
676
+ if (status.isSymbolicLink() || !status.isDirectory()) throw new Error(`RUNTIME_RELEASE_PATH_NOT_DIRECTORY: ${path}`);
677
+ const actual = `sha256:${sha256Directory(path)}`;
678
+ if (actual !== expected) throw new Error(`RUNTIME_RELEASE_ARTIFACT_IDENTITY_MISMATCH: ${path}`);
679
+ };
680
+ const assertComponentFile = (input: { path: string; identity?: string; missingCode: string; executable?: boolean }): void => {
681
+ if (!input.identity) return;
682
+ assertRegularFile(input.path, input.missingCode);
683
+ assertFileIdentity(input.path, input.identity);
684
+ if (input.executable) assertExecutable(input.path);
685
+ };
686
+
604
687
  if (!existsSync(release.releasePath) || !existsSync(release.manifestPath)) {
605
688
  throw new Error(`RUNTIME_RELEASE_FILES_MISSING: ${release.releasePath}`);
606
689
  }
607
- const runtimePath = join(release.releasePath, 'forge-runtime');
608
- if (!existsSync(runtimePath)) {
609
- throw new Error(`RUNTIME_RELEASE_ENTRYPOINT_MISSING: ${runtimePath}`);
690
+ const releasesRoot = join(resolve(release.controllerHome), 'runtime', 'releases');
691
+ if (resolve(release.releasePath) !== join(releasesRoot, release.releaseId)
692
+ || resolve(release.manifestPath) !== join(release.releasePath, 'manifest.json')) {
693
+ throw new Error('RUNTIME_RELEASE_PATH_OUTSIDE_CONTROLLER_HOME');
694
+ }
695
+ const releaseStatus = lstatSync(release.releasePath);
696
+ const manifestStatus = lstatSync(release.manifestPath);
697
+ if (releaseStatus.isSymbolicLink() || !releaseStatus.isDirectory()
698
+ || manifestStatus.isSymbolicLink() || !manifestStatus.isFile()) {
699
+ throw new Error(`RUNTIME_RELEASE_PATH_NOT_PHYSICAL: ${release.releasePath}`);
610
700
  }
701
+ if (sha256(release.manifestPath) !== release.manifestSha256) {
702
+ throw new Error('RUNTIME_RELEASE_MANIFEST_IDENTITY_MISMATCH');
703
+ }
704
+ const runtimePath = join(release.releasePath, 'forge-runtime');
705
+ assertRegularFile(runtimePath, 'RUNTIME_RELEASE_ENTRYPOINT_MISSING');
706
+ assertExecutable(runtimePath);
707
+ assertFileIdentity(runtimePath, release.artifactIdentity);
611
708
  const platform = dependencies.platform ?? process.platform;
612
709
  if (platform === 'darwin' && release.macosCodeSigning) {
613
710
  const actual = (dependencies.inspectMacOSRuntime ?? inspectMacOSRuntimeCodeSigning)(runtimePath);
@@ -618,35 +715,31 @@ export function assertRuntimeReleaseFiles(release: StagedRuntimeRelease, depende
618
715
  throw new Error('RUNTIME_RELEASE_MACOS_SIGNING_MISMATCH');
619
716
  }
620
717
  }
621
- if (release.diagnosticArtifactIdentity && !existsSync(join(release.releasePath, 'forge-cli'))) {
622
- throw new Error(`RUNTIME_RELEASE_DIAGNOSTIC_ENTRYPOINT_MISSING: ${join(release.releasePath, 'forge-cli')}`);
623
- }
624
- if (release.browserNodeBridgeArtifactIdentity && !existsSync(join(release.releasePath, 'browser-node-bridge-host.js'))) {
625
- throw new Error(`RUNTIME_RELEASE_BROWSER_NODE_HOST_MISSING: ${join(release.releasePath, 'browser-node-bridge-host.js')}`);
626
- }
627
- if (release.browserHandoffArtifactIdentity && !existsSync(join(release.releasePath, 'browser-handoff-host.js'))) {
628
- throw new Error(`RUNTIME_RELEASE_BROWSER_HANDOFF_HOST_MISSING: ${join(release.releasePath, 'browser-handoff-host.js')}`);
629
- }
630
- if (release.processRunnerArtifactIdentity && !existsSync(join(release.releasePath, 'process-runner.js'))) {
631
- throw new Error(`RUNTIME_RELEASE_PROCESS_RUNNER_MISSING: ${join(release.releasePath, 'process-runner.js')}`);
632
- }
633
- if (release.checkRunnerArtifactIdentity && !existsSync(join(release.releasePath, 'forge-check-runner'))) {
634
- throw new Error(`RUNTIME_RELEASE_CHECK_RUNNER_MISSING: ${join(release.releasePath, 'forge-check-runner')}`);
635
- }
636
- if (release.externalPluginProbeArtifactIdentity && !existsSync(join(release.releasePath, 'external-unix-socket-probe.cjs'))) {
637
- throw new Error(`RUNTIME_RELEASE_EXTERNAL_PLUGIN_PROBE_MISSING: ${join(release.releasePath, 'external-unix-socket-probe.cjs')}`);
638
- }
639
- if (release.codeGraphNodeArtifactIdentity && !existsSync(join(release.releasePath, 'codegraph-node'))) {
640
- throw new Error(`RUNTIME_RELEASE_CODEGRAPH_NODE_MISSING: ${join(release.releasePath, 'codegraph-node')}`);
641
- }
642
- if (release.codeGraphSidecarArtifactIdentity && !existsSync(join(release.releasePath, 'codegraph-sidecar.cjs'))) {
643
- throw new Error(`RUNTIME_RELEASE_CODEGRAPH_SIDECAR_MISSING: ${join(release.releasePath, 'codegraph-sidecar.cjs')}`);
718
+ assertComponentFile({ path: join(release.releasePath, 'forge-cli'), identity: release.diagnosticArtifactIdentity, missingCode: 'RUNTIME_RELEASE_DIAGNOSTIC_ENTRYPOINT_MISSING', executable: true });
719
+ assertComponentFile({ path: join(release.releasePath, 'browser-node-bridge-host.js'), identity: release.browserNodeBridgeArtifactIdentity, missingCode: 'RUNTIME_RELEASE_BROWSER_NODE_HOST_MISSING', executable: true });
720
+ assertComponentFile({ path: join(release.releasePath, 'browser-handoff-host.js'), identity: release.browserHandoffArtifactIdentity, missingCode: 'RUNTIME_RELEASE_BROWSER_HANDOFF_HOST_MISSING', executable: true });
721
+ assertComponentFile({ path: join(release.releasePath, 'process-runner.js'), identity: release.processRunnerArtifactIdentity, missingCode: 'RUNTIME_RELEASE_PROCESS_RUNNER_MISSING', executable: true });
722
+ assertComponentFile({ path: join(release.releasePath, 'forge-check-runner'), identity: release.checkRunnerArtifactIdentity, missingCode: 'RUNTIME_RELEASE_CHECK_RUNNER_MISSING', executable: true });
723
+ assertComponentFile({ path: join(release.releasePath, 'forge-plugin-action-sidecar'), identity: release.pluginActionSidecarArtifactIdentity, missingCode: 'RUNTIME_RELEASE_PLUGIN_ACTION_SIDECAR_MISSING', executable: true });
724
+ assertComponentFile({ path: join(release.releasePath, 'external-unix-socket-probe.cjs'), identity: release.externalPluginProbeArtifactIdentity, missingCode: 'RUNTIME_RELEASE_EXTERNAL_PLUGIN_PROBE_MISSING', executable: true });
725
+ assertComponentFile({ path: join(release.releasePath, 'codegraph-node'), identity: release.codeGraphNodeArtifactIdentity, missingCode: 'RUNTIME_RELEASE_CODEGRAPH_NODE_MISSING', executable: true });
726
+ assertComponentFile({ path: join(release.releasePath, 'codegraph-sidecar.cjs'), identity: release.codeGraphSidecarArtifactIdentity, missingCode: 'RUNTIME_RELEASE_CODEGRAPH_SIDECAR_MISSING', executable: true });
727
+ if (release.codeGraphLibraryArtifactIdentity) {
728
+ const libraryRoot = join(release.releasePath, 'codegraph-lib');
729
+ if (!existsSync(join(libraryRoot, 'dist', 'index.js'))) {
730
+ throw new Error(`RUNTIME_RELEASE_CODEGRAPH_LIBRARY_MISSING: ${join(libraryRoot, 'dist', 'index.js')}`);
731
+ }
732
+ assertDirectoryIdentity(libraryRoot, release.codeGraphLibraryArtifactIdentity);
644
733
  }
645
- if (release.codeGraphLibraryArtifactIdentity && !existsSync(join(release.releasePath, 'codegraph-lib', 'dist', 'index.js'))) {
646
- throw new Error(`RUNTIME_RELEASE_CODEGRAPH_LIBRARY_MISSING: ${join(release.releasePath, 'codegraph-lib', 'dist', 'index.js')}`);
734
+ if (release.packageArtifactIdentity) {
735
+ const packageRoot = join(release.releasePath, 'package');
736
+ assertDirectoryIdentity(packageRoot, release.packageArtifactIdentity);
647
737
  }
648
- if (release.controllerUiArtifactIdentity
649
- && (!existsSync(join(release.releasePath, 'ui-dist', 'app.js')) || !existsSync(join(release.releasePath, 'ui-dist', 'app.css')))) {
650
- throw new Error(`RUNTIME_RELEASE_CONTROLLER_UI_MISSING: ${join(release.releasePath, 'ui-dist')}`);
738
+ if (release.controllerUiArtifactIdentity) {
739
+ const uiRoot = join(release.releasePath, 'ui-dist');
740
+ if (!existsSync(join(uiRoot, 'app.js')) || !existsSync(join(uiRoot, 'app.css'))) {
741
+ throw new Error(`RUNTIME_RELEASE_CONTROLLER_UI_MISSING: ${uiRoot}`);
742
+ }
743
+ assertDirectoryIdentity(uiRoot, release.controllerUiArtifactIdentity);
651
744
  }
652
745
  }
@@ -1,5 +1,5 @@
1
1
  import { createHash, randomUUID } from 'crypto';
2
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'fs';
2
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'fs';
3
3
  import { dirname, join, resolve } from 'path';
4
4
  import { ensureControllerHome } from '../../cli/repositories/controller-home';
5
5
  import {
@@ -102,9 +102,14 @@ function validRelease(controllerHome: string, release: RuntimePublishedRelease |
102
102
  }
103
103
  }
104
104
 
105
- export function readRuntimeReleaseAuthority(controllerHome: string): RuntimeReleaseAuthority | undefined {
105
+ type RuntimeReleaseAuthorityRead =
106
+ | { state: 'missing' }
107
+ | { state: 'valid'; authority: RuntimeReleaseAuthority }
108
+ | { state: 'invalid'; reason: string };
109
+
110
+ function inspectRuntimeReleaseAuthority(controllerHome: string): RuntimeReleaseAuthorityRead {
106
111
  const path = runtimeReleaseAuthorityPath(controllerHome);
107
- if (!existsSync(path)) return undefined;
112
+ if (!existsSync(path)) return { state: 'missing' };
108
113
  try {
109
114
  const value = JSON.parse(readFileSync(path, 'utf8')) as RuntimeReleaseAuthority;
110
115
  if (
@@ -117,11 +122,24 @@ export function readRuntimeReleaseAuthority(controllerHome: string): RuntimeRele
117
122
  || !Number.isFinite(Date.parse(value.committedAt))
118
123
  || !validRelease(controllerHome, value.active)
119
124
  || (value.previous !== undefined && !validRelease(controllerHome, value.previous))
120
- ) return undefined;
121
- return value;
122
- } catch {
123
- return undefined;
125
+ ) return { state: 'invalid', reason: 'authority fields or referenced release evidence are invalid' };
126
+ return { state: 'valid', authority: value };
127
+ } catch (error) {
128
+ return { state: 'invalid', reason: error instanceof Error ? error.message : String(error) };
129
+ }
130
+ }
131
+
132
+ function mutableRuntimeReleaseAuthority(controllerHome: string): RuntimeReleaseAuthority | undefined {
133
+ const observed = inspectRuntimeReleaseAuthority(controllerHome);
134
+ if (observed.state === 'invalid') {
135
+ throw new Error(`RUNTIME_RELEASE_AUTHORITY_INVALID_EXISTING: ${observed.reason}`);
124
136
  }
137
+ return observed.state === 'valid' ? observed.authority : undefined;
138
+ }
139
+
140
+ export function readRuntimeReleaseAuthority(controllerHome: string): RuntimeReleaseAuthority | undefined {
141
+ const observed = inspectRuntimeReleaseAuthority(controllerHome);
142
+ return observed.state === 'valid' ? observed.authority : undefined;
125
143
  }
126
144
 
127
145
  function writeRuntimeReleaseAuthority(controllerHome: string, authority: RuntimeReleaseAuthority): RuntimeReleaseAuthority {
@@ -145,7 +163,7 @@ export function ensureActiveRuntimeRelease(
145
163
  operationId = 'runtime-start',
146
164
  ): RuntimeReleaseAuthority {
147
165
  const active = manifestRecord(controllerHome, manifestPath);
148
- const current = readRuntimeReleaseAuthority(controllerHome);
166
+ const current = mutableRuntimeReleaseAuthority(controllerHome);
149
167
  if (current) {
150
168
  if (!sameRelease(current.active, active)) throw new Error('RUNTIME_RELEASE_AUTHORITY_MISMATCH');
151
169
  return current;
@@ -169,8 +187,18 @@ export function publishRuntimeRelease(
169
187
  ): RuntimeReleaseAuthority {
170
188
  if (!operationId.trim()) throw new Error('RUNTIME_RELEASE_OPERATION_ID_REQUIRED');
171
189
  const candidate = manifestRecord(controllerHome, manifestPath);
172
- const current = readRuntimeReleaseAuthority(controllerHome);
173
- if (!current) return ensureActiveRuntimeRelease(controllerHome, manifestPath, operationId);
190
+ const current = mutableRuntimeReleaseAuthority(controllerHome);
191
+ if (!current) {
192
+ return writeRuntimeReleaseAuthority(controllerHome, {
193
+ schemaVersion: 1,
194
+ status: 'committed',
195
+ revision: 1,
196
+ fencingToken: randomUUID(),
197
+ active: candidate,
198
+ operationId,
199
+ committedAt: new Date().toISOString(),
200
+ });
201
+ }
174
202
  if (sameRelease(current.active, candidate)) return current;
175
203
  const backup = backupPath(controllerHome, current.active.releaseId, operationId);
176
204
  const inspection = dependencies.backupDatabase(controllerHome, backup);
@@ -190,13 +218,22 @@ export function publishRuntimeRelease(
190
218
  });
191
219
  }
192
220
 
221
+ export function revertInitialRuntimeReleasePublication(controllerHome: string, operationId: string): void {
222
+ const current = mutableRuntimeReleaseAuthority(controllerHome);
223
+ if (!current) return;
224
+ if (current.revision !== 1 || current.previous !== undefined || current.operationId !== operationId) {
225
+ throw new Error('RUNTIME_RELEASE_INITIAL_PUBLICATION_REVERT_MISMATCH');
226
+ }
227
+ rmSync(runtimeReleaseAuthorityPath(controllerHome), { force: true });
228
+ }
229
+
193
230
  export function rollbackRuntimeRelease(
194
231
  controllerHome: string,
195
232
  operationId: string,
196
233
  dependencies: RuntimeReleaseStoreDependencies = DEFAULT_DEPENDENCIES,
197
234
  ): RuntimeReleaseAuthority {
198
235
  if (!operationId.trim()) throw new Error('RUNTIME_RELEASE_OPERATION_ID_REQUIRED');
199
- const current = readRuntimeReleaseAuthority(controllerHome);
236
+ const current = mutableRuntimeReleaseAuthority(controllerHome);
200
237
  const target = current?.previous;
201
238
  if (!current || !target?.databaseBackup) throw new Error('RUNTIME_PREVIOUS_RELEASE_UNAVAILABLE');
202
239
  const currentBackup = backupPath(controllerHome, current.active.releaseId, operationId);
@@ -6,6 +6,8 @@ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/
6
6
  import type { ControlPlaneDatabaseInspection } from '../control-plane/persistence/sqlite-store';
7
7
  import { inspectControlPlaneDatabase } from '../control-plane/persistence/sqlite-store';
8
8
  import { activateExclusiveWorkAdmission } from '../control-plane/facade/work-admission-policy';
9
+ import { closeCodeGraphReadProviderSessions } from '../context/codegraph-read-provider';
10
+ import { cancelAllLightweightProcesses } from '../execution/process-runtime/lightweight-managed';
9
11
  import {
10
12
  collectRuntimeSourceIdentity,
11
13
  rotateRuntimeGeneration,
@@ -40,6 +42,9 @@ export interface CanonicalRuntimeDependencies {
40
42
  runMcpProbe(endpoint: string, authToken: string): Promise<void>;
41
43
  collectRuntimeSourceIdentity: typeof collectRuntimeSourceIdentity;
42
44
  rotateRuntimeGeneration: typeof rotateRuntimeGeneration;
45
+ stopLightweightProcesses(controllerHome: string): Promise<number>;
46
+ stopContextReadHelpers(): Promise<void>;
47
+ computeToolSurfaceFingerprint: typeof runtimeGatewayToolSurfaceFingerprint;
43
48
  }
44
49
 
45
50
  async function defaultMcpProbe(endpoint: string, authToken: string): Promise<void> {
@@ -77,6 +82,9 @@ const DEFAULT_DEPENDENCIES: CanonicalRuntimeDependencies = {
77
82
  runMcpProbe: defaultMcpProbe,
78
83
  collectRuntimeSourceIdentity,
79
84
  rotateRuntimeGeneration,
85
+ stopLightweightProcesses: cancelAllLightweightProcesses,
86
+ stopContextReadHelpers: closeCodeGraphReadProviderSessions,
87
+ computeToolSurfaceFingerprint: runtimeGatewayToolSurfaceFingerprint,
80
88
  };
81
89
 
82
90
  export class CanonicalForgeRuntime {
@@ -143,6 +151,17 @@ export class CanonicalForgeRuntime {
143
151
  }
144
152
  }
145
153
 
154
+ private refreshToolSurfaceFingerprint(runtimeSourceRoot: string): void {
155
+ const next = this.dependencies.computeToolSurfaceFingerprint({
156
+ controllerHome: this.config.controllerHome,
157
+ runtimeInstanceId: this.runtimeInstanceId,
158
+ runtimeSourceRoot,
159
+ });
160
+ if (next === this.toolSurfaceFingerprint) return;
161
+ this.toolSurfaceFingerprint = next;
162
+ this.publishStatus();
163
+ }
164
+
146
165
  endpoint(): string | undefined {
147
166
  return this.transport?.endpoint;
148
167
  }
@@ -218,11 +237,7 @@ export class CanonicalForgeRuntime {
218
237
  });
219
238
 
220
239
  stage = 'transport';
221
- this.toolSurfaceFingerprint = runtimeGatewayToolSurfaceFingerprint({
222
- controllerHome: this.config.controllerHome,
223
- runtimeInstanceId: this.runtimeInstanceId,
224
- runtimeSourceRoot,
225
- });
240
+ this.refreshToolSurfaceFingerprint(runtimeSourceRoot);
226
241
  this.transport = await this.dependencies.startTransport({
227
242
  host: this.config.host,
228
243
  port: this.config.port,
@@ -235,6 +250,7 @@ export class CanonicalForgeRuntime {
235
250
  sessionId,
236
251
  controllerType,
237
252
  }),
253
+ onToolSurfaceObservation: () => this.refreshToolSurfaceFingerprint(runtimeSourceRoot),
238
254
  onFatal: (error) => this.failCore('MCP_TRANSPORT_FAILED', error.message),
239
255
  });
240
256
  this.publishStatus();
@@ -298,6 +314,8 @@ export class CanonicalForgeRuntime {
298
314
  // Stop accepting new MCP work before quiescing Scheduler activity, then
299
315
  // release the Controller Home claim only after all in-process services stop.
300
316
  await this.transport?.close().catch(() => undefined);
317
+ await this.dependencies.stopLightweightProcesses(this.config.controllerHome).catch(() => undefined);
318
+ await this.dependencies.stopContextReadHelpers().catch(() => undefined);
301
319
  await this.localBridge?.close().catch(() => undefined);
302
320
  await this.scheduler?.stop().catch(() => undefined);
303
321
  const ownerPid = this.ownership?.record.pid;