@moretea-labs/forge 1.7.1 → 1.8.0

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 (685) hide show
  1. package/AGENTS.md +58 -62
  2. package/CHANGELOG.md +39 -0
  3. package/CLAUDE.md +42 -62
  4. package/README.md +2 -2
  5. package/SKILL.md +0 -1
  6. package/THIRD_PARTY_NOTICES.md +5 -2
  7. package/adapters/browser/index.ts +2 -0
  8. package/adapters/chatgpt/browser-delivery-host.ts +71 -0
  9. package/adapters/chatgpt/browser-delivery-runtime.ts +783 -0
  10. package/adapters/chatgpt/controller-binding-store.ts +55 -0
  11. package/adapters/chatgpt/controller-host.ts +119 -0
  12. package/adapters/chatgpt/controller-round-host.ts +78 -0
  13. package/adapters/chatgpt/controller-round-settlement-store.ts +45 -0
  14. package/adapters/chatgpt/provider-delivery.ts +113 -0
  15. package/adapters/chatgpt/work-conversation-binding-store.ts +192 -0
  16. package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
  17. package/adapters/computer/desktop-operator-contract.ts +7 -0
  18. package/adapters/computer/desktop-operator-discovery.ts +201 -0
  19. package/adapters/computer/desktop-operator-negotiation.ts +174 -0
  20. package/adapters/computer/desktop-operator-provider.ts +312 -0
  21. package/adapters/computer/index.ts +1 -0
  22. package/adapters/computer/interaction-target-authority.ts +944 -0
  23. package/adapters/controller-process/binding-store.ts +34 -0
  24. package/adapters/controller-process/controller-host.ts +26 -0
  25. package/adapters/mcp/access-mode.ts +132 -0
  26. package/adapters/mcp/audit.ts +32 -0
  27. package/adapters/mcp/auth.ts +407 -0
  28. package/adapters/mcp/controller-round-compatibility.ts +184 -0
  29. package/adapters/mcp/frozen-client-semantic-compatibility.ts +292 -0
  30. package/adapters/mcp/instructions.ts +36 -0
  31. package/adapters/mcp/legacy-context.ts +18 -0
  32. package/adapters/mcp/multi-repository.ts +437 -0
  33. package/adapters/mcp/oauth.ts +391 -0
  34. package/adapters/mcp/paths.ts +140 -0
  35. package/adapters/mcp/policy.ts +232 -0
  36. package/adapters/mcp/proxy-env.ts +62 -0
  37. package/adapters/mcp/readiness-projection.ts +24 -0
  38. package/adapters/mcp/redaction.ts +21 -0
  39. package/adapters/mcp/repo.ts +24 -0
  40. package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
  41. package/adapters/mcp/runtime-gateway/context-adapter.ts +829 -0
  42. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +191 -0
  43. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +147 -0
  44. package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
  45. package/adapters/mcp/runtime-gateway/execution-tools.ts +207 -0
  46. package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
  47. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +411 -0
  48. package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
  49. package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
  50. package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
  51. package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
  52. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +308 -0
  53. package/adapters/mcp/runtime-gateway/process-tools.ts +289 -0
  54. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +76 -0
  55. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +268 -0
  56. package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
  57. package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
  58. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +110 -0
  59. package/adapters/mcp/runtime-gateway/result-adapter.ts +108 -0
  60. package/adapters/mcp/runtime-gateway/router.ts +728 -0
  61. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1251 -0
  62. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +634 -0
  63. package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
  64. package/adapters/mcp/runtime-gateway/runtime-tools.ts +68 -0
  65. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +186 -0
  66. package/adapters/mcp/runtime-gateway/shared-adapter.ts +25 -0
  67. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +870 -0
  68. package/adapters/mcp/runtime-gateway/work-adapter.ts +3186 -0
  69. package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +502 -0
  70. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
  71. package/adapters/mcp/server.ts +1048 -0
  72. package/adapters/mcp/setup.ts +791 -0
  73. package/adapters/mcp/tool-capability-cache.ts +87 -0
  74. package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
  75. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5265 -0
  76. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +150 -0
  77. package/adapters/mcp/tool-mapping/repository-tools.ts +1691 -0
  78. package/adapters/mcp/tool-mapping/tools.ts +9 -0
  79. package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
  80. package/adapters/mcp/toolset-names.ts +74 -0
  81. package/adapters/mcp/toolset-selection.ts +41 -0
  82. package/adapters/mcp/toolset.ts +336 -0
  83. package/adapters/mcp/transports/http.ts +1456 -0
  84. package/adapters/mcp/transports/session-registry.ts +513 -0
  85. package/adapters/mcp/transports/stdio.ts +8 -0
  86. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +139 -0
  87. package/adapters/mcp/types.ts +41 -0
  88. package/assets/AGENTS.md +2 -16
  89. package/assets/CLAUDE.md +2 -16
  90. package/assets/hooks/AGENTS.md +3 -14
  91. package/assets/hooks/CLAUDE.md +3 -14
  92. package/assets/hooks/hook-input.sh +9 -2
  93. package/assets/hooks/lib/workflow-state.sh +28 -10
  94. package/assets/hooks/prompt-guard.sh +63 -37
  95. package/assets/hooks/security-sentinel.sh +2 -1
  96. package/assets/hooks/session-start-context.sh +7 -5
  97. package/assets/hooks/stop-orchestrator.sh +13 -3
  98. package/assets/initializer-question-pack.v4.json +11 -7
  99. package/assets/partials/05-workflow.partial.md +18 -73
  100. package/assets/partials/07-footer.partial.md +1 -1
  101. package/assets/partials-agents/02-operating-mode.partial.md +1 -2
  102. package/assets/partials-agents/04-task-protocol.partial.md +14 -59
  103. package/assets/partials-agents/06-quality-safety.partial.md +11 -16
  104. package/assets/partials-agents/08-deep-docs.partial.md +1 -2
  105. package/assets/plan-map.json +11 -11
  106. package/assets/plugin-registry.v1.json +9 -3
  107. package/assets/plugin-trust-policy.v1.json +6 -0
  108. package/assets/recovery/ForgeRecovery.ps1 +100 -0
  109. package/assets/recovery/forge-wsl-rescue +271 -0
  110. package/assets/reference-configs/development-protocol.md +11 -15
  111. package/assets/reference-configs/document-generation.md +1 -1
  112. package/assets/reference-configs/global-working-rules.md +9 -1
  113. package/assets/reference-configs/harness-overview.md +22 -112
  114. package/assets/reference-configs/hook-operations.md +1 -1
  115. package/assets/reference-configs/sprint-contracts.md +2 -2
  116. package/assets/skill-version.json +2 -2
  117. package/assets/templates/helpers/architecture-event.ts +3 -34
  118. package/assets/templates/helpers/architecture-queue.sh +61 -10
  119. package/assets/templates/helpers/archive-workflow.sh +90 -180
  120. package/assets/templates/helpers/capability-config.ts +8 -11
  121. package/assets/templates/helpers/capability-resolver.ts +37 -19
  122. package/assets/templates/helpers/capture-plan.sh +2 -2
  123. package/assets/templates/helpers/check-architecture-sync.sh +1 -9
  124. package/assets/templates/helpers/check-task-sync.sh +82 -11
  125. package/assets/templates/helpers/check-task-workflow.sh +1 -3
  126. package/assets/templates/helpers/context-contract-sync.sh +1 -12
  127. package/assets/templates/helpers/contract-worktree.sh +26 -24
  128. package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
  129. package/assets/templates/helpers/inspect-project-state.ts +1 -1
  130. package/assets/templates/helpers/migrate-project-template.sh +1 -1
  131. package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
  132. package/assets/templates/helpers/new-plan.sh +2 -2
  133. package/assets/templates/helpers/plan-to-todo.sh +3 -31
  134. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  135. package/assets/templates/helpers/switch-plan.sh +1 -1
  136. package/assets/templates/helpers/workflow-contract.ts +1 -0
  137. package/assets/workflow-contract.v1.json +63 -171
  138. package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
  139. package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
  140. package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
  141. package/docs/architecture/CURRENT.md +148 -13
  142. package/docs/architecture/EVOLUTION.md +13 -0
  143. package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
  144. package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
  145. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
  146. package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
  147. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +35 -0
  148. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +296 -0
  149. package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
  150. package/docs/architecture/domains/verification.md +7 -0
  151. package/docs/architecture/domains/workflow-engine.md +1 -1
  152. package/docs/architecture/index.md +3 -3
  153. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
  154. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
  155. package/docs/forge-chatgpt-mcp-setup.md +5 -3
  156. package/docs/forge-plugin-management.md +20 -4
  157. package/docs/forge-runtime-self-healing-loop.md +2 -0
  158. package/docs/operations/benchmark-cross-version-freeze.md +38 -0
  159. package/docs/operations/controller-browser-plugin.md +3 -1
  160. package/docs/operations/homebrew.md +2 -2
  161. package/docs/operations/platform-support.md +16 -8
  162. package/docs/operations/platform-support.zh-CN.md +2 -2
  163. package/docs/operations/standalone-disaster-recovery.md +26 -1
  164. package/docs/operations/uu-remote-rescue.md +41 -0
  165. package/docs/operations/windows-wsl-host-rescue.md +133 -0
  166. package/docs/public-usage-guide.md +1 -1
  167. package/docs/public-usage-guide.zh-CN.md +1 -1
  168. package/docs/tutorials/01-install-and-start.md +1 -1
  169. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  170. package/docs/tutorials/02-connect-chatgpt.md +4 -4
  171. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
  172. package/docs/wiki/Implementation.md +1 -1
  173. package/package.json +23 -14
  174. package/packages/kernel/controller/api/index.ts +2 -0
  175. package/packages/kernel/controller/application/controller-round-service.ts +29 -0
  176. package/packages/kernel/controller/application/controller-service.ts +30 -0
  177. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +308 -0
  178. package/packages/kernel/controller/domain/controller-round.ts +107 -0
  179. package/packages/kernel/controller/domain/execution-quality.ts +133 -0
  180. package/packages/kernel/controller/domain/types.ts +56 -0
  181. package/packages/kernel/controller/index.ts +1 -0
  182. package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
  183. package/packages/kernel/controller/infrastructure/controller-round-store.ts +1384 -0
  184. package/packages/kernel/controller/infrastructure/controller-session-store.ts +578 -0
  185. package/packages/kernel/controller/ports/controller-host.ts +16 -0
  186. package/packages/kernel/goal/api/index.ts +1 -0
  187. package/packages/kernel/goal/domain/types.ts +40 -0
  188. package/packages/kernel/goal/index.ts +1 -0
  189. package/packages/kernel/identity/api/index.ts +5 -0
  190. package/packages/kernel/identity/application/identity-service.ts +67 -0
  191. package/packages/kernel/identity/domain/scope.ts +63 -0
  192. package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
  193. package/packages/kernel/identity/domain/types.ts +44 -0
  194. package/packages/kernel/identity/domain/workspace.ts +56 -0
  195. package/packages/kernel/identity/index.ts +1 -0
  196. package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
  197. package/packages/kernel/memory/api/index.ts +5 -0
  198. package/packages/kernel/memory/application/experience-service.ts +118 -0
  199. package/packages/kernel/memory/application/outcome-service.ts +77 -0
  200. package/packages/kernel/memory/domain/experience.ts +103 -0
  201. package/packages/kernel/memory/domain/operational-prior.ts +92 -0
  202. package/packages/kernel/memory/index.ts +1 -0
  203. package/packages/kernel/progression/api/index.ts +2 -0
  204. package/packages/kernel/progression/application/projector.ts +211 -0
  205. package/packages/kernel/progression/domain/types.ts +116 -0
  206. package/packages/kernel/progression/index.ts +1 -0
  207. package/packages/kernel/scheduler/api/index.ts +6 -0
  208. package/packages/kernel/scheduler/application/continuation-service.ts +212 -0
  209. package/packages/kernel/scheduler/application/eligibility.ts +252 -0
  210. package/packages/kernel/scheduler/application/schedule-service.ts +24 -0
  211. package/packages/kernel/scheduler/application/settlement.ts +197 -0
  212. package/packages/kernel/scheduler/domain/continuation.ts +38 -0
  213. package/packages/kernel/scheduler/domain/schedule.ts +149 -0
  214. package/packages/kernel/scheduler/index.ts +1 -0
  215. package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +38 -0
  216. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +785 -0
  217. package/packages/kernel/work/api/index.ts +15 -0
  218. package/packages/kernel/work/application/work-service.ts +148 -0
  219. package/packages/kernel/work/domain/admission-policy.ts +34 -0
  220. package/packages/kernel/work/domain/check-receipt.ts +43 -0
  221. package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
  222. package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
  223. package/packages/kernel/work/domain/engineering-design.ts +281 -0
  224. package/packages/kernel/work/domain/engineering-profile.ts +242 -0
  225. package/packages/kernel/work/domain/execution-concurrency.ts +254 -0
  226. package/packages/kernel/work/domain/execution-snapshot.ts +33 -0
  227. package/packages/kernel/work/domain/implementation-review.ts +510 -0
  228. package/packages/kernel/work/domain/project-engineering-contract.ts +202 -0
  229. package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
  230. package/packages/kernel/work/domain/state-machine.ts +259 -0
  231. package/packages/kernel/work/domain/types.ts +528 -0
  232. package/packages/kernel/work/index.ts +1 -0
  233. package/packages/kernel/work/infrastructure/work-contract-store.ts +1876 -0
  234. package/packages/kernel/work/ports/work-contract-store.ts +18 -0
  235. package/packages/plugin-runtime/browser/index.ts +3 -0
  236. package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
  237. package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
  238. package/packages/plugin-runtime/browser/session-authority.ts +61 -0
  239. package/packages/plugin-runtime/computer/index.ts +6 -0
  240. package/packages/plugin-runtime/computer/provider-error.ts +26 -0
  241. package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
  242. package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
  243. package/packages/plugin-runtime/computer/provider.ts +19 -0
  244. package/packages/plugin-runtime/computer/target-authority.ts +190 -0
  245. package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
  246. package/packages/plugin-runtime/external/index.ts +1 -0
  247. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +457 -0
  248. package/packages/plugin-runtime/index.ts +3 -0
  249. package/packages/protocols/browser/index.ts +1 -0
  250. package/packages/protocols/browser/session.ts +98 -0
  251. package/packages/protocols/computer/contract.ts +199 -0
  252. package/packages/protocols/computer/index.ts +1 -0
  253. package/packages/protocols/failure/contract.ts +158 -0
  254. package/packages/protocols/failure/index.ts +1 -0
  255. package/packages/protocols/handoff/index.ts +1 -0
  256. package/packages/protocols/handoff/status.ts +22 -0
  257. package/packages/protocols/mcp/execution-context.ts +11 -0
  258. package/packages/protocols/mcp/index.ts +2 -0
  259. package/packages/protocols/mcp/tool-contract.ts +42 -0
  260. package/packages/workflow-runtime/api/index.ts +2 -0
  261. package/packages/workflow-runtime/application/interpreter.ts +342 -0
  262. package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
  263. package/packages/workflow-runtime/index.ts +1 -0
  264. package/scripts/assemble-template.ts +1 -1
  265. package/scripts/benchmark-computer-hotpath.ts +255 -0
  266. package/scripts/benchmark-controller-round2.ts +54 -2
  267. package/scripts/benchmark-ios-agent-device.ts +3 -6
  268. package/scripts/benchmark-mcp-transport.ts +3 -4
  269. package/scripts/benchmark-operational-memory-activation.ts +235 -0
  270. package/scripts/benchmark-operational-shadow.ts +28 -0
  271. package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
  272. package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
  273. package/scripts/canary-controller-semantic-wait.ts +195 -0
  274. package/scripts/canary-global-hook.sh +2 -2
  275. package/scripts/check-mcp-compatibility.ts +173 -7
  276. package/scripts/check-platform-support.mjs +28 -2
  277. package/scripts/check-public-docs.sh +1 -1
  278. package/scripts/check-release-readiness.sh +1 -1
  279. package/scripts/check-repository-hygiene.mjs +92 -0
  280. package/scripts/check-runtime-architecture.mjs +1206 -95
  281. package/scripts/check-source-duplication.mjs +9 -1
  282. package/scripts/check-tarball-install-smoke.sh +98 -15
  283. package/scripts/controller-ngrok-rotation.sh +5 -4
  284. package/scripts/create-project-dirs.sh +10 -74
  285. package/scripts/forge-local-recovery-helper.mjs +204 -0
  286. package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
  287. package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
  288. package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
  289. package/scripts/hook-shim.sh +2 -2
  290. package/scripts/init-project.sh +12 -70
  291. package/scripts/install-desktop-operator-registration.ts +4 -2
  292. package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
  293. package/scripts/install-standalone-recovery.ts +122 -10
  294. package/scripts/install-uu-remote-rescue-registration.ts +74 -0
  295. package/scripts/install-windows-host-recovery-registration.ts +22 -0
  296. package/scripts/lib/controller-home-env.sh +6 -3
  297. package/scripts/lib/project-init-lib.sh +39 -582
  298. package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
  299. package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
  300. package/scripts/open-source-audit-allowlist.txt +12 -1
  301. package/scripts/portability-bindings.mjs +70 -0
  302. package/scripts/run-bounded-command.mjs +55 -0
  303. package/scripts/run-bun-test-file.ts +22 -6
  304. package/scripts/run-governed-gate.ts +4 -1
  305. package/scripts/smoke-mcp-http-runtime.ts +1 -1
  306. package/scripts/smoke-runtime-recovery.ts +34 -1
  307. package/scripts/smoke-schedule-engine.ts +1 -1
  308. package/scripts/stage-runtime-release.ts +6 -1
  309. package/scripts/test-governance.ts +51 -2
  310. package/src/cli/agent-jobs/job-manager.ts +10 -1
  311. package/src/cli/chatgpt-browser/bind-server.ts +2 -1
  312. package/src/cli/chatgpt-browser/binding.ts +20 -10
  313. package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
  314. package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
  315. package/src/cli/chatgpt-browser/engine.ts +10 -11
  316. package/src/cli/chatgpt-browser/native-provider.ts +24 -83
  317. package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
  318. package/src/cli/chatgpt-browser/types.ts +1 -0
  319. package/src/cli/commands/bootstrap-control.ts +334 -0
  320. package/src/cli/commands/brain-assistant.ts +38 -0
  321. package/src/cli/commands/brain.ts +193 -40
  322. package/src/cli/commands/chatgpt.ts +127 -0
  323. package/src/cli/commands/computer.ts +437 -0
  324. package/src/cli/commands/doctor.ts +18 -1
  325. package/src/cli/commands/init-hook.ts +71 -61
  326. package/src/cli/commands/init.ts +3 -1
  327. package/src/cli/commands/mcp.ts +16 -10
  328. package/src/cli/commands/plugin.ts +38 -12
  329. package/src/cli/commands/recovery.ts +493 -80
  330. package/src/cli/commands/runtime.ts +9 -4
  331. package/src/cli/commands/setup-profile.ts +89 -39
  332. package/src/cli/commands/status.ts +2 -1
  333. package/src/cli/controller/check-runner.ts +487 -98
  334. package/src/cli/controller/context/types.ts +7 -1
  335. package/src/cli/controller/context-pack.ts +23 -3
  336. package/src/cli/controller/issue-store.ts +1 -1
  337. package/src/cli/controller/systemd-user.ts +110 -0
  338. package/src/cli/controller/types.ts +10 -70
  339. package/src/cli/controller/worklog.ts +67 -13
  340. package/src/cli/editing/edit-operation-contract.ts +92 -0
  341. package/src/cli/editing/edit-session.ts +289 -13
  342. package/src/cli/github/plugin.ts +17 -12
  343. package/src/cli/hook/route-registry.ts +3 -5
  344. package/src/cli/hook/runtime.ts +49 -3
  345. package/src/cli/index.ts +3 -0
  346. package/src/cli/local-bridge/connector-freshness.ts +2 -2
  347. package/src/cli/local-bridge/console-automations.ts +2 -2
  348. package/src/cli/local-bridge/facade-api.ts +41 -14
  349. package/src/cli/local-bridge/job-store.ts +1 -1
  350. package/src/cli/local-bridge/server.ts +24 -27
  351. package/src/cli/mcp/access-mode.ts +2 -132
  352. package/src/cli/mcp/access-tools.ts +2 -387
  353. package/src/cli/mcp/audit.ts +2 -32
  354. package/src/cli/mcp/auth.ts +2 -404
  355. package/src/cli/mcp/instructions.ts +2 -34
  356. package/src/cli/mcp/legacy-context.ts +2 -16
  357. package/src/cli/mcp/legacy-tool-service.ts +2 -5308
  358. package/src/cli/mcp/multi-repository.ts +2 -436
  359. package/src/cli/mcp/oauth.ts +2 -375
  360. package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
  361. package/src/cli/mcp/paths.ts +2 -140
  362. package/src/cli/mcp/policy.ts +2 -232
  363. package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
  364. package/src/cli/mcp/proxy-env.ts +2 -62
  365. package/src/cli/mcp/readiness-projection.ts +2 -24
  366. package/src/cli/mcp/redaction.ts +2 -21
  367. package/src/cli/mcp/repo.ts +2 -24
  368. package/src/cli/mcp/repository-tools.ts +2 -1585
  369. package/src/cli/mcp/server.ts +2 -951
  370. package/src/cli/mcp/setup.ts +2 -737
  371. package/src/cli/mcp/tool-capability-cache.ts +2 -87
  372. package/src/cli/mcp/tool-contract.ts +2 -14
  373. package/src/cli/mcp/tools.ts +2 -9
  374. package/src/cli/mcp/toolset-names.ts +2 -70
  375. package/src/cli/mcp/toolset-selection.ts +2 -41
  376. package/src/cli/mcp/toolset.ts +2 -336
  377. package/src/cli/mcp/transports/AGENTS.md +1 -10
  378. package/src/cli/mcp/transports/CLAUDE.md +1 -10
  379. package/src/cli/mcp/transports/http.ts +2 -1414
  380. package/src/cli/mcp/transports/session-registry.ts +2 -490
  381. package/src/cli/mcp/transports/stdio.ts +2 -8
  382. package/src/cli/mcp/types.ts +2 -41
  383. package/src/cli/mcp/umbrella-tools.ts +2 -117
  384. package/src/cli/repositories/command-executor.ts +64 -18
  385. package/src/cli/repositories/command-scope.ts +117 -1
  386. package/src/cli/repositories/controller-home.ts +119 -1
  387. package/src/cli/repositories/identity.ts +13 -0
  388. package/src/cli/repositories/registry.ts +68 -49
  389. package/src/cli/repositories/repository-snapshot.ts +53 -7
  390. package/src/cli/repositories/runtime-storage.ts +27 -2
  391. package/src/cli/repositories/safe-patch.ts +34 -38
  392. package/src/cli/repositories/selected-path-actions.ts +22 -2
  393. package/src/cli/repositories/structured-git.ts +105 -25
  394. package/src/cli/runtime/helper-runner.ts +7 -0
  395. package/src/cli/runtime-invocation.ts +8 -1
  396. package/src/cli/tools/codegraph.ts +44 -25
  397. package/src/core/adoption/manifest-templates.ts +1 -1
  398. package/src/core/adoption/plan.ts +3 -26
  399. package/src/core/adoption/workflow-contract-plan.ts +17 -9
  400. package/src/effects/process-runner.ts +8 -14
  401. package/src/runtime/context/assistant-context.ts +154 -0
  402. package/src/runtime/context/assistant-work-context.ts +147 -0
  403. package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
  404. package/src/runtime/context/codegraph-read-provider.ts +55 -22
  405. package/src/runtime/context/codegraph-sidecar.cjs +8 -1
  406. package/src/runtime/context/context-closure.ts +268 -0
  407. package/src/runtime/context/context-plane.ts +211 -0
  408. package/src/runtime/context/project-engineering-contract.ts +60 -0
  409. package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
  410. package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
  411. package/src/runtime/control-plane/bootstrap/index.ts +5 -0
  412. package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
  413. package/src/runtime/control-plane/bootstrap/store.ts +21 -0
  414. package/src/runtime/control-plane/bootstrap/types.ts +87 -0
  415. package/src/runtime/control-plane/cleanup-artifact-retention.ts +328 -0
  416. package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
  417. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +320 -0
  418. package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +169 -0
  419. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +350 -0
  420. package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +85 -0
  421. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +417 -100
  422. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +103 -15
  423. package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
  424. package/src/runtime/control-plane/execution/repository-work-attribution.ts +2 -2
  425. package/src/runtime/control-plane/execution/retained-work-resume.ts +336 -6
  426. package/src/runtime/control-plane/execution/validation.ts +28 -3
  427. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +116 -0
  428. package/src/runtime/control-plane/execution/work-completion-authority.ts +44 -2
  429. package/src/runtime/control-plane/execution/work-evidence-policy.ts +34 -3
  430. package/src/runtime/control-plane/execution/work-execution-support.ts +42 -22
  431. package/src/runtime/control-plane/execution/work-finalization-service.ts +1103 -166
  432. package/src/runtime/control-plane/execution/work-handle-authority.ts +267 -16
  433. package/src/runtime/control-plane/execution/work-handle-store.ts +77 -0
  434. package/src/runtime/control-plane/execution/work-head-settlement.ts +75 -0
  435. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +145 -23
  436. package/src/runtime/control-plane/execution/work-operation-service.ts +49 -13
  437. package/src/runtime/control-plane/execution/work-preparation-service.ts +31 -15
  438. package/src/runtime/control-plane/execution/work-remote-delivery.ts +1 -1
  439. package/src/runtime/control-plane/execution/work-task-receipt.ts +20 -1
  440. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +407 -76
  441. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +88 -18
  442. package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
  443. package/src/runtime/control-plane/execution/work-verification-service.ts +258 -4
  444. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +200 -15
  445. package/src/runtime/control-plane/facade/capability-registry.ts +21 -10
  446. package/src/runtime/control-plane/facade/controller-round-relay.ts +3 -1118
  447. package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
  448. package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -3
  449. package/src/runtime/control-plane/facade/goal-workloop.ts +972 -137
  450. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +158 -0
  451. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +10 -0
  452. package/src/runtime/control-plane/facade/index.ts +4 -2
  453. package/src/runtime/control-plane/facade/operation-digest.ts +22 -58
  454. package/src/runtime/control-plane/facade/plan-contract-store.ts +1004 -38
  455. package/src/runtime/control-plane/facade/repository-work-admission.ts +19 -1
  456. package/src/runtime/control-plane/facade/requirement-authority.ts +218 -0
  457. package/src/runtime/control-plane/facade/requirement-board.ts +6 -1
  458. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +48 -0
  459. package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
  460. package/src/runtime/control-plane/facade/suggested-actions.ts +4 -1
  461. package/src/runtime/control-plane/facade/types.ts +189 -532
  462. package/src/runtime/control-plane/facade/work-admission-policy.ts +84 -35
  463. package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
  464. package/src/runtime/control-plane/facade/work-implementation-review.ts +2 -0
  465. package/src/runtime/control-plane/facade/work-state-machine.ts +2 -250
  466. package/src/runtime/control-plane/generated-cache-retention.ts +239 -18
  467. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +12 -1
  468. package/src/runtime/control-plane/global-scheduler/maintenance.ts +76 -13
  469. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
  470. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
  471. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -1
  472. package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
  473. package/src/runtime/control-plane/global-scheduler/scheduler.ts +173 -81
  474. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
  475. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +397 -0
  476. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
  477. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +301 -560
  478. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +20 -4
  479. package/src/runtime/control-plane/launcher/thin-launcher.ts +50 -4
  480. package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
  481. package/src/runtime/control-plane/persistence/experience-store.ts +169 -0
  482. package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
  483. package/src/runtime/control-plane/persistence/requirement-store.ts +30 -22
  484. package/src/runtime/control-plane/persistence/sqlite-store.ts +545 -76
  485. package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
  486. package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
  487. package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
  488. package/src/runtime/control-plane/release-retention.ts +56 -16
  489. package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
  490. package/src/runtime/control-plane/routing/route-policy.ts +19 -0
  491. package/src/runtime/control-plane/runtime-cleanup.ts +574 -37
  492. package/src/runtime/control-plane/runtime-generation.ts +6 -0
  493. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +465 -0
  494. package/src/runtime/control-plane/workspace/workspace-store.ts +80 -0
  495. package/src/runtime/diagnostics/incident-repair.ts +406 -0
  496. package/src/runtime/diagnostics/mcp-timing.ts +125 -6
  497. package/src/runtime/diagnostics/performance.ts +6 -14
  498. package/src/runtime/diagnostics/read-only-tool.ts +1 -1
  499. package/src/runtime/evidence/artifact-store.ts +65 -2
  500. package/src/runtime/evidence/operational-shadow.ts +38 -0
  501. package/src/runtime/evidence/process-check-receipt.ts +5 -43
  502. package/src/runtime/evidence/sensitive-output.ts +4 -4
  503. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
  504. package/src/runtime/execution/jobs/store.ts +121 -2
  505. package/src/runtime/execution/managed-workspace.ts +55 -5
  506. package/src/runtime/execution/process-runtime/canary.ts +19 -5
  507. package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
  508. package/src/runtime/execution/process-runtime/check-facade.ts +23 -9
  509. package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
  510. package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
  511. package/src/runtime/execution/process-runtime/check-result.ts +73 -2
  512. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -3
  513. package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
  514. package/src/runtime/execution/process-runtime/command-facade.ts +38 -87
  515. package/src/runtime/execution/process-runtime/index.ts +1 -0
  516. package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
  517. package/src/runtime/execution/process-runtime/lightweight-managed.ts +32 -1
  518. package/src/runtime/execution/process-runtime/persisted-check.ts +128 -21
  519. package/src/runtime/execution/process-runtime/resource-claims.ts +60 -3
  520. package/src/runtime/execution/process-runtime/runtime.ts +132 -15
  521. package/src/runtime/execution/process-runtime/types.ts +22 -0
  522. package/src/runtime/execution/workers/executor.ts +16 -13
  523. package/src/runtime/execution/workers/worker-entry.ts +1 -1
  524. package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
  525. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
  526. package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
  527. package/src/runtime/gateway/mcp/process-tools.ts +2 -289
  528. package/src/runtime/gateway/mcp/router.ts +2 -1125
  529. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
  530. package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6651
  531. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
  532. package/src/runtime/maintenance/cleanup.ts +38 -24
  533. package/src/runtime/platform/browser-product-discovery.ts +174 -0
  534. package/src/runtime/platform/computer-platform.ts +14 -0
  535. package/src/runtime/platform/executable-discovery.ts +54 -0
  536. package/src/runtime/platform/fetch-http-server.ts +90 -0
  537. package/src/runtime/platform/service-manager.ts +138 -0
  538. package/src/runtime/platform/windows-host-discovery.ts +118 -0
  539. package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
  540. package/src/runtime/plugins/action-application.ts +135 -0
  541. package/src/runtime/plugins/app-store-connect-adapter.ts +9 -21
  542. package/src/runtime/plugins/browser-adapter.ts +140 -100
  543. package/src/runtime/plugins/browser-automation-service.ts +15 -36
  544. package/src/runtime/plugins/browser-macos-bridge.ts +69 -21
  545. package/src/runtime/plugins/browser-manifest-surface.ts +30 -6
  546. package/src/runtime/plugins/browser-native-session-service.ts +1 -1
  547. package/src/runtime/plugins/browser-provider-registry.ts +2 -109
  548. package/src/runtime/plugins/browser-registration.ts +16 -0
  549. package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
  550. package/src/runtime/plugins/browser-runtime.ts +41 -9
  551. package/src/runtime/plugins/browser-session-authority.ts +101 -291
  552. package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
  553. package/src/runtime/plugins/browser-session-store.ts +229 -36
  554. package/src/runtime/plugins/browser-session-types.ts +2 -98
  555. package/src/runtime/plugins/capability-authorization-grants.ts +17 -0
  556. package/src/runtime/plugins/catalog-trust.ts +82 -0
  557. package/src/runtime/plugins/computer-registration.ts +839 -0
  558. package/src/runtime/plugins/config-store.ts +119 -0
  559. package/src/runtime/plugins/desktop-operator-external-policy.ts +932 -0
  560. package/src/runtime/plugins/desktop-operator-registration.ts +111 -8
  561. package/src/runtime/plugins/errors.ts +8 -1
  562. package/src/runtime/plugins/external-adapter.ts +45 -322
  563. package/src/runtime/plugins/external-provider-policy.ts +48 -0
  564. package/src/runtime/plugins/external-registration.ts +9 -1
  565. package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
  566. package/src/runtime/plugins/external-unix-socket.ts +64 -193
  567. package/src/runtime/plugins/first-party-external-registration.ts +163 -0
  568. package/src/runtime/plugins/first-party-registry.ts +3 -1
  569. package/src/runtime/plugins/github-adapter.ts +1 -2
  570. package/src/runtime/plugins/gmail-adapter.ts +6 -6
  571. package/src/runtime/plugins/google-calendar-adapter.ts +6 -6
  572. package/src/runtime/plugins/google-shared.ts +31 -46
  573. package/src/runtime/plugins/google-tasks-adapter.ts +6 -6
  574. package/src/runtime/plugins/ios-adapter.ts +1 -2
  575. package/src/runtime/plugins/local-system-adapter.ts +180 -0
  576. package/src/runtime/plugins/macos-capability-broker.ts +15 -105
  577. package/src/runtime/plugins/resend-adapter.ts +15 -24
  578. package/src/runtime/plugins/store.ts +493 -51
  579. package/src/runtime/plugins/types.ts +8 -1
  580. package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
  581. package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
  582. package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
  583. package/src/runtime/plugins/xiaohongshu-publish.ts +86 -430
  584. package/src/runtime/projections/controller-context.ts +12 -6
  585. package/src/runtime/projections/git-status-sampler.ts +73 -22
  586. package/src/runtime/projections/materialized-view.ts +14 -8
  587. package/src/runtime/recovery/capability-action-application.ts +228 -0
  588. package/src/runtime/recovery/index.ts +3 -0
  589. package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
  590. package/src/runtime/recovery/local-jobs-repair.ts +14 -14
  591. package/src/runtime/recovery/maintenance-executor.ts +358 -76
  592. package/src/runtime/recovery/quarantine-retention.ts +256 -0
  593. package/src/runtime/release/identity.ts +6 -0
  594. package/src/runtime/resources/leases/store.ts +1 -1
  595. package/src/runtime/root/active-execution-power-assertion.ts +139 -0
  596. package/src/runtime/root/assistant-learning-loop.ts +45 -0
  597. package/src/runtime/root/browser-session-composition.ts +59 -0
  598. package/src/runtime/root/computer-composition.ts +191 -0
  599. package/src/runtime/root/computer-target-composition.ts +16 -0
  600. package/src/runtime/root/computer-target-persistence.ts +39 -0
  601. package/src/runtime/root/controller-round-composition.ts +107 -0
  602. package/src/runtime/root/controller-services.ts +2 -0
  603. package/src/runtime/root/entry.ts +4 -3
  604. package/src/runtime/root/gateway-adapter.ts +3 -3
  605. package/src/runtime/root/local-bridge.ts +5 -2
  606. package/src/runtime/root/mcp-transport.ts +5 -5
  607. package/src/runtime/root/package-connector-service.ts +108 -56
  608. package/src/runtime/root/package-runtime-release.ts +86 -18
  609. package/src/runtime/root/package-runtime-service.ts +63 -86
  610. package/src/runtime/root/release-execution-canary.ts +71 -0
  611. package/src/runtime/root/release-loader.ts +68 -0
  612. package/src/runtime/root/release-manifest.ts +106 -2
  613. package/src/runtime/root/release-materialize.ts +103 -4
  614. package/src/runtime/root/release-store.ts +31 -2
  615. package/src/runtime/root/runtime.ts +240 -25
  616. package/src/runtime/root/scheduled-controller-composition.ts +51 -0
  617. package/src/runtime/root/scheduler.ts +12 -4
  618. package/src/runtime/root/service-runner.ts +1 -1
  619. package/src/runtime/root/service.ts +41 -16
  620. package/src/runtime/root/status.ts +1 -0
  621. package/src/runtime/root/types.ts +24 -1
  622. package/src/runtime/shared/local-bridge-surface.ts +1 -1
  623. package/src/runtime/shared/process-environment.ts +19 -4
  624. package/src/runtime/shared/storage-capacity.ts +68 -0
  625. package/src/runtime/standalone-recovery/controller-home-migration.ts +1116 -0
  626. package/src/runtime/standalone-recovery/core.ts +984 -230
  627. package/src/runtime/standalone-recovery/entry.ts +182 -51
  628. package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
  629. package/src/runtime/standalone-recovery/installer.ts +212 -104
  630. package/src/runtime/standalone-recovery/mcp-server.ts +243 -0
  631. package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
  632. package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
  633. package/src/runtime/standalone-recovery/performance.ts +135 -0
  634. package/src/runtime/standalone-recovery/release.ts +5 -0
  635. package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
  636. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -1
  637. package/src/runtime/workflow/schedules/engine.ts +191 -343
  638. package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
  639. package/src/runtime/workflow/schedules/settlement.ts +2 -191
  640. package/src/runtime/workflow/schedules/store.ts +2 -607
  641. package/src/runtime/workflow/schedules/types.ts +2 -127
  642. package/src/runtime/workflow/schedules/work-continuation.ts +180 -15
  643. package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
  644. package/src/runtime/workflows/runtime.ts +486 -0
  645. package/src/testing/test-governance.ts +90 -29
  646. package/assets/templates/helpers/archive-architecture-request.sh +0 -244
  647. package/docs/architecture/history/README.md +0 -7
  648. package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
  649. package/docs/architecture/history/global-hook-runtime.md +0 -166
  650. package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
  651. package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  652. package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
  653. package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
  654. package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
  655. package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
  656. package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  657. package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  658. package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  659. package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  660. package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
  661. package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  662. package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  663. package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  664. package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  665. package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  666. package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  667. package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  668. package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  669. package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  670. package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
  671. package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
  672. package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
  673. package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  674. package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  675. package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  676. package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  677. package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
  678. package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
  679. package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
  680. package/docs/architecture/requests/archive/2026/root.md +0 -68
  681. package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
  682. package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
  683. package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
  684. package/scripts/archive-architecture-request.sh +0 -4
  685. package/scripts/run-skill-evals.ts +0 -1022
