@moretea-labs/forge 1.8.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (528) hide show
  1. package/AGENTS.md +70 -7
  2. package/CHANGELOG.md +14 -0
  3. package/CLAUDE.md +17 -2
  4. package/CONTRIBUTING.md +3 -2
  5. package/THIRD_PARTY_NOTICES.md +1 -1
  6. package/adapters/chatgpt/browser-delivery-host.ts +7 -1
  7. package/adapters/chatgpt/browser-delivery-runtime.ts +529 -56
  8. package/adapters/chatgpt/controller-binding-store.ts +101 -22
  9. package/adapters/chatgpt/controller-round-host.ts +15 -25
  10. package/adapters/chatgpt/controller-round-settlement-store.ts +58 -4
  11. package/adapters/chatgpt/provider-delivery.ts +228 -1
  12. package/adapters/chatgpt/work-conversation-binding-store.ts +79 -53
  13. package/adapters/computer/chatgpt-extension-target.ts +463 -0
  14. package/adapters/computer/chatgpt-target-router.ts +103 -0
  15. package/adapters/computer/chrome-extension/background.js +385 -0
  16. package/adapters/computer/chrome-extension/content.js +378 -0
  17. package/adapters/computer/chrome-extension/core.js +24 -0
  18. package/adapters/computer/chrome-extension/manifest.json +30 -0
  19. package/adapters/computer/desktop-operator-discovery.ts +11 -31
  20. package/adapters/computer/interaction-target-authority.ts +142 -18
  21. package/adapters/mcp/auth.ts +0 -1
  22. package/adapters/mcp/instructions.ts +5 -4
  23. package/adapters/mcp/multi-repository.ts +3 -19
  24. package/adapters/mcp/oauth.ts +73 -0
  25. package/adapters/mcp/paths.ts +4 -1
  26. package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +87 -0
  27. package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
  28. package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
  29. package/adapters/mcp/runtime-gateway/context-adapter.ts +411 -73
  30. package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
  31. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +20 -5
  32. package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
  33. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +10 -27
  34. package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
  35. package/adapters/mcp/runtime-gateway/execution-tools.ts +8 -42
  36. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +2 -1
  37. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +117 -45
  38. package/adapters/mcp/runtime-gateway/process-tools.ts +190 -22
  39. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +16 -4
  40. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +69 -52
  41. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +4 -35
  42. package/adapters/mcp/runtime-gateway/result-adapter.ts +10 -4
  43. package/adapters/mcp/runtime-gateway/router.ts +18 -504
  44. package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
  45. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +14 -65
  46. package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
  47. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +113 -173
  48. package/adapters/mcp/runtime-gateway/runtime-tools.ts +15 -5
  49. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +1 -0
  50. package/adapters/mcp/runtime-gateway/shared-adapter.ts +20 -0
  51. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +358 -266
  52. package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
  53. package/adapters/mcp/runtime-gateway/work-adapter.ts +261 -3122
  54. package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
  55. package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
  56. package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
  57. package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
  58. package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
  59. package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
  60. package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
  61. package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
  62. package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
  63. package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
  64. package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
  65. package/adapters/mcp/server.ts +67 -23
  66. package/adapters/mcp/setup.ts +1 -3
  67. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +44 -170
  68. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +4 -9
  69. package/adapters/mcp/tool-mapping/repository-tools.ts +133 -142
  70. package/adapters/mcp/toolset-names.ts +5 -0
  71. package/adapters/mcp/toolset.ts +6 -5
  72. package/adapters/mcp/transports/http-observation.ts +365 -0
  73. package/adapters/mcp/transports/http.ts +154 -677
  74. package/adapters/mcp/transports/oauth-http.ts +322 -0
  75. package/adapters/mcp/transports/session-registry.ts +6 -0
  76. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +89 -5
  77. package/assets/plugin-registry.v1.json +6 -4
  78. package/assets/reference-configs/agentic-development-flow.md +25 -0
  79. package/assets/reference-configs/global-working-rules.md +19 -0
  80. package/assets/skill-version.json +2 -2
  81. package/docs/architecture/CURRENT.md +111 -41
  82. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
  83. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
  84. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +2 -0
  85. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +19 -4
  86. package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
  87. package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
  88. package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
  89. package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
  90. package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
  91. package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
  92. package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
  93. package/docs/architecture/domains/verification.md +1 -0
  94. package/docs/architecture/domains/workflow-engine.md +6 -0
  95. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  96. package/docs/forge-plugin-management.md +2 -2
  97. package/docs/operations/controller-performance-and-502.md +17 -0
  98. package/docs/operations/controller-reliability-runbook.md +2 -2
  99. package/docs/operations/features.md +1 -1
  100. package/docs/operations/features.zh-CN.md +1 -1
  101. package/docs/operations/homebrew.md +2 -2
  102. package/docs/operations/mcp-tool-exposure.md +2 -0
  103. package/docs/operations/releasing.md +2 -0
  104. package/docs/operations/standalone-disaster-recovery.md +14 -6
  105. package/docs/wiki/Implementation.md +1 -1
  106. package/package.json +25 -14
  107. package/packages/kernel/cognition/api/index.ts +6 -0
  108. package/packages/kernel/cognition/application/activation-service.ts +451 -0
  109. package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
  110. package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
  111. package/packages/kernel/cognition/application/memory-service.ts +64 -0
  112. package/packages/kernel/cognition/domain/learning.ts +106 -0
  113. package/packages/kernel/cognition/domain/memory.ts +254 -0
  114. package/packages/kernel/controller/api/index.ts +1 -0
  115. package/packages/kernel/controller/application/continuation-service.ts +281 -0
  116. package/packages/kernel/controller/application/controller-round-service.ts +12 -0
  117. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +242 -21
  118. package/packages/kernel/controller/domain/controller-round.ts +4 -1
  119. package/packages/kernel/controller/domain/execution-quality.ts +3 -1
  120. package/packages/kernel/controller/infrastructure/controller-round-store.ts +689 -103
  121. package/packages/kernel/controller/infrastructure/controller-session-store.ts +297 -71
  122. package/packages/kernel/controller/ports/controller-host.ts +2 -0
  123. package/packages/kernel/identity/api/index.ts +10 -0
  124. package/packages/kernel/identity/domain/activity-projection.ts +43 -0
  125. package/packages/kernel/identity/domain/capability-broker.ts +60 -0
  126. package/packages/kernel/identity/domain/grant.ts +72 -0
  127. package/packages/kernel/identity/domain/owned-resource.ts +116 -0
  128. package/packages/kernel/identity/domain/user-request.ts +93 -0
  129. package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
  130. package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
  131. package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
  132. package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
  133. package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
  134. package/packages/kernel/scheduler/api/index.ts +0 -2
  135. package/packages/kernel/scheduler/application/schedule-service.ts +3 -0
  136. package/packages/kernel/scheduler/application/settlement.ts +0 -15
  137. package/packages/kernel/scheduler/domain/schedule.ts +87 -0
  138. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +153 -133
  139. package/packages/kernel/work/api/index.ts +3 -2
  140. package/packages/kernel/work/application/work-service.ts +17 -42
  141. package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
  142. package/packages/kernel/work/domain/engineering-design.ts +10 -5
  143. package/packages/kernel/work/domain/engineering-profile.ts +8 -12
  144. package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
  145. package/packages/kernel/work/domain/execution-concurrency.ts +19 -26
  146. package/packages/kernel/work/domain/execution-snapshot.ts +0 -31
  147. package/packages/kernel/work/domain/implementation-review.ts +14 -331
  148. package/packages/kernel/work/domain/objective-graph.ts +119 -0
  149. package/packages/kernel/work/domain/project-engineering-contract.ts +23 -1
  150. package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
  151. package/packages/kernel/work/domain/state-machine.ts +25 -63
  152. package/packages/kernel/work/domain/types.ts +57 -51
  153. package/packages/kernel/work/infrastructure/work-contract-store.ts +819 -728
  154. package/packages/kernel/work/ports/work-contract-store.ts +5 -2
  155. package/packages/plugin-runtime/browser/session-authority.ts +2 -1
  156. package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
  157. package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
  158. package/packages/plugin-runtime/computer/index.ts +2 -0
  159. package/packages/plugin-runtime/computer/target-authority.ts +26 -1
  160. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +142 -18
  161. package/packages/protocols/computer/contract.ts +27 -1
  162. package/scripts/activate-source-baseline.ts +7 -9
  163. package/scripts/canary-controller-semantic-wait.ts +12 -24
  164. package/scripts/check-mcp-compatibility.ts +48 -136
  165. package/scripts/check-runtime-architecture.mjs +622 -217
  166. package/scripts/forge-local-recovery-helper.mjs +93 -15
  167. package/scripts/lib/project-init-lib.sh +3 -0
  168. package/scripts/open-source-audit-allowlist.txt +1 -2
  169. package/scripts/run-bounded-command.mjs +5 -0
  170. package/scripts/run-governed-gate.ts +0 -1
  171. package/scripts/run-mobile-monitor-bridge.ts +0 -1
  172. package/scripts/smoke-control-plane-failure-domain.ts +94 -0
  173. package/scripts/smoke-mcp-http-runtime.ts +5 -0
  174. package/scripts/smoke-runtime-recovery.ts +12 -43
  175. package/scripts/smoke-schedule-engine.ts +23 -15
  176. package/scripts/smoke-unattended-transport-recovery.ts +223 -0
  177. package/scripts/smoke-user-request-continuation-restart.ts +232 -0
  178. package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
  179. package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
  180. package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
  181. package/scripts/smoke-workflow-supervisor.ts +103 -0
  182. package/scripts/verify-forge-runtime.sh +2 -1
  183. package/src/cli/agent-jobs/executor-health.ts +11 -34
  184. package/src/cli/agent-jobs/integration.ts +18 -49
  185. package/src/cli/agent-jobs/job-manager.ts +7 -3
  186. package/src/cli/commands/brain-assistant.ts +27 -13
  187. package/src/cli/commands/brain.ts +13 -15
  188. package/src/cli/commands/chatgpt.ts +1 -163
  189. package/src/cli/commands/recovery.ts +135 -121
  190. package/src/cli/commands/runtime.ts +3 -3
  191. package/src/cli/commands/setup-profile.ts +8 -0
  192. package/src/cli/controller/check-runner.ts +13 -2
  193. package/src/cli/controller/completion-backlog.ts +3 -3
  194. package/src/cli/controller/completion-orchestrator.ts +4 -43
  195. package/src/cli/controller/completion-target.ts +3 -72
  196. package/src/cli/controller/context/source-materializer.ts +81 -100
  197. package/src/cli/controller/context-pack.ts +10 -8
  198. package/src/cli/controller/execution-completion.ts +4 -7
  199. package/src/cli/controller/execution-policy.ts +36 -153
  200. package/src/cli/controller/governance.ts +2 -3
  201. package/src/cli/controller/issue-store.ts +4 -99
  202. package/src/cli/controller/launch-agents.ts +59 -18
  203. package/src/cli/controller/operational-plan.ts +24 -51
  204. package/src/cli/controller/runtime-config.ts +5 -1
  205. package/src/cli/controller/stuck-state-migration.ts +2 -3
  206. package/src/cli/controller/systemd-user.ts +39 -1
  207. package/src/cli/editing/edit-session.ts +1 -7
  208. package/src/cli/local-bridge/console-automations.ts +18 -13
  209. package/src/cli/local-bridge/console-view-models.ts +3 -12
  210. package/src/cli/local-bridge/facade-api.ts +64 -191
  211. package/src/cli/local-bridge/server.ts +24 -147
  212. package/src/cli/local-bridge/types.ts +0 -1
  213. package/src/cli/mcp/oauth-client-retirement.ts +74 -0
  214. package/src/cli/repositories/command-executor.ts +29 -23
  215. package/src/cli/repositories/command-scope.ts +35 -16
  216. package/src/cli/repositories/controller-home.ts +48 -0
  217. package/src/cli/repositories/ephemeral-workspace.ts +20 -30
  218. package/src/cli/repositories/git-command-executor.ts +2 -2
  219. package/src/cli/repositories/registry.ts +77 -11
  220. package/src/cli/repositories/structured-git.ts +62 -2
  221. package/src/cli/repository/session-cache.ts +92 -6
  222. package/src/runtime/context/assistant-context.ts +20 -7
  223. package/src/runtime/context/assistant-work-context.ts +89 -27
  224. package/src/runtime/context/automatic-learning.ts +565 -0
  225. package/src/runtime/context/cognitive-attention.ts +173 -0
  226. package/src/runtime/context/cognitive-skill-canary.ts +158 -0
  227. package/src/runtime/context/context-closure.ts +46 -9
  228. package/src/runtime/context/context-pack-process.ts +103 -0
  229. package/src/runtime/context/context-plane.ts +1 -9
  230. package/src/runtime/context/direct-controller-learning.ts +298 -0
  231. package/src/runtime/context/semantic-navigation-contract.ts +9 -0
  232. package/src/runtime/context/semantic-navigation.ts +6 -34
  233. package/src/runtime/context/typescript-navigation-process.ts +145 -0
  234. package/src/runtime/context/typescript-navigation.ts +89 -3
  235. package/src/runtime/control-plane/cleanup-artifact-retention.ts +212 -9
  236. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +9 -47
  237. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +34 -4
  238. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +29 -20
  239. package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
  240. package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
  241. package/src/runtime/control-plane/execution/retained-work-resume.ts +206 -199
  242. package/src/runtime/control-plane/execution/session-store.ts +31 -1
  243. package/src/runtime/control-plane/execution/validation.ts +6 -8
  244. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +13 -42
  245. package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
  246. package/src/runtime/control-plane/execution/work-completion-authority.ts +30 -85
  247. package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
  248. package/src/runtime/control-plane/execution/work-evidence-policy.ts +12 -50
  249. package/src/runtime/control-plane/execution/work-execution-support.ts +50 -149
  250. package/src/runtime/control-plane/execution/work-handle-authority.ts +188 -109
  251. package/src/runtime/control-plane/execution/work-handle-store.ts +19 -43
  252. package/src/runtime/control-plane/execution/work-head-settlement.ts +53 -20
  253. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +52 -29
  254. package/src/runtime/control-plane/execution/work-operation-service.ts +43 -27
  255. package/src/runtime/control-plane/execution/work-preparation-service.ts +126 -266
  256. package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
  257. package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
  258. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +173 -186
  259. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -84
  260. package/src/runtime/control-plane/execution/work-verification-service.ts +406 -50
  261. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
  262. package/src/runtime/control-plane/facade/capability-registry.ts +362 -37
  263. package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
  264. package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
  265. package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
  266. package/src/runtime/control-plane/facade/goal-workloop-access.ts +1 -20
  267. package/src/runtime/control-plane/facade/goal-workloop.ts +281 -1753
  268. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +12 -25
  269. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +256 -52
  270. package/src/runtime/control-plane/facade/index.ts +0 -1
  271. package/src/runtime/control-plane/facade/operation-digest.ts +12 -15
  272. package/src/runtime/control-plane/facade/plan-contract-store.ts +451 -991
  273. package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
  274. package/src/runtime/control-plane/facade/repository-work-admission.ts +12 -22
  275. package/src/runtime/control-plane/facade/requirement-authority.ts +102 -199
  276. package/src/runtime/control-plane/facade/requirement-board.ts +3 -3
  277. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +51 -14
  278. package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
  279. package/src/runtime/control-plane/facade/suggested-actions.ts +15 -18
  280. package/src/runtime/control-plane/facade/types.ts +49 -108
  281. package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
  282. package/src/runtime/control-plane/generated-cache-retention.ts +97 -1
  283. package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
  284. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +1 -1
  285. package/src/runtime/control-plane/global-scheduler/maintenance.ts +239 -112
  286. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +2 -1
  287. package/src/runtime/control-plane/global-scheduler/scheduler.ts +66 -40
  288. package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
  289. package/src/runtime/control-plane/governance/access-policy.ts +19 -2
  290. package/src/runtime/control-plane/governance/authorization.ts +20 -3
  291. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +63 -300
  292. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +28 -300
  293. package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
  294. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +5 -2
  295. package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -240
  296. package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
  297. package/src/runtime/control-plane/persistence/experience-store.ts +162 -29
  298. package/src/runtime/control-plane/persistence/requirement-store.ts +216 -74
  299. package/src/runtime/control-plane/persistence/sqlite-store.ts +113 -6
  300. package/src/runtime/control-plane/release-retention.ts +135 -47
  301. package/src/runtime/control-plane/repository-namespace-retention.ts +2 -2
  302. package/src/runtime/control-plane/runtime-cleanup.ts +290 -21
  303. package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
  304. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +19 -5
  305. package/src/runtime/control-plane/workspace/workspace-store.ts +169 -0
  306. package/src/runtime/diagnostics/incident-repair.ts +313 -146
  307. package/src/runtime/diagnostics/mcp-timing.ts +114 -0
  308. package/src/runtime/evidence/artifact-store.ts +158 -40
  309. package/src/runtime/evidence/evidence-store.ts +112 -8
  310. package/src/runtime/evidence/output-handle-index.ts +225 -0
  311. package/src/runtime/evidence/result-store.ts +158 -44
  312. package/src/runtime/execution/jobs/store.ts +3 -4
  313. package/src/runtime/execution/managed-workspace.ts +57 -0
  314. package/src/runtime/execution/process-runtime/check-facade.ts +1 -1
  315. package/src/runtime/execution/process-runtime/check-result-retention.ts +2 -2
  316. package/src/runtime/execution/process-runtime/check-result.ts +105 -1
  317. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +18 -8
  318. package/src/runtime/execution/process-runtime/command-facade.ts +79 -27
  319. package/src/runtime/execution/process-runtime/gc.ts +15 -2
  320. package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
  321. package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
  322. package/src/runtime/execution/process-runtime/index.ts +3 -0
  323. package/src/runtime/execution/process-runtime/lightweight-managed.ts +251 -72
  324. package/src/runtime/execution/process-runtime/persisted-check.ts +3 -3
  325. package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
  326. package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
  327. package/src/runtime/execution/process-runtime/resource-claims.ts +65 -61
  328. package/src/runtime/execution/process-runtime/runtime.ts +35 -2
  329. package/src/runtime/execution/process-runtime/store.ts +30 -2
  330. package/src/runtime/execution/process-runtime/types.ts +6 -1
  331. package/src/runtime/execution/workers/executor.ts +2 -6
  332. package/src/runtime/platform/service-inventory.ts +130 -0
  333. package/src/runtime/platform/service-manager.ts +8 -0
  334. package/src/runtime/plugins/action-application.ts +85 -28
  335. package/src/runtime/plugins/app-store-connect-adapter.ts +38 -14
  336. package/src/runtime/plugins/browser-adapter.ts +757 -34
  337. package/src/runtime/plugins/browser-automation-service.ts +23 -3
  338. package/src/runtime/plugins/browser-macos-bridge.ts +321 -29
  339. package/src/runtime/plugins/browser-manifest-surface.ts +45 -11
  340. package/src/runtime/plugins/browser-native-session-service.ts +63 -13
  341. package/src/runtime/plugins/browser-registration.ts +16 -0
  342. package/src/runtime/plugins/browser-runtime.ts +14 -1
  343. package/src/runtime/plugins/browser-session-authority.ts +2 -2
  344. package/src/runtime/plugins/browser-session-store.ts +46 -76
  345. package/src/runtime/plugins/capability-authorization-grants.ts +258 -62
  346. package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
  347. package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
  348. package/src/runtime/plugins/computer-registration.ts +222 -11
  349. package/src/runtime/plugins/desktop-operator-external-policy.ts +36 -4
  350. package/src/runtime/plugins/desktop-operator-registration.ts +12 -4
  351. package/src/runtime/plugins/external-adapter.ts +42 -6
  352. package/src/runtime/plugins/external-registration.ts +21 -1
  353. package/src/runtime/plugins/gmail-adapter.ts +23 -2
  354. package/src/runtime/plugins/google-calendar-adapter.ts +2 -2
  355. package/src/runtime/plugins/google-shared.ts +14 -7
  356. package/src/runtime/plugins/google-tasks-adapter.ts +2 -2
  357. package/src/runtime/plugins/ios-adapter.ts +1 -1
  358. package/src/runtime/plugins/lightweight-action.ts +208 -2
  359. package/src/runtime/plugins/local-system-adapter.ts +12 -2
  360. package/src/runtime/plugins/managed-process-adapter.ts +12 -5
  361. package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
  362. package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
  363. package/src/runtime/plugins/store.ts +602 -428
  364. package/src/runtime/plugins/types.ts +20 -0
  365. package/src/runtime/plugins/xiaohongshu-publish.ts +16 -0
  366. package/src/runtime/projections/invalidation.ts +7 -3
  367. package/src/runtime/projections/materialized-view.ts +80 -36
  368. package/src/runtime/recovery/actions.ts +3 -3
  369. package/src/runtime/recovery/capability-action-application.ts +46 -45
  370. package/src/runtime/recovery/lifecycle-inventory.ts +29 -29
  371. package/src/runtime/recovery/maintenance-executor.ts +374 -123
  372. package/src/runtime/recovery/store.ts +29 -11
  373. package/src/runtime/release/release-coordinator.ts +296 -0
  374. package/src/runtime/release/release-session.ts +716 -0
  375. package/src/runtime/release/release-state-migration.ts +25 -0
  376. package/src/runtime/resources/leases/store.ts +56 -25
  377. package/src/runtime/root/computer-composition.ts +191 -6
  378. package/src/runtime/root/controller-progression-composition.ts +192 -0
  379. package/src/runtime/root/controller-round-composition.ts +20 -1
  380. package/src/runtime/root/deployment-topology.ts +106 -0
  381. package/src/runtime/root/entry.ts +4 -3
  382. package/src/runtime/root/known-good-recovery.ts +184 -0
  383. package/src/runtime/root/local-bridge.ts +0 -1
  384. package/src/runtime/root/mcp-transport.ts +111 -18
  385. package/src/runtime/root/ownership.ts +261 -9
  386. package/src/runtime/root/package-connector-service.ts +105 -19
  387. package/src/runtime/root/package-runtime-release.ts +117 -18
  388. package/src/runtime/root/package-runtime-service.ts +17 -11
  389. package/src/runtime/root/release-execution-canary.ts +44 -12
  390. package/src/runtime/root/release-loader.ts +90 -8
  391. package/src/runtime/root/release-manifest.ts +80 -6
  392. package/src/runtime/root/release-materialize.ts +372 -28
  393. package/src/runtime/root/release-store.ts +395 -36
  394. package/src/runtime/root/runtime-lane.ts +191 -0
  395. package/src/runtime/root/runtime.ts +159 -27
  396. package/src/runtime/root/scheduled-controller-composition.ts +28 -3
  397. package/src/runtime/root/service-runner.ts +2 -2
  398. package/src/runtime/root/service.ts +36 -33
  399. package/src/runtime/root/status.ts +48 -0
  400. package/src/runtime/root/types.ts +32 -3
  401. package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
  402. package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
  403. package/src/runtime/root/write-fence.ts +50 -2
  404. package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
  405. package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
  406. package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
  407. package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
  408. package/src/runtime/shared/process-environment.ts +20 -9
  409. package/src/runtime/shared/response-budget.ts +1 -3
  410. package/src/runtime/standalone-recovery/controller-home-migration.ts +16 -24
  411. package/src/runtime/standalone-recovery/core.ts +3702 -342
  412. package/src/runtime/standalone-recovery/entry.ts +469 -39
  413. package/src/runtime/standalone-recovery/installer.ts +262 -148
  414. package/src/runtime/standalone-recovery/mcp-server.ts +29 -193
  415. package/src/runtime/standalone-recovery/operation-lock.ts +1 -1
  416. package/src/runtime/standalone-recovery/performance.ts +54 -16
  417. package/src/runtime/standalone-recovery/release.ts +53 -5
  418. package/src/runtime/standalone-recovery/service-labels.ts +5 -2
  419. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
  420. package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
  421. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +10 -3
  422. package/src/runtime/workflow/schedules/engine.ts +138 -172
  423. package/src/runtime/workflow/schedules/work-continuation.ts +44 -5
  424. package/src/runtime/workflows/runtime.ts +15 -19
  425. package/src/testing/test-governance.ts +5 -1
  426. package/supervisor/chatgpt-conversation.ts +23 -0
  427. package/supervisor/client.ts +201 -0
  428. package/supervisor/control-plane.ts +868 -0
  429. package/supervisor/entry.ts +15 -0
  430. package/supervisor/forge-validators.ts +80 -0
  431. package/supervisor/index.ts +5 -0
  432. package/supervisor/native-browser-adapter.ts +786 -0
  433. package/supervisor/native-messaging/host.ts +39 -0
  434. package/supervisor/native-messaging/manifest.ts +26 -0
  435. package/supervisor/native-messaging/protocol.ts +27 -0
  436. package/supervisor/paths.ts +18 -0
  437. package/supervisor/protocol.ts +202 -0
  438. package/supervisor/server.ts +404 -0
  439. package/supervisor/store.ts +1451 -0
  440. package/supervisor/tsconfig.json +8 -0
  441. package/supervisor/types.ts +255 -0
  442. package/adapters/chatgpt/controller-host.ts +0 -119
  443. package/adapters/mcp/controller-round-compatibility.ts +0 -184
  444. package/adapters/mcp/frozen-client-semantic-compatibility.ts +0 -292
  445. package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +0 -502
  446. package/packages/kernel/progression/api/index.ts +0 -2
  447. package/packages/kernel/progression/application/projector.ts +0 -211
  448. package/packages/kernel/progression/domain/types.ts +0 -116
  449. package/packages/kernel/progression/index.ts +0 -1
  450. package/packages/kernel/scheduler/application/continuation-service.ts +0 -212
  451. package/packages/kernel/scheduler/domain/continuation.ts +0 -38
  452. package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +0 -38
  453. package/packages/kernel/work/domain/admission-policy.ts +0 -34
  454. package/scripts/benchmark-background-check-overlap.ts +0 -228
  455. package/scripts/benchmark-computer-hotpath.ts +0 -255
  456. package/scripts/benchmark-controller-hotpath.ts +0 -212
  457. package/scripts/benchmark-controller-round2.ts +0 -191
  458. package/scripts/benchmark-ios-agent-device.ts +0 -511
  459. package/scripts/benchmark-mcp-host.sh +0 -117
  460. package/scripts/benchmark-mcp-transport.ts +0 -169
  461. package/scripts/benchmark-operational-memory-activation.ts +0 -235
  462. package/scripts/benchmark-operational-shadow.ts +0 -28
  463. package/scripts/benchmark-quality-harness.ts +0 -146
  464. package/scripts/benchmark-route-session-concurrency.ts +0 -915
  465. package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
  466. package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
  467. package/scripts/benchmark-thin-harness.ts +0 -251
  468. package/scripts/benchmark-typescript-navigation.ts +0 -186
  469. package/scripts/benchmark-work-efficiency.ts +0 -334
  470. package/scripts/build-controller-ui.ts +0 -28
  471. package/scripts/check-npm-release.sh +0 -32
  472. package/scripts/check-public-release-surface.sh +0 -19
  473. package/scripts/check-release-published.sh +0 -57
  474. package/scripts/check-release-readiness.sh +0 -37
  475. package/scripts/check-release-version.mjs +0 -74
  476. package/scripts/check-tarball-install-smoke.sh +0 -150
  477. package/scripts/package-source-archive.sh +0 -147
  478. package/scripts/public-release-files.txt +0 -39
  479. package/scripts/public-release.ts +0 -174
  480. package/scripts/publish-release-tarball.sh +0 -27
  481. package/scripts/route-nl-vs-ts-eval.ts +0 -679
  482. package/scripts/stage-runtime-release.ts +0 -44
  483. package/src/cli/controller/progress.ts +0 -656
  484. package/src/cli/controller/work-mode.ts +0 -181
  485. package/src/cli/local-bridge/console-assets.ts +0 -17
  486. package/src/cli/local-bridge/dashboard.ts +0 -17
  487. package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
  488. package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
  489. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
  490. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
  491. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
  492. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
  493. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
  494. package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
  495. package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
  496. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
  497. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
  498. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
  499. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
  500. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
  501. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
  502. package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
  503. package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
  504. package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
  505. package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
  506. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
  507. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
  508. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
  509. package/src/cli/local-bridge/frontend/src/styles.css +0 -28
  510. package/src/cli/local-bridge/frontend/src/types.ts +0 -15
  511. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
  512. package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
  513. package/src/cli/local-bridge/ui-dist/app.css +0 -1
  514. package/src/cli/local-bridge/ui-dist/app.js +0 -11228
  515. package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +0 -169
  516. package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +0 -85
  517. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -655
  518. package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -2850
  519. package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
  520. package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -468
  521. package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -3
  522. package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -142
  523. package/src/runtime/control-plane/facade/work-implementation-review.ts +0 -2
  524. package/src/runtime/control-plane/facade/work-state-machine.ts +0 -2
  525. package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
  526. package/src/runtime/control-plane/routing/index.ts +0 -1
  527. package/src/runtime/control-plane/routing/route-policy.ts +0 -401
  528. package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
