@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
@@ -0,0 +1,963 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { parseChatgptConversationIdentity } from './chatgpt-conversation';
3
+ import { parseSupervisorCompletion, renderEffectMarker, renderSupervisorPrompt, sha256, validateEffectId, validateSupervisorObjective } from './protocol';
4
+ import { WorkflowSupervisorStore } from './store';
5
+ import { getWorkContract } from '../packages/kernel/work/api/index';
6
+ import { readRequirement } from '../src/runtime/control-plane/persistence/requirement-store';
7
+ import { resolveWorkflowSupervisorForgeHome } from './paths';
8
+ import type { WorkflowAssistantObservation, WorkflowAssistantObservationResult, WorkflowContractValidation, WorkflowEffectKind, WorkflowSupervisorBrowserPollResult, WorkflowSupervisorBrowserTask, WorkflowSupervisorCompletion, WorkflowSupervisorDiscoveredConversation, WorkflowSupervisorEffect, WorkflowSupervisorLifecycleHooks, WorkflowSupervisorProjectScope, WorkflowSupervisorTask, WorkflowSupervisorTaskInput, WorkflowSupervisorTerminalState, WorkflowSupervisorValidators, WorkflowSupervisorPreflightComposerObservation } from './types';
9
+
10
+ function compactProjectIdentity(value: string): string {
11
+ return value.normalize('NFKC').toLocaleLowerCase().replace(/[^\p{L}\p{N}]+/gu, '');
12
+ }
13
+
14
+ function projectIdentityTokens(value: string): string[] {
15
+ return value.normalize('NFKC').toLocaleLowerCase().split(/[^\p{L}\p{N}]+/gu).filter(Boolean);
16
+ }
17
+
18
+ function boundedProjectIdentityMatch(expected: string, observed: string): boolean {
19
+ const compactExpected = compactProjectIdentity(expected);
20
+ const compactObserved = compactProjectIdentity(observed);
21
+ if (!compactExpected || !compactObserved) return false;
22
+ if (compactExpected === compactObserved) return true;
23
+ if (compactExpected.length < 4) return false;
24
+ const expectedTokens = projectIdentityTokens(expected);
25
+ const observedTokens = projectIdentityTokens(observed);
26
+ if (expectedTokens.length === 0 || expectedTokens.length >= observedTokens.length) return false;
27
+ return expectedTokens.every((token, index) => observedTokens[index] === token);
28
+ }
29
+
30
+ function projectSlugFromUrl(value: string): string | undefined {
31
+ try {
32
+ const parsed = new URL(value);
33
+ const match = /^\/g\/(g-p-[a-z0-9]+)(?:-([^/]+))?\/project\/?$/i.exec(parsed.pathname);
34
+ return match?.[2]?.toLocaleLowerCase();
35
+ } catch {
36
+ return undefined;
37
+ }
38
+ }
39
+
40
+ function projectMatchesScope(conversation: WorkflowSupervisorDiscoveredConversation, scope: WorkflowSupervisorProjectScope): boolean {
41
+ const names = [scope.title, ...(scope.aliases ?? [])]
42
+ .map((value) => value.trim().toLocaleLowerCase())
43
+ .filter(Boolean);
44
+ const projectTitle = conversation.projectTitle?.trim().toLocaleLowerCase();
45
+ if (projectTitle && names.some((name) => boundedProjectIdentityMatch(name, projectTitle))) return true;
46
+ const projectSlug = conversation.projectUrl ? projectSlugFromUrl(conversation.projectUrl) : undefined;
47
+ if (!projectSlug) return false;
48
+ return names.some((name) => boundedProjectIdentityMatch(name, projectSlug));
49
+ }
50
+
51
+ function effectId(): string { return `fx_${randomUUID().replaceAll('-', '')}`; }
52
+ function stableEffectId(originKey: string): string {
53
+ return validateEffectId(`fx_${sha256(originKey).slice(0, 32)}`);
54
+ }
55
+ const rejectUnconfigured = async (): Promise<WorkflowContractValidation> => ({ valid: false, reason: 'validator_unconfigured' });
56
+
57
+ export class WorkflowSupervisorControlPlane {
58
+ readonly validators: WorkflowSupervisorValidators;
59
+ readonly hooks: WorkflowSupervisorLifecycleHooks;
60
+ constructor(readonly store: WorkflowSupervisorStore, validators: Partial<WorkflowSupervisorValidators> = {}, hooks: WorkflowSupervisorLifecycleHooks = {}) {
61
+ this.validators = { completionContract: validators.completionContract ?? rejectUnconfigured, userBlockerPolicy: validators.userBlockerPolicy ?? rejectUnconfigured };
62
+ this.hooks = hooks;
63
+ }
64
+ registerTask(input: WorkflowSupervisorTaskInput): WorkflowSupervisorTask { return this.store.registerTask(input); }
65
+ reviseTaskObjective(input: {
66
+ taskId: string;
67
+ objective: string;
68
+ expectedObjectiveSha256: string;
69
+ requestId: string;
70
+ authorizedBy?: string;
71
+ workId?: string;
72
+ requirementId?: string;
73
+ expectedCompletionContractSha256?: string;
74
+ }): { task: WorkflowSupervisorTask; changed: boolean; deduplicated: boolean; objectiveSha256: string; completionContractSha256: string; semanticBindingChanged: boolean; refreshedEffect?: WorkflowSupervisorEffect } {
75
+ const task = this.requireTask(input.taskId);
76
+ requireNonTerminalTask(this.store, task.taskId);
77
+ const objective = validateSupervisorObjective(input.objective);
78
+ if (!/^[0-9a-f]{64}$/i.test(input.expectedObjectiveSha256)) throw new Error('WORKFLOW_SUPERVISOR_OBJECTIVE_REVISION_EXPECTED_SHA_INVALID');
79
+ if (!input.requestId.trim()) throw new Error('WORKFLOW_SUPERVISOR_OBJECTIVE_REVISION_REQUEST_REQUIRED');
80
+ const workId = input.workId?.trim() || undefined;
81
+ const requirementId = input.requirementId?.trim() || undefined;
82
+ if (workId && requirementId) throw new Error('WORKFLOW_SUPERVISOR_SEMANTIC_BINDING_TARGET_INVALID');
83
+ if ((workId || requirementId) && !input.expectedCompletionContractSha256) {
84
+ throw new Error('WORKFLOW_SUPERVISOR_COMPLETION_CONTRACT_REVISION_EXPECTED_SHA_REQUIRED');
85
+ }
86
+ if (!workId && !requirementId && input.expectedCompletionContractSha256) {
87
+ throw new Error('WORKFLOW_SUPERVISOR_COMPLETION_CONTRACT_REVISION_TARGET_REQUIRED');
88
+ }
89
+ let semanticBinding: { workId?: string; requirementId?: string; expectedCompletionContractSha256: string } | undefined;
90
+ if (workId || requirementId) {
91
+ if (task.completionContract.kind !== 'model_semantic_completion') {
92
+ throw new Error('WORKFLOW_SUPERVISOR_SEMANTIC_BINDING_REVISION_KIND_UNSUPPORTED');
93
+ }
94
+ const controllerHome = typeof task.completionContract.controller_home === 'string' ? task.completionContract.controller_home.trim() : '';
95
+ const repoId = typeof task.completionContract.repo_id === 'string' ? task.completionContract.repo_id.trim() : '';
96
+ if (!controllerHome || !repoId
97
+ || resolveWorkflowSupervisorForgeHome(controllerHome) !== resolveWorkflowSupervisorForgeHome(this.store.forgeHome)) {
98
+ throw new Error('WORKFLOW_SUPERVISOR_SEMANTIC_BINDING_CONTEXT_REQUIRED');
99
+ }
100
+ if (workId && !getWorkContract({ controllerHome, scopeKey: repoId }, workId)) {
101
+ throw new Error('WORKFLOW_SUPERVISOR_SEMANTIC_BINDING_WORK_NOT_FOUND');
102
+ }
103
+ if (requirementId && !readRequirement({ controllerHome }, requirementId)?.value) {
104
+ throw new Error('WORKFLOW_SUPERVISOR_SEMANTIC_BINDING_REQUIREMENT_NOT_FOUND');
105
+ }
106
+ semanticBinding = {
107
+ ...(workId ? { workId } : {}),
108
+ ...(requirementId ? { requirementId } : {}),
109
+ expectedCompletionContractSha256: input.expectedCompletionContractSha256!,
110
+ };
111
+ }
112
+ const pending = this.store.currentUnappliedEffect(task.taskId);
113
+ let refreshEffect: { effectId: string; expectedPrompt: string; prompt: string } | undefined;
114
+ if (pending && !this.store.latestEffectDispatch(pending.effectId)) {
115
+ const source = pending.sourceCompletionFingerprint ? this.store.getCompletion(pending.sourceCompletionFingerprint) : undefined;
116
+ const checkpoint = source?.proposal.reason === 'compact_receipt' ? undefined : source?.proposal.checkpoint;
117
+ refreshEffect = {
118
+ effectId: pending.effectId,
119
+ expectedPrompt: pending.prompt,
120
+ prompt: this.renderPrompt({ ...task, objective }, pending.effectId, pending.kind, checkpoint),
121
+ };
122
+ }
123
+ return this.store.reviseTaskObjective({
124
+ taskId: task.taskId,
125
+ objective,
126
+ expectedObjectiveSha256: input.expectedObjectiveSha256.toLowerCase(),
127
+ requestId: input.requestId.trim(),
128
+ authorizedBy: input.authorizedBy?.trim() || 'operator',
129
+ ...(semanticBinding ? { semanticBinding } : {}),
130
+ ...(refreshEffect ? { refreshEffect } : {}),
131
+ });
132
+ }
133
+ /**
134
+ * Explicit operator recovery. It is deliberately evidence-classified rather
135
+ * than a generic "try again": each class names the durable mechanical fact
136
+ * that makes exactly one bounded next step safe, and none of them can replay
137
+ * a submission whose outcome is unknown.
138
+ *
139
+ * - proven-un-applied effect at its retry ceiling -> refund the mechanical
140
+ * budget once and re-dispatch that same effect;
141
+ * - unknown-outcome effect -> read-only reclassification only, unless the
142
+ * operator explicitly authorizes retiring it so a *fresh* turn can run;
143
+ * - applied effect whose single provider resume is exhausted -> reserve one
144
+ * new, separately identified recovery turn on the same conversation.
145
+ */
146
+ recoverTask(input: {
147
+ taskId: string;
148
+ sourceEffectId?: string;
149
+ requestId: string;
150
+ reason: string;
151
+ /** Explicit operator authority required to retire an outcome-unknown effect. */
152
+ supersedeUnknown?: boolean;
153
+ authorizedBy?: string;
154
+ }): { recoveryEffect: WorkflowSupervisorEffect; action: 'retry_authorized' | 'recovery_reserved' | 'unknown_superseded' } {
155
+ const task = this.requireTask(input.taskId);
156
+ requireNonTerminalTask(this.store, task.taskId);
157
+ if (!input.requestId.trim() || !input.reason.trim()) throw new Error('WORKFLOW_SUPERVISOR_RECOVERY_REASON_REQUIRED');
158
+ const authorizedBy = input.authorizedBy?.trim() || 'operator';
159
+ // Identity-stable replay first: an operator recovery already reserved for
160
+ // this exact source effect is the durable answer, whether or not it has
161
+ // since been dispatched.
162
+ const legacyOrigin = input.sourceEffectId ? `provider-recovery:${input.sourceEffectId}` : undefined;
163
+ const existingLegacy = legacyOrigin ? this.store.getEffectByOriginKey(legacyOrigin) : undefined;
164
+ if (existingLegacy?.taskId === task.taskId) return { recoveryEffect: existingLegacy, action: 'recovery_reserved' };
165
+ // The same explicit supersession decision is one durable fact; replaying it
166
+ // returns its replacement instead of failing on the now-retired source.
167
+ const supersedeOrigin = `operator-recovery:${input.sourceEffectId ?? ''}:${input.requestId}`;
168
+ const existingSupersede = this.store.getEffectByOriginKey(supersedeOrigin);
169
+ if (existingSupersede?.taskId === task.taskId) return { recoveryEffect: existingSupersede, action: 'unknown_superseded' };
170
+ const pending = this.store.currentUnappliedEffect(task.taskId);
171
+ if (input.sourceEffectId && pending && pending.effectId !== input.sourceEffectId) {
172
+ throw new Error('WORKFLOW_SUPERVISOR_RECOVERY_SOURCE_CHANGED');
173
+ }
174
+ if (pending) {
175
+ if (this.store.effectDispatchBudget(pending.effectId).generations === 0) {
176
+ // Browser provider was repaired independently of Forge's source release.
177
+ // The old enrollment text and effect identity remain authoritative;
178
+ // rearm a single preparation without manufacturing a provider send.
179
+ this.store.authorizeOperatorPreflightRetry({ effectId: pending.effectId, requestId: input.requestId,
180
+ reason: input.reason, authorizedBy });
181
+ return { recoveryEffect: pending, action: 'retry_authorized' };
182
+ }
183
+ // Read-only reclassification first: a dispatch-owner pre-send return is a
184
+ // stronger fact than any operator decision and makes the effect retryable
185
+ // through the normal mechanical path.
186
+ if (this.store.reconcileNativePreSendFailure(pending.effectId, input.requestId)) {
187
+ return { recoveryEffect: pending, action: 'retry_authorized' };
188
+ }
189
+ try {
190
+ this.store.authorizeOperatorRetry({ effectId: pending.effectId, requestId: input.requestId, reason: input.reason, authorizedBy });
191
+ return { recoveryEffect: pending, action: 'retry_authorized' };
192
+ } catch (error) {
193
+ const message = error instanceof Error ? error.message : String(error);
194
+ if (message !== 'WORKFLOW_SUPERVISOR_RECOVERY_OUTCOME_UNKNOWN') throw error;
195
+ if (input.supersedeUnknown !== true) throw error;
196
+ }
197
+ // The operator explicitly decided to stop waiting on an unknown
198
+ // submission. Record that decision durably and reserve one *new* turn in
199
+ // the same transaction; the unknown effect is never resent and never
200
+ // silently discarded.
201
+ const origin = `operator-recovery:${pending.effectId}:${input.requestId}`;
202
+ const supersededId = stableEffectId(origin);
203
+ const prompt = this.renderPrompt(task, supersededId, 'recovery', undefined,
204
+ `The operator explicitly superseded outcome-unknown effect ${pending.effectId} (${input.requestId}): ${input.reason}. That effect is retired and must never be resent; read durable Forge state and continue this same task with one fresh turn.`);
205
+ const superseded = this.store.supersedeUnknownEffect({
206
+ effectId: pending.effectId, requestId: input.requestId, reason: input.reason, authorizedBy,
207
+ replacement: { effectId: supersededId, prompt },
208
+ });
209
+ return { recoveryEffect: superseded.recoveryEffect, action: 'unknown_superseded' };
210
+ }
211
+ const source = this.store.latestAppliedLeafEffectWithoutCompletion(task.taskId);
212
+ if (!source || (input.sourceEffectId && source.effectId !== input.sourceEffectId)) throw new Error('WORKFLOW_SUPERVISOR_RECOVERY_SOURCE_CHANGED');
213
+ const origin = `provider-recovery:${source.effectId}`;
214
+ const id = stableEffectId(origin);
215
+ if (this.store.providerResumeExhausted(source.effectId)) {
216
+ // The automatic resume ceiling is the exact mechanical fact that permits
217
+ // a further operator turn; without it this path must not exist.
218
+ const prompt = this.renderPrompt(task, id, 'recovery', undefined,
219
+ `The operator explicitly requested recovery (${input.requestId}): ${input.reason}. The single bounded provider resume for applied effect ${source.effectId} is exhausted; the source effect remains applied and must not be replayed. Read durable Forge state and continue this same task.`);
220
+ return { recoveryEffect: this.store.reserveOperatorProviderRecovery({
221
+ taskId: task.taskId, sourceEffectId: source.effectId, requestId: input.requestId, reason: input.reason,
222
+ authorizedBy, originKey: origin, effectId: id, prompt,
223
+ }), action: 'recovery_reserved' };
224
+ }
225
+ const prompt = this.renderPrompt(task, id, 'recovery', undefined,
226
+ `The user explicitly requested recovery (${input.requestId}): ${input.reason}. Preserve the prior applied effect and all completed source work; read durable state and continue this same task. This is one operator-authorized recovery, not a reset of automatic recovery or dispatch budgets.`);
227
+ return { recoveryEffect: this.store.reserveOperatorRecovery({
228
+ taskId: task.taskId, sourceEffectId: source.effectId, requestId: input.requestId, reason: input.reason, effectId: id, prompt,
229
+ }), action: 'recovery_reserved' };
230
+ }
231
+ reserveEnrollment(taskId: string, canonicalEffectId?: string): WorkflowSupervisorEffect {
232
+ const task = this.requireTask(taskId);
233
+ // A task that already reached a terminal supervisor action is not deliverable.
234
+ // Reserving another enrollment effect for it produced an "enrolled" result that
235
+ // could never be delivered, so the ControllerRound waited forever instead of
236
+ // surfacing the operator/provider decision that terminal state represents.
237
+ requireNonTerminalTask(this.store, task.taskId);
238
+ const originKey = `enrollment:${taskId}`;
239
+ // Supervisor owns the outer-turn effect identity. A lower ControllerRound may
240
+ // rotate when the semantic Work carrier changes, but the Requirement + exact
241
+ // conversation task must keep one enrollment effect. Reuse by origin before
242
+ // considering a newer lower-layer canonical effect id.
243
+ const existingForOrigin = this.store.getEffectByOriginKey(originKey);
244
+ let effect: WorkflowSupervisorEffect;
245
+ if (existingForOrigin) {
246
+ if (existingForOrigin.taskId !== taskId) throw new Error(`WORKFLOW_SUPERVISOR_EFFECT_TASK_CONFLICT:${originKey}`);
247
+ if (existingForOrigin.kind !== 'enrollment') throw new Error(`WORKFLOW_SUPERVISOR_EFFECT_KIND_CONFLICT:${originKey}`);
248
+ effect = existingForOrigin;
249
+ } else {
250
+ const id = canonicalEffectId ? validateEffectId(canonicalEffectId) : stableEffectId(originKey);
251
+ const existing = this.store.getEffect(id);
252
+ if (existing) {
253
+ if (existing.taskId !== taskId) throw new Error(`WORKFLOW_SUPERVISOR_EFFECT_TASK_CONFLICT:${originKey}`);
254
+ if (existing.kind !== 'enrollment') throw new Error(`WORKFLOW_SUPERVISOR_EFFECT_KIND_CONFLICT:${originKey}`);
255
+ effect = existing;
256
+ } else {
257
+ effect = this.store.reserveEffect({ taskId, effectId: id, kind: 'enrollment', originKey, prompt: this.renderPrompt(task, id, 'enrollment') });
258
+ }
259
+ }
260
+ const inheritedDispatch = this.hooks.inheritedEffectDispatch?.(task, effect);
261
+ if (inheritedDispatch && !this.store.effectApplied(effect.effectId)) {
262
+ this.store.recordEffectDispatchStarted(
263
+ effect.effectId,
264
+ inheritedDispatch.generation,
265
+ inheritedDispatch.dispatchId,
266
+ inheritedDispatch.evidence,
267
+ );
268
+ }
269
+ return effect;
270
+ }
271
+ observeEffect(input: { effectId: string; observationId: string; outcome: 'applied' | 'not_applied' | 'unknown'; evidence?: Record<string, unknown> }): void {
272
+ this.store.recordEffectObservation(validateEffectId(input.effectId), input.observationId, input.outcome, input.evidence);
273
+ }
274
+ getTask(taskId: string): WorkflowSupervisorTask | undefined { return this.store.getTask(taskId); }
275
+ getTaskByConversationId(conversationId: string): { task: WorkflowSupervisorTask; terminal?: WorkflowSupervisorTerminalState } | undefined {
276
+ const task = this.store.getTaskByConversationId(conversationId);
277
+ if (!task) return undefined;
278
+ const terminal = this.store.terminalAction(task.taskId);
279
+ return { task, ...(terminal ? { terminal } : {}) };
280
+ }
281
+ listTasks(activeOnly = false): WorkflowSupervisorTask[] {
282
+ const tasks = this.store.listTasks();
283
+ return activeOnly ? tasks.filter((task) => !this.store.terminalAction(task.taskId)) : tasks;
284
+ }
285
+ /**
286
+ * Completed/blocked/stopped history is a read-only projection of canonical
287
+ * terminal events. It never archives the ChatGPT conversation or rewrites Work.
288
+ */
289
+ listTerminalTasks(): Array<{ task: WorkflowSupervisorTask; terminal: WorkflowSupervisorTerminalState }> {
290
+ return this.store.listTasks().flatMap((task) => {
291
+ const terminal = this.store.terminalAction(task.taskId);
292
+ return terminal ? [{ task, terminal }] : [];
293
+ });
294
+ }
295
+ /** Reconcile Work-owned task closure after a crash or a direct Work writer.
296
+ * Independent Requirement and standalone Goals are intentionally excluded.
297
+ */
298
+ reconcileTerminalWorkBoundTasks(): string[] {
299
+ if (!this.hooks.terminalWorkStopReasonForTask) return [];
300
+ const stopped: string[] = [];
301
+ for (const task of this.store.listTasks()) {
302
+ if (this.store.terminalAction(task.taskId)) continue;
303
+ const reason = this.hooks.terminalWorkStopReasonForTask(task);
304
+ if (!reason) continue;
305
+ const result = this.store.stopTask(task.taskId, reason);
306
+ if (!result.deduplicated) stopped.push(task.taskId);
307
+ }
308
+ return stopped;
309
+ }
310
+ stopTask(taskId: string, reason = 'Stopped by operator request.'): { taskId: string; terminal: 'STOPPED'; deduplicated: boolean } {
311
+ return this.store.stopTask(taskId, reason);
312
+ }
313
+ bindBootstrapConversation(input: { taskId: string; conversationId: string; conversationUrl: string }): WorkflowSupervisorTask {
314
+ const identity = parseChatgptConversationIdentity(input.conversationUrl);
315
+ if (identity.conversationId !== input.conversationId) throw new Error('WORKFLOW_SUPERVISOR_BOOTSTRAP_CONVERSATION_MISMATCH');
316
+ const pendingMigration = this.store.freshConversationMigrationContext(input.taskId);
317
+ const task = this.store.bindBootstrapConversation(input.taskId, identity.conversationId, identity.canonicalUrl);
318
+ this.hooks.bootstrapConversationBound?.(task, pendingMigration ? {
319
+ migratedFromConversationId: pendingMigration.fromConversationId,
320
+ migrationRequestId: pendingMigration.requestId,
321
+ } : undefined);
322
+ return task;
323
+ }
324
+ getEffect(id: string): WorkflowSupervisorEffect | undefined { return this.store.getEffect(validateEffectId(id)); }
325
+ preSendComposerObservation(effectId: string): WorkflowSupervisorPreflightComposerObservation | undefined {
326
+ return this.store.preSendComposerObservation(validateEffectId(effectId));
327
+ }
328
+ /** Bind the one canonical Scheduler occurrence lane to an exact Work-owned Supervisor task. */
329
+ bindScheduledWake(input: { taskId: string; repoId: string; workId: string; scheduleId: string }) {
330
+ const task = this.requireTask(input.taskId);
331
+ requireNonTerminalTask(this.store, task.taskId);
332
+ const bound = this.store.bindScheduledWake(input);
333
+ const pending = this.store.currentUnappliedEffect(task.taskId);
334
+ if (pending && !this.store.latestEffectDispatch(pending.effectId) && !this.store.effectApplied(pending.effectId)) {
335
+ const source = pending.sourceCompletionFingerprint ? this.store.getCompletion(pending.sourceCompletionFingerprint) : undefined;
336
+ const checkpoint = source?.proposal.reason === 'compact_receipt' ? undefined : source?.proposal.checkpoint;
337
+ this.store.refreshUndispatchedEffectPrompt({
338
+ taskId: task.taskId,
339
+ effectId: pending.effectId,
340
+ expectedPrompt: pending.prompt,
341
+ prompt: this.renderPrompt(bound.task, pending.effectId, pending.kind, checkpoint),
342
+ });
343
+ }
344
+ return bound;
345
+ }
346
+ /** Consume a completed WAIT receipt exactly once for one exact Scheduler occurrence. */
347
+ wakeScheduledTask(input: { taskId: string; scheduleId: string; occurrenceId: string }): { effect: WorkflowSupervisorEffect; deduplicated: boolean } {
348
+ const task = this.requireTask(input.taskId);
349
+ if (task.continuationPolicy.scheduled_wake_schedule_id !== input.scheduleId) {
350
+ throw new Error('WORKFLOW_SUPERVISOR_SCHEDULE_BINDING_MISMATCH');
351
+ }
352
+ const originKey = `scheduled-wake:${input.scheduleId}:${input.occurrenceId}`;
353
+ const id = stableEffectId(originKey);
354
+ const prior = this.store.getEffectByOriginKey(originKey);
355
+ if (prior) {
356
+ if (prior.taskId !== task.taskId || prior.effectId !== id || prior.kind !== 'continuation' || !prior.sourceCompletionFingerprint) {
357
+ throw new Error('WORKFLOW_SUPERVISOR_SCHEDULED_WAKE_IDENTITY_CONFLICT');
358
+ }
359
+ return { effect: prior, deduplicated: true };
360
+ }
361
+ requireNonTerminalTask(this.store, task.taskId);
362
+ return this.store.reserveScheduledWake({
363
+ taskId: task.taskId,
364
+ scheduleId: input.scheduleId,
365
+ occurrenceId: input.occurrenceId,
366
+ effectId: id,
367
+ prompt: this.renderPrompt(task, id, 'continuation', `scheduled wake ${input.scheduleId} occurrence ${input.occurrenceId}`),
368
+ });
369
+ }
370
+ /** Read-only stall classification for one task; no lifecycle authority. */
371
+ taskStall(taskId: string): ReturnType<WorkflowSupervisorStore['taskStall']> { return this.store.taskStall(this.requireTask(taskId).taskId); }
372
+ /**
373
+ * Explicit operator move of one task's exact conversation. Chatting in a new
374
+ * conversation is never an automatic answer to provider backpressure: a 429 is
375
+ * answered by fewer requests, and only a conversation that is durably unusable
376
+ * is replaced. Requires the caller's expected current conversation so a stale
377
+ * decision cannot silently move a task that already advanced.
378
+ */
379
+ migrateConversation(input: { taskId: string; expectedConversationId: string; conversationId?: string; conversationUrl?: string; fresh?: boolean; requestId: string; reason: string; authorizedBy?: string }): { taskId: string; conversationId: string; conversationUrl: string; migrated: boolean; freshConversation?: boolean } {
380
+ const task = this.requireTask(input.taskId);
381
+ requireNonTerminalTask(this.store, task.taskId);
382
+ if (!input.requestId.trim() || !input.reason.trim()) throw new Error('WORKFLOW_SUPERVISOR_MIGRATION_REASON_REQUIRED');
383
+ if (input.fresh === true) {
384
+ if (input.conversationId?.trim() || input.conversationUrl?.trim()) throw new Error('WORKFLOW_SUPERVISOR_MIGRATION_FRESH_CONVERSATION_CONFLICT');
385
+ const origin = `fresh-conversation:${task.taskId}:${input.requestId}`;
386
+ const effectId = stableEffectId(origin);
387
+ const migrated = this.store.migrateToFreshConversation({
388
+ taskId: task.taskId,
389
+ expectedConversationId: input.expectedConversationId,
390
+ requestId: input.requestId,
391
+ reason: input.reason,
392
+ authorizedBy: input.authorizedBy?.trim() || 'operator',
393
+ // A fresh conversation has no prior turns, so the replacement turn must
394
+ // carry the objective again instead of the bare "继续。" continuation.
395
+ replacement: { effectId, prompt: this.renderPrompt(task, effectId, 'enrollment') },
396
+ });
397
+ return { taskId: migrated.task.taskId, conversationId: migrated.task.conversationId, conversationUrl: migrated.task.conversationUrl, migrated: migrated.migrated, freshConversation: migrated.migrated };
398
+ }
399
+ if (!input.conversationId?.trim() || !input.conversationUrl?.trim()) throw new Error('WORKFLOW_SUPERVISOR_MIGRATION_CONVERSATION_REQUIRED');
400
+ const identity = parseChatgptConversationIdentity(input.conversationUrl);
401
+ if (identity.conversationId !== input.conversationId) throw new Error('WORKFLOW_SUPERVISOR_MIGRATION_CONVERSATION_MISMATCH');
402
+ const migrated = this.store.migrateConversation({
403
+ taskId: task.taskId,
404
+ expectedConversationId: input.expectedConversationId,
405
+ conversationId: identity.conversationId,
406
+ conversationUrl: identity.canonicalUrl,
407
+ requestId: input.requestId,
408
+ reason: input.reason,
409
+ authorizedBy: input.authorizedBy?.trim() || 'operator',
410
+ });
411
+ return {
412
+ taskId: migrated.task.taskId, conversationId: migrated.task.conversationId,
413
+ conversationUrl: migrated.task.conversationUrl, migrated: migrated.migrated,
414
+ };
415
+ }
416
+ /** Mechanical provider re-dispatch budget for one effect, used by recovery to surface exhaustion. */
417
+ effectDispatchBudget(effectId: string): ReturnType<WorkflowSupervisorStore['effectDispatchBudget']> {
418
+ return this.store.effectDispatchBudget(validateEffectId(effectId));
419
+ }
420
+ continuationProof(input: { repoId?: string; activeReleaseId: string; notBefore: string }) { return this.store.continuationProof(input); }
421
+ browserDiscoverySnapshot() { return this.store.discoverySnapshot(); }
422
+ bootstrapProjectUrl(taskId: string): string {
423
+ const task = this.requireTask(taskId);
424
+ const scope = this.hooks.projectScopeForTask?.(task);
425
+ const title = scope?.title.trim();
426
+ if (!scope || !title) throw new Error('WORKFLOW_SUPERVISOR_BOOTSTRAP_PROJECT_SCOPE_REQUIRED');
427
+ const projects = new Map<string, string>();
428
+ for (const conversation of this.store.discoverySnapshot().conversations) {
429
+ if (!projectMatchesScope(conversation, scope)) continue;
430
+ const value = conversation.projectUrl?.trim();
431
+ if (!value) continue;
432
+ try {
433
+ const parsed = new URL(value);
434
+ if (parsed.protocol !== 'https:' || parsed.hostname !== 'chatgpt.com') continue;
435
+ const match = /^\/g\/(g-p-[a-z0-9]+)(?:-[^/]+)?\/project\/?$/i.exec(parsed.pathname);
436
+ if (!match) continue;
437
+ const projectId = match[1]!.toLocaleLowerCase();
438
+ projects.set(projectId, `https://chatgpt.com/g/${match[1]!}/project`);
439
+ } catch {
440
+ continue;
441
+ }
442
+ }
443
+ if (projects.size === 0) throw new Error('WORKFLOW_SUPERVISOR_BOOTSTRAP_PROJECT_NOT_DISCOVERED');
444
+ if (projects.size !== 1) throw new Error('WORKFLOW_SUPERVISOR_BOOTSTRAP_PROJECT_AMBIGUOUS');
445
+ return [...projects.values()][0]!;
446
+ }
447
+ recordBrowserDiscovery(source: string, conversations: readonly WorkflowSupervisorDiscoveredConversation[]) {
448
+ // Discovery is durable observation only. Creating a Supervisor task/effect
449
+ // requires the explicit Work/current-conversation enrollment path.
450
+ return this.store.recordDiscovery(source, conversations);
451
+ }
452
+ browserProjectScopes(): WorkflowSupervisorProjectScope[] {
453
+ const scopes = new Map<string, WorkflowSupervisorProjectScope>();
454
+ for (const task of this.store.listTasks()) {
455
+ const scope = this.hooks.projectScopeForTask?.(task);
456
+ if (!scope?.title.trim()) continue;
457
+ const normalized: WorkflowSupervisorProjectScope = {
458
+ title: scope.title.trim().slice(0, 512),
459
+ ...(scope.aliases?.length ? { aliases: [...new Set(scope.aliases.map((value) => value.trim().slice(0, 256)).filter(Boolean))].slice(0, 16) } : {}),
460
+ ...(scope.repoId?.trim() ? { repoId: scope.repoId.trim().slice(0, 256) } : {}),
461
+ ...(scope.controllerHome?.trim() ? { controllerHome: scope.controllerHome.trim().slice(0, 2048) } : {}),
462
+ };
463
+ const key = `${normalized.title.toLocaleLowerCase()}\n${normalized.repoId ?? ''}\n${normalized.controllerHome ?? ''}`;
464
+ scopes.set(key, normalized);
465
+ }
466
+ return [...scopes.values()];
467
+ }
468
+ browserTasks(): WorkflowSupervisorBrowserTask[] {
469
+ const active = this.store.listTasks().flatMap((task) => {
470
+ if (this.store.terminalAction(task.taskId)) return [];
471
+ // A Computer page is an ephemeral delivery surface. An applied effect
472
+ // awaiting its receipt remains eligible for read-only provider-health
473
+ // observation, so a terminal stream error can reserve its one bounded
474
+ // recovery effect. Rendered assistant text never decides the workflow;
475
+ // only a persisted receipt does that.
476
+ const pending = this.store.nextBrowserEffect(task.taskId);
477
+ // Any applied effect awaiting its receipt stays observable, including one
478
+ // whose bounded provider resume is already exhausted. Observation is
479
+ // read-only and mints nothing, so keeping it attached is what lets late
480
+ // provider/completion evidence resolve the turn instead of stranding it.
481
+ const providerTurnAwaitingReceipt = this.store.hasAppliedEffectAwaitingCompletion(task.taskId);
482
+ if (!pending && !providerTurnAwaitingReceipt) return [];
483
+ // Bootstrap has no exact conversation yet, so it cannot satisfy the
484
+ // normal Work-boundary predicate. Its already-persisted enrollment effect
485
+ // is the narrow authority to acquire one through Computer exactly once.
486
+ if (!task.conversationId.startsWith('bootstrap:')) {
487
+ // Optional lower-layer Work/Requirement compatibility must be task-local.
488
+ // A stale legacy task is allowed to fail closed, but it must never poison
489
+ // the global Supervisor delivery queue and block standalone tasks.
490
+ try { if (pending?.mode === 'send' && !this.browserTaskActive(task)) return []; }
491
+ catch { return []; }
492
+ }
493
+ return [{ task, mode: pending?.mode ?? 'reconcile' }];
494
+ });
495
+ // Fresh effects are bounded work with no prior external mutation. Outcome-
496
+ // unknown reconciliation remains durable and exactly-once, but cannot be
497
+ // allowed to monopolize the single native browser lane indefinitely.
498
+ active.sort((left, right) => {
499
+ const leftPriority = left.mode === 'send' ? 0 : 1;
500
+ const rightPriority = right.mode === 'send' ? 0 : 1;
501
+ return leftPriority - rightPriority;
502
+ });
503
+ return active.map(({ task }) => browserTask(task));
504
+ }
505
+ browserPoll(input: { conversationId: string; conversationUrl: string }): WorkflowSupervisorBrowserPollResult {
506
+ const task = this.requireBrowserTask(input.conversationId, input.conversationUrl);
507
+ const projection = browserTask(task);
508
+ const terminal = this.store.terminalAction(task.taskId);
509
+ // STOPPED is an operator terminal, not a semantic completion. Return it
510
+ // before the normal external-effect activity gate so an already-running
511
+ // browser poll observes the stop and cannot turn it into another command.
512
+ if (terminal === 'STOPPED') return { authorized: true, task: projection, terminal };
513
+ if (terminal) return { authorized: true, task: projection, terminal };
514
+ const pending = this.store.nextBrowserEffect(task.taskId);
515
+ // Read-only reconciliation belongs to the already-started effect even when
516
+ // its Work/conversation binding was retired. It never authorizes a resend.
517
+ if (pending?.mode === 'send' && !this.browserTaskActive(task)) throw new Error('WORKFLOW_SUPERVISOR_BROWSER_TASK_INACTIVE');
518
+ if (!pending) return { authorized: true, task: projection };
519
+ return { authorized: true, task: projection, command: { mode: pending.mode, effectId: pending.effect.effectId, kind: pending.effect.kind, prompt: pending.effect.prompt, dispatchGeneration: pending.generation, conversationId: task.conversationId, conversationUrl: task.conversationUrl } };
520
+ }
521
+ bootstrapPoll(taskId: string): WorkflowSupervisorBrowserPollResult {
522
+ const task = this.requireTask(taskId);
523
+ if (!task.conversationId.startsWith('bootstrap:')) throw new Error('WORKFLOW_SUPERVISOR_BOOTSTRAP_NOT_PENDING');
524
+ const terminal = this.store.terminalAction(task.taskId);
525
+ if (terminal === 'STOPPED') return { authorized: true, task: browserTask(task), terminal };
526
+ // While identity is still bootstrap, reconcile the latest applied leaf
527
+ // before dispatching any successor. An unstarted recovery child must not
528
+ // send from a Project/new-composer surface while the source conversation
529
+ // is still unbound. The existing effect ledger owns observation cooldown.
530
+ const applied = this.store.latestAppliedLeafEffectWithoutCompletion(task.taskId);
531
+ if (applied) {
532
+ if (!this.store.bootstrapAppliedReconcileDue(applied.effectId)) {
533
+ return { authorized: true, task: browserTask(task) };
534
+ }
535
+ const dispatch = this.store.latestEffectDispatch(applied.effectId);
536
+ return {
537
+ authorized: true,
538
+ task: browserTask(task),
539
+ command: {
540
+ mode: 'reconcile',
541
+ effectId: applied.effectId,
542
+ kind: applied.kind,
543
+ prompt: applied.prompt,
544
+ dispatchGeneration: dispatch?.generation ?? 1,
545
+ conversationId: task.conversationId,
546
+ conversationUrl: task.conversationUrl,
547
+ },
548
+ };
549
+ }
550
+ const pending = this.store.nextBrowserEffect(task.taskId);
551
+ if (!pending) return { authorized: true, task: browserTask(task) };
552
+ return {
553
+ authorized: true,
554
+ task: browserTask(task),
555
+ command: {
556
+ mode: pending.mode,
557
+ effectId: pending.effect.effectId,
558
+ kind: pending.effect.kind,
559
+ prompt: pending.effect.prompt,
560
+ dispatchGeneration: pending.generation,
561
+ conversationId: task.conversationId,
562
+ conversationUrl: task.conversationUrl,
563
+ },
564
+ };
565
+ }
566
+ bootstrapBeginEffect(input: { taskId: string; effectId: string; dispatchId: string; dispatchGeneration: number }): boolean {
567
+ const task = this.requireTask(input.taskId);
568
+ requireNonTerminalTask(this.store, task.taskId);
569
+ if (!task.conversationId.startsWith('bootstrap:')) throw new Error('WORKFLOW_SUPERVISOR_BOOTSTRAP_NOT_PENDING');
570
+ const pending = this.store.nextBrowserEffect(task.taskId);
571
+ if (!pending || pending.effect.effectId !== validateEffectId(input.effectId) || pending.mode !== 'send') return false;
572
+ return this.store.recordEffectDispatchStarted(input.effectId, input.dispatchGeneration, input.dispatchId, {
573
+ surface: 'computer-bootstrap',
574
+ ...(this.hooks.effectDispatchEvidence?.({ task, effectId: input.effectId }) ?? {}),
575
+ });
576
+ }
577
+ bootstrapObserveEffect(input: { taskId: string; effectId: string; observationId: string; outcome: 'applied' | 'not_applied' | 'unknown'; evidence?: Record<string, unknown> }): void {
578
+ const task = this.requireTask(input.taskId);
579
+ const effect = this.store.getEffect(validateEffectId(input.effectId));
580
+ if (!effect || effect.taskId !== task.taskId) throw new Error('WORKFLOW_SUPERVISOR_BOOTSTRAP_EFFECT_TASK_MISMATCH');
581
+ if (input.outcome === 'not_applied') {
582
+ // This path is private to the provider dispatch adapter after its exact
583
+ // dispatch function returned without clicking Send. It is therefore a
584
+ // mechanical negative proof, unlike a missing post-send conversation
585
+ // marker, which remains outcome-unknown and must reconcile.
586
+ const pending = this.store.nextBrowserEffect(task.taskId);
587
+ const dispatch = this.store.latestEffectDispatch(effect.effectId);
588
+ if (!pending || pending.effect.effectId !== effect.effectId || pending.mode !== 'reconcile'
589
+ || !dispatch || pending.generation !== dispatch.generation
590
+ || dispatch.evidence.surface !== 'computer-bootstrap'
591
+ || input.evidence?.dispatch_mutation !== 'not_attempted'
592
+ || input.evidence?.send_clicked !== false) {
593
+ throw new Error('WORKFLOW_SUPERVISOR_BOOTSTRAP_NOT_APPLIED_PROOF_INVALID');
594
+ }
595
+ this.store.recordEffectNotAppliedProof(effect.effectId, input.observationId, {
596
+ surface: 'computer-bootstrap', send_clicked: false,
597
+ dispatch_generation: dispatch.generation,
598
+ pre_send_rejection: true,
599
+ attestation: 'local_computer_dispatch_returned_without_send',
600
+ });
601
+ return;
602
+ }
603
+ const evidence = { surface: 'computer-bootstrap', ...(input.evidence ?? {}) };
604
+ this.observeEffect({ effectId: effect.effectId, observationId: input.observationId, outcome: input.outcome, evidence });
605
+ if (input.outcome === 'applied') this.hooks.effectApplied?.(task, effect, { observationId: input.observationId, evidence });
606
+ }
607
+ /** Persist a bounded preflight rejection; started generations require an exact no-click proof first. */
608
+ recordProviderPreparationFailure(input: { taskId: string; effectId: string; reasonCode: string; composerObservation?: WorkflowSupervisorPreflightComposerObservation }): void {
609
+ const task = this.requireTask(input.taskId);
610
+ const effect = this.store.getEffect(input.effectId);
611
+ if (effect?.taskId !== task.taskId) throw new Error('WORKFLOW_SUPERVISOR_PRE_SEND_EFFECT_TASK_MISMATCH');
612
+ this.store.recordEffectPreparationFailure(input.effectId, input.reasonCode, input.composerObservation);
613
+ }
614
+
615
+ browserBeginEffect(input: { conversationId: string; conversationUrl: string; effectId: string; dispatchId: string; dispatchGeneration: number; evidence?: Record<string, unknown> }): { started: boolean; mode: 'send' | 'reconcile'; generation: number } {
616
+ const task = this.requireBrowserTask(input.conversationId, input.conversationUrl);
617
+ requireNonTerminalTask(this.store, task.taskId);
618
+ const pending = this.store.nextBrowserEffect(task.taskId);
619
+ const effectId = validateEffectId(input.effectId);
620
+ if (!pending || pending.effect.effectId !== effectId) throw new Error('WORKFLOW_SUPERVISOR_BROWSER_EFFECT_NOT_CURRENT');
621
+ if (pending.mode !== 'send' || pending.generation !== input.dispatchGeneration) return { started: false, mode: 'reconcile', generation: pending.generation };
622
+ // Re-check immediately before committing dispatch; a queued browser command
623
+ // must not outlive an explicit conversation rebind.
624
+ if (!this.browserTaskActive(task)) throw new Error('WORKFLOW_SUPERVISOR_BROWSER_TASK_INACTIVE');
625
+ const snapshot = browserSnapshot(input.evidence);
626
+ if (!snapshot || browserTextHasEffect(snapshot.latestUserText, effectId) || !this.browserSnapshotMatchesSource(task, pending.effect, snapshot)) {
627
+ return { started: false, mode: 'reconcile', generation: pending.generation };
628
+ }
629
+ const dispatchEvidence = {
630
+ surface: typeof input.evidence?.surface === 'string' ? input.evidence.surface.slice(0, 128) : 'chrome-extension',
631
+ baseline_user_sha256: browserTextSha256(snapshot.latestUserText),
632
+ baseline_assistant_sha256: sha256(snapshot.latestAssistantResponse),
633
+ baseline_has_source_completion: Boolean(pending.effect.sourceCompletionFingerprint),
634
+ ...(this.hooks.effectDispatchEvidence?.({ task, effectId }) ?? {}),
635
+ };
636
+ const started = this.store.recordEffectDispatchStarted(effectId, input.dispatchGeneration, input.dispatchId, dispatchEvidence);
637
+ return { started, mode: started ? 'send' : 'reconcile', generation: input.dispatchGeneration };
638
+ }
639
+ browserObserveEffect(input: { conversationId: string; conversationUrl: string; effectId: string; observationId: string; outcome: 'applied' | 'not_applied' | 'unknown'; evidence?: Record<string, unknown> }): { recorded: true } {
640
+ const task = this.requireBrowserTask(input.conversationId, input.conversationUrl);
641
+ const effect = this.store.getEffect(validateEffectId(input.effectId));
642
+ if (!effect || effect.taskId !== task.taskId) throw new Error('WORKFLOW_SUPERVISOR_BROWSER_EFFECT_TASK_MISMATCH');
643
+ if (input.outcome !== 'not_applied') {
644
+ const evidence = sanitizeBrowserEvidence(input.evidence);
645
+ this.observeEffect({ effectId: effect.effectId, observationId: input.observationId, outcome: input.outcome, evidence });
646
+ if (input.outcome === 'applied') this.hooks.effectApplied?.(task, effect, { observationId: input.observationId, evidence });
647
+ return { recorded: true };
648
+ }
649
+ // A rendered page can still be stale, including an empty composer and the
650
+ // exact pre-send history. Absence in that page is never proof that the
651
+ // provider rejected a submission. Only the dispatch owner can attest a
652
+ // mechanical pre-send failure (bootstrapObserveEffect); browser observers
653
+ // cannot authorize re-dispatch of an outcome-unknown mutation.
654
+ this.store.recordEffectObservation(effect.effectId, input.observationId, 'unknown', {
655
+ ...sanitizeBrowserEvidence(input.evidence), reconciliation: true, reason: 'not_applied_proof_incomplete',
656
+ });
657
+ return { recorded: true };
658
+ }
659
+ /** Dispatch-owner attestation. Only the adapter that reserved this generation may call it. */
660
+ browserObserveDispatchFailure(input: { conversationId: string; conversationUrl: string; effectId: string; observationId: string; dispatchGeneration: number; reason: string; surface?: string }): void {
661
+ const task = this.requireBrowserTask(input.conversationId, input.conversationUrl);
662
+ const effect = this.store.getEffect(input.effectId);
663
+ if (effect?.taskId !== task.taskId) throw new Error('WORKFLOW_SUPERVISOR_BROWSER_EFFECT_TASK_MISMATCH');
664
+ const dispatch = this.store.latestEffectDispatch(input.effectId);
665
+ if (dispatch?.generation !== input.dispatchGeneration) throw new Error('WORKFLOW_SUPERVISOR_DISPATCH_GENERATION_CHANGED');
666
+ this.store.recordEffectNotAppliedProof(input.effectId, input.observationId, {
667
+ surface: input.surface?.trim().slice(0, 128) || 'macos-native', reason: input.reason, send_clicked: false, dispatch_generation: input.dispatchGeneration,
668
+ // This call is the dispatch owner's exact mechanical return before any
669
+ // Send click. The store refunds this generation once; the existing preflight
670
+ // gate bounds ordinary repeats without spending actual send budget.
671
+ pre_send_rejection: true,
672
+ });
673
+ const recoveryStopPending = effect.kind === 'recovery' && input.reason.trim() === 'provider_recovery_stop_pending';
674
+ if (!recoveryStopPending) {
675
+ this.recordProviderPreparationFailure({ taskId: task.taskId, effectId: effect.effectId, reasonCode: input.reason });
676
+ }
677
+ }
678
+ browserObserveProviderTurn(input: { conversationId: string; conversationUrl: string; generating: boolean; latestAssistantResponse: string; providerActivityText?: string; providerFailureCode?: string; receiptMissing?: boolean; observedAtMs: number; graceMs: number }): { state: 'inactive' | 'none' | 'generating' | 'idle_pending' | 'recovery_reserved' | 'exhausted'; recoveryEffect?: WorkflowSupervisorEffect } {
679
+ const task = this.requireBrowserTask(input.conversationId, input.conversationUrl);
680
+ if (!this.browserTaskActiveForExternalEffect(task)) return { state: 'inactive' };
681
+ // Exhaustion stops new recovery mutations, not read-only observation of the exhausted applied leaf.
682
+ let sourceEffect = this.store.latestAppliedEffectWithoutCompletion(task.taskId);
683
+ if (!sourceEffect) {
684
+ const exhaustedLeaf = this.store.latestAppliedLeafEffectWithoutCompletion(task.taskId);
685
+ if (exhaustedLeaf && this.store.providerResumeExhausted(exhaustedLeaf.effectId)) sourceEffect = exhaustedLeaf;
686
+ }
687
+ if (!sourceEffect) return { state: 'none' };
688
+ const recoveryId = stableEffectId(`provider-recovery:${sourceEffect.effectId}`);
689
+ const providerFailureCode = input.providerFailureCode?.trim();
690
+ const recoveryReason = providerFailureCode
691
+ ? `Applied Supervisor effect ${sourceEffect.effectId} ended with provider failure ${providerFailureCode} before a committed Supervisor completion. Resume from durable Forge state; the source effect remains applied and must not be replayed.`
692
+ : `Applied Supervisor effect ${sourceEffect.effectId} stopped making observable provider progress without a committed Supervisor completion. Resume from durable Forge state; the source effect remains applied and must not be replayed.`;
693
+ return this.store.observeProviderTurn({
694
+ taskId: task.taskId,
695
+ effectId: sourceEffect.effectId,
696
+ generating: input.generating,
697
+ receiptMissing: input.receiptMissing === true,
698
+ assistantDigest: sha256(`${input.latestAssistantResponse}\n${input.providerActivityText ?? ''}`),
699
+ providerFailureCode,
700
+ observedAtMs: input.observedAtMs,
701
+ graceMs: input.graceMs,
702
+ recovery: { effectId: recoveryId, prompt: this.renderPrompt(task, recoveryId, 'recovery', undefined, recoveryReason) },
703
+ });
704
+ }
705
+ async browserObserveAssistant(input: { conversationId: string; conversationUrl: string; responseText: string }): Promise<WorkflowAssistantObservationResult> {
706
+ const task = this.requireBrowserTask(input.conversationId, input.conversationUrl);
707
+ return await this.observeAssistantTurn({ taskId: task.taskId, conversationId: task.conversationId, responseText: input.responseText });
708
+ }
709
+
710
+ /**
711
+ * Repair the crash boundary where a CONTINUE completion was durably committed
712
+ * before successor reservation finished in a previous Runtime. This never
713
+ * replays the provider response; it only reads the committed causal receipt and
714
+ * reserves the exactly-once successor effect.
715
+ */
716
+ async reconcileCommittedContinuations(limit = 16): Promise<{ scanned: number; reconciled: number }> {
717
+ const completions = this.store.listContinueCompletionsAwaitingSuccessor(limit);
718
+ let reconciled = 0;
719
+ for (const completion of completions) {
720
+ const task = this.store.getTask(completion.taskId);
721
+ if (!task || this.store.terminalAction(task.taskId)) continue;
722
+ const committed = this.reserveContinuation(task, completion);
723
+ if (committed.successorEffect) reconciled += 1;
724
+ }
725
+
726
+ // A non-terminal Supervisor task must always have one derivable causal
727
+ // obligation: an unapplied effect, an applied effect awaiting completion,
728
+ // or a committed completion whose successor/terminal resolution can be
729
+ // reconstructed from durable facts. Registration is intentionally a
730
+ // separate transaction from effect reservation, so a Runtime crash between
731
+ // them must converge here instead of leaving an "active" inert task.
732
+ const inertTasks = this.store.listTasksWithoutCausalObligation(limit);
733
+ for (const task of inertTasks) {
734
+ if (this.store.terminalAction(task.taskId)) continue;
735
+ const latest = this.store.getLatestCompletion(task.taskId);
736
+ if (!latest) {
737
+ this.reserveEnrollment(task.taskId);
738
+ reconciled += 1;
739
+ continue;
740
+ }
741
+ if (latest.action === 'CONTINUE') {
742
+ const committed = this.reserveContinuation(task, latest);
743
+ if (committed.successorEffect) reconciled += 1;
744
+ continue;
745
+ }
746
+ if (latest.action === 'WAIT') continue;
747
+ const validation = await this.validateTerminalProposal(task, latest.proposal);
748
+ const correctionId = validation.valid ? undefined : stableEffectId(`completion:${latest.completionFingerprint}`);
749
+ this.store.resolveTerminal({
750
+ completionFingerprint: latest.completionFingerprint,
751
+ taskId: task.taskId,
752
+ action: latest.action,
753
+ accepted: validation.valid,
754
+ reason: validation.reason,
755
+ ...(correctionId ? {
756
+ correction: {
757
+ effectId: correctionId,
758
+ prompt: this.renderPrompt(task, correctionId, 'correction', latest.proposal.checkpoint, validation.reason),
759
+ },
760
+ } : {}),
761
+ });
762
+ reconciled += 1;
763
+ }
764
+ return { scanned: completions.length + inertTasks.length, reconciled };
765
+ }
766
+
767
+ async observeAssistantTurn(input: WorkflowAssistantObservation): Promise<WorkflowAssistantObservationResult> {
768
+ const task = this.requireTask(input.taskId);
769
+ if (task.conversationId !== input.conversationId) throw new Error('WORKFLOW_SUPERVISOR_CONVERSATION_MISMATCH');
770
+ const responseSha256 = sha256(input.responseText);
771
+ const expectedEffect = this.store.latestAppliedEffectWithoutCompletion(task.taskId)
772
+ ?? this.store.latestAppliedLeafEffectWithoutCompletion(task.taskId);
773
+ // A live applied effect always wins: an old compact receipt must never be
774
+ // allowed to satisfy a newer causal obligation. Only when no effect is
775
+ // awaiting completion may an exact persisted response hash recover the
776
+ // original effect context for idempotent duplicate observation.
777
+ const priorCompletion = expectedEffect ? undefined : this.store.getCompletionByResponseSha256(task.taskId, responseSha256);
778
+ const expectedEffectId = expectedEffect?.effectId ?? priorCompletion?.sourceEffectId;
779
+ const receiptEffect = expectedEffect ?? (expectedEffectId ? this.store.getEffect(expectedEffectId) : undefined);
780
+ const parsed = parseSupervisorCompletion(
781
+ input.responseText,
782
+ expectedEffectId ? { task, effectId: expectedEffectId, prompt: receiptEffect?.prompt } : undefined,
783
+ );
784
+ const sourceEffect = this.store.getEffect(parsed.proposal.sourceEffectId);
785
+ if (!sourceEffect || sourceEffect.taskId !== task.taskId || !this.store.effectApplied(sourceEffect.effectId)) throw new Error('WORKFLOW_SUPERVISOR_CAUSAL_EFFECT_NOT_APPLIED');
786
+ const explicitIdentityProtocol = sourceEffect.prompt.includes('conversation_id=') && sourceEffect.prompt.includes('task_id=') && sourceEffect.prompt.includes('supervisor_state');
787
+ if (parsed.proposal.conversationId && parsed.proposal.conversationId !== task.conversationId) throw new Error('WORKFLOW_SUPERVISOR_RESPONSE_CONVERSATION_MISMATCH');
788
+ if (parsed.proposal.taskId && parsed.proposal.taskId !== task.taskId) throw new Error('WORKFLOW_SUPERVISOR_RESPONSE_TASK_MISMATCH');
789
+ if (explicitIdentityProtocol && !parsed.proposal.conversationId) throw new Error('WORKFLOW_SUPERVISOR_RESPONSE_CONVERSATION_ID_REQUIRED');
790
+ if (explicitIdentityProtocol && !parsed.proposal.taskId) throw new Error('WORKFLOW_SUPERVISOR_RESPONSE_TASK_ID_REQUIRED');
791
+ if (explicitIdentityProtocol && !parsed.proposal.supervisorState) throw new Error('WORKFLOW_SUPERVISOR_RESPONSE_STATE_REQUIRED');
792
+ if (explicitIdentityProtocol && !parsed.proposal.activeScope) throw new Error('WORKFLOW_SUPERVISOR_RESPONSE_ACTIVE_SCOPE_REQUIRED');
793
+ const expectedScope = typeof task.completionContract.requirement_id === 'string' && task.completionContract.requirement_id.trim()
794
+ ? `requirement:${task.completionContract.requirement_id.trim()}`
795
+ : typeof task.continuationPolicy.active_scope === 'string' && task.continuationPolicy.active_scope.trim()
796
+ ? task.continuationPolicy.active_scope.trim()
797
+ : undefined;
798
+ if (expectedScope && parsed.proposal.activeScope && parsed.proposal.activeScope !== expectedScope) throw new Error('WORKFLOW_SUPERVISOR_RESPONSE_ACTIVE_SCOPE_MISMATCH');
799
+ const controlBlockSha256 = sha256(parsed.controlBlock);
800
+ const completionFingerprint = sha256(jsonIdentity(task.taskId, task.conversationId, parsed.proposal.sourceEffectId, responseSha256, controlBlockSha256));
801
+ const completion: WorkflowSupervisorCompletion = { completionFingerprint, taskId: task.taskId, sourceEffectId: parsed.proposal.sourceEffectId, action: parsed.proposal.action, responseSha256, controlBlockSha256, proposal: parsed.proposal, committedAt: new Date().toISOString() };
802
+ const terminal = this.store.terminalAction(task.taskId);
803
+ if (terminal) throw new Error(`WORKFLOW_SUPERVISOR_TASK_TERMINAL:${terminal}`);
804
+
805
+ if (parsed.proposal.action === 'CONTINUE') {
806
+ const committed = this.reserveContinuation(task, completion);
807
+ return { action: 'CONTINUE', completionFingerprint, terminal: false, successorEffect: committed.successorEffect!, deduplicated: committed.deduplicated };
808
+ }
809
+ if (parsed.proposal.action === 'WAIT') {
810
+ const committed = this.store.commitCompletion(completion);
811
+ return { action: 'WAIT', completionFingerprint, terminal: false, deduplicated: committed.deduplicated };
812
+ }
813
+ const committed = this.store.commitCompletion(completion);
814
+
815
+ const validation = await this.validateTerminalProposal(task, parsed.proposal);
816
+ const correctionId = validation.valid ? undefined : stableEffectId(`completion:${completionFingerprint}`);
817
+ const resolved = this.store.resolveTerminal({ completionFingerprint, taskId: task.taskId, action: parsed.proposal.action, accepted: validation.valid, reason: validation.reason,
818
+ ...(correctionId ? { correction: { effectId: correctionId, prompt: this.renderPrompt(task, correctionId, 'correction', parsed.proposal.reason === 'compact_receipt' ? undefined : parsed.proposal.checkpoint, validation.reason) } } : {}) });
819
+ return { action: parsed.proposal.action, completionFingerprint, terminal: validation.valid, ...(resolved.successorEffect ? { successorEffect: resolved.successorEffect } : {}), validation, deduplicated: committed.deduplicated || resolved.deduplicated };
820
+ }
821
+
822
+ /** Model CONTINUE and its causal successor commit in the same authority transaction. */
823
+ private reserveContinuation(task: WorkflowSupervisorTask, completion: WorkflowSupervisorCompletion) {
824
+ const nextId = stableEffectId(`completion:${completion.completionFingerprint}`);
825
+ const checkpoint = completion.proposal.reason === 'compact_receipt' ? undefined : completion.proposal.checkpoint;
826
+ return this.store.commitCompletion(completion, {
827
+ effectId: nextId,
828
+ kind: 'continuation',
829
+ prompt: this.renderPrompt(task, nextId, 'continuation', checkpoint),
830
+ // Rebuild from the same durable goal in a new logical conversation after
831
+ // a complete 15-receipt cycle; never migrate an uncompleted mutation.
832
+ ...(task.continuationPolicy.kind === 'standalone_supervisor'
833
+ ? { freshEnrollmentPrompt: this.renderPrompt(task, nextId, 'enrollment') } : {}),
834
+ });
835
+ }
836
+
837
+ private async validateTerminalProposal(
838
+ task: WorkflowSupervisorTask,
839
+ proposal: WorkflowSupervisorCompletion['proposal'],
840
+ ): Promise<WorkflowContractValidation> {
841
+ const validator = proposal.action === 'DONE' ? this.validators.completionContract : this.validators.userBlockerPolicy;
842
+ const validation = await validator(task, proposal);
843
+ if (typeof task.continuationPolicy.scheduled_wake_schedule_id !== 'string') return validation;
844
+ if (proposal.action === 'DONE') {
845
+ return {
846
+ ...validation,
847
+ valid: false,
848
+ reason: validation.valid ? 'scheduled_work_must_wait' : validation.reason,
849
+ evidence: [...(validation.evidence ?? []), 'A Work-bound recurring Schedule owns the next run; use WAIT instead of terminal DONE.'],
850
+ };
851
+ }
852
+ if (proposal.action === 'NEEDS_USER') {
853
+ return {
854
+ ...validation,
855
+ valid: false,
856
+ reason: validation.valid ? 'scheduled_work_must_remain_open' : validation.reason,
857
+ evidence: [...(validation.evidence ?? []), 'Persist the exact user blocker and use WAIT; a recurring scheduled Work must remain open.'],
858
+ };
859
+ }
860
+ return validation;
861
+ }
862
+
863
+ private renderPrompt(
864
+ task: WorkflowSupervisorTask,
865
+ effectId: string,
866
+ kind: WorkflowEffectKind,
867
+ checkpoint?: string,
868
+ correctionReason?: string,
869
+ lowerLayerContext?: string,
870
+ ): string {
871
+ const canonicalObjective = this.hooks.canonicalObjectiveForTask?.(task)?.trim();
872
+ const promptTask = canonicalObjective ? { ...task, objective: canonicalObjective } : task;
873
+ const cognition = this.hooks.advisoryCognitionForTask?.(promptTask);
874
+ return renderSupervisorPrompt(promptTask, effectId, kind, checkpoint, correctionReason, lowerLayerContext, cognition);
875
+ }
876
+
877
+ private browserTaskActive(task: WorkflowSupervisorTask): boolean { return this.hooks.browserTaskActive?.(task) ?? true; }
878
+ private browserTaskActiveForExternalEffect(task: WorkflowSupervisorTask): boolean {
879
+ return this.store.hasAppliedEffectAwaitingCompletion(task.taskId) || this.browserTaskActive(task);
880
+ }
881
+
882
+ /**
883
+ * Progress is a mechanical change in the canonical Work this turn is meant to
884
+ * advance, never the provider's willingness to answer again. Both endpoints of
885
+ * the comparison are durable facts: the Work projection captured in this
886
+ * effect's dispatch evidence and the current projection. A missing signal on
887
+ * either side is unknown, so it proves neither progress nor its absence.
888
+ */
889
+ private automationTurnChangedNothing(task: WorkflowSupervisorTask, effectId: string): boolean {
890
+ const dispatched = boundedBrowserText(this.store.latestEffectDispatch(effectId)?.evidence.work_progress_fingerprint, 256);
891
+ const current = boundedBrowserText(this.hooks.workProgressFingerprint?.(task), 256);
892
+ return Boolean(dispatched && current && dispatched === current);
893
+ }
894
+
895
+ private browserSnapshotMatchesSource(task: WorkflowSupervisorTask, effect: WorkflowSupervisorEffect, snapshot: { latestUserText: string; latestAssistantResponse: string }): boolean {
896
+ if (!effect.sourceCompletionFingerprint) return true;
897
+ const completion = this.store.getCompletion(effect.sourceCompletionFingerprint);
898
+ if (!completion || completion.taskId !== task.taskId) return false;
899
+ // A compact completion is committed only after observing the provider's
900
+ // finished assistant reply and checking its causal challenge. ChatGPT may
901
+ // subsequently collapse that historical reply or its original user marker;
902
+ // requiring the bytes to remain in the DOM would strand a valid successor.
903
+ // Persisted legacy tool receipts are read-only migration evidence; the MCP
904
+ // receipt writer has been retired. In both cases the committed completion,
905
+ // not a new heuristic, is the one authority for the successor.
906
+ const durableReceipt = completion.proposal.reason === 'compact_receipt'
907
+ || completion.proposal.reason === 'automation_tool_receipt';
908
+ const sourceEffect = this.store.getEffect(completion.sourceEffectId);
909
+ if (!sourceEffect || sourceEffect.taskId !== task.taskId) return false;
910
+ if (durableReceipt) return true;
911
+ if (sha256(snapshot.latestAssistantResponse) !== completion.responseSha256) return false;
912
+ return normalizeBrowserText(snapshot.latestUserText) === normalizeBrowserText(sourceEffect.prompt)
913
+ || browserTextHasEffect(snapshot.latestUserText, sourceEffect.effectId);
914
+ }
915
+
916
+ private requireTask(taskId: string): WorkflowSupervisorTask { const task = this.store.getTask(taskId); if (!task) throw new Error('WORKFLOW_SUPERVISOR_TASK_UNKNOWN'); return task; }
917
+ private requireBrowserTask(conversationId: string, conversationUrl: string): WorkflowSupervisorTask {
918
+ const observed = parseChatgptConversationIdentity(conversationUrl);
919
+ if (observed.conversationId !== conversationId) throw new Error('WORKFLOW_SUPERVISOR_BROWSER_CONVERSATION_MISMATCH');
920
+ const task = this.store.getTaskByConversationId(conversationId);
921
+ if (!task) throw new Error('WORKFLOW_SUPERVISOR_BROWSER_CONVERSATION_NOT_ENROLLED');
922
+ const registered = parseChatgptConversationIdentity(task.conversationUrl);
923
+ // Project routing is presentation metadata. A ChatGPT conversation can move
924
+ // between `/c/<id>` and a Project-prefixed route without becoming a different
925
+ // provider conversation, so the durable id is the only identity fence here.
926
+ if (task.conversationId !== conversationId || registered.conversationId !== conversationId || observed.conversationId !== conversationId) throw new Error('WORKFLOW_SUPERVISOR_BROWSER_CONVERSATION_MISMATCH');
927
+ return task;
928
+ }
929
+ }
930
+
931
+ function boundedBrowserText(value: unknown, max: number): string | undefined { return typeof value === 'string' && Buffer.byteLength(value, 'utf8') <= max ? value : undefined; }
932
+ /**
933
+ * A task that already reached DONE/NEEDS_USER can never deliver another effect.
934
+ * Reserving into it reported `enrolled` and left the caller waiting for a turn
935
+ * that the terminal action forbids.
936
+ */
937
+ function requireNonTerminalTask(store: WorkflowSupervisorStore, taskId: string): void {
938
+ const terminal = store.terminalAction(taskId);
939
+ if (terminal) throw new Error(`WORKFLOW_SUPERVISOR_TASK_TERMINAL:${terminal}`);
940
+ }
941
+ function browserSnapshot(evidence: Record<string, unknown> | undefined): { latestUserText: string; latestAssistantResponse: string } | undefined {
942
+ const latestUserText = boundedBrowserText(evidence?.latest_user_text, 128 * 1024);
943
+ const latestAssistantResponse = boundedBrowserText(evidence?.latest_assistant_response, 512 * 1024);
944
+ if (latestUserText === undefined || latestAssistantResponse === undefined) return undefined;
945
+ return { latestUserText, latestAssistantResponse };
946
+ }
947
+ function normalizeBrowserText(value: string): string { return value.replace(/\s+/g, ' ').trim(); }
948
+ function browserTextSha256(value: string): string { return sha256(normalizeBrowserText(value)); }
949
+ function browserTextHasEffect(value: string, effectId: string): boolean { return value.includes(renderEffectMarker(effectId)); }
950
+ const PERSISTED_BROWSER_EVIDENCE_KEYS = new Set(['exact_user_message', 'observation_fingerprint', 'reconciliation', 'reason', 'surface', 'target_marker_present']);
951
+ function sanitizeBrowserEvidence(evidence: Record<string, unknown> | undefined): Record<string, unknown> {
952
+ if (!evidence) return {};
953
+ const sanitized: Record<string, unknown> = {};
954
+ for (const [key, value] of Object.entries(evidence)) {
955
+ if (!PERSISTED_BROWSER_EVIDENCE_KEYS.has(key)) continue;
956
+ if (typeof value === 'boolean' || (typeof value === 'number' && Number.isFinite(value))) sanitized[key] = value;
957
+ else if (typeof value === 'string' && value.length <= 512) sanitized[key] = value;
958
+ }
959
+ return sanitized;
960
+ }
961
+
962
+ function browserTask(task: WorkflowSupervisorTask): WorkflowSupervisorBrowserTask { return { taskId: task.taskId, conversationId: task.conversationId, conversationUrl: task.conversationUrl }; }
963
+ function jsonIdentity(...values: string[]): string { return JSON.stringify(values); }