@@ -1,7 +1,28 @@
1
1
  import { createHash, randomUUID } from 'crypto';
2
- import { buildBrowserPluginManifest, executeBrowserPluginAction } from '../../plugins/browser-adapter';
3
- import { controllerPluginRepository } from '../../plugins/store';
4
- import { getWorkContract } from '../facade/work-contract-store';
2
+ import { isWslWindowsRuntime } from '../../../cli/chatgpt-browser/bridge-provider';
3
+ import { createChatgptBrowserDeliveryHost } from '../../../../adapters/chatgpt/browser-delivery-host';
4
+ import { createChatgptWslBridgeDeliveryHost } from '../../../../adapters/chatgpt/wsl-bridge-delivery-host';
5
+ import {
6
+ CHATGPT_AUTOMATION_SUBMISSION_OUTCOME_UNKNOWN,
7
+ DEFAULT_CHATGPT_AUTOMATION_MODEL,
8
+ DEFAULT_CHATGPT_AUTOMATION_REASONING,
9
+ DEFAULT_CHATGPT_AUTOMATION_TAB_POLICY,
10
+ type ChatgptAutomationReasoning,
11
+ type ChatgptAutomationTabCleanupStatus,
12
+ type ChatgptAutomationTabPolicy,
13
+ type ChatgptProviderDeliveryHost,
14
+ type ChatgptProviderDeliveryStatus,
15
+ } from '../../../../adapters/chatgpt/provider-delivery';
16
+ import {
17
+ closeChatgptAutomationTabAfterDispatch,
18
+ ensureChatgptExecutionPreference,
19
+ ensureControllerChatgptBrowser,
20
+ navigateWorkConversation,
21
+ submitChatgptPrompt,
22
+ withChatgptBrowserActionOrigin,
23
+ } from '../../../../adapters/chatgpt/browser-delivery-runtime';
24
+ import { getWorkContract } from '../../../../packages/kernel/work/api/index';
25
+ import { readForgeInstanceIdentity } from '../../../../packages/kernel/identity/api/index';
5
26
  import {
6
27
  bindChatgptWorkConversation,
7
28
  getChatgptWorkConversationBinding,
@@ -9,25 +30,48 @@ import {
9
30
  parseChatgptConversationIdentity,
10
31
  rebindChatgptWorkConversation,
11
32
  type ChatgptWorkConversationBinding,
12
- } from './chatgpt-work-binding-store';
33
+ } from '../../../../adapters/chatgpt/work-conversation-binding-store';
34
+
35
+ export {
36
+ CHATGPT_AUTOMATION_SUBMISSION_OUTCOME_UNKNOWN,
37
+ DEFAULT_CHATGPT_AUTOMATION_MODEL,
38
+ DEFAULT_CHATGPT_AUTOMATION_REASONING,
39
+ DEFAULT_CHATGPT_AUTOMATION_TAB_POLICY,
40
+ type ChatgptAutomationReasoning,
41
+ type ChatgptAutomationTabCleanupStatus,
42
+ type ChatgptAutomationTabPolicy,
43
+ type ChatgptProviderDeliveryStatus,
44
+ } from '../../../../adapters/chatgpt/provider-delivery';
45
+ export {
46
+ chatgptAutomationControlQueryLimit,
47
+ chatgptAutomationControlWaitBudgets,
48
+ chatgptAutomationNavigationRequiresReplacement,
49
+ chatgptAutomationPageFailure,
50
+ chatgptAutomationReasoningLevelFromLabel,
51
+ chatgptBrowserActionArgs,
52
+ chatgptBrowserActionResult,
53
+ chatgptOutboundMessageMatchesPrompt,
54
+ isChatgptConversationUrl,
55
+ reconciledNewChatgptOpenPageSessionId,
56
+ settleWorkChatgptAutomationTab,
57
+ } from '../../../../adapters/chatgpt/browser-delivery-runtime';
13
58
 
