@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,10 +1,10 @@
1
- import { randomUUID, timingSafeEqual } from 'crypto';
1
+ import { randomUUID } from 'crypto';
2
2
  import { existsSync, watch } from 'fs';
3
3
  import { dirname } from 'path';
4
4
  import express, { type Request, type Response, type NextFunction } from 'express';
5
5
  import { NodeStreamableHTTPServerTransport, toNodeHandler, toWebRequest, type NodeMcpRequestHandler } from "@modelcontextprotocol/node";
6
- import { createMcpHandler, isLegacyRequest, type McpRequestContext, type OAuthClientInformationFull } from "@modelcontextprotocol/server";
7
- import { tokenHandler, revocationHandler, clientRegistrationHandler, redirectUriMatches, InvalidTokenError } from "@modelcontextprotocol/server-legacy/auth";
6
+ import { createMcpHandler, isLegacyRequest, type McpRequestContext } from "@modelcontextprotocol/server";
7
+ import { InvalidTokenError } from "@modelcontextprotocol/server-legacy/auth";
8
8
  import {
9
9
  buildMultiRepositoryToolDefinitions,
10
10
  createCanonicalRuntimeProxy,
@@ -17,39 +17,31 @@ import {
17
17
  } from '../server';
18
18
  import {
19
19
  loadMcpServiceLocalConfig,
20
- loadMcpServiceRuntimeState,
21
20
  mcpServiceOAuthTokenStoreFallbackPaths,
22
21
  mcpServiceOAuthTokenStorePath,
23
22
  parseMcpHttpAuthMode,
24
23
  readMcpServiceBearerToken,
25
24
  readMcpServiceOAuthPassphrase,
26
- type McpLocalConfig,
27
25
  type McpHttpAuthMode,
28
26
  } from '../auth';
29
27
  import { createMcpOAuthProvider, McpOAuthTokenStore } from '../oauth';
30
28
  import { resolveMcpRepoRoot } from '../repo';
31
- import { buildMcpToolDefinitions } from '../tool-mapping/tools';
32
29
  import { resolveControllerHome } from '../../../src/cli/repositories/controller-home';
33
- import {
34
- controllerExposureSnapshot,
35
- } from '../toolset';
36
- import { readForgeRuntimeStatus } from '../../../src/runtime/control-plane/runtime-status-client';
37
- import { invalidateExecutionSession } from '../../../src/runtime/control-plane/execution/session-store';
38
- import { runtimeIdentitySnapshot } from '../runtime-gateway/runtime-tools';
39
- import { projectionBlocksReadiness, readRepositoryProjectionSnapshot } from '../../../src/runtime/projections/materialized-view';
40
30
  import { readRuntimeGeneration } from '../../../src/runtime/control-plane/runtime-generation';
31
+ import { recordMcpTransportEvent } from '../../../src/runtime/diagnostics/mcp-timing';
41
32
  import { readRuntimeStatusSnapshot, runtimeStatusPath } from '../../../src/runtime/root/status';
42
- import { getRepository, listRepositories } from '../../../src/cli/repositories/registry';
43
- import { buildControllerTaskLedgerProjection } from '../../../src/cli/controller/task-ledger';
44
- import { legacyIssueAuthorityRetired } from '../../../src/cli/controller/legacy-issue-cutover';
45
- import { reconcileReadinessProjectionSource } from '../readiness-projection';
46
33
  import {
47
34
  FORGE_MCP_SCHEMA_VERSION,
48
35
  FORGE_TOOL_SURFACE,
49
36
  FORGE_VERSION,
50
- repositoryIdentity,
51
37
  } from '../../../src/cli/controller/runtime-config';
52
- import { McpSessionRegistry, type McpSessionRoute } from './session-registry';
38
+ import { McpSessionRegistry, type ClosableMcpTransport, type McpSessionRegistryOptions, type McpSessionRoute } from './session-registry';
39
+ import { getConfiguredPublicOrigin, getPublicOrigin, registerMcpOAuthHttpRoutes } from './oauth-http';
40
+ import { registerMcpHttpObservationRoutes } from './http-observation';
41
+ import { reconcileFinishedMcpPosts } from './http-post-lifecycle';
42
+ import { RUNTIME_MCP_CUTOVER_OUTCOME_UNKNOWN } from '../../../src/runtime/root/mcp-transport';
43
+ import { packageConnectorCutoverDrainIsActive } from '../../../src/runtime/root/package-connector-service';
44
+ export { isAllowedMcpOAuthRedirectUri, isIncompleteOAuthAuthorizeRequest } from './oauth-http';
53
45
  import {
54
46
  connectionIdentity,
55
47
  ensureForgeInstanceIdentity,
@@ -65,17 +57,6 @@ export interface McpHttpOptions extends McpServerOptions {
65
57
  auth?: string;
66
58
  }
67
59
 
68
- function localControllerDiagnosticMatchesRuntime(
69
- payload: Record<string, unknown> | null,
70
- generation?: string,
71
- ): boolean {
72
- return payload?.status === 'ok'
73
- && payload.toolSurface === FORGE_TOOL_SURFACE
74
- && payload.schemaVersion === FORGE_MCP_SCHEMA_VERSION
75
- && payload.version === FORGE_VERSION
76
- && (generation === undefined || payload.generation === generation);
77
- }
78
-
79
60
  function bearerFromRequest(req: Request): string | null {
80
61
  const header = req.headers.authorization;
81
62
  if (typeof header !== 'string') return null;
@@ -228,8 +209,42 @@ function sendMcpToolSurfaceReset(
228
209
  });
229
210
  }
230
211
 
212
+ /**
213
+ * A schema-fenced transport cannot be reused after the reset response. Delay
214
+ * retirement until Express has flushed the reconnect instruction, then release
215
+ * its session-local state. Work identity is Controller-owned, not transport-owned.
216
+ */
217
+ function retireAfterToolSurfaceReset(
218
+ res: Response,
219
+ registry: HttpSessionRegistry,
220
+ sessionId: string,
221
+ ): void {
222
+ let retired = false;
223
+ const retire = (): void => {
224
+ if (retired) return;
225
+ retired = true;
226
+ void registry.close(sessionId, 'tool_surface_changed');
227
+ };
228
+ res.once('finish', retire);
229
+ res.once('close', retire);
230
+ }
231
+
231
232
  export function mcpRequestError(error: unknown) {
232
233
  const rawMessage = error instanceof Error ? error.message : String(error);
234
+ if (rawMessage.includes(RUNTIME_MCP_CUTOVER_OUTCOME_UNKNOWN)) {
235
+ return {
236
+ status: 503 as const,
237
+ body: {
238
+ error: 'runtime_cutover_outcome_unknown' as const,
239
+ code: RUNTIME_MCP_CUTOVER_OUTCOME_UNKNOWN,
240
+ message: 'The Canonical Runtime changed after this request was admitted. Reconcile the original request/effect identity before retrying.',
241
+ recoverable: true as const,
242
+ retryable: false as const,
243
+ sessionPreserved: true as const,
244
+ action: 'reconcile' as const,
245
+ },
246
+ };
247
+ }
233
248
  const retryable = /(?:\b502\b|\b503\b|\b429\b|ECONNRESET|ETIMEDOUT|EAI_AGAIN|CANONICAL_RUNTIME_TIMEOUT|server_busy|session_capacity|gateway)/i.test(rawMessage);
234
249
  return {
235
250
  status: (retryable ? 503 : 500) as 500 | 503,
@@ -254,343 +269,6 @@ export function sendMcpRequestError(res: Response, error: unknown): void {
254
269
  res.status(response.status).json(response.body);
255
270
  }
256
271
 
257
- function getConfiguredPublicOrigin(config: McpLocalConfig | null): string | undefined {
258
- const configured = process.env.FORGE_MCP_PUBLIC_ORIGIN?.trim();
259
- if (configured) {
260
- try {
261
- return new URL(configured).origin;
262
- } catch (_error) {
263
- // Fall through to service or legacy config.
264
- }
265
- }
266
- const endpoint = config?.chatgpt?.endpoint?.trim();
267
- if (!endpoint) return undefined;
268
- try {
269
- return new URL(endpoint).origin;
270
- } catch (_error) {
271
- return undefined;
272
- }
273
- }
274
-
275
- function getPublicOrigin(req: Request, configuredOrigin: string | undefined): string {
276
- if (configuredOrigin) return configuredOrigin;
277
- const proto = (req.headers['x-forwarded-proto'] as string | undefined) ?? 'https';
278
- const host = (req.headers['x-forwarded-host'] as string | undefined) ?? req.headers.host ?? '127.0.0.1:8765';
279
- return `${proto}://${host}`;
280
- }
281
-
282
- function localControllerHealthUrl(host: string, port: number): string {
283
- return `http://${host === '::1' ? '[::1]' : host}:${port}/health`;
284
- }
285
-
286
- async function jsonHealth(url: string): Promise<Record<string, unknown> | null> {
287
- const controller = new AbortController();
288
- const timeout = setTimeout(() => controller.abort(), 2_000);
289
- try {
290
- const response = await fetch(url, {
291
- signal: controller.signal,
292
- headers: { accept: 'application/json' },
293
- });
294
- if (!response.ok) return null;
295
- return await response.json() as Record<string, unknown>;
296
- } catch (_error) {
297
- return null;
298
- } finally {
299
- clearTimeout(timeout);
300
- }
301
- }
302
-
303
- export function isAllowedMcpOAuthRedirectUri(redirectUri: string): boolean {
304
- try {
305
- const url = new URL(redirectUri);
306
- if (url.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)) {
307
- return true;
308
- }
309
- if (
310
- url.protocol === 'https:' &&
311
- (url.origin === 'https://chatgpt.com' || url.origin === 'https://chat.openai.com') &&
312
- url.pathname.startsWith('/connector/oauth/')
313
- ) {
314
- return true;
315
- }
316
- return false;
317
- } catch (_error) {
318
- return false;
319
- }
320
- }
321
-
322
- function isRegisteredRedirectUri(redirectUri: string, client: { redirect_uris?: string[] }): boolean {
323
- return (client.redirect_uris ?? []).some((registered) => redirectUriMatches(redirectUri, registered));
324
- }
325
-
326
- function isRegisteredExternalHttpsRedirectUri(redirectUri: string, client: { redirect_uris?: string[] }): boolean {
327
- try {
328
- const url = new URL(redirectUri);
329
- return url.protocol === 'https:' && !url.username && !url.password && isRegisteredRedirectUri(redirectUri, client);
330
- } catch (_error) {
331
- return false;
332
- }
333
- }
334
-
335
- function isSafeOAuthFallbackRedirectUri(redirectUri: string): boolean {
336
- try {
337
- const url = new URL(redirectUri);
338
- if (url.username || url.password) return false;
339
- if (url.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)) return true;
340
- return url.protocol === 'https:';
341
- } catch (_error) {
342
- return false;
343
- }
344
- }
345
-
346
- async function getOrRegisterPublicOAuthClient(
347
- provider: ReturnType<typeof createMcpOAuthProvider>,
348
- clientId: string,
349
- redirectUri: string | undefined,
350
- req: Request,
351
- ): Promise<OAuthClientInformationFull | undefined> {
352
- const existing = await provider.clientsStore.getClient(clientId);
353
- if (existing) return existing as OAuthClientInformationFull;
354
- if (!redirectUri || !isSafeOAuthFallbackRedirectUri(redirectUri) || !provider.clientsStore.registerClient) return undefined;
355
- oauthTrace(req, 'authorize:auto_register_public_client', {
356
- redirectScheme: new URL(redirectUri).protocol,
357
- redirectHost: new URL(redirectUri).hostname,
358
- });
359
- return provider.clientsStore.registerClient({
360
- client_id: clientId,
361
- client_id_issued_at: Math.floor(Date.now() / 1000),
362
- client_name: 'forge OAuth fallback client',
363
- redirect_uris: [redirectUri],
364
- token_endpoint_auth_method: 'none',
365
- grant_types: ['authorization_code', 'refresh_token'],
366
- response_types: ['code'],
367
- } as unknown as Omit<OAuthClientInformationFull, 'client_id' | 'client_id_issued_at'>) as OAuthClientInformationFull;
368
- }
369
-
370
- function escapeHtmlAttribute(value: string): string {
371
- return value
372
- .replace(/&/g, '&amp;')
373
- .replace(/"/g, '&quot;')
374
- .replace(/'/g, '&#39;')
375
- .replace(/</g, '&lt;')
376
- .replace(/>/g, '&gt;');
377
- }
378
-
379
- function renderPassphrasePage(params: URLSearchParams): string {
380
- const hiddenFields = Array.from(params.entries())
381
- .filter(([key]) => key !== 'passphrase')
382
- .map(([key, value]) => `<input type="hidden" name="${escapeHtmlAttribute(key)}" value="${escapeHtmlAttribute(value)}">`)
383
- .join('\n');
384
-
385
- return `<!doctype html>
386
- <html lang="en">
387
- <head><meta charset="utf-8"><title>Authorize forge</title>
388
- <meta name="viewport" content="width=device-width,initial-scale=1">
389
- <style>
390
- body{font-family:-apple-system,BlinkMacSystemFont,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#f6f6f3;color:#1f2328}
391
- .card{width:min(420px,92vw);background:#fff;border:1px solid #d8d8d0;border-radius:12px;padding:32px;box-shadow:0 12px 40px rgba(0,0,0,.08)}
392
- h1{font-size:20px;margin:0 0 8px}p{margin:0 0 20px;color:#60666d;line-height:1.45}
393
- input{width:100%;box-sizing:border-box;border:1px solid #bfc4c9;border-radius:8px;padding:12px;font-size:16px}
394
- button{width:100%;margin-top:14px;border:0;border-radius:8px;padding:12px;background:#1f2328;color:#fff;font-size:16px;font-weight:600}
395
- </style></head>
396
- <body><main class="card">
397
- <h1>Authorize forge</h1>
398
- <p>Enter the local MCP passphrase to let this MCP client use this workflow-scoped connector.</p>
399
- <form method="POST" action="/authorize">
400
- ${hiddenFields}
401
- <input type="password" name="passphrase" placeholder="Passphrase" autofocus>
402
- <button type="submit">Authorize</button>
403
- </form>
404
- </main></body></html>`;
405
- }
406
-
407
- /** Collect OAuth authorize params from query (GET) or body (POST form). */
408
- function oauthAuthorizeParamSource(req: Request): Record<string, unknown> {
409
- if (req.method === 'POST' && req.body && typeof req.body === 'object' && !Buffer.isBuffer(req.body)) {
410
- return req.body as Record<string, unknown>;
411
- }
412
- return req.query as Record<string, unknown>;
413
- }
414
-
415
- function readOAuthAuthorizeString(source: Record<string, unknown>, key: string): string {
416
- const value = source[key];
417
- return typeof value === 'string' ? value.trim() : '';
418
- }
419
-
420
- /**
421
- * True when the request lacks the OAuth parameters needed for a real authorization.
422
- * Incomplete requests must not render the passphrase form (incompatible clients loop there).
423
- */
424
- export function isIncompleteOAuthAuthorizeRequest(source: Record<string, unknown>): boolean {
425
- const clientId = readOAuthAuthorizeString(source, 'client_id');
426
- const responseType = readOAuthAuthorizeString(source, 'response_type');
427
- const codeChallenge = readOAuthAuthorizeString(source, 'code_challenge');
428
- const redirectUri = readOAuthAuthorizeString(source, 'redirect_uri');
429
- // redirect_uri may be omitted when the client has a single registered URI; client_id is the usable redirect context.
430
- const hasRedirectContext = Boolean(redirectUri) || Boolean(clientId);
431
- return !clientId || !responseType || !codeChallenge || !hasRedirectContext;
432
- }
433
-
434
- function incompleteOAuthAuthorizeResponseBody(): {
435
- error: 'invalid_request';
436
- error_description: string;
437
- message: string;
438
- hint: string;
439
- } {
440
- return {
441
- error: 'invalid_request',
442
- error_description:
443
- 'OAuth authorization request is incomplete. Required: client_id, response_type, code_challenge, and a usable redirect context (redirect_uri or a registered client).',
444
- message:
445
- 'This endpoint expects a complete OAuth authorization request (PKCE). Non-OAuth MCP clients should use /mcp-bearer with Authorization: Bearer <token> instead of /authorize.',
446
- hint: 'Use POST/GET /mcp-bearer with a forge bearer token for clients that cannot complete OAuth dynamic registration and PKCE.',
447
- };
448
- }
449
-
450
- function isOAuthDebugTraceEnabled(): boolean {
451
- return process.env.FORGE_MCP_OAUTH_TRACE === '1' || process.env.FORGE_MCP_OAUTH_TRACE === 'true';
452
- }
453
-
454
- const SENSITIVE_OAUTH_FIELDS = new Set([
455
- 'passphrase',
456
- 'code',
457
- 'code_verifier',
458
- 'client_secret',
459
- 'access_token',
460
- 'refresh_token',
461
- 'token',
462
- 'authorization',
463
- ]);
464
-
465
- function safeOAuthFieldNames(source: Record<string, unknown>): string[] {
466
- return Object.keys(source)
467
- .filter((key) => !SENSITIVE_OAUTH_FIELDS.has(key.toLowerCase()))
468
- .sort();
469
- }
470
-
471
- function oauthTrace(req: Request, event: string, extra: Record<string, unknown> = {}): void {
472
- if (!isOAuthDebugTraceEnabled()) return;
473
- const source = oauthAuthorizeParamSource(req);
474
- const userAgent = typeof req.headers['user-agent'] === 'string'
475
- ? req.headers['user-agent'].split(/[\s/]/)[0]
476
- : undefined;
477
- const safe = {
478
- event,
479
- method: req.method,
480
- path: req.path,
481
- fieldNames: safeOAuthFieldNames(source),
482
- hasClientId: Boolean(readOAuthAuthorizeString(source, 'client_id')),
483
- hasRedirectUri: Boolean(readOAuthAuthorizeString(source, 'redirect_uri')),
484
- hasCodeChallenge: Boolean(readOAuthAuthorizeString(source, 'code_challenge')),
485
- responseType: readOAuthAuthorizeString(source, 'response_type') || undefined,
486
- codeChallengeMethod: readOAuthAuthorizeString(source, 'code_challenge_method') || undefined,
487
- grantType: readOAuthAuthorizeString(source, 'grant_type') || undefined,
488
- hasResource: Boolean(readOAuthAuthorizeString(source, 'resource')),
489
- userAgent,
490
- ...extra,
491
- };
492
- console.error(`[forge:mcp-oauth] ${JSON.stringify(safe)}`);
493
- }
494
-
495
- function oauthTraceMiddleware(event: string): (req: Request, res: Response, next: NextFunction) => void {
496
- return (req, res, next) => {
497
- oauthTrace(req, `${event}:request`);
498
- res.once('finish', () => oauthTrace(req, `${event}:response`, { statusCode: res.statusCode }));
499
- next();
500
- };
501
- }
502
-
503
- function rejectIncompleteOAuthAuthorize(req: Request, res: Response, next: NextFunction): void {
504
- const source = oauthAuthorizeParamSource(req);
505
- if (isIncompleteOAuthAuthorizeRequest(source)) {
506
- oauthTrace(req, 'authorize:incomplete');
507
- res.status(400).json(incompleteOAuthAuthorizeResponseBody());
508
- return;
509
- }
510
- oauthTrace(req, 'authorize:complete');
511
- next();
512
- }
513
-
514
- function requirePassphrase(passphrase: string): (req: Request, res: Response, next: NextFunction) => void {
515
- return (req, res, next) => {
516
- const provided = typeof req.body?.passphrase === 'string' ? req.body.passphrase : undefined;
517
- if (provided) {
518
- const a = Buffer.from(provided);
519
- const b = Buffer.from(passphrase);
520
- if (a.length === b.length && timingSafeEqual(a, b)) {
521
- next();
522
- return;
523
- }
524
- }
525
- // Prefer body hidden fields on POST (failed passphrase re-render), else query string on GET.
526
- const source = oauthAuthorizeParamSource(req);
527
- const params = new URLSearchParams();
528
- for (const [key, value] of Object.entries(source)) {
529
- if (key === 'passphrase') continue;
530
- if (typeof value === 'string') params.set(key, value);
531
- }
532
- if ([...params.keys()].length === 0 && req.url.includes('?')) {
533
- const fromUrl = new URLSearchParams(req.url.slice(req.url.indexOf('?')));
534
- for (const [key, value] of fromUrl.entries()) {
535
- if (key !== 'passphrase') params.set(key, value);
536
- }
537
- }
538
- res.type('html').send(renderPassphrasePage(params));
539
- };
540
- }
541
-
542
- function oauthAuthorizationHandler(provider: ReturnType<typeof createMcpOAuthProvider>) {
543
- return async (req: Request, res: Response) => {
544
- const query = req.method === 'POST' ? req.body : req.query;
545
- const clientId = typeof query.client_id === 'string' ? query.client_id : '';
546
- const responseType = typeof query.response_type === 'string' ? query.response_type : '';
547
- const codeChallenge = typeof query.code_challenge === 'string' ? query.code_challenge : '';
548
- const codeChallengeMethod = typeof query.code_challenge_method === 'string' ? query.code_challenge_method : '';
549
- const state = typeof query.state === 'string' ? query.state : undefined;
550
- const scope = typeof query.scope === 'string' ? query.scope : undefined;
551
- let redirectUri = typeof query.redirect_uri === 'string' ? query.redirect_uri : undefined;
552
-
553
- if (responseType !== 'code') {
554
- res.status(400).json({ error: 'unsupported_response_type', error_description: 'Only code response type is supported' });
555
- return;
556
- }
557
- if (!codeChallenge || codeChallengeMethod !== 'S256') {
558
- res.status(400).json({ error: 'invalid_request', error_description: 'PKCE S256 is required' });
559
- return;
560
- }
561
-
562
- const client = await getOrRegisterPublicOAuthClient(provider, clientId, redirectUri, req);
563
- if (!client) {
564
- res.status(400).json({ error: 'invalid_client', error_description: 'Unknown client_id' });
565
- return;
566
- }
567
- if (!redirectUri && client.redirect_uris.length === 1) {
568
- redirectUri = client.redirect_uris[0];
569
- }
570
- if (!redirectUri || (!isAllowedMcpOAuthRedirectUri(redirectUri) && !isRegisteredExternalHttpsRedirectUri(redirectUri, client))) {
571
- res.status(400).json({
572
- error: 'invalid_request',
573
- error_description: 'redirect_uri must be localhost, a ChatGPT connector callback URL, or a registered HTTPS client redirect_uri',
574
- });
575
- return;
576
- }
577
- if (!isRegisteredRedirectUri(redirectUri, client)) {
578
- res.status(400).json({
579
- error: 'invalid_request',
580
- error_description: 'redirect_uri must match a registered client redirect_uri',
581
- });
582
- return;
583
- }
584
-
585
- await provider.authorize(client as OAuthClientInformationFull, {
586
- state,
587
- scopes: scope ? scope.split(' ') : [],
588
- redirectUri,
589
- codeChallenge,
590
- }, res);
591
- };
592
- }
593
-
594
272
  function sendBearerUnauthorized(res: Response, description: string, hasConfiguredToken: boolean): void {
595
273
  res.setHeader('www-authenticate', 'Bearer realm="forge-mcp"');
596
274
  res.status(hasConfiguredToken ? 401 : 503).json({
@@ -663,7 +341,8 @@ function requireMcpHttpAuth(
663
341
 
664
342
  interface McpRuntimeStats {
665
343
  initializing: number;
666
- activePosts: number;
344
+ /** Canonical HTTP POST admission owner; one entry per unfinished response. */
345
+ activePostStartedAt: Map<Response, number>;
667
346
  rejectedOverload: number;
668
347
  }
669
348
 
@@ -682,9 +361,88 @@ const MCP_STREAM_LEASE_MS = positiveIntegerEnv('FORGE_MCP_STREAM_LEASE_MS', 30 *
682
361
  const MCP_SESSION_ABSOLUTE_LIFETIME_MS = positiveIntegerEnv('FORGE_MCP_SESSION_ABSOLUTE_LIFETIME_MS', 2 * 60 * 60_000);
683
362
  const MCP_ACTIVE_POST_STALL_MS = positiveIntegerEnv('FORGE_MCP_ACTIVE_POST_STALL_MS', 10 * 60_000);
684
363
 
364
+ function sendReleaseCutoverDraining(res: Response): void {
365
+ res.setHeader('retry-after', '1');
366
+ res.status(503).json({
367
+ error: 'release_cutover_draining',
368
+ code: 'MCP_RELEASE_CUTOVER_DRAINING',
369
+ message: 'This Connector is draining admitted MCP requests for a Runtime release cutover; retry shortly.',
370
+ recoverable: true,
371
+ retryable: true,
372
+ sessionPreserved: true,
373
+ action: 'retry',
374
+ });
375
+ }
376
+
377
+ function connectorCutoverDrainActive(req: Request, controllerHome?: string): boolean {
378
+ if (!controllerHome || !req.socket.localPort) return false;
379
+ return packageConnectorCutoverDrainIsActive(
380
+ controllerHome,
381
+ `http://127.0.0.1:${req.socket.localPort}/mcp`,
382
+ );
383
+ }
384
+
385
+ export function admitMcpPost(
386
+ stats: McpRuntimeStats,
387
+ res: Response,
388
+ cutoverDrain?: { controllerHome: string; connectorEndpoint: string },
389
+ ): boolean {
390
+ if (cutoverDrain && packageConnectorCutoverDrainIsActive(cutoverDrain.controllerHome, cutoverDrain.connectorEndpoint)) {
391
+ sendReleaseCutoverDraining(res);
392
+ return false;
393
+ }
394
+ reconcileFinishedMcpPosts(stats.activePostStartedAt);
395
+ if (stats.activePostStartedAt.size >= MAX_ACTIVE_POSTS) {
396
+ stats.rejectedOverload += 1;
397
+ res.setHeader('retry-after', '1');
398
+ res.status(503).json({
399
+ error: 'server_busy',
400
+ code: 'MCP_SERVER_BUSY',
401
+ message: 'Too many MCP requests are active; retry shortly',
402
+ recoverable: true,
403
+ retryable: true,
404
+ sessionPreserved: true,
405
+ action: 'retry',
406
+ });
407
+ return false;
408
+ }
409
+
410
+ stats.activePostStartedAt.set(res, Date.now());
411
+ const release = (): void => {
412
+ if (!stats.activePostStartedAt.has(res)) return;
413
+ stats.activePostStartedAt.delete(res);
414
+ res.off('finish', release);
415
+ res.off('close', release);
416
+ };
417
+ res.once('finish', release);
418
+ res.once('close', release);
419
+ return true;
420
+ }
421
+
685
422
  type McpToolContext = ReturnType<typeof createMcpToolContext>;
686
423
  type HttpSessionRegistry = McpSessionRegistry<NodeStreamableHTTPServerTransport, McpToolContext>;
687
424
 
425
+
426
+ /**
427
+ * Public HTTP transport registry. It owns transport admission/leases and
428
+ * transport-local observability only; closing a transport session must never
429
+ * invalidate durable execution, Work, or ControllerRound authority.
430
+ */
431
+ export function createMcpHttpSessionRegistry<
432
+ TTransport extends ClosableMcpTransport = NodeStreamableHTTPServerTransport,
433
+ TContext = McpToolContext,
434
+ >(options: McpSessionRegistryOptions<TTransport, TContext> = {}): McpSessionRegistry<TTransport, TContext> {
435
+ return new McpSessionRegistry<TTransport, TContext>({
436
+ maximumSessions: MAX_MCP_SESSIONS,
437
+ maximumSessionsPerPrincipal: MAX_MCP_SESSIONS_PER_PRINCIPAL,
438
+ idleTtlMs: MCP_SESSION_IDLE_TTL_MS,
439
+ streamLeaseMs: MCP_STREAM_LEASE_MS,
440
+ absoluteLifetimeMs: MCP_SESSION_ABSOLUTE_LIFETIME_MS,
441
+ activePostStallMs: MCP_ACTIVE_POST_STALL_MS,
442
+ ...options,
443
+ });
444
+ }
445
+
688
446
  function principalIdFromModernRequestContext(context: McpRequestContext): string {
689
447
  const clientId = context.authInfo?.clientId?.trim();
690
448
  if (clientId) return `oauth-client:${clientId}`;
@@ -731,6 +489,12 @@ async function handleMcpPost(
731
489
  res.status(400).json({ error: 'invalid JSON request body' });
732
490
  return;
733
491
  }
492
+ const connectorEndpoint = req.socket.localPort
493
+ ? `http://127.0.0.1:${req.socket.localPort}/mcp`
494
+ : undefined;
495
+ if (!admitMcpPost(stats, res, baseOptions.controllerHome && connectorEndpoint
496
+ ? { controllerHome: baseOptions.controllerHome, connectorEndpoint }
497
+ : undefined)) return;
734
498
  if (modernHandler) {
735
499
  const webRequest = await toWebRequest(req, body);
736
500
  if (!(await isLegacyRequest(webRequest, body))) {
@@ -744,7 +508,7 @@ async function handleMcpPost(
744
508
  res.setHeader('Mcp-Session-Reset', 'reinitialized');
745
509
  res.setHeader('x-forge-session-reset', 'reinitialized');
746
510
  }
747
- if (stats.initializing >= MAX_INITIALIZING_SESSIONS || stats.activePosts >= MAX_ACTIVE_POSTS) {
511
+ if (stats.initializing >= MAX_INITIALIZING_SESSIONS) {
748
512
  stats.rejectedOverload += 1;
749
513
  res.setHeader('retry-after', '1');
750
514
  res.status(503).json({
@@ -759,7 +523,6 @@ async function handleMcpPost(
759
523
  return;
760
524
  }
761
525
  stats.initializing += 1;
762
- stats.activePosts += 1;
763
526
  let transport: NodeStreamableHTTPServerTransport | undefined;
764
527
  let reservationId: string | undefined;
765
528
  let initializedSessionId: string | undefined;
@@ -839,11 +602,23 @@ async function handleMcpPost(
839
602
  server = createForgeMcpServerFromContext(sessionContext, runtimeSchema, sharedRuntimeProxy);
840
603
  await server.connect(transport);
841
604
  await transport.handleRequest(req, res, body);
605
+ if (
606
+ initializedSessionId
607
+ && 'controllerHome' in sessionContext
608
+ && typeof sessionContext.controllerHome === 'string'
609
+ ) {
610
+ recordMcpTransportEvent(sessionContext.controllerHome, {
611
+ kind: 'session_initialized',
612
+ sessionId: initializedSessionId,
613
+ connectionId: connection.connectionId,
614
+ route,
615
+ principalId,
616
+ });
617
+ }
842
618
  } finally {
843
619
  if (initializedSessionId) registry.endPost(initializedSessionId);
844
620
  if (reservationId) registry.releaseInitialize(reservationId);
845
- stats.initializing -= 1;
846
- stats.activePosts -= 1;
621
+ stats.initializing = Math.max(0, stats.initializing - 1);
847
622
  if (!transport?.sessionId) await transport?.close().catch(() => undefined);
848
623
  }
849
624
  return;
@@ -851,14 +626,13 @@ async function handleMcpPost(
851
626
  if (sessionId) {
852
627
  const managed = registry.get(sessionId);
853
628
  if (managed && managed.route === route && managed.principalId === principalFromRequest(req)) {
854
- if (managed.inFlightPosts >= MAX_POSTS_PER_SESSION || stats.activePosts >= MAX_ACTIVE_POSTS) {
629
+ if (managed.inFlightPosts >= MAX_POSTS_PER_SESSION) {
855
630
  stats.rejectedOverload += 1;
856
631
  res.setHeader('retry-after', '1');
857
632
  res.status(429).json({ error: 'session_busy', message: 'Too many MCP requests are active; retry shortly' });
858
633
  return;
859
634
  }
860
635
  registry.beginPost(sessionId);
861
- stats.activePosts += 1;
862
636
  try {
863
637
  const currentFingerprint = await resolveMcpSessionCurrentFingerprint(
864
638
  currentToolSurfaceFingerprint(),
@@ -878,23 +652,21 @@ async function handleMcpPost(
878
652
  return;
879
653
  }
880
654
  }
881
- // Keep the transport/session alive long enough for the host to observe
882
- // the recoverable reset and issue a replacement initialize request.
883
- // The initialize path explicitly supersedes this session afterward.
884
655
  sendMcpToolSurfaceReset(res, managed.toolSurfaceFingerprint, currentFingerprint);
656
+ retireAfterToolSurfaceReset(res, registry, sessionId);
885
657
  return;
886
658
  }
887
659
  if (toolCallOutsideSessionSchema(body, managed.toolNames)) {
888
660
  // A call against a newer discovery surface is the same recoverable
889
- // schema-fence condition. Closing here can make hosts unregister the
890
- // entire MCP namespace before they can reinitialize it.
661
+ // schema-fence condition: this transport must reinitialize before it
662
+ // can execute anything else.
891
663
  sendMcpToolSurfaceReset(res, managed.toolSurfaceFingerprint, currentFingerprint);
664
+ retireAfterToolSurfaceReset(res, registry, sessionId);
892
665
  return;
893
666
  }
894
667
  await managed.transport.handleRequest(req, res, body);
895
668
  } finally {
896
669
  registry.endPost(sessionId);
897
- stats.activePosts -= 1;
898
670
  }
899
671
  return;
900
672
  }
@@ -909,7 +681,12 @@ async function handleMcpGet(
909
681
  route: McpSessionRoute,
910
682
  currentToolSurfaceFingerprint: () => string | undefined,
911
683
  resolveRuntimeSchema?: (context: McpToolContext) => Promise<CanonicalRuntimeToolSchema | undefined>,
684
+ controllerHome?: string,
912
685
  ): Promise<void> {
686
+ if (connectorCutoverDrainActive(req, controllerHome)) {
687
+ sendReleaseCutoverDraining(res);
688
+ return;
689
+ }
913
690
  const sessionId = req.headers['mcp-session-id'] as string | undefined;
914
691
  const managed = sessionId ? registry.get(sessionId) : undefined;
915
692
  if (!managed || managed.route !== route || managed.principalId !== principalFromRequest(req)) {
@@ -933,9 +710,8 @@ async function handleMcpGet(
933
710
  : undefined,
934
711
  );
935
712
  if (!mcpSessionToolSurfaceFingerprintIsCurrent(managed.toolSurfaceFingerprint, currentFingerprint)) {
936
- // Preserve the existing SSE transport while asking the host to
937
- // reinitialize. The replacement initialize owns supersession/cleanup.
938
713
  sendMcpToolSurfaceReset(res, managed.toolSurfaceFingerprint, currentFingerprint);
714
+ retireAfterToolSurfaceReset(res, registry, sessionId!);
939
715
  return;
940
716
  }
941
717
  await managed.transport.handleRequest(req, res);
@@ -992,32 +768,25 @@ export async function startMcpHttp(opts: McpHttpOptions): Promise<void> {
992
768
  tokenStore?.load();
993
769
  const oauthProvider = tokenStore ? createMcpOAuthProvider(tokenStore) : null;
994
770
  const configuredPublicOrigin = getConfiguredPublicOrigin(serviceConfig);
995
- const sessionRegistry = new McpSessionRegistry<NodeStreamableHTTPServerTransport, McpToolContext>({
996
- maximumSessions: MAX_MCP_SESSIONS,
997
- maximumSessionsPerPrincipal: MAX_MCP_SESSIONS_PER_PRINCIPAL,
998
- idleTtlMs: MCP_SESSION_IDLE_TTL_MS,
999
- streamLeaseMs: MCP_STREAM_LEASE_MS,
1000
- absoluteLifetimeMs: MCP_SESSION_ABSOLUTE_LIFETIME_MS,
1001
- activePostStallMs: MCP_ACTIVE_POST_STALL_MS,
771
+ const sessionRegistry = createMcpHttpSessionRegistry<NodeStreamableHTTPServerTransport, McpToolContext>({
1002
772
  onSessionClosed: (session, reason) => {
1003
- const context = session.toolContext;
1004
- if (!('controllerHome' in context) || typeof context.controllerHome !== 'string') return;
1005
- const executionSessionId = typeof context.sessionId === 'string' ? context.sessionId.trim() : '';
1006
- if (!executionSessionId) return;
1007
- invalidateExecutionSession(
1008
- context.controllerHome,
1009
- executionSessionId,
1010
- `mcp_transport_${reason}`,
1011
- );
773
+ if (reason !== 'transport_close') return;
774
+ recordMcpTransportEvent(controllerHome, {
775
+ kind: 'interruption',
776
+ sessionId: session.sessionId,
777
+ connectionId: session.connectionId,
778
+ route: session.route,
779
+ principalId: session.principalId,
780
+ reason,
781
+ });
1012
782
  },
1013
783
  });
1014
- const runtimeStats: McpRuntimeStats = { initializing: 0, activePosts: 0, rejectedOverload: 0 };
784
+ const runtimeStats: McpRuntimeStats = { initializing: 0, activePostStartedAt: new Map(), rejectedOverload: 0 };
1015
785
  const toolContext = createMcpToolContext({ ...opts, repo: repoRoot, controllerHome, profile });
1016
786
  const baseOptions: McpServerOptions = {
1017
787
  repo: repoRoot,
1018
788
  controllerHome,
1019
789
  profile,
1020
- toolset: opts.toolset,
1021
790
  enableChatgptBrowser: opts.enableChatgptBrowser,
1022
791
  enableDevRunner: opts.enableDevRunner,
1023
792
  devRunnerAgents: opts.devRunnerAgents,
@@ -1055,295 +824,43 @@ export async function startMcpHttp(opts: McpHttpOptions): Promise<void> {
1055
824
  return await readCanonicalRuntimeToolSchema(context, sharedRuntimeProxy);
1056
825
  }
1057
826
  : undefined;
1058
- const localControllerConfig = {
1059
- enabled: serviceConfig?.localController?.enabled ?? profile === 'controller',
1060
- host: serviceConfig?.localController?.host ?? '127.0.0.1',
1061
- port: serviceConfig?.localController?.port ?? 8766,
1062
- };
1063
- const compatibilityToolDefinitions = buildMcpToolDefinitions(toolContext.policy, { enableChatgptBrowser: opts.enableChatgptBrowser === true });
1064
827
  const toolSurface = toolContext.policy.profile === 'controller' ? FORGE_TOOL_SURFACE : `${toolContext.policy.profile}-legacy-v1`;
1065
828
  const toolSurfaceSchemaVersion = toolContext.policy.profile === 'controller' ? FORGE_MCP_SCHEMA_VERSION : 1;
1066
829
  const forgeVersion = FORGE_VERSION;
1067
- const repoId = toolContext.policy.profile === 'controller' || !repoRoot ? undefined : repositoryIdentity(repoRoot);
1068
- const startedAt = new Date().toISOString();
1069
- const localOrigin = `http://${host === '::' || host === '0.0.0.0' ? '127.0.0.1' : host}:${port}`;
1070
- const advertisedOrigin = configuredPublicOrigin ?? localOrigin;
1071
830
  const app = express();
1072
831
  app.set('trust proxy', 1);
1073
832
 
1074
- const controllerHealth = () => {
1075
- if (!('controllerHome' in toolContext)) return null;
1076
- const runtimeGeneration = currentRuntimeGeneration();
1077
- const exposure = controllerExposureSnapshot(toolContext);
1078
- // Health is a bounded diagnostic endpoint, not an MCP discovery authority.
1079
- // Session initialization and invocation obtain a live Runtime tools/list;
1080
- // health only reports the Runtime's published identity.
1081
- const runtimeFingerprint = currentRuntimeToolSurfaceFingerprint();
1082
- return {
1083
- configuredAccessMode: exposure.access.configuredAccessMode,
1084
- effectiveAccessMode: exposure.access.effectiveAccessMode,
1085
- effectiveToolset: exposure.access.effectiveToolset,
1086
- exposureRevision: exposure.access.exposureRevision,
1087
- accessModeSource: exposure.access.source,
1088
- accessModeLastAppliedAt: exposure.access.lastAppliedAt,
1089
- toolset: exposure.access.effectiveToolset,
1090
- toolSurfaceFingerprint: runtimeFingerprint,
1091
- runtimeToolSurfaceFingerprint: runtimeFingerprint,
1092
- toolCount: undefined,
1093
- generation: runtimeGeneration?.generation,
1094
- source: runtimeGeneration?.source,
1095
- runtimeIdentity: runtimeIdentitySnapshot(toolContext),
1096
- };
1097
- };
1098
-
1099
- app.get('/health', (_req, res) => {
1100
- const health = controllerHealth();
1101
- res.setHeader('x-forge-tool-surface', toolSurface);
1102
- res.setHeader('x-forge-version', String(forgeVersion));
1103
- res.setHeader('x-forge-schema-version', String(toolSurfaceSchemaVersion));
1104
- if (health?.toolset) res.setHeader('x-forge-toolset', health.toolset);
1105
- if (health?.runtimeToolSurfaceFingerprint) res.setHeader('x-forge-runtime-tool-surface-fingerprint', health.runtimeToolSurfaceFingerprint);
1106
- if (health?.toolSurfaceFingerprint) res.setHeader('x-forge-tool-surface-fingerprint', health.toolSurfaceFingerprint);
1107
- const sessionSnapshot = sessionRegistry.snapshot();
1108
- res.json({
1109
- status: 'ok',
1110
- server: 'forge-mcp',
1111
- forgeInstanceId: forgeInstance.instanceId,
1112
- ...(process.env.FORGE_MCP_INSTANCE_ID
1113
- ? { controllerInstanceId: process.env.FORGE_MCP_INSTANCE_ID }
1114
- : {}),
1115
- version: forgeVersion,
1116
- profile: toolContext.policy.profile,
1117
- toolSurface,
1118
- schemaVersion: toolSurfaceSchemaVersion,
1119
- toolSurfaceFingerprint: health?.toolSurfaceFingerprint,
1120
- runtimeToolSurfaceFingerprint: health?.runtimeToolSurfaceFingerprint,
1121
- generation: health?.generation,
1122
- source: health?.source,
1123
- // Deprecated diagnostic alias; not a Runtime schema claim.
1124
- toolset: health?.toolset ?? 'full',
1125
- gatewayToolset: health?.toolset ?? 'full',
1126
- toolCount: health?.toolCount,
1127
- compatibilityToolCount: compatibilityToolDefinitions.length,
1128
- runtimeIdentity: health?.runtimeIdentity,
1129
- configuredAccessMode: health?.configuredAccessMode,
1130
- effectiveAccessMode: health?.effectiveAccessMode,
1131
- effectiveToolset: health?.effectiveToolset,
1132
- accessModeSource: health?.accessModeSource,
1133
- accessModeLastAppliedAt: health?.accessModeLastAppliedAt,
1134
- exposureRevision: health?.exposureRevision,
1135
- ...(repoId ? { repoId } : {}),
1136
- startedAt,
1137
- runner: {
1138
- enabled: toolContext.policy.execution.agentRunner,
1139
- defaultTimeoutMs: toolContext.policy.execution.runnerTimeoutMs,
1140
- maxTimeoutMs: toolContext.policy.execution.runnerMaxTimeoutMs,
1141
- },
1142
- auth: authMode === 'oauth'
1143
- ? (oauthPassphrase ? 'oauth' : 'missing')
1144
- : authMode === 'bearer'
1145
- ? (authToken ? 'required' : 'missing')
1146
- : 'none',
1147
- ...(oauthProvider ? { oauthAuthorizationCodes: oauthProvider.authorizationCodeDiagnostics() } : {}),
1148
- mcpEndpoint: `${advertisedOrigin}/mcp`,
1149
- // Grok now completes standard OAuth dynamic registration + PKCE on the
1150
- // canonical MCP resource. Keep /mcp-grok below only as a legacy alias.
1151
- grokEndpoint: `${advertisedOrigin}/mcp`,
1152
- bearerEndpoint: `${advertisedOrigin}/mcp-bearer`,
1153
- sessions: {
1154
- ...sessionSnapshot,
1155
- initializing: runtimeStats.initializing,
1156
- activePosts: runtimeStats.activePosts,
1157
- maximumActivePosts: MAX_ACTIVE_POSTS,
1158
- rejectedOverload: runtimeStats.rejectedOverload,
1159
- },
1160
- });
1161
- });
1162
-
1163
- // Recovery probes this endpoint every few seconds. Keep it strictly in-memory
1164
- // and transport-scoped: whole-control-plane readiness below may traverse every
1165
- // repository projection and probe the local bridge, which must never become a
1166
- // periodic event-loop load generator for the public MCP Connector itself.
1167
- app.get('/transport-ready', (_req, res) => {
1168
- const sessionSnapshot = sessionRegistry.snapshot();
1169
- const sessionCapacityReady = sessionSnapshot.acceptingNewSessions
1170
- && runtimeStats.initializing < MAX_INITIALIZING_SESSIONS
1171
- && runtimeStats.activePosts < MAX_ACTIVE_POSTS;
1172
- res.status(sessionCapacityReady ? 200 : 503).json({
1173
- ready: sessionCapacityReady,
1174
- profile: toolContext.policy.profile,
1175
- gateway: sessionCapacityReady ? 'ready' : 'saturated',
1176
- sessionCapacity: sessionSnapshot,
1177
- runtimeCapacity: {
1178
- initializing: runtimeStats.initializing,
1179
- maximumInitializing: MAX_INITIALIZING_SESSIONS,
1180
- activePosts: runtimeStats.activePosts,
1181
- maximumActivePosts: MAX_ACTIVE_POSTS,
1182
- },
1183
- });
1184
- });
1185
-
1186
- app.get('/ready', async (_req, res) => {
1187
- const runtimeGeneration = currentRuntimeGeneration();
1188
- const sessionSnapshot = sessionRegistry.snapshot();
1189
- const sessionCapacityReady = sessionSnapshot.acceptingNewSessions
1190
- && runtimeStats.initializing < MAX_INITIALIZING_SESSIONS
1191
- && runtimeStats.activePosts < MAX_ACTIVE_POSTS;
1192
- if (!runtimeControllerHome) {
1193
- res.status(sessionCapacityReady ? 200 : 503).json({
1194
- ready: sessionCapacityReady,
1195
- profile: toolContext.policy.profile,
1196
- gateway: sessionCapacityReady ? 'ready' : 'saturated',
1197
- controllerDaemon: 'not-required',
1198
- sessionCapacity: sessionSnapshot,
1199
- });
1200
- return;
1201
- }
1202
- const daemon = readForgeRuntimeStatus(runtimeControllerHome);
1203
- const runtimeState = loadMcpServiceRuntimeState(runtimeControllerHome, repoRoot);
1204
- const repositories = listRepositories(runtimeControllerHome).filter((repository) => repository.enabled && !repository.removedAt);
1205
- const projectionSnapshots = repositories.map((repository) => {
1206
- const snapshot = readRepositoryProjectionSnapshot(runtimeControllerHome, repository.repoId);
1207
- const reconciliation = reconcileReadinessProjectionSource(
1208
- snapshot,
1209
- legacyIssueAuthorityRetired(repository.canonicalRoot)
1210
- ? undefined
1211
- : buildControllerTaskLedgerProjection(repository.canonicalRoot),
1212
- );
1213
- return { repoId: repository.repoId, snapshot, reconciliation };
1214
- });
1215
- const staleRepositories = projectionSnapshots
1216
- .filter(({ snapshot }) => snapshot.stale)
1217
- .map(({ repoId }) => repoId);
1218
- const blockingStaleRepositories = projectionSnapshots
1219
- .filter(({ snapshot }) => projectionBlocksReadiness(snapshot))
1220
- .map(({ repoId }) => repoId);
1221
- const sourceMismatches = projectionSnapshots
1222
- .filter(({ reconciliation }) => reconciliation.status === 'mismatch')
1223
- .map(({ repoId, reconciliation }) => ({ repoId, ...reconciliation }));
1224
- const localBridgeHealth = localControllerConfig.enabled
1225
- ? await jsonHealth(localControllerHealthUrl(localControllerConfig.host, localControllerConfig.port))
1226
- : null;
1227
- const localBridgeReady = !localControllerConfig.enabled
1228
- || localControllerDiagnosticMatchesRuntime(localBridgeHealth, runtimeGeneration?.generation);
1229
- const daemonReady = daemon.status === 'ready' && daemon.degraded !== true;
1230
- const projectionReady = blockingStaleRepositories.length === 0;
1231
- const publicConfigured = Boolean(runtimeState?.tunnel?.publicEndpoint);
1232
- const publicReady = !publicConfigured || runtimeState?.tunnel?.healthy === true;
1233
- const connectorReady = !publicConfigured || (
1234
- publicReady
1235
- && runtimeState?.tunnel?.connectorNeedsReconnect !== true
1236
- );
1237
- const ready = daemonReady && projectionReady && localBridgeReady && sessionCapacityReady;
1238
- res.status(ready ? 200 : 503).json({
1239
- ready,
1240
- generation: runtimeGeneration?.generation,
1241
- source: runtimeGeneration?.source,
1242
- gateway: { status: ready ? 'ready' : 'degraded', thin: true, eventLoopIsolatedFromWorkers: true },
1243
- controllerDaemon: daemon,
1244
- localBridge: {
1245
- enabled: localControllerConfig.enabled,
1246
- ready: localBridgeReady,
1247
- endpoint: `http://${localControllerConfig.host === '::1' ? '[::1]' : localControllerConfig.host}:${localControllerConfig.port}/`,
1248
- },
1249
- projections: {
1250
- ready: projectionReady,
1251
- repositoryCount: repositories.length,
1252
- staleRepositories,
1253
- blockingStaleRepositories,
1254
- sourceMismatches,
1255
- },
1256
- publicReadiness: {
1257
- configured: publicConfigured,
1258
- ready: publicReady,
1259
- endpoint: runtimeState?.tunnel?.publicEndpoint,
1260
- },
1261
- connectorReadiness: {
1262
- configured: publicConfigured,
1263
- ready: connectorReady,
1264
- connectorNeedsReconnect: runtimeState?.tunnel?.connectorNeedsReconnect === true,
1265
- },
1266
- sessionCapacity: sessionSnapshot,
1267
- });
1268
- });
1269
-
1270
- app.get('/repos/:repoId/health', (req, res) => {
1271
- if (!runtimeControllerHome) {
1272
- res.status(404).json({ error: 'controller profile required' });
1273
- return;
1274
- }
1275
- try {
1276
- const repository = getRepository(req.params.repoId, runtimeControllerHome, { includeRemoved: true });
1277
- const projection = readRepositoryProjectionSnapshot(runtimeControllerHome, repository.repoId);
1278
- res.json({
1279
- status: repository.enabled && !repository.removedAt ? 'ok' : 'disabled',
1280
- repository: {
1281
- repoId: repository.repoId,
1282
- checkoutId: repository.activeCheckoutId,
1283
- enabled: repository.enabled,
1284
- removedAt: repository.removedAt,
1285
- },
1286
- projection,
1287
- });
1288
- } catch (error) {
1289
- res.status(404).json({ error: error instanceof Error ? error.message : String(error) });
1290
- }
833
+ registerMcpHttpObservationRoutes({
834
+ app,
835
+ toolContext,
836
+ sessionRegistry,
837
+ runtimeStats,
838
+ runtimeControllerHome,
839
+ repoRoot,
840
+ forgeInstanceId: forgeInstance.instanceId,
841
+ currentRuntimeToolSurfaceFingerprint,
842
+ toolSurface,
843
+ toolSurfaceSchemaVersion,
844
+ forgeVersion,
845
+ authMode,
846
+ authTokenConfigured: Boolean(authToken),
847
+ oauthPassphraseConfigured: Boolean(oauthPassphrase),
848
+ oauthAuthorizationCodeDiagnostics: oauthProvider ? () => oauthProvider.authorizationCodeDiagnostics() : undefined,
849
+ configuredPublicOrigin,
850
+ host,
851
+ port,
852
+ enableChatgptBrowser: opts.enableChatgptBrowser === true,
853
+ localController: {
854
+ enabled: serviceConfig?.localController?.enabled ?? profile === 'controller',
855
+ host: serviceConfig?.localController?.host ?? '127.0.0.1',
856
+ port: serviceConfig?.localController?.port ?? 8766,
857
+ },
858
+ maxInitializingSessions: MAX_INITIALIZING_SESSIONS,
859
+ maxActivePosts: MAX_ACTIVE_POSTS,
1291
860
  });
1292
861
 
1293
862
  if (authMode === 'oauth' && oauthProvider) {
1294
- app.use('/authorize', express.urlencoded({ extended: false, limit: '10kb' }));
1295
- app.use('/authorize', oauthTraceMiddleware('authorize'));
1296
- // Reject incomplete OAuth requests before rendering the passphrase form.
1297
- app.use('/authorize', rejectIncompleteOAuthAuthorize);
1298
- app.use('/authorize', requirePassphrase(oauthPassphrase ?? ''));
1299
- app.use('/authorize', oauthAuthorizationHandler(oauthProvider));
1300
- app.use('/token', oauthTraceMiddleware('token'));
1301
- app.use('/token', tokenHandler({ provider: oauthProvider, rateLimit: false }));
1302
- app.use('/revoke', oauthTraceMiddleware('revoke'));
1303
- app.use('/revoke', revocationHandler({ provider: oauthProvider, rateLimit: false }));
1304
- app.use('/register', oauthTraceMiddleware('register'));
1305
- app.use('/register', clientRegistrationHandler({ clientsStore: oauthProvider.clientsStore, rateLimit: false }));
1306
- app.get('/.well-known/oauth-authorization-server', (req, res) => {
1307
- const origin = getPublicOrigin(req, configuredPublicOrigin);
1308
- res.json({
1309
- issuer: origin,
1310
- authorization_endpoint: `${origin}/authorize`,
1311
- token_endpoint: `${origin}/token`,
1312
- revocation_endpoint: `${origin}/revoke`,
1313
- registration_endpoint: `${origin}/register`,
1314
- response_types_supported: ['code'],
1315
- grant_types_supported: ['authorization_code', 'refresh_token'],
1316
- code_challenge_methods_supported: ['S256'],
1317
- token_endpoint_auth_methods_supported: ['client_secret_post', 'none'],
1318
- scopes_supported: ['forge'],
1319
- });
1320
- });
1321
- app.get('/.well-known/openid-configuration', (req, res) => {
1322
- const origin = getPublicOrigin(req, configuredPublicOrigin);
1323
- res.json({
1324
- issuer: origin,
1325
- authorization_endpoint: `${origin}/authorize`,
1326
- token_endpoint: `${origin}/token`,
1327
- registration_endpoint: `${origin}/register`,
1328
- response_types_supported: ['code'],
1329
- grant_types_supported: ['authorization_code', 'refresh_token'],
1330
- code_challenge_methods_supported: ['S256'],
1331
- token_endpoint_auth_methods_supported: ['client_secret_post', 'none'],
1332
- scopes_supported: ['forge'],
1333
- });
1334
- });
1335
- const protectedResourceMetadata = (resourcePath: '/mcp' | '/mcp-grok' | '/mcp-bearer') => (req: Request, res: Response): void => {
1336
- const origin = getPublicOrigin(req, configuredPublicOrigin);
1337
- res.json({
1338
- resource: `${origin}${resourcePath}`,
1339
- authorization_servers: [origin],
1340
- scopes_supported: ['forge'],
1341
- bearer_methods_supported: ['header'],
1342
- });
1343
- };
1344
- app.get('/.well-known/oauth-protected-resource/mcp', protectedResourceMetadata('/mcp'));
1345
- app.get('/.well-known/oauth-protected-resource/mcp-grok', protectedResourceMetadata('/mcp-grok'));
1346
- app.get('/.well-known/oauth-protected-resource/mcp-bearer', protectedResourceMetadata('/mcp-bearer'));
863
+ registerMcpOAuthHttpRoutes(app, oauthProvider, oauthPassphrase ?? '', configuredPublicOrigin);
1347
864
  }
1348
865
 
1349
866
  const setMcpResponseHeaders = (_req: Request, res: Response, next: NextFunction): void => {
@@ -1367,7 +884,7 @@ export async function startMcpHttp(opts: McpHttpOptions): Promise<void> {
1367
884
  });
1368
885
  });
1369
886
  app.get('/mcp', requireMcpHttpAuth(authMode, authToken, oauthProvider, configuredPublicOrigin), (req, res) => {
1370
- handleMcpGet(req, res, sessionRegistry, '/mcp', currentRuntimeToolSurfaceFingerprint, resolveRuntimeSchema).catch((error: unknown) => {
887
+ handleMcpGet(req, res, sessionRegistry, '/mcp', currentRuntimeToolSurfaceFingerprint, resolveRuntimeSchema, controllerHome).catch((error: unknown) => {
1371
888
  if (!res.headersSent) sendMcpRequestError(res, error);
1372
889
  });
1373
890
  });
@@ -1385,7 +902,7 @@ export async function startMcpHttp(opts: McpHttpOptions): Promise<void> {
1385
902
  });
1386
903
  });
1387
904
  app.get('/mcp-grok', requireMcpHttpAuth(authMode, authToken, oauthProvider, configuredPublicOrigin, '/mcp-grok'), (req, res) => {
1388
- handleMcpGet(req, res, sessionRegistry, '/mcp-grok', currentRuntimeToolSurfaceFingerprint, resolveRuntimeSchema).catch((error: unknown) => {
905
+ handleMcpGet(req, res, sessionRegistry, '/mcp-grok', currentRuntimeToolSurfaceFingerprint, resolveRuntimeSchema, controllerHome).catch((error: unknown) => {
1389
906
  if (!res.headersSent) sendMcpRequestError(res, error);
1390
907
  });
1391
908
  });
@@ -1403,7 +920,7 @@ export async function startMcpHttp(opts: McpHttpOptions): Promise<void> {
1403
920
  });
1404
921
  });
1405
922
  app.get('/mcp-bearer', requireMcpHttpAuth('bearer', authToken, null, configuredPublicOrigin), (req, res) => {
1406
- handleMcpGet(req, res, sessionRegistry, '/mcp-bearer', currentRuntimeToolSurfaceFingerprint, resolveRuntimeSchema).catch((error: unknown) => {
923
+ handleMcpGet(req, res, sessionRegistry, '/mcp-bearer', currentRuntimeToolSurfaceFingerprint, resolveRuntimeSchema, controllerHome).catch((error: unknown) => {
1407
924
  if (!res.headersSent) sendMcpRequestError(res, error);
1408
925
  });
1409
926
  });