@@ -0,0 +1,248 @@
1
+ import { createHash } from 'crypto';
2
+ import type { RepositoryRecord } from '../../../cli/repositories/types';
3
+ import {
4
+ controllerRoundBlockerClass,
5
+ controllerSessionBlocksRecovery,
6
+ getControllerRoundRelay,
7
+ listCurrentControllerRoundRelays,
8
+ getControllerSession,
9
+ getRetainedControllerSession,
10
+ } from '../../../../packages/kernel/controller/api/index';
11
+ import {
12
+ currentTaskSemanticProjectionForWork,
13
+ getWorkContract,
14
+ readActiveWorkCandidates,
15
+ semanticWorkState,
16
+ workSemanticView,
17
+ } from '../../../../packages/kernel/work/api/index';
18
+ import { workHasActiveExecution } from '../../execution/work-activity';
19
+ import { readRequirement } from '../persistence/requirement-store';
20
+ import { assertAutomatedOperationAllowed } from '../governance/external-effects';
21
+ import {
22
+ controllerHostForScheduledBinding,
23
+ ensureScheduledControllerBindingForWork,
24
+ } from '../../root/scheduled-controller-composition';
25
+ import { ensureWorkflowSupervisorEnrollmentForWork } from '../../root/workflow-supervisor-composition';
26
+ import { reconcileControllerProgression } from '../../root/controller-progression-composition';
27
+ import { prepareControllerRoundOccurrence, resumeControllerRoundOccurrence } from '../../../../packages/kernel/controller/api/index';
28
+ import { ensureControllerDispositionContinuation } from '../../workflow/schedules/work-continuation';
29
+ import { deriveForgeActionableFailureCode, maybeRegisterForgeActionableFailureRepair } from '../../diagnostics/incident-repair';
30
+
31
+ const DEFAULT_MAX_CONTINUATIONS = 2;
32
+
33
+ export interface SchedulerAutonomousContinuationResult {
34
+ scanned: number;
35
+ eligible: number;
36
+ dispatched: number;
37
+ supervisorEnrolled: number;
38
+ failed: number;
39
+ skippedByReason: Record<string, number>;
40
+ }
41
+
42
+ export interface SchedulerAutonomousContinuationDependencies {
43
+ hasActiveExecution?: typeof workHasActiveExecution;
44
+ authorizeWake?: typeof assertAutomatedOperationAllowed;
45
+ ensureSupervisorEnrollment?: typeof ensureWorkflowSupervisorEnrollmentForWork;
46
+ hostForBinding?: typeof controllerHostForScheduledBinding;
47
+ ensureBinding?: typeof ensureScheduledControllerBindingForWork;
48
+ prepareOccurrence?: typeof prepareControllerRoundOccurrence;
49
+ resumeOccurrence?: typeof resumeControllerRoundOccurrence;
50
+ }
51
+
52
+ function skip(counts: Record<string, number>, reason: string): void {
53
+ counts[reason] = (counts[reason] ?? 0) + 1;
54
+ }
55
+
56
+ function planlessOccurrenceId(workId: string, semanticRevision: number): string {
57
+ const digest = createHash('sha256').update(workId + '\0semantic:' + semanticRevision).digest('hex').slice(0, 32);
58
+ return 'work-liveness:v2:' + digest;
59
+ }
60
+
61
+ function livenessMayReconcileExistingRound(record: ReturnType<typeof getControllerRoundRelay>): boolean {
62
+ if (!record) return true;
63
+ if (record.status === 'pending_release' || record.status === 'claimed') return true;
64
+ if (record.status === 'waiting') return true;
65
+ if (record.status === 'dispatching') return !record.providerDispatchStartedAt;
66
+ if (record.status === 'dispatched') return true;
67
+ return record.status === 'blocked' && controllerRoundBlockerClass(record) === 'provider_dispatch_outcome_unknown';
68
+ }
69
+
70
+ /**
71
+ * Single Scheduler liveness scanner. Scheduler selects only which open Work
72
+ * needs another mechanical opportunity; reconcileControllerProgression owns the
73
+ * stateless composition of existing authorities.
74
+ */
75
+ export async function runSchedulerAutonomousContinuationReconciliation(input: {
76
+ controllerHome: string;
77
+ nowMs: number;
78
+ repositories: readonly Pick<RepositoryRecord, 'repoId' | 'canonicalRoot' | 'localRoot'>[];
79
+ maxContinuations?: number;
80
+ dependencies?: SchedulerAutonomousContinuationDependencies;
81
+ }): Promise<SchedulerAutonomousContinuationResult> {
82
+ const maxContinuations = Math.max(1, Math.min(Math.trunc(input.maxContinuations ?? DEFAULT_MAX_CONTINUATIONS), 8));
83
+ const hasActiveExecution = input.dependencies?.hasActiveExecution ?? workHasActiveExecution;
84
+ const authorizeWake = input.dependencies?.authorizeWake ?? assertAutomatedOperationAllowed;
85
+
86
+ let scanned = 0;
87
+ let eligible = 0;
88
+ let dispatched = 0;
89
+ let supervisorEnrolled = 0;
90
+ let failed = 0;
91
+ let materialized = 0;
92
+ const skippedByReason: Record<string, number> = {};
93
+
94
+ for (const repository of input.repositories) {
95
+ if (materialized >= maxContinuations) break;
96
+ const store = { controllerHome: input.controllerHome, repoId: repository.repoId };
97
+ let works: ReturnType<typeof readActiveWorkCandidates>['contracts'];
98
+ try {
99
+ const active = readActiveWorkCandidates({ ...store, limit: 100 });
100
+ const candidates = new Map(active.contracts.map((work) => [work.workId, work]));
101
+ for (const invalid of active.invalid) {
102
+ skip(skippedByReason, 'invalid_work_contract');
103
+ console.error('[forge liveness] skipped invalid Work authority ' + repository.repoId + '/' + invalid.workId + ': ' + invalid.error);
104
+ }
105
+ // Semantic Work authority is Forge-scoped; the repository list is only
106
+ // an execution projection. Existing relay references must use the same
107
+ // canonical lookup as launcher admission, never infer deletion from a list.
108
+ for (const relay of listCurrentControllerRoundRelays(store, 100)) {
109
+ try {
110
+ const work = getWorkContract(store, relay.originWorkId);
111
+ if (work) candidates.set(work.workId, work);
112
+ } catch (error) {
113
+ skip(skippedByReason, 'invalid_relay_work_contract');
114
+ console.error('[forge liveness] skipped invalid relay Work authority ' + repository.repoId + '/' + relay.originWorkId + ':', error);
115
+ }
116
+ }
117
+ works = [...candidates.values()];
118
+ } catch (error) {
119
+ failed += 1;
120
+ console.error('[forge liveness] failed to read current Work authority for ' + repository.repoId + ':', error);
121
+ continue;
122
+ }
123
+
124
+ for (const work of works) {
125
+ if (materialized >= maxContinuations) break;
126
+ scanned += 1;
127
+ if (semanticWorkState(work) !== 'open') { skip(skippedByReason, 'semantic_work_terminal'); continue; }
128
+ if (hasActiveExecution(input.controllerHome, repository.repoId, work.workId)) { skip(skippedByReason, 'active_execution'); continue; }
129
+
130
+ const liveOwner = getControllerSession(store, work.workId);
131
+ if (liveOwner && controllerSessionBlocksRecovery(store, work.workId, { nowMs: input.nowMs })) {
132
+ skip(skippedByReason, 'active_controller_session');
133
+ continue;
134
+ }
135
+
136
+ const currentTask = currentTaskSemanticProjectionForWork(work);
137
+ const relayScopeId = currentTask.requirementId ? 'requirement:' + currentTask.requirementId : undefined;
138
+ const continuationHint = 'Resume exact Work ' + currentTask.workId + '; scheduler observed no active execution or live Controller owner and no unresolved semantic blocker.';
139
+
140
+ const requirementRecord = work.requirementId
141
+ ? readRequirement({ controllerHome: input.controllerHome }, work.requirementId)
142
+ : undefined;
143
+ if (work.requirementId && !requirementRecord) { skip(skippedByReason, 'requirement_missing'); continue; }
144
+ const requirementState = requirementRecord?.value.state;
145
+ if (requirementState === 'waiting_for_user') { skip(skippedByReason, 'requirement_waiting_for_user'); continue; }
146
+ if (requirementState === 'done' || requirementState === 'cancelled') { skip(skippedByReason, 'requirement:' + requirementState); continue; }
147
+
148
+ const existingRound = getControllerRoundRelay(store, work.workId);
149
+ if (!livenessMayReconcileExistingRound(existingRound)) {
150
+ skip(skippedByReason, `controller_round_${existingRound!.status}`);
151
+ continue;
152
+ }
153
+ const occurrenceId = existingRound?.occurrenceId
154
+ ?? planlessOccurrenceId(work.workId, workSemanticView(work).revision);
155
+
156
+ const retainedSession = getRetainedControllerSession(store, work.workId);
157
+ if (!retainedSession) { skip(skippedByReason, 'retained_controller_session_missing'); continue; }
158
+ if (retainedSession.controllerType === 'human') { skip(skippedByReason, 'human_controller'); continue; }
159
+
160
+ eligible += 1;
161
+ try {
162
+ authorizeWake('external_controller_wake', {
163
+ work_id: work.workId,
164
+ controller_type: retainedSession.controllerType,
165
+ relay_scope_id: relayScopeId,
166
+ recovery_reason: 'ownerless_work_ready_to_continue',
167
+ });
168
+
169
+ const progression = await reconcileControllerProgression(
170
+ {
171
+ controllerHome: input.controllerHome,
172
+ repoId: repository.repoId,
173
+ repoRoot: repository.canonicalRoot ?? repository.localRoot,
174
+ },
175
+ {
176
+ occurrenceId,
177
+ workId: work.workId,
178
+ relayScopeId,
179
+ continuationHint,
180
+ scheduleName: 'autonomous-work-liveness',
181
+ },
182
+ {
183
+ ...(input.dependencies?.ensureSupervisorEnrollment
184
+ ? { ensureSupervisorEnrollment: input.dependencies.ensureSupervisorEnrollment }
185
+ : {}),
186
+ ...(input.dependencies?.ensureBinding ? { ensureBinding: input.dependencies.ensureBinding } : {}),
187
+ ...(input.dependencies?.prepareOccurrence ? { prepareOccurrence: input.dependencies.prepareOccurrence } : {}),
188
+ ...(input.dependencies?.resumeOccurrence ? { resumeOccurrence: input.dependencies.resumeOccurrence } : {}),
189
+ ...(input.dependencies?.hostForBinding ? { hostForBinding: input.dependencies.hostForBinding } : {}),
190
+ },
191
+ );
192
+
193
+ if (progression.status === 'chatgpt_enrolled') {
194
+ supervisorEnrolled += 1;
195
+ materialized += 1;
196
+ continue;
197
+ }
198
+ if (progression.status === 'provider_dispatched') {
199
+ // The occurrence identity is the dedupe fence. A reused dispatched
200
+ // relay is already complete; reporting it as a fresh dispatch makes
201
+ // repeated scheduler scans look like new progress even though no
202
+ // provider call occurred.
203
+ if (progression.reused) {
204
+ skip(skippedByReason, 'controller_progression:already_dispatched');
205
+ continue;
206
+ }
207
+ dispatched += 1;
208
+ materialized += 1;
209
+ continue;
210
+ }
211
+ if (progression.status === 'wait_for_user') {
212
+ const settledRound = getControllerRoundRelay(store, work.workId);
213
+ if (settledRound?.status === 'waiting_for_user' && settledRound.handoffId) {
214
+ ensureControllerDispositionContinuation(input.controllerHome, repository.repoId, settledRound);
215
+ }
216
+ }
217
+ if (progression.status === 'chatgpt_not_enrolled') {
218
+ skip(skippedByReason, 'workflow_supervisor:' + progression.supervisorEnrollment.status);
219
+ } else {
220
+ skip(skippedByReason, 'controller_progression:' + progression.status);
221
+ }
222
+ } catch (error) {
223
+ failed += 1;
224
+ const reason = error instanceof Error ? error.message : String(error);
225
+ if (retainedSession.controllerType !== 'chatgpt') {
226
+ try {
227
+ maybeRegisterForgeActionableFailureRepair({
228
+ controllerHome: input.controllerHome,
229
+ now: () => input.nowMs,
230
+ observation: {
231
+ observationId: `progression:liveness:${repository.repoId}:${work.workId}:${input.nowMs}`,
232
+ source: 'progression',
233
+ code: deriveForgeActionableFailureCode('SCHEDULER_AUTONOMOUS_CONTINUATION_FAILED', reason),
234
+ message: reason,
235
+ at: new Date(input.nowMs).toISOString(),
236
+ repoId: repository.repoId,
237
+ workId: work.workId,
238
+ },
239
+ });
240
+ } catch {}
241
+ }
242
+ console.error('[forge liveness] autonomous continuation failed for ' + repository.repoId + '/' + work.workId + ':', reason);
243
+ }
244
+ }
245
+ }
246
+
247
+ return { scanned, eligible, dispatched, supervisorEnrolled, failed, skippedByReason };
248
+ }
@@ -5,7 +5,7 @@ import {
5
5
  import { tickSchedules } from '../../workflow/schedules/engine';
6
6
  import type { ExecutionJob } from '../../execution/jobs/types';
7
7
 
8
- const SCHEDULE_TICK_INTERVAL_MS = 30_000;
8
+ export const SCHEDULE_TICK_INTERVAL_MS = 30_000;
9
9
 
10
10
  export function schedulerDurableAdmissionRequiresPolicy(input: {
11
11
  activeJobs: readonly ExecutionJob[];
@@ -1,6 +1,6 @@
1
1
  import type { RepositoryRecord } from '../../../cli/repositories/types';
2
2
  import { cleanupRuntimeComputerInteractionTargets } from '../../root/computer-target-composition';
3
- import { cleanupControllerHomeBrowserArtifacts, cleanupGeneratedRepositoryCaches, cleanupIdleXCTestDevices } from '../generated-cache-retention';
3
+ import { cleanupControllerHomeBrowserArtifacts, cleanupGeneratedRepositoryCheckoutCaches, cleanupIdleXCTestDevices } from '../generated-cache-retention';
4
4
  import {
5
5
  cleanupRuntimeBrowserSessionTombstones,
6
6
  closeRuntimeBrowserSessionLegacyImportCutover,
@@ -8,18 +8,100 @@ import {
8
8
  import type { cleanupControllerRuntimeState } from '../runtime-cleanup';
9
9
  import type { reconcileTerminalWorkCleanups } from '../execution/work-terminal-cleanup';
10
10
  import type { gcTerminalProcesses } from '../../execution/process-runtime/gc';
11
+ import { assertRuntimeMayWrite } from '../../root/write-fence';
11
12
  import { cleanupPersistedCheckResults } from '../../execution/process-runtime/check-result-retention';
12
13
  import { cleanupRetiredExecutionJobs } from '../../execution/jobs/store';
13
14
  import type { reconcilePendingWorkValidations } from '../execution/work-validation-reconciler';
14
15
  import type { reconcilePendingEditValidations } from '../execution/edit-validation-coordinator';
15
16
  import {
16
- claimStalledControllerRoundRelays,
17
- finishControllerRoundRelayDispatch,
17
+ controllerRoundBlockerClass,
18
+ listCurrentControllerRoundRelays,
18
19
  } from '../../../../packages/kernel/controller/api/index';
19
- import { assertAutomatedOperationAllowed } from '../governance/external-effects';
20
- import { runWorkChatgptContinuation } from '../launcher/chatgpt-work-continuation';
20
+ import { settleWorkChatgptAutomationTab } from '../launcher/chatgpt-work-continuation';
21
21
  import { getChatgptWorkConversationBinding } from '../../../../adapters/chatgpt/work-conversation-binding-store';
22
- import { renderChatgptControllerRoundPrompt } from '../../root/controller-round-composition';
22
+ import {
23
+ chatgptControllerRoundSettlementAttemptIdentity,
24
+ getChatgptControllerRoundSettlement,
25
+ } from '../../../../adapters/chatgpt/controller-round-settlement-store';
26
+ import { recordChatgptControllerRoundTabSettlement } from '../../root/controller-round-composition';
27
+ import { deriveForgeActionableFailureCode, maybeRegisterFailedReleaseSessionRepairs, maybeRegisterForgeActionableFailureRepair } from '../../diagnostics/incident-repair';
28
+
29
+ const PERIODIC_RETENTION_INTERVAL_MS = 5 * 60_000;
30
+ const PERIODIC_DEEP_RETENTION_INTERVAL_MS = 15 * 60_000;
31
+ // A provider-ambiguous ControllerRound remains durable and claimable, but its
32
+ // Forge-owned Chrome tab is not durable authority. Give an in-flight ChatGPT
33
+ // turn a bounded claim window, then release the ephemeral resource without
34
+ // replaying or clearing the semantic outcome-unknown fence.
35
+ const CHATGPT_OUTCOME_UNKNOWN_TAB_SETTLEMENT_GRACE_MS = 5 * 60_000;
36
+ // Retention is a fallback GC path, not the primary ControllerRound settlement path.
37
+ // Keep its external-effect volume deliberately small so one stale repository set
38
+ // cannot create a provider burst or hold a maintenance worker for hours.
39
+ const CHATGPT_TAB_SETTLEMENT_EFFECT_BUDGET_PER_PASS = 4;
40
+
41
+ function runtimeMaintenanceAuthorityCurrent(controllerHome: string): boolean {
42
+ const fence = assertRuntimeMayWrite('cleanup', controllerHome);
43
+ if (fence.allowed) return true;
44
+ console.error(`[forge cleanup] stale periodic cleanup worker fenced: ${fence.reason ?? 'denied'}`);
45
+ return false;
46
+ }
47
+
48
+ function registerSchedulerFailure(input: {
49
+ controllerHome: string;
50
+ source: 'progression' | 'maintenance';
51
+ prefix: string;
52
+ message: string;
53
+ observationId: string;
54
+ repoId?: string;
55
+ workId?: string;
56
+ atMs?: number;
57
+ }): void {
58
+ try {
59
+ maybeRegisterForgeActionableFailureRepair({
60
+ controllerHome: input.controllerHome,
61
+ observation: {
62
+ observationId: input.observationId,
63
+ source: input.source,
64
+ code: deriveForgeActionableFailureCode(input.prefix, input.message),
65
+ message: input.message,
66
+ at: new Date(input.atMs ?? Date.now()).toISOString(),
67
+ repoId: input.repoId,
68
+ workId: input.workId,
69
+ },
70
+ });
71
+ } catch {
72
+ // Repair promotion is evidence-side reconciliation, not the owning maintenance result.
73
+ }
74
+ }
75
+
76
+ export function planSchedulerPeriodicMaintenance(input: {
77
+ nowMs: number;
78
+ cleanupIntervalMs: number;
79
+ repositoryCount: number;
80
+ }): {
81
+ periodicSequence: number;
82
+ deepRetentionSequence: number;
83
+ runRetention: boolean;
84
+ runDeepRetention: boolean;
85
+ processGcRepositoryIndex?: number;
86
+ deepRetentionRepositoryIndex?: number;
87
+ } {
88
+ const cleanupIntervalMs = Math.max(1, input.cleanupIntervalMs);
89
+ const periodicSequence = Math.floor(input.nowMs / cleanupIntervalMs);
90
+ const retentionEvery = Math.max(1, Math.ceil(PERIODIC_RETENTION_INTERVAL_MS / cleanupIntervalMs));
91
+ const deepRetentionEvery = Math.max(retentionEvery, Math.ceil(PERIODIC_DEEP_RETENTION_INTERVAL_MS / cleanupIntervalMs));
92
+ const repositoryCount = Math.max(0, Math.trunc(input.repositoryCount));
93
+ const deepRetentionSequence = Math.floor(periodicSequence / deepRetentionEvery);
94
+ return {
95
+ periodicSequence,
96
+ deepRetentionSequence,
97
+ runRetention: periodicSequence % retentionEvery === 0,
98
+ runDeepRetention: periodicSequence % deepRetentionEvery === 0,
99
+ processGcRepositoryIndex: repositoryCount > 0 ? periodicSequence % repositoryCount : undefined,
100
+ deepRetentionRepositoryIndex: repositoryCount > 0 && periodicSequence % deepRetentionEvery === 0
101
+ ? deepRetentionSequence % repositoryCount
102
+ : undefined,
103
+ };
104
+ }
23
105
 
24
106
  export async function runSchedulerPeriodicCleanup(input: {
25
107
  controllerHome: string;
@@ -30,53 +112,181 @@ export async function runSchedulerPeriodicCleanup(input: {
30
112
  runtimeCleanup: typeof cleanupControllerRuntimeState;
31
113
  terminalWorkCleanup: typeof reconcileTerminalWorkCleanups;
32
114
  processGc: typeof gcTerminalProcesses;
115
+ settleBrowserTab?: typeof settleWorkChatgptAutomationTab;
33
116
  }): Promise<void> {
117
+ const plan = planSchedulerPeriodicMaintenance({
118
+ nowMs: input.nowMs,
119
+ cleanupIntervalMs: input.cleanupIntervalMs,
120
+ repositoryCount: input.repositories.length,
121
+ });
122
+ // The cleanup process inherits the spawning Runtime's exact write claim. Re-check
123
+ // that claim throughout a long pass so a detached child cannot survive a Runtime
124
+ // rollover and continue producing effects under retired authority.
125
+ if (!runtimeMaintenanceAuthorityCurrent(input.controllerHome)) return;
126
+ // Runtime-state phase rotation and terminal Work cleanup are lifecycle
127
+ // reconciliation, not retention. Keep them at the base cleanup cadence.
34
128
  try {
35
129
  input.runtimeCleanup(input.controllerHome, {
36
130
  reason: 'periodic',
37
131
  nowMs: input.nowMs,
38
- periodicSequence: Math.floor(input.nowMs / input.cleanupIntervalMs),
132
+ periodicSequence: plan.periodicSequence,
39
133
  protectedControllerPid: input.controllerPid,
40
134
  });
41
135
  } catch (error) {
136
+ const reason = error instanceof Error ? error.message : String(error);
137
+ registerSchedulerFailure({
138
+ controllerHome: input.controllerHome,
139
+ source: 'maintenance',
140
+ prefix: 'RUNTIME_CLEANUP_FAILED',
141
+ message: reason,
142
+ observationId: `maintenance:runtime-cleanup:${plan.periodicSequence}`,
143
+ atMs: input.nowMs,
144
+ });
42
145
  console.error('[forge cleanup] periodic cleanup failed:', error);
43
146
  }
147
+ if (!runtimeMaintenanceAuthorityCurrent(input.controllerHome)) return;
44
148
  try {
45
149
  await input.terminalWorkCleanup(input.controllerHome, { nowMs: input.nowMs });
46
150
  } catch (error) {
151
+ const reason = error instanceof Error ? error.message : String(error);
152
+ registerSchedulerFailure({
153
+ controllerHome: input.controllerHome,
154
+ source: 'maintenance',
155
+ prefix: 'TERMINAL_WORK_CLEANUP_FAILED',
156
+ message: reason,
157
+ observationId: `maintenance:terminal-work-cleanup:${plan.periodicSequence}`,
158
+ atMs: input.nowMs,
159
+ });
47
160
  console.error('[forge cleanup] terminal Work cleanup failed:', error);
48
161
  }
162
+ if (!runtimeMaintenanceAuthorityCurrent(input.controllerHome)) return;
163
+
49
164
  try {
50
- const xctestCleanup = cleanupIdleXCTestDevices(input.controllerHome);
51
- if (xctestCleanup.error) console.error('[forge cleanup] XCTest device cleanup failed:', xctestCleanup.error);
165
+ maybeRegisterFailedReleaseSessionRepairs({ controllerHome: input.controllerHome, now: () => input.nowMs });
52
166
  } catch (error) {
53
- console.error('[forge cleanup] XCTest device cleanup failed:', error);
167
+ console.error('[forge cleanup] release failure repair reconciliation failed:', error);
54
168
  }
55
- try {
56
- closeRuntimeBrowserSessionLegacyImportCutover(
57
- input.controllerHome,
58
- input.repositories.map((repository) => ({ repoId: repository.repoId, repoRoot: repository.canonicalRoot })),
59
- );
60
- const browserSessions = cleanupRuntimeBrowserSessionTombstones(input.controllerHome, { nowMs: input.nowMs });
61
- if (browserSessions.blockers.length > 0 || browserSessions.budgetExhausted) {
62
- console.error('[forge cleanup] Browser session retention reported bounded blockers');
169
+
170
+ // Browser/computer tombstones are retention state. Their lifecycle truth is
171
+ // written synchronously by their owning authorities, so a five-minute sweep
172
+ // is sufficient and avoids repeating controller-wide scans every minute.
173
+ if (plan.runRetention) {
174
+ if (!runtimeMaintenanceAuthorityCurrent(input.controllerHome)) return;
175
+ try {
176
+ closeRuntimeBrowserSessionLegacyImportCutover(
177
+ input.controllerHome,
178
+ input.repositories.map((repository) => ({ repoId: repository.repoId, repoRoot: repository.canonicalRoot })),
179
+ );
180
+ const browserSessions = cleanupRuntimeBrowserSessionTombstones(input.controllerHome, { nowMs: input.nowMs });
181
+ if (browserSessions.blockers.length > 0 || browserSessions.budgetExhausted) {
182
+ console.error('[forge cleanup] Browser session retention reported bounded blockers');
183
+ }
184
+ } catch (error) {
185
+ console.error('[forge cleanup] Browser session retention failed:', error);
186
+ }
187
+ if (!runtimeMaintenanceAuthorityCurrent(input.controllerHome)) return;
188
+ try {
189
+ const computerTargets = await cleanupRuntimeComputerInteractionTargets(input.controllerHome, { nowMs: input.nowMs });
190
+ if (computerTargets.blockers.length > 0 || computerTargets.overCapacity || computerTargets.budgetExhausted) {
191
+ console.error('[forge cleanup] Computer interaction-target retention reported bounded blockers');
192
+ }
193
+ } catch (error) {
194
+ console.error('[forge cleanup] Computer interaction-target retention failed:', error);
195
+ }
196
+ if (!runtimeMaintenanceAuthorityCurrent(input.controllerHome)) return;
197
+ let chatgptTabSettlementEffects = 0;
198
+ chatgptTabSettlementSweep:
199
+ for (const repository of input.repositories) {
200
+ const store = { controllerHome: input.controllerHome, repoId: repository.repoId };
201
+ for (const relay of listCurrentControllerRoundRelays(store, 100)) {
202
+ const blocker = controllerRoundBlockerClass(relay);
203
+ const outcomeUnknown = blocker === 'provider_dispatch_outcome_unknown';
204
+ const durableInactiveRound = ['waiting', 'waiting_for_user', 'goal_complete', 'failed'].includes(relay.status)
205
+ || (relay.status === 'blocked' && !outcomeUnknown);
206
+ if (!durableInactiveRound && !outcomeUnknown) continue;
207
+ if (outcomeUnknown) {
208
+ const blockedAtMs = Date.parse(relay.updatedAt);
209
+ if (!Number.isFinite(blockedAtMs) || input.nowMs - blockedAtMs < CHATGPT_OUTCOME_UNKNOWN_TAB_SETTLEMENT_GRACE_MS) continue;
210
+ }
211
+ const existingSettlement = getChatgptControllerRoundSettlement(store, {
212
+ workId: relay.originWorkId,
213
+ relayScopeId: relay.relayScopeId,
214
+ });
215
+ if (existingSettlement && ['closed', 'preserved_user_owned', 'session_closed'].includes(existingSettlement.status)) continue;
216
+ const binding = getChatgptWorkConversationBinding(store, relay.originWorkId);
217
+ if (!binding?.latestBrowserSessionId) continue;
218
+ const attemptIdentity = chatgptControllerRoundSettlementAttemptIdentity({
219
+ browserSessionId: binding.latestBrowserSessionId,
220
+ authorizationGrantRefs: binding.authorizationGrantRefs,
221
+ });
222
+ // Failed close_page calls are commonly authorization failures. Retrying
223
+ // the same browser/grant tuple on every retention pass only creates
224
+ // provider traffic and audit churn; a changed resource or grant set is a
225
+ // distinct attempt and remains eligible for recovery.
226
+ if (existingSettlement?.status === 'failed' && existingSettlement.attemptIdentity === attemptIdentity) continue;
227
+ if (chatgptTabSettlementEffects >= CHATGPT_TAB_SETTLEMENT_EFFECT_BUDGET_PER_PASS) {
228
+ console.error('[forge cleanup] ChatGPT tab settlement reached bounded effect budget');
229
+ break chatgptTabSettlementSweep;
230
+ }
231
+ if (!runtimeMaintenanceAuthorityCurrent(input.controllerHome)) return;
232
+ chatgptTabSettlementEffects += 1;
233
+ const settlement = await (input.settleBrowserTab ?? settleWorkChatgptAutomationTab)({
234
+ controllerHome: input.controllerHome,
235
+ workId: relay.originWorkId,
236
+ browserSessionId: binding.latestBrowserSessionId,
237
+ authorizationGrantRefs: binding.authorizationGrantRefs,
238
+ });
239
+ if (!runtimeMaintenanceAuthorityCurrent(input.controllerHome)) return;
240
+ recordChatgptControllerRoundTabSettlement(store, {
241
+ workId: relay.originWorkId,
242
+ relayScopeId: relay.relayScopeId,
243
+ status: settlement.status,
244
+ error: settlement.error?.message,
245
+ attemptIdentity,
246
+ });
247
+ }
63
248
  }
64
- } catch (error) {
65
- console.error('[forge cleanup] Browser session retention failed:', error);
66
249
  }
67
- try {
68
- const computerTargets = await cleanupRuntimeComputerInteractionTargets(input.controllerHome, { nowMs: input.nowMs });
69
- if (computerTargets.blockers.length > 0 || computerTargets.overCapacity || computerTargets.budgetExhausted) {
70
- console.error('[forge cleanup] Computer interaction-target retention reported bounded blockers');
250
+
251
+ // Process GC includes stale-active reconciliation with a five-minute minimum
252
+ // age. Preserve the existing one-repository-per-base-pass round robin so that
253
+ // recovery remains smooth instead of concentrating all repositories in one
254
+ // periodic spike.
255
+ if (!runtimeMaintenanceAuthorityCurrent(input.controllerHome)) return;
256
+ if (plan.processGcRepositoryIndex !== undefined) {
257
+ const processRepo = input.repositories[plan.processGcRepositoryIndex]!;
258
+ const result = input.processGc({ controllerHome: input.controllerHome, repoId: processRepo.repoId });
259
+ if (!result.ok) {
260
+ const reason = result.error ?? 'unknown error';
261
+ registerSchedulerFailure({
262
+ controllerHome: input.controllerHome,
263
+ source: 'maintenance',
264
+ prefix: 'PROCESS_GC_FAILED',
265
+ message: reason,
266
+ observationId: `maintenance:process-gc:${processRepo.repoId}:${plan.periodicSequence}`,
267
+ repoId: processRepo.repoId,
268
+ atMs: input.nowMs,
269
+ });
270
+ console.error('[forge cleanup] Process GC failed:', reason);
71
271
  }
272
+ }
273
+
274
+ if (!plan.runDeepRetention) return;
275
+ if (!runtimeMaintenanceAuthorityCurrent(input.controllerHome)) return;
276
+
277
+ // Generated caches and persisted artifacts have hour/day-scale retention
278
+ // thresholds. Run one repository per deep-retention pass, and rotate the
279
+ // repository index on the deep cadence rather than the one-minute sequence.
280
+ try {
281
+ const xctestCleanup = cleanupIdleXCTestDevices(input.controllerHome);
282
+ if (xctestCleanup.error) console.error('[forge cleanup] XCTest device cleanup failed:', xctestCleanup.error);
72
283
  } catch (error) {
73
- console.error('[forge cleanup] Computer interaction-target retention failed:', error);
284
+ console.error('[forge cleanup] XCTest device cleanup failed:', error);
74
285
  }
75
- if (input.repositories.length === 0) return;
76
- const slot = Math.floor(input.nowMs / input.cleanupIntervalMs) % input.repositories.length;
77
- const repo = input.repositories[slot]!;
286
+ if (plan.deepRetentionRepositoryIndex === undefined) return;
287
+ const repo = input.repositories[plan.deepRetentionRepositoryIndex]!;
78
288
  try {
79
- const generated = cleanupGeneratedRepositoryCaches(repo.canonicalRoot, { nowMs: input.nowMs });
289
+ const generated = cleanupGeneratedRepositoryCheckoutCaches(repo, { nowMs: input.nowMs, rootStartIndex: plan.deepRetentionSequence });
80
290
  if (generated.errors.length > 0) {
81
291
  console.error(`[forge cleanup] generated-cache retention reported ${generated.errors.length} error(s) for ${repo.repoId}`);
82
292
  }
@@ -91,8 +301,6 @@ export async function runSchedulerPeriodicCleanup(input: {
91
301
  } catch (error) {
92
302
  console.error(`[forge cleanup] Browser artifact retention failed for ${repo.repoId}:`, error);
93
303
  }
94
- const result = input.processGc({ controllerHome: input.controllerHome, repoId: repo.repoId });
95
- if (!result.ok) console.error('[forge cleanup] Process GC failed:', result.error ?? 'unknown error');
96
304
  try {
97
305
  const checkResults = cleanupPersistedCheckResults(input.controllerHome, repo.repoId, { nowMs: input.nowMs });
98
306
  if (checkResults.blockers.length > 0 || checkResults.budgetExhausted) {
@@ -132,84 +340,3 @@ export async function runSchedulerValidationReconciliation(input: {
132
340
  }
133
341
  }
134
342
  }
135
-
136
- export async function runSchedulerControllerRoundRecovery(input: {
137
- controllerHome: string;
138
- nowMs: number;
139
- repositories: readonly Pick<RepositoryRecord, 'repoId' | 'canonicalRoot' | 'localRoot'>[];
140
- graceMs?: number;
141
- maxRecoveries?: number;
142
- dispatchPrompt?: typeof runWorkChatgptContinuation;
143
- authorizeWake?: typeof assertAutomatedOperationAllowed;
144
- }): Promise<{ claimed: number; dispatched: number; failed: number }> {
145
- const dispatchPrompt = input.dispatchPrompt ?? runWorkChatgptContinuation;
146
- const authorizeWake = input.authorizeWake ?? assertAutomatedOperationAllowed;
147
- const maxRecoveries = Math.max(1, Math.min(Math.trunc(input.maxRecoveries ?? 2), 8));
148
- let claimed = 0;
149
- let dispatched = 0;
150
- let failed = 0;
151
-
152
- for (const repository of input.repositories) {
153
- if (claimed >= maxRecoveries) break;
154
- const store = { controllerHome: input.controllerHome, repoId: repository.repoId };
155
- let records: ReturnType<typeof claimStalledControllerRoundRelays>;
156
- try {
157
- records = claimStalledControllerRoundRelays(store, {
158
- nowMs: input.nowMs,
159
- graceMs: input.graceMs,
160
- limit: maxRecoveries - claimed,
161
- controllerTypes: ['chatgpt'],
162
- });
163
- } catch (error) {
164
- const reason = error instanceof Error ? error.message : String(error);
165
- failed += 1;
166
- console.error(`[forge controller relay] stalled round scan failed for ${repository.repoId}:`, reason);
167
- continue;
168
- }
169
- claimed += records.length;
170
- for (const record of records) {
171
- try {
172
- authorizeWake('external_controller_wake', {
173
- work_id: record.originWorkId,
174
- controller_type: 'chatgpt',
175
- relay_scope_id: record.relayScopeId,
176
- recovery_reason: 'unclosed_dispatched_round',
177
- });
178
- const binding = getChatgptWorkConversationBinding(store, record.originWorkId);
179
- const result = await dispatchPrompt({
180
- controllerHome: input.controllerHome,
181
- repoId: repository.repoId,
182
- repoRoot: repository.canonicalRoot ?? repository.localRoot,
183
- workId: record.originWorkId,
184
- prompt: renderChatgptControllerRoundPrompt(store, record, { exactOriginWork: !record.requirementId }),
185
- browserSessionId: binding?.latestBrowserSessionId,
186
- conversationUrl: binding?.conversationUrl,
187
- model: 'gpt-5.6',
188
- reasoning: 'high',
189
- tabPolicy: 'auto',
190
- timeoutMs: 30_000,
191
- });
192
- if (result.status === 'failed') throw new Error(result.error?.message ?? 'CHATGPT_CONTROLLER_RELAY_RECOVERY_FAILED');
193
- const updatedBinding = getChatgptWorkConversationBinding(store, record.originWorkId);
194
- finishControllerRoundRelayDispatch(store, {
195
- workId: record.originWorkId,
196
- ok: true,
197
- bindingId: updatedBinding?.bindingId,
198
- });
199
- dispatched += 1;
200
- } catch (error) {
201
- const reason = error instanceof Error ? error.message : String(error);
202
- finishControllerRoundRelayDispatch(store, {
203
- workId: record.originWorkId,
204
- ok: false,
205
- error: reason,
206
- recovery: true,
207
- nowMs: input.nowMs,
208
- });
209
- failed += 1;
210
- console.error(`[forge controller relay] stalled round recovery failed for ${record.relayScopeId}:`, reason);
211
- }
212
- }
213
- }
214
- return { claimed, dispatched, failed };
215
- }