14
59
  const LEGACY_CONTROLLER_CHATGPT_SESSION_ID = 'forge-chatgpt-supercontroller';
15
- export const DEFAULT_CHATGPT_AUTOMATION_MODEL = 'gpt-5.6';
16
- export const DEFAULT_CHATGPT_AUTOMATION_REASONING = 'high';
17
- export const DEFAULT_CHATGPT_AUTOMATION_TAB_POLICY = 'auto';
18
60
  export const DEFAULT_CHATGPT_AUTOMATION_PLUGIN_MENTION = '@forge';
19
- const CHATGPT_PROMPT_SELECTOR = 'div#prompt-textarea[contenteditable="true"]';
20
- const CHATGPT_SEND_SELECTOR = '[data-testid="send-button"], button[aria-label*="Send"], button[data-testid*="send"]';
21
- const CHATGPT_INTELLIGENCE_CONTROL_SELECTORS = [
22
- 'main button, main [role="button"]',
23
- 'button, [role="button"]',
24
- ] as const;
25
- const CHATGPT_CAPABILITY_SLIDER_SELECTOR = '[role="slider"]';
26
- const CHATGPT_CAPABILITY_MENUITEM_SELECTOR = '[role="menuitem"][aria-keyshortcuts~="ArrowLeft"][aria-keyshortcuts~="ArrowRight"]';
27
61
 
28
- export type ChatgptAutomationReasoning = 'medium' | 'high' | 'xhigh';
29
- export type ChatgptAutomationTabPolicy = 'auto' | 'reuse' | 'new';
30
- export type ChatgptAutomationTabCleanupStatus = 'closed' | 'preserved_user_owned' | 'session_closed' | 'failed';
62
+ export class ChatgptExecutionPlacementError extends Error {
63
+ readonly code = 'CHATGPT_EXECUTION_PLACEMENT_MISMATCH';
64
+ readonly targetForgeInstanceId: string;
65
+ readonly currentForgeInstanceId?: string;
66
+
67
+ constructor(targetForgeInstanceId: string, currentForgeInstanceId?: string) {
68
+ super(`CHATGPT_EXECUTION_PLACEMENT_MISMATCH: target=${targetForgeInstanceId} current=${currentForgeInstanceId ?? 'unavailable'}`);
69
+ this.name = 'ChatgptExecutionPlacementError';
70
+ this.targetForgeInstanceId = targetForgeInstanceId;
71
+ this.currentForgeInstanceId = currentForgeInstanceId;
72
+ }
73
+ }
74
+
31
75
 
32
76
  export interface WorkChatgptContinuationInput {
33
77
  controllerHome: string;
@@ -35,18 +79,34 @@ export interface WorkChatgptContinuationInput {
35
79
  repoRoot: string;
36
80
  workId: string;
37
81
  prompt: string;
82
+ /** Durable per-round capability minted before ChatGPT dispatch. */
83
+ controllerAuthorityId?: string;
84
+ /** Durable semantic relay scope paired with controllerAuthorityId. */
85
+ relayScopeId?: string;
38
86
  title?: string;
39
87
  browserSessionId?: string;
40
88
  conversationUrl?: string;
41
89
  model?: string;
42
90
  reasoning?: ChatgptAutomationReasoning;
43
91
  tabPolicy?: ChatgptAutomationTabPolicy;
92
+ /** Transport-only conversation policy. `fresh` starts a new ChatGPT conversation while preserving the same durable Work/ControllerRound authority. */
93
+ transportConversation?: 'bound' | 'fresh';
44
94
  timeoutMs?: number;
95
+ /** Authorization provenance for Browser actions. Immediate/source launches default to chatgpt-action; Scheduler resume must pass schedule. */
96
+ originSurface?: 'chatgpt-action' | 'schedule';
97
+ /** Explicit controller-scoped Browser grant refs already authorized for this Work transport. */
98
+ authorizationGrantRefs?: readonly string[];
99
+ }
100
+
101
+ export interface WorkChatgptContinuationDependencies {
102
+ bridgeRuntime?: boolean;
103
+ browserHost?: ChatgptProviderDeliveryHost;
104
+ wslHost?: ChatgptProviderDeliveryHost;
45
105
  }
46
106
 
47
107
  export interface WorkChatgptContinuationResult {
48
108
  status: 'dispatched' | 'failed';
49
- provider: 'controller-browser';
109
+ provider: 'controller-browser' | 'chatgpt-bridge';
50
110
  browserSessionId: string;
51
111
  conversationUrl?: string;
52
112
  conversationId?: string;
@@ -56,6 +116,9 @@ export interface WorkChatgptContinuationResult {
56
116
  reasoning: ChatgptAutomationReasoning;
57
117
  tabPolicy: ChatgptAutomationTabPolicy;
58
118
  executionPreferenceVerified: boolean;
119
+ authorizationGrantRefs?: string[];
120
+ /** Typed provider delivery disposition. Present when provider dispatch was attempted; callers must not infer this from error strings. */
121
+ providerDeliveryStatus?: ChatgptProviderDeliveryStatus;
59
122
  tabCleanupStatus?: ChatgptAutomationTabCleanupStatus;
60
123
  tabCleanupError?: { code: string; message: string };
61
124
  error?: { code: string; message: string };
@@ -64,6 +127,8 @@ export interface WorkChatgptContinuationResult {
64
127
  export interface StandaloneChatgptPromptInput {
65
128
  controllerHome: string;
66
129
  repoId: string;
130
+ /** Optional transport context only; current browser/bridge hosts do not derive semantic authority from this path. */
131
+ repoRoot?: string;
67
132
  scopeId: string;
68
133
  prompt: string;
69
134
  browserSessionId?: string;
@@ -74,61 +139,25 @@ export interface StandaloneChatgptPromptInput {
74
139
  timeoutMs?: number;
75
140
  }
76
141
 
77
- const workflowToolAttributionInstruction = (workId: string): string => `Forge Workflow execution contract: first claim exact Work ${workId}. For every repository_command_execute and repository_safe_patch_apply call in this turn, pass work_id=${workId} explicitly. MCP transport sessions may change between tool calls; never omit this Work id.`;
78
-
79
- function requestId(workId: string, actionId: string): string {
80
- return `chatgpt-work:${workId}:${actionId}:${randomUUID()}`;
81
- }
82
-
83
- const CHATGPT_BROWSER_TRANSPORT_OVERRIDES = {
84
- browser_mode: 'attach_preferred',
85
- cdp_attach_fallback: 'fail_closed',
86
- native_attach_mode: 'auto',
87
- native_browser_candidates: ['chrome'],
88
- } as const;
89
-
90
- export function chatgptBrowserActionArgs(actionId: string, args: Record<string, unknown>): Record<string, unknown> {
91
- if (actionId === 'configure' || actionId === 'list_sessions' || actionId === 'reconcile_sessions') return args;
92
- return { ...args, ...CHATGPT_BROWSER_TRANSPORT_OVERRIDES };
93
- }
94
-
95
- async function controllerBrowserAction(
96
- controllerHome: string,
97
- workId: string,
98
- actionId: string,
99
- args: Record<string, unknown>,
100
- timeoutMs?: number,
101
- ): Promise<Record<string, unknown>> {
102
- const repository = controllerPluginRepository(controllerHome);
103
- const repoRoot = repository.canonicalRoot ?? repository.localRoot;
104
- if (!repoRoot) throw new Error('CHATGPT_CONTROLLER_BROWSER_ROOT_UNAVAILABLE');
105
- return executeBrowserPluginAction({
106
- controllerHome,
107
- repoId: repository.repoId,
108
- repoRoot,
109
- pluginId: 'browser',
110
- actionId,
111
- requestId: requestId(workId, actionId),
112
- args: chatgptBrowserActionArgs(actionId, args),
113
- timeoutMs,
114
- origin: { surface: 'schedule', actor: 'chatgpt-work-continuation' },
115
- });
116
- }
117
-
118
- async function ensureControllerChatgptBrowser(controllerHome: string, workId: string): Promise<void> {
119
- const repository = controllerPluginRepository(controllerHome);
120
- const repoRoot = repository.canonicalRoot ?? repository.localRoot;
121
- if (!repoRoot) throw new Error('CHATGPT_CONTROLLER_BROWSER_ROOT_UNAVAILABLE');
122
- // Browser configure is not a read: it persists configuration and closes managed
123
- // contexts. Scheduled continuations must not disturb an already-enabled provider
124
- // merely to prove it is available. Only enable it when the persisted authority is
125
- // explicitly disabled; action-level transport overrides still fail closed later.
126
- if (buildBrowserPluginManifest(0, undefined, repoRoot).enabled) return;
127
- await controllerBrowserAction(controllerHome, workId, 'configure', { enabled: true });
142
+ function workflowToolAttributionInstruction(input: WorkChatgptContinuationInput): string {
143
+ const workId = input.workId;
144
+ const authorityId = input.controllerAuthorityId?.trim();
145
+ const relayScopeId = input.relayScopeId?.trim();
146
+ const requirementScoped = relayScopeId?.startsWith('requirement:') === true;
147
+ const repositoryAttribution = requirementScoped
148
+ ? `本轮普通 repository_command_execute 和 repository_safe_patch_apply 必须显式传入实际被本轮语义选择且已成功 claim 的 repository-change Work 的 work_id;如果仍在推进 origin Work,则使用 work_id=${workId}。不得把只读/编排 Supervisor Work 的 work_id 用来归属 child Work 的源码修改,也不得省略 work_id。`
149
+ : `本轮每一次 repository_command_execute 和 repository_safe_patch_apply 都必须显式传 work_id=${workId},不得省略。`;
150
+ if (authorityId && relayScopeId) {
151
+ return `Forge Workflow 执行契约:精确 Work ${workId}。本次启动的 controller round 已具备 durable controller authority:controller_authority_id=${authorityId},relay_scope_id=${relayScopeId}。第一次 controller_claim 必须使用这组完全相同的 authority;不得先调用不带 scope 的 controller_claim,也不得等待 claim 响应后再生成另一份 authority。如果当前 client 暴露 controller_authority_id 与 relay_scope_id,controller_claim 时必须同时传入。如果当前 frozen client schema 缺少任一字段,调用 rh_work operation=repair, work_id=${workId}, capability_id=controller.round:controller_claim:${authorityId}:${relayScopeId};Forge 会把它映射到同一个 fenced claim。claim 成功后,data.controllerAuthorityId 必须等于 ${authorityId};continue、verify、finalize、stop、controller_release 必须原样沿用同一 durable authority,必要时使用对应 compatibility capability。绝不能把 data.session.sessionId 当作 durable capability,因为 MCP execution session 可能轮换。${repositoryAttribution}唯一例外是 prompt 明确标记为 exact current-source Controller lifecycle invocation 的 repository_command_execute,它必须使用给定 source checkout_id 且不得传 wrapper work_id,因为 CLI 内部 --work-id 与 ControllerRound authority 才是该控制面变更的 fenced authority。`;
152
+ }
153
+ return `Forge Workflow 执行契约:首先 claim 精确 Work ${workId}。从成功的 controller_claim 响应中取得 data.controllerAuthorityId;此后该 Work 的每一次 rh_work lifecycle 调用(continue、verify、finalize、stop、controller_release)都必须把它原样作为 controller_authority_id 传入。如果当前 frozen client schema 不暴露 controller_authority_id,则把同一个 opaque value 作为 session_id compatibility carrier。绝不能把 data.session.sessionId 当作 durable capability,因为 MCP execution session 可能在工具调用之间被替换或失效。本轮每一次 repository_command_execute 和 repository_safe_patch_apply 都必须显式传 work_id=${workId},不得省略;唯一例外是 prompt 明确标记为 exact current-source Controller lifecycle invocation 的 repository_command_execute,它必须使用给定 source checkout_id 且不得传 wrapper work_id,因为 CLI 内部 --work-id 与 ControllerRound authority 才是该控制面变更的 fenced authority。`;
128
154
  }
129
155
 
130
- function stringField(value: unknown): string | undefined {
131
- return typeof value === 'string' && value.trim() ? value.trim() : undefined;
156
+ function controllerRoundAuthorityInputError(input: WorkChatgptContinuationInput): Error | undefined {
157
+ const hasAuthority = Boolean(input.controllerAuthorityId?.trim());
158
+ const hasRelayScope = Boolean(input.relayScopeId?.trim());
159
+ if (hasAuthority === hasRelayScope) return undefined;
160
+ return new Error('CHATGPT_CONTROLLER_ROUND_AUTHORITY_INCOMPLETE: controllerAuthorityId and relayScopeId must be supplied together');
132
161
  }
133
162
 
134
163
  function normalizeModel(value?: string): string {
@@ -141,15 +170,41 @@ function normalizeReasoning(value?: ChatgptAutomationReasoning): ChatgptAutomati
141
170
  return value ?? DEFAULT_CHATGPT_AUTOMATION_REASONING;
142
171
  }
143
172
 
173
+ const DEFAULT_CHATGPT_CONTROLLER_REASONING: ChatgptAutomationReasoning = 'xhigh';
174
+
175
+ function normalizeControllerReasoning(value?: ChatgptAutomationReasoning): ChatgptAutomationReasoning {
176
+ return value ?? DEFAULT_CHATGPT_CONTROLLER_REASONING;
177
+ }
178
+
144
179
  function normalizeTabPolicy(value?: ChatgptAutomationTabPolicy): ChatgptAutomationTabPolicy {
145
180
  return value ?? DEFAULT_CHATGPT_AUTOMATION_TAB_POLICY;
146
181
  }
147
182
 
183
+ function resolveChatgptProviderDeliveryHost(
184
+ dependencies: WorkChatgptContinuationDependencies = {},
185
+ ): { bridgeRuntime: boolean; host: ChatgptProviderDeliveryHost } {
186
+ const bridgeRuntime = dependencies.bridgeRuntime ?? isWslWindowsRuntime();
187
+ const host = bridgeRuntime
188
+ ? dependencies.wslHost ?? createChatgptWslBridgeDeliveryHost()
189
+ : dependencies.browserHost ?? createChatgptBrowserDeliveryHost({
190
+ ensureBrowser: ensureControllerChatgptBrowser,
191
+ navigate: navigateWorkConversation,
192
+ ensureExecutionPreference: ensureChatgptExecutionPreference,
193
+ submitPrompt: submitChatgptPrompt,
194
+ });
195
+ return { bridgeRuntime, host };
196
+ }
197
+
148
198
  export function stableChatgptWorkBrowserSessionId(repoId: string, workId: string): string {
149
199
  const digest = createHash('sha256').update(`${repoId}\n${workId}`).digest('hex').slice(0, 20);
150
200
  return `forge-chatgpt-work-${digest}`;
151
201
  }
152
202
 
203
+ export function stableChatgptWorkBridgeSessionId(repoId: string, workId: string): string {
204
+ const digest = createHash('sha256').update(`${repoId}\nbridge\n${workId}`).digest('hex').slice(0, 20);
205
+ return `forge-chatgpt-bridge-${digest}`;
206
+ }
207
+
153
208
  export function stableStandaloneChatgptBrowserSessionId(repoId: string, scopeId: string): string {
154
209
  const digest = createHash('sha256').update(`${repoId}\nstandalone\n${scopeId}`).digest('hex').slice(0, 20);
155
210
  return `forge-chatgpt-standalone-${digest}`;
@@ -179,411 +234,6 @@ export function resolveChatgptWorkBrowserSessionId(input: {
179
234
  return stable;
180
235
  }
181
236
 
182
- interface BrowserQueryMatch {
183
- text?: string;
184
- selectorHint?: string;
185
- }
186
-
187
- function queryMatches(result: Record<string, unknown> | undefined): BrowserQueryMatch[] {
188
- if (!result || !Array.isArray(result.matches)) return [];
189
- return result.matches.filter((value): value is BrowserQueryMatch => Boolean(value) && typeof value === 'object');
190
- }
191
-
192
- function matchText(value: BrowserQueryMatch): string {
193
- return typeof value.text === 'string' ? value.text.trim() : '';
194
- }
195
-
196
- function matchSelector(value: BrowserQueryMatch | undefined): string | undefined {
197
- return typeof value?.selectorHint === 'string' && value.selectorHint.trim() ? value.selectorHint.trim() : undefined;
198
- }
199
-
200
- function normalizeExecutionControlLabel(label: string | undefined): string {
201
- return (label ?? '').trim().toLowerCase().replace(/[\s_-]+/g, '');
202
- }
203
-
204
- export function chatgptAutomationReasoningLevelFromLabel(label: string | undefined): ChatgptAutomationReasoning | undefined {
205
- const normalized = normalizeExecutionControlLabel(label);
206
- if (!normalized) return undefined;
207
-
208
- const exact = new Map<string, ChatgptAutomationReasoning>([
209
- ['medium', 'medium'], ['中', 'medium'], ['中等', 'medium'],
210
- ['high', 'high'], ['高', 'high'],
211
- ['xhigh', 'xhigh'], ['extrahigh', 'xhigh'], ['超高', 'xhigh'], ['极高', 'xhigh'],
212
- ]);
213
- const exactLevel = exact.get(normalized);
214
- if (exactLevel) return exactLevel;
215
-
216
- const hasReasoningContext = ['reasoning', 'thinking', '推理', '思考']
217
- .some((token) => normalized.includes(token));
218
- if (!hasReasoningContext) return undefined;
219
- if (['xhigh', 'extrahigh', '超高', '极高'].some((token) => normalized.includes(token))) return 'xhigh';
220
- if (['medium', '中等'].some((token) => normalized.includes(token))) return 'medium';
221
- if (['high', '高'].some((token) => normalized.includes(token))) return 'high';
222
- return undefined;
223
- }
224
-
225
- function modelLabelMatches(label: string | undefined, model: string): boolean {
226
- if (model !== DEFAULT_CHATGPT_AUTOMATION_MODEL || !label) return false;
227
- const normalized = normalizeExecutionControlLabel(label);
228
- return normalized.includes('5.6sol') || normalized.includes('gpt5.6sol');
229
- }
230
-
231
- function reasoningLabelMatches(label: string | undefined, reasoning: ChatgptAutomationReasoning): boolean {
232
- return chatgptAutomationReasoningLevelFromLabel(label) === reasoning;
233
- }
234
-
235
- function isReasoningControlLabel(label: string | undefined): boolean {
236
- return reasoningLabelMatches(label, 'medium')
237
- || reasoningLabelMatches(label, 'high')
238
- || reasoningLabelMatches(label, 'xhigh');
239
- }
240
-
241
- function reasoningSliderValue(reasoning: ChatgptAutomationReasoning): number {
242
- if (reasoning === 'medium') return 2;
243
- if (reasoning === 'high') return 3;
244
- return 4;
245
- }
246
-
247
- async function findChatgptIntelligenceControl(
248
- controllerHome: string,
249
- workId: string,
250
- browserSessionId: string,
251
- timeoutMs?: number,
252
- ): Promise<BrowserQueryMatch | undefined> {
253
- for (const selector of CHATGPT_INTELLIGENCE_CONTROL_SELECTORS) {
254
- const result = await controllerBrowserAction(controllerHome, workId, 'query_all', {
255
- session_id: browserSessionId,
256
- selector,
257
- // Prefer the composer/main region so sidebar history cannot crowd the
258
- // reasoning control out of a bounded query. Keep a larger global fallback
259
- // for ChatGPT layouts that place the control outside <main>.
260
- limit: chatgptAutomationControlQueryLimit(selector),
261
- timeout_ms: timeoutMs ?? 60_000,
262
- }, timeoutMs);
263
- const match = queryMatches(result).find((candidate) => {
264
- const label = matchText(candidate);
265
- return modelLabelMatches(label, DEFAULT_CHATGPT_AUTOMATION_MODEL) || isReasoningControlLabel(label);
266
- });
267
- if (match) return match;
268
- }
269
- return undefined;
270
- }
271
-
272
- export function chatgptAutomationControlQueryLimit(selector: string): number {
273
- // Long tool-heavy conversations can contribute well over 80 buttons before
274
- // the composer controls. Keep the preferred <main> scan bounded but large
275
- // enough that the current reasoning control is not crowded out by history.
276
- return selector.startsWith('main ') ? 160 : 320;
277
- }
278
-
279
- export function chatgptAutomationControlWaitBudgets(timeoutMs?: number): { waitBudgetMs: number; probeTimeoutMs: number } {
280
- const waitBudgetMs = Math.min(Math.max(timeoutMs ?? 30_000, 1_000), 30_000);
281
- // Native Chrome attachment has a few seconds of Apple Events/DOM latency on
282
- // long conversations. Keep each probe bounded while allowing a real query to
283
- // complete; the total readiness window remains capped at 30 seconds.
284
- return { waitBudgetMs, probeTimeoutMs: Math.min(waitBudgetMs, 5_000) };
285
- }
286
-
287
- async function waitForChatgptIntelligenceControl(
288
- controllerHome: string,
289
- workId: string,
290
- browserSessionId: string,
291
- timeoutMs?: number,
292
- ): Promise<BrowserQueryMatch | undefined> {
293
- const { waitBudgetMs, probeTimeoutMs } = chatgptAutomationControlWaitBudgets(timeoutMs);
294
- const deadline = Date.now() + waitBudgetMs;
295
- do {
296
- const control = await findChatgptIntelligenceControl(controllerHome, workId, browserSessionId, probeTimeoutMs);
297
- if (control) return control;
298
- if (Date.now() >= deadline) break;
299
- await new Promise((resolveWait) => setTimeout(resolveWait, 500));
300
- } while (Date.now() < deadline);
301
- return undefined;
302
- }
303
-
304
- export function chatgptAutomationPageFailure(
305
- bodyText: string | undefined,
306
- composerAvailable: boolean,
307
- ): 'CHATGPT_AUTOMATION_LOGIN_REQUIRED' | 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE' | undefined {
308
- if (composerAvailable) return undefined;
309
- const normalized = (bodyText ?? '').toLowerCase();
310
- const loginMarkers = [
311
- 'log in', 'sign up', 'continue with google', 'continue with apple',
312
- '登录', '登陆', '注册', '使用 google 继续', '使用 apple 继续',
313
- ];
314
- return loginMarkers.some((marker) => normalized.includes(marker))
315
- ? 'CHATGPT_AUTOMATION_LOGIN_REQUIRED'
316
- : 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE';
317
- }
318
-
319
- async function assertChatgptAutomationComposerReady(
320
- controllerHome: string,
321
- workId: string,
322
- browserSessionId: string,
323
- timeoutMs?: number,
324
- ): Promise<void> {
325
- const composerResult = await controllerBrowserAction(controllerHome, workId, 'query_all', {
326
- session_id: browserSessionId,
327
- selector: CHATGPT_PROMPT_SELECTOR,
328
- limit: 1,
329
- timeout_ms: Math.min(timeoutMs ?? 5_000, 5_000),
330
- }, timeoutMs).catch(() => undefined);
331
- const composerAvailable = queryMatches(composerResult).length > 0;
332
- if (composerAvailable) return;
333
-
334
- const bodyResult = await controllerBrowserAction(controllerHome, workId, 'get_text', {
335
- session_id: browserSessionId,
336
- selector: 'body',
337
- max_chars: 4_000,
338
- timeout_ms: Math.min(timeoutMs ?? 5_000, 5_000),
339
- }, timeoutMs).catch(() => undefined);
340
- const failure = chatgptAutomationPageFailure(stringField(bodyResult?.text), composerAvailable);
341
- throw new Error(failure ?? 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE');
342
- }
343
-
344
- async function ensureChatgptExecutionPreference(
345
- controllerHome: string,
346
- workId: string,
347
- browserSessionId: string,
348
- model: string,
349
- reasoning: ChatgptAutomationReasoning,
350
- timeoutMs?: number,
351
- ): Promise<boolean> {
352
- if (model !== DEFAULT_CHATGPT_AUTOMATION_MODEL) throw new Error(`CHATGPT_AUTOMATION_MODEL_UNSUPPORTED:${model}`);
353
- let control = await waitForChatgptIntelligenceControl(controllerHome, workId, browserSessionId, timeoutMs);
354
- if (!control) {
355
- await assertChatgptAutomationComposerReady(controllerHome, workId, browserSessionId, timeoutMs);
356
- throw new Error('CHATGPT_AUTOMATION_INTELLIGENCE_CONTROL_UNAVAILABLE');
357
- }
358
- // Current ChatGPT UI exposes the reasoning level (for example `高`) on the
359
- // composer control instead of the model label. The model remains fail-closed
360
- // through normalizeModel above; only the user-adjustable reasoning control is
361
- // required to be observable in the page before unattended dispatch proceeds.
362
- if (reasoningLabelMatches(matchText(control), reasoning)) return true;
363
- let controlSelector = matchSelector(control);
364
- if (!controlSelector) throw new Error('CHATGPT_AUTOMATION_INTELLIGENCE_CONTROL_UNAVAILABLE');
365
-
366
- const expanded = await controllerBrowserAction(controllerHome, workId, 'get_attribute', {
367
- session_id: browserSessionId,
368
- selector: controlSelector,
369
- attribute: 'aria-expanded',
370
- timeout_ms: timeoutMs ?? 60_000,
371
- }, timeoutMs).catch(() => undefined);
372
- if (stringField(expanded?.value) !== 'true') {
373
- await controllerBrowserAction(controllerHome, workId, 'press', {
374
- session_id: browserSessionId,
375
- selector: controlSelector,
376
- key: 'ArrowDown',
377
- timeout_ms: timeoutMs ?? 60_000,
378
- post_action_wait_ms: 150,
379
- }, timeoutMs);
380
- }
381
-
382
- const currentValueResult = await controllerBrowserAction(controllerHome, workId, 'get_attribute', {
383
- session_id: browserSessionId,
384
- selector: CHATGPT_CAPABILITY_SLIDER_SELECTOR,
385
- attribute: 'aria-valuenow',
386
- timeout_ms: timeoutMs ?? 60_000,
387
- }, timeoutMs).catch(() => undefined);
388
- const currentValue = Number(stringField(currentValueResult?.value));
389
- const targetValue = reasoningSliderValue(reasoning);
390
- if (!Number.isInteger(currentValue) || currentValue < 0 || currentValue > 4) {
391
- throw new Error(`CHATGPT_AUTOMATION_REASONING_STATE_UNAVAILABLE:${reasoning}`);
392
- }
393
- const direction = targetValue > currentValue ? 'ArrowRight' : 'ArrowLeft';
394
- for (let index = 0; index < Math.abs(targetValue - currentValue); index += 1) {
395
- await controllerBrowserAction(controllerHome, workId, 'press', {
396
- session_id: browserSessionId,
397
- selector: CHATGPT_CAPABILITY_MENUITEM_SELECTOR,
398
- key: direction,
399
- timeout_ms: timeoutMs ?? 60_000,
400
- post_action_wait_ms: 120,
401
- }, timeoutMs);
402
- }
403
-
404
- const verifiedValueResult = await controllerBrowserAction(controllerHome, workId, 'get_attribute', {
405
- session_id: browserSessionId,
406
- selector: CHATGPT_CAPABILITY_SLIDER_SELECTOR,
407
- attribute: 'aria-valuenow',
408
- timeout_ms: timeoutMs ?? 60_000,
409
- }, timeoutMs).catch(() => undefined);
410
- if (Number(stringField(verifiedValueResult?.value)) !== targetValue) {
411
- throw new Error(`CHATGPT_AUTOMATION_REASONING_NOT_VERIFIED:${reasoning}`);
412
- }
413
- control = await findChatgptIntelligenceControl(controllerHome, workId, browserSessionId, timeoutMs) ?? control;
414
- if (isReasoningControlLabel(matchText(control)) && !reasoningLabelMatches(matchText(control), reasoning)) {
415
- throw new Error(`CHATGPT_AUTOMATION_REASONING_NOT_VERIFIED:${reasoning}`);
416
- }
417
- controlSelector = matchSelector(control) ?? controlSelector;
418
- await controllerBrowserAction(controllerHome, workId, 'press', {
419
- session_id: browserSessionId,
420
- selector: controlSelector,
421
- key: 'Escape',
422
- timeout_ms: timeoutMs ?? 60_000,
423
- post_action_wait_ms: 100,
424
- }, timeoutMs).catch(() => undefined);
425
- return true;
426
- }
427
-
428
- function resultUrl(result: Record<string, unknown>): string | undefined {
429
- return stringField(result.url)
430
- ?? stringField((result.session as Record<string, unknown> | undefined)?.url);
431
- }
432
-
433
- function resultSessionId(result: Record<string, unknown>): string | undefined {
434
- return stringField(result.session_id)
435
- ?? stringField((result.session as Record<string, unknown> | undefined)?.sessionId);
436
- }
437
-
438
- export function chatgptAutomationNavigationRequiresReplacement(error: unknown): boolean {
439
- if (!(error instanceof Error)) return false;
440
- return [
441
- 'BROWSER_AUTOMATION_BACKGROUND_NAVIGATION_REQUIRES_REPLACEMENT',
442
- 'PLUGIN_BROWSER_SESSION_STATE_LOST',
443
- 'PLUGIN_SESSION_NOT_FOUND',
444
- 'PLUGIN_BROWSER_NATIVE_TAB_IDENTITY_UNPROVEN',
445
- ].some((marker) => error.message.includes(marker));
446
- }
447
-
448
- async function closeChatgptAutomationTabAfterDispatch(
449
- controllerHome: string,
450
- workId: string,
451
- browserSessionId: string,
452
- timeoutMs?: number,
453
- ): Promise<{ status: ChatgptAutomationTabCleanupStatus; error?: { code: string; message: string } }> {
454
- try {
455
- const closed = await controllerBrowserAction(controllerHome, workId, 'close_page', {
456
- session_id: browserSessionId,
457
- }, Math.min(timeoutMs ?? 15_000, 15_000));
458
- if (closed.preservedUserOwnedTab === true) return { status: 'preserved_user_owned' };
459
- if (closed.resourceClosed === true) return { status: 'closed' };
460
- return { status: 'session_closed' };
461
- } catch (error) {
462
- const message = error instanceof Error ? error.message : String(error);
463
- return {
464
- status: 'failed',
465
- error: {
466
- code: error instanceof Error && error.message.includes(':') ? error.message.split(':', 1)[0] : 'CHATGPT_AUTOMATION_TAB_CLEANUP_FAILED',
467
- message,
468
- },
469
- };
470
- }
471
- }
472
-
473
- /**
474
- * Settles the browser resource for one completed Work-bound ChatGPT round.
475
- * Conversation identity is durable in the Work binding and intentionally
476
- * survives this ephemeral tab/session cleanup. Browser ownership policy keeps
477
- * user-owned native tabs intact.
478
- */
479
- export async function settleWorkChatgptAutomationTab(input: {
480
- controllerHome: string;
481
- workId: string;
482
- browserSessionId: string;
483
- timeoutMs?: number;
484
- }): Promise<{ status: ChatgptAutomationTabCleanupStatus; error?: { code: string; message: string } }> {
485
- return closeChatgptAutomationTabAfterDispatch(
486
- input.controllerHome,
487
- input.workId,
488
- input.browserSessionId,
489
- input.timeoutMs,
490
- );
491
- }
492
-
493
- export function isChatgptConversationUrl(value: string): boolean {
494
- try {
495
- const url = new URL(value);
496
- return url.protocol === 'https:'
497
- && ['chatgpt.com', 'www.chatgpt.com', 'chat.openai.com'].includes(url.hostname)
498
- && /\/c\/[^/?#]+/.test(url.pathname);
499
- } catch {
500
- return false;
501
- }
502
- }
503
-
504
- async function navigateWorkConversation(
505
- controllerHome: string,
506
- workId: string,
507
- browserSessionId: string,
508
- targetUrl: string,
509
- timeoutMs?: number,
510
- ): Promise<{ submissionTargetUrl: string; recoveredFromStaleBinding: boolean; browserSessionId: string }> {
511
- const navigate = async (sessionId: string, url: string) => controllerBrowserAction(controllerHome, workId, 'navigate', {
512
- session_id: sessionId,
513
- url,
514
- wait_until: 'domcontentloaded',
515
- timeout_ms: timeoutMs ?? 60_000,
516
- retries: 1,
517
- }, timeoutMs);
518
- const openReplacement = async (url: string): Promise<string> => {
519
- const opened = await controllerBrowserAction(controllerHome, workId, 'open_page', {
520
- url,
521
- wait_until: 'domcontentloaded',
522
- timeout_ms: timeoutMs ?? 60_000,
523
- retries: 1,
524
- }, timeoutMs);
525
- const replacementSessionId = resultSessionId(opened);
526
- if (!replacementSessionId) throw new Error('CHATGPT_AUTOMATION_REPLACEMENT_SESSION_NOT_CONFIRMED');
527
- return replacementSessionId;
528
- };
529
- try {
530
- await navigate(browserSessionId, targetUrl);
531
- return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: false, browserSessionId };
532
- } catch (error) {
533
- if (chatgptAutomationNavigationRequiresReplacement(error)) {
534
- const replacementSessionId = await openReplacement(targetUrl);
535
- return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: false, browserSessionId: replacementSessionId };
536
- }
537
- if (!isChatgptConversationUrl(targetUrl)) throw error;
538
- try {
539
- await navigate(browserSessionId, 'https://chatgpt.com/');
540
- return { submissionTargetUrl: 'https://chatgpt.com/', recoveredFromStaleBinding: true, browserSessionId };
541
- } catch (fallbackError) {
542
- if (!chatgptAutomationNavigationRequiresReplacement(fallbackError)) throw fallbackError;
543
- const replacementSessionId = await openReplacement('https://chatgpt.com/');
544
- return { submissionTargetUrl: 'https://chatgpt.com/', recoveredFromStaleBinding: true, browserSessionId: replacementSessionId };
545
- }
546
- }
547
- }
548
-
549
- async function submitChatgptPrompt(
550
- controllerHome: string,
551
- workId: string,
552
- browserSessionId: string,
553
- prompt: string,
554
- targetUrl: string,
555
- timeoutMs?: number,
556
- ): Promise<string> {
557
- await controllerBrowserAction(controllerHome, workId, 'fill', {
558
- session_id: browserSessionId,
559
- selector: CHATGPT_PROMPT_SELECTOR,
560
- text: withForgePluginMention(prompt),
561
- timeout_ms: timeoutMs ?? 60_000,
562
- post_action_wait_ms: 100,
563
- }, timeoutMs);
564
- const sent = await controllerBrowserAction(controllerHome, workId, 'click', {
565
- session_id: browserSessionId,
566
- selector: CHATGPT_SEND_SELECTOR,
567
- timeout_ms: timeoutMs ?? 60_000,
568
- post_action_wait_ms: 1_000,
569
- }, timeoutMs);
570
- let observedUrl = resultUrl(sent) ?? targetUrl;
571
- if (/\/c\/[^/?#]+/.test(observedUrl)) return observedUrl;
572
- const deadline = Date.now() + Math.min(Math.max(timeoutMs ?? 8_000, 2_000), 8_000);
573
- do {
574
- const observed = await controllerBrowserAction(controllerHome, workId, 'query_all', {
575
- session_id: browserSessionId,
576
- selector: 'main',
577
- limit: 1,
578
- timeout_ms: Math.min(timeoutMs ?? 3_000, 3_000),
579
- }, timeoutMs).catch(() => undefined);
580
- observedUrl = observed ? resultUrl(observed) ?? observedUrl : observedUrl;
581
- if (/\/c\/[^/?#]+/.test(observedUrl)) return observedUrl;
582
- await new Promise((resolveWait) => setTimeout(resolveWait, 250));
583
- } while (Date.now() < deadline);
584
- throw new Error(`CHATGPT_AUTOMATION_SUBMISSION_NOT_CONFIRMED:${observedUrl}`);
585
- }
586
-
587
237
  export function withForgePluginMention(prompt: string): string {
588
238
  const value = prompt.trim();
589
239
  if (!value) throw new Error('CHATGPT_AUTOMATION_PROMPT_REQUIRED');
@@ -596,63 +246,75 @@ export function withForgePluginMention(prompt: string): string {
596
246
  * ChatGPT browser path without creating or requiring a WorkContract. The scope
597
247
  * id is only a stable browser correlation key (for example a Schedule id).
598
248
  */
599
- export async function runStandaloneChatgptPrompt(input: StandaloneChatgptPromptInput): Promise<WorkChatgptContinuationResult> {
249
+ export async function runStandaloneChatgptPrompt(
250
+ input: StandaloneChatgptPromptInput,
251
+ dependencies: WorkChatgptContinuationDependencies = {},
252
+ ): Promise<WorkChatgptContinuationResult> {
600
253
  const model = normalizeModel(input.model);
601
254
  const reasoning = normalizeReasoning(input.reasoning);
602
255
  const tabPolicy = normalizeTabPolicy(input.tabPolicy);
603
- const sessionId = resolveStandaloneChatgptBrowserSessionId(input);
604
256
  const browserScopeId = `standalone:${input.scopeId}`;
605
257
  const seedUrl = input.conversationUrl?.trim();
258
+ const { bridgeRuntime, host } = resolveChatgptProviderDeliveryHost(dependencies);
259
+ const sessionId = bridgeRuntime
260
+ ? stableChatgptWorkBridgeSessionId(input.repoId, browserScopeId)
261
+ : resolveStandaloneChatgptBrowserSessionId(input);
262
+ const targetUrl = seedUrl ?? 'https://chatgpt.com/';
606
263
 
607
264
  try {
608
- await ensureControllerChatgptBrowser(input.controllerHome, browserScopeId);
609
- const targetUrl = seedUrl ?? 'https://chatgpt.com/';
610
- const navigation = await navigateWorkConversation(
611
- input.controllerHome,
612
- browserScopeId,
613
- sessionId,
265
+ const delivery = await host.dispatch({
266
+ controllerHome: input.controllerHome,
267
+ repoId: input.repoId,
268
+ repoRoot: input.repoRoot ?? process.cwd(),
269
+ workId: browserScopeId,
270
+ prompt: input.prompt,
271
+ browserSessionId: sessionId,
614
272
  targetUrl,
615
- input.timeoutMs,
616
- );
617
- const executionPreferenceVerified = await ensureChatgptExecutionPreference(
618
- input.controllerHome,
619
- browserScopeId,
620
- navigation.browserSessionId,
621
273
  model,
622
274
  reasoning,
623
- input.timeoutMs,
624
- );
625
- const observedUrl = await submitChatgptPrompt(
626
- input.controllerHome,
627
- browserScopeId,
628
- navigation.browserSessionId,
629
- input.prompt,
630
- navigation.submissionTargetUrl,
631
- input.timeoutMs,
632
- );
633
- const tabCleanup = await closeChatgptAutomationTabAfterDispatch(
634
- input.controllerHome,
635
- browserScopeId,
636
- navigation.browserSessionId,
637
- input.timeoutMs,
638
- );
275
+ timeoutMs: input.timeoutMs,
276
+ });
277
+ if (delivery.status !== 'dispatch_confirmed') {
278
+ return {
279
+ status: 'failed',
280
+ provider: delivery.provider,
281
+ browserSessionId: delivery.browserSessionId,
282
+ conversationUrl: delivery.conversationUrl ?? seedUrl,
283
+ resumedFromBinding: false,
284
+ model,
285
+ reasoning,
286
+ tabPolicy,
287
+ executionPreferenceVerified: delivery.executionPreferenceVerified,
288
+ providerDeliveryStatus: delivery.status,
289
+ error: delivery.error ?? { code: `CHATGPT_PROVIDER_${delivery.status.toUpperCase()}`, message: delivery.status },
290
+ };
291
+ }
292
+ const tabCleanup = delivery.provider === 'controller-browser'
293
+ ? await closeChatgptAutomationTabAfterDispatch(
294
+ input.controllerHome,
295
+ browserScopeId,
296
+ delivery.browserSessionId,
297
+ input.timeoutMs,
298
+ )
299
+ : undefined;
639
300
  return {
640
301
  status: 'dispatched',
641
- provider: 'controller-browser',
642
- browserSessionId: navigation.browserSessionId,
643
- conversationUrl: observedUrl,
302
+ provider: delivery.provider,
303
+ browserSessionId: delivery.browserSessionId,
304
+ conversationUrl: delivery.conversationUrl ?? targetUrl,
644
305
  resumedFromBinding: false,
645
306
  model,
646
307
  reasoning,
647
308
  tabPolicy,
648
- executionPreferenceVerified,
649
- tabCleanupStatus: tabCleanup.status,
650
- ...(tabCleanup.error ? { tabCleanupError: tabCleanup.error } : {}),
309
+ executionPreferenceVerified: delivery.executionPreferenceVerified,
310
+ providerDeliveryStatus: delivery.status,
311
+ ...(tabCleanup ? { tabCleanupStatus: tabCleanup.status } : {}),
312
+ ...(tabCleanup?.error ? { tabCleanupError: tabCleanup.error } : {}),
651
313
  };
652
314
  } catch (error) {
653
315
  return {
654
316
  status: 'failed',
655
- provider: 'controller-browser',
317
+ provider: bridgeRuntime ? 'chatgpt-bridge' : 'controller-browser',
656
318
  browserSessionId: sessionId,
657
319
  conversationUrl: seedUrl,
658
320
  resumedFromBinding: false,
@@ -661,7 +323,7 @@ export async function runStandaloneChatgptPrompt(input: StandaloneChatgptPromptI
661
323
  tabPolicy,
662
324
  executionPreferenceVerified: false,
663
325
  error: {
664
- code: error instanceof Error && error.message.includes(':') ? error.message.split(':', 1)[0] : 'CHATGPT_CONTROLLER_BROWSER_FAILED',
326
+ code: error instanceof Error && error.message.includes(':') ? error.message.split(':', 1)[0] : bridgeRuntime ? 'CHATGPT_BRIDGE_DISPATCH_FAILED' : 'CHATGPT_CONTROLLER_BROWSER_FAILED',
665
327
  message: error instanceof Error ? error.message : String(error),
666
328
  },
667
329
  };
@@ -672,13 +334,54 @@ export async function runStandaloneChatgptPrompt(input: StandaloneChatgptPromptI
672
334
  * Dispatches a bounded resume prompt to one controller-owned ChatGPT Web tab.
673
335
  * Chat history is transport context only. Forge Work/Plan/evidence remain authoritative.
674
336
  */
675
- export async function runWorkChatgptContinuation(input: WorkChatgptContinuationInput): Promise<WorkChatgptContinuationResult> {
337
+ export async function runWorkChatgptContinuation(
338
+ input: WorkChatgptContinuationInput,
339
+ dependencies: WorkChatgptContinuationDependencies = {},
340
+ ): Promise<WorkChatgptContinuationResult> {
676
341
  const store = { controllerHome: input.controllerHome, repoId: input.repoId };
677
342
  const existing = getChatgptWorkConversationBinding(store, input.workId);
678
- const seedUrl = input.conversationUrl?.trim() || existing?.conversationUrl;
343
+ const authorizationGrantRefs = new Set(
344
+ [...(existing?.authorizationGrantRefs ?? []), ...(input.authorizationGrantRefs ?? [])]
345
+ .map((ref) => ref.trim())
346
+ .filter(Boolean),
347
+ );
348
+ const transportConversation = input.transportConversation ?? 'bound';
349
+ const seedUrl = transportConversation === 'fresh' ? undefined : input.conversationUrl?.trim() || existing?.conversationUrl;
679
350
  const model = normalizeModel(input.model);
680
- const reasoning = normalizeReasoning(input.reasoning);
681
- const tabPolicy = normalizeTabPolicy(input.tabPolicy);
351
+ const reasoning = normalizeControllerReasoning(input.reasoning);
352
+ const tabPolicy = transportConversation === 'fresh' ? 'new' : normalizeTabPolicy(input.tabPolicy);
353
+ const { bridgeRuntime, host } = resolveChatgptProviderDeliveryHost(dependencies);
354
+ const authorityInputError = controllerRoundAuthorityInputError(input);
355
+ if (authorityInputError) {
356
+ const browserSessionId = bridgeRuntime
357
+ ? stableChatgptWorkBridgeSessionId(input.repoId, input.workId)
358
+ : resolveChatgptWorkBrowserSessionId({
359
+ repoId: input.repoId,
360
+ workId: input.workId,
361
+ tabPolicy,
362
+ explicitSessionId: input.browserSessionId,
363
+ boundSessionId: existing?.latestBrowserSessionId,
364
+ });
365
+ return {
366
+ status: 'failed',
367
+ provider: bridgeRuntime ? 'chatgpt-bridge' : 'controller-browser',
368
+ browserSessionId,
369
+ conversationUrl: seedUrl,
370
+ conversationId: existing?.conversationId,
371
+ localAlias: existing?.localAlias,
372
+ resumedFromBinding: Boolean(existing),
373
+ model,
374
+ reasoning,
375
+ tabPolicy,
376
+ executionPreferenceVerified: false,
377
+ authorizationGrantRefs: [...authorizationGrantRefs],
378
+ error: {
379
+ code: 'CHATGPT_CONTROLLER_ROUND_AUTHORITY_INCOMPLETE',
380
+ message: authorityInputError.message,
381
+ },
382
+ };
383
+ }
384
+
682
385
  const sessionId = resolveChatgptWorkBrowserSessionId({
683
386
  repoId: input.repoId,
684
387
  workId: input.workId,
@@ -687,63 +390,92 @@ export async function runWorkChatgptContinuation(input: WorkChatgptContinuationI
687
390
  boundSessionId: existing?.latestBrowserSessionId,
688
391
  });
689
392
  let binding: ChatgptWorkConversationBinding | undefined = existing;
393
+ const deliverySessionId = bridgeRuntime
394
+ ? stableChatgptWorkBridgeSessionId(input.repoId, input.workId)
395
+ : sessionId;
690
396
 
691
397
  try {
692
398
  const work = getWorkContract(store, input.workId);
693
399
  if (!work || work.repoId !== input.repoId) {
694
400
  throw new Error(`CHATGPT_WORK_CONTRACT_NOT_FOUND: ${input.repoId}:${input.workId}`);
695
401
  }
696
- await ensureControllerChatgptBrowser(input.controllerHome, input.workId);
697
- if (seedUrl && !binding && hasChatgptConversationIdentity(seedUrl)) {
402
+ const targetForgeInstanceId = work.executionPlacement?.forgeInstanceId?.trim();
403
+ if (targetForgeInstanceId) {
404
+ const currentForgeInstanceId = readForgeInstanceIdentity(input.controllerHome)?.instanceId?.trim();
405
+ if (currentForgeInstanceId !== targetForgeInstanceId) {
406
+ throw new ChatgptExecutionPlacementError(targetForgeInstanceId, currentForgeInstanceId);
407
+ }
408
+ }
409
+ if (!bridgeRuntime && seedUrl && !binding && hasChatgptConversationIdentity(seedUrl)) {
698
410
  binding = bindChatgptWorkConversation(store, {
699
411
  workId: input.workId,
700
412
  conversationUrl: seedUrl,
701
- latestBrowserSessionId: sessionId,
413
+ latestBrowserSessionId: deliverySessionId,
414
+ authorizationGrantRefs: [...authorizationGrantRefs],
702
415
  localAlias: input.title,
703
416
  });
704
417
  }
705
- const targetUrl = binding?.conversationUrl ?? seedUrl ?? 'https://chatgpt.com/';
706
- const navigation = await navigateWorkConversation(
707
- input.controllerHome,
708
- input.workId,
709
- sessionId,
710
- targetUrl,
711
- input.timeoutMs,
712
- );
713
- const executionPreferenceVerified = await ensureChatgptExecutionPreference(
714
- input.controllerHome,
715
- input.workId,
716
- navigation.browserSessionId,
717
- model,
718
- reasoning,
719
- input.timeoutMs,
418
+ const targetUrl = transportConversation === 'fresh' ? 'https://chatgpt.com/' : binding?.conversationUrl ?? seedUrl ?? 'https://chatgpt.com/';
419
+ const renderedPrompt = `${workflowToolAttributionInstruction(input)}\n\n${input.prompt}`;
420
+ const delivery = await withChatgptBrowserActionOrigin(
421
+ { surface: input.originSurface ?? 'chatgpt-action', actor: 'chatgpt-work-continuation' },
422
+ () => host.dispatch({
423
+ controllerHome: input.controllerHome,
424
+ repoId: input.repoId,
425
+ repoRoot: input.repoRoot,
426
+ workId: input.workId,
427
+ prompt: renderedPrompt,
428
+ browserSessionId: deliverySessionId,
429
+ targetUrl,
430
+ model,
431
+ reasoning,
432
+ timeoutMs: input.timeoutMs,
433
+ }),
434
+ authorizationGrantRefs,
720
435
  );
721
- const observedUrl = await submitChatgptPrompt(input.controllerHome, input.workId, navigation.browserSessionId, `${workflowToolAttributionInstruction(input.workId)}\n\n${input.prompt}`, navigation.submissionTargetUrl, input.timeoutMs);
722
- if (/\/c\/[^/?#]+/.test(observedUrl)) {
436
+ const observedUrl = delivery.conversationUrl ?? targetUrl;
437
+ const mayPersistObservedConversation = delivery.status === 'dispatch_confirmed' || delivery.status === 'outcome_unknown';
438
+ if (mayPersistObservedConversation && /\/c\/[^/?#]+/.test(observedUrl)) {
723
439
  const observedIdentity = parseChatgptConversationIdentity(observedUrl);
724
440
  binding = binding && binding.conversationId !== observedIdentity.conversationId
725
441
  ? rebindChatgptWorkConversation(store, {
726
- workId: input.workId,
727
- previousConversationId: binding.conversationId,
728
- conversationUrl: observedUrl,
729
- latestBrowserSessionId: navigation.browserSessionId,
730
- localAlias: binding.localAlias ?? input.title,
731
- })
442
+ workId: input.workId,
443
+ previousConversationId: binding.conversationId,
444
+ conversationUrl: observedUrl,
445
+ latestBrowserSessionId: delivery.browserSessionId,
446
+ authorizationGrantRefs: [...authorizationGrantRefs],
447
+ localAlias: binding.localAlias ?? input.title,
448
+ })
732
449
  : bindChatgptWorkConversation(store, {
733
- workId: input.workId,
734
- conversationUrl: observedUrl,
735
- latestBrowserSessionId: navigation.browserSessionId,
736
- localAlias: binding?.localAlias ?? input.title,
737
- });
450
+ workId: input.workId,
451
+ conversationUrl: observedUrl,
452
+ latestBrowserSessionId: delivery.browserSessionId,
453
+ authorizationGrantRefs: [...authorizationGrantRefs],
454
+ localAlias: binding?.localAlias ?? input.title,
455
+ });
456
+ }
457
+ if (delivery.status !== 'dispatch_confirmed') {
458
+ return {
459
+ status: 'failed',
460
+ provider: delivery.provider,
461
+ browserSessionId: delivery.browserSessionId,
462
+ conversationUrl: binding?.conversationUrl ?? delivery.conversationUrl ?? seedUrl,
463
+ conversationId: binding?.conversationId,
464
+ localAlias: binding?.localAlias,
465
+ resumedFromBinding: Boolean(existing),
466
+ model,
467
+ reasoning,
468
+ tabPolicy,
469
+ executionPreferenceVerified: delivery.executionPreferenceVerified,
470
+ authorizationGrantRefs: [...authorizationGrantRefs],
471
+ providerDeliveryStatus: delivery.status,
472
+ error: delivery.error ?? { code: `CHATGPT_PROVIDER_${delivery.status.toUpperCase()}`, message: delivery.status },
473
+ };
738
474
  }
739
- // Work-bound dispatch keeps the Forge-owned automation tab/session alive for
740
- // the launched semantic Controller round. The exact Controller release path
741
- // settles it after an explicit disposition. Closing here would race the new
742
- // ChatGPT round and make dispatch success look like round completion.
743
475
  return {
744
476
  status: 'dispatched',
745
- provider: 'controller-browser',
746
- browserSessionId: navigation.browserSessionId,
477
+ provider: delivery.provider,
478
+ browserSessionId: delivery.browserSessionId,
747
479
  conversationUrl: binding?.conversationUrl ?? observedUrl,
748
480
  conversationId: binding?.conversationId,
749
481
  localAlias: binding?.localAlias,
@@ -751,13 +483,17 @@ export async function runWorkChatgptContinuation(input: WorkChatgptContinuationI
751
483
  model,
752
484
  reasoning,
753
485
  tabPolicy,
754
- executionPreferenceVerified,
486
+ executionPreferenceVerified: delivery.executionPreferenceVerified,
487
+ authorizationGrantRefs: [...authorizationGrantRefs],
488
+ providerDeliveryStatus: delivery.status,
755
489
  };
756
490
  } catch (error) {
491
+ const provider = bridgeRuntime ? 'chatgpt-bridge' : 'controller-browser';
492
+ const fallbackSessionId = bridgeRuntime ? deliverySessionId : sessionId;
757
493
  return {
758
494
  status: 'failed',
759
- provider: 'controller-browser',
760
- browserSessionId: sessionId,
495
+ provider,
496
+ browserSessionId: fallbackSessionId,
761
497
  conversationUrl: binding?.conversationUrl ?? seedUrl,
762
498
  conversationId: binding?.conversationId,
763
499
  localAlias: binding?.localAlias,
@@ -766,8 +502,13 @@ export async function runWorkChatgptContinuation(input: WorkChatgptContinuationI
766
502
  reasoning,
767
503
  tabPolicy,
768
504
  executionPreferenceVerified: false,
505
+ authorizationGrantRefs: [...authorizationGrantRefs],
769
506
  error: {
770
- code: error instanceof Error && error.message.includes(':') ? error.message.split(':', 1)[0] : 'CHATGPT_CONTROLLER_BROWSER_FAILED',
507
+ code: error instanceof ChatgptExecutionPlacementError
508
+ ? error.code
509
+ : error instanceof Error && error.message.includes(':')
510
+ ? error.message.split(':', 1)[0]
511
+ : bridgeRuntime ? 'CHATGPT_BRIDGE_DISPATCH_FAILED' : 'CHATGPT_CONTROLLER_BROWSER_FAILED',
771
512
  message: error instanceof Error ? error.message : String(error),
772
513
  },
773
514
  };