@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
@@ -0,0 +1,783 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { AsyncLocalStorage } from 'node:async_hooks';
3
+ import type { ExecutionJobOrigin } from '../../src/runtime/execution/jobs/types';
4
+ import { browserActions } from '../../src/runtime/plugins/browser-manifest-surface';
5
+ import { controllerPluginRepository, executeControllerScopedPluginAction, getControllerPluginManifest, submitAssistantPluginAction } from '../../src/runtime/plugins/store';
6
+ import {
7
+ CHATGPT_AUTOMATION_SUBMISSION_OUTCOME_UNKNOWN,
8
+ ChatgptProviderDeliveryError,
9
+ DEFAULT_CHATGPT_AUTOMATION_MODEL,
10
+ type ChatgptAutomationReasoning,
11
+ type ChatgptAutomationTabCleanupStatus,
12
+ } from './provider-delivery';
13
+
14
+ const DEFAULT_CHATGPT_AUTOMATION_PLUGIN_MENTION = '@forge';
15
+
16
+ type ChatgptBrowserActionOrigin = Pick<ExecutionJobOrigin, 'surface' | 'actor'>;
17
+ interface ChatgptBrowserActionContext {
18
+ origin: ChatgptBrowserActionOrigin;
19
+ authorizationGrantRefs: Set<string>;
20
+ }
21
+ const chatgptBrowserActionOrigin = new AsyncLocalStorage<ChatgptBrowserActionContext>();
22
+
23
+ export function withChatgptBrowserActionOrigin<T>(
24
+ origin: ChatgptBrowserActionOrigin,
25
+ operation: () => Promise<T>,
26
+ authorizationGrantRefs: Set<string> = new Set(),
27
+ ): Promise<T> {
28
+ return chatgptBrowserActionOrigin.run({ origin, authorizationGrantRefs }, operation);
29
+ }
30
+
31
+ function withForgePluginMention(prompt: string): string {
32
+ const value = prompt.trim();
33
+ if (!value) throw new Error('CHATGPT_AUTOMATION_PROMPT_REQUIRED');
34
+ if (/^@forge(?:\s|$)/i.test(value)) return value;
35
+ return `${DEFAULT_CHATGPT_AUTOMATION_PLUGIN_MENTION} ${value}`;
36
+ }
37
+
38
+ const CHATGPT_PROMPT_SELECTOR = 'div#prompt-textarea[contenteditable="true"]';
39
+ const CHATGPT_SEND_SELECTOR = '[data-testid="send-button"], button[aria-label*="Send"], button[data-testid*="send"]';
40
+ const CHATGPT_USER_MESSAGE_SELECTOR = '[data-message-author-role="user"]';
41
+ const CHATGPT_INTELLIGENCE_CONTROL_SELECTORS = [
42
+ 'main button, main [role="button"]',
43
+ 'button, [role="button"]',
44
+ ] as const;
45
+ const CHATGPT_CAPABILITY_SLIDER_SELECTOR = '[role="slider"]';
46
+ const CHATGPT_CAPABILITY_MENUITEM_SELECTOR = '[role="menuitem"][aria-keyshortcuts~="ArrowLeft"][aria-keyshortcuts~="ArrowRight"]';
47
+
48
+ function requestId(workId: string, actionId: string): string {
49
+ return `chatgpt-work:${workId}:${actionId}:${randomUUID()}`;
50
+ }
51
+
52
+ const CHATGPT_BROWSER_AUTHORIZATION_ACTIONS = new Set(
53
+ browserActions()
54
+ .filter((action) => !action.readOnly && action.confirmation === 'authorization')
55
+ .map((action) => action.actionId),
56
+ );
57
+
58
+ const CHATGPT_BROWSER_TRANSPORT_OVERRIDES = {
59
+ browser_mode: 'attach_preferred',
60
+ cdp_attach_fallback: 'fail_closed',
61
+ native_attach_mode: 'auto',
62
+ native_browser_candidates: ['chrome'],
63
+ } as const;
64
+
65
+ export function chatgptBrowserActionArgs(actionId: string, args: Record<string, unknown>): Record<string, unknown> {
66
+ if (actionId === 'configure' || actionId === 'list_sessions' || actionId === 'reconcile_sessions') return args;
67
+ return { ...args, ...CHATGPT_BROWSER_TRANSPORT_OVERRIDES };
68
+ }
69
+
70
+ export function chatgptBrowserActionResult(envelope: Record<string, unknown>, actionId: string): Record<string, unknown> {
71
+ const result = envelope.result;
72
+ if (!result || typeof result !== 'object' || Array.isArray(result)) {
73
+ throw new Error(`CHATGPT_BROWSER_ACTION_RESULT_INVALID:${actionId}`);
74
+ }
75
+ return result as Record<string, unknown>;
76
+ }
77
+
78
+ async function controllerBrowserAction(
79
+ controllerHome: string,
80
+ workId: string,
81
+ actionId: string,
82
+ args: Record<string, unknown>,
83
+ timeoutMs?: number,
84
+ ): Promise<Record<string, unknown>> {
85
+ const context = chatgptBrowserActionOrigin.getStore();
86
+ const origin = context?.origin ?? { surface: 'schedule', actor: 'chatgpt-work-continuation' };
87
+ const actionRequest = {
88
+ pluginId: 'browser',
89
+ actionId,
90
+ requestId: requestId(workId, actionId),
91
+ args: chatgptBrowserActionArgs(actionId, args),
92
+ timeoutMs,
93
+ origin,
94
+ };
95
+
96
+ // Interactive ChatGPT delivery is the only place allowed to establish a
97
+ // reusable controller-scoped Browser grant. Scheduled delivery stays on the
98
+ // low-level executor and must present explicit refs already bound to this Work.
99
+ if (origin.surface === 'chatgpt-action' && CHATGPT_BROWSER_AUTHORIZATION_ACTIONS.has(actionId)) {
100
+ const submitted = await submitAssistantPluginAction(
101
+ controllerHome,
102
+ controllerPluginRepository(controllerHome),
103
+ actionRequest,
104
+ );
105
+ const grantId = submitted.authorization?.grantId?.trim();
106
+ if (grantId) context?.authorizationGrantRefs.add(grantId);
107
+ if (!submitted.result) throw new Error(`CHATGPT_BROWSER_ACTION_RESULT_INVALID:${actionId}`);
108
+ return chatgptBrowserActionResult(submitted.result, actionId);
109
+ }
110
+
111
+ const envelope = await executeControllerScopedPluginAction({
112
+ controllerHome,
113
+ ...actionRequest,
114
+ authorizationGrantRefs: [...(context?.authorizationGrantRefs ?? [])],
115
+ });
116
+ return chatgptBrowserActionResult(envelope, actionId);
117
+ }
118
+
119
+ export async function ensureControllerChatgptBrowser(controllerHome: string, workId: string): Promise<void> {
120
+ // Browser configure is not a read: it persists configuration and closes managed
121
+ // contexts. Scheduled continuations must not disturb an already-enabled provider
122
+ // merely to prove it is available. Read the existing controller-scoped manifest
123
+ // authority, and only enable it when that persisted authority is explicitly disabled;
124
+ // action-level transport overrides still fail closed later.
125
+ if (getControllerPluginManifest(controllerHome, 'browser').enabled) return;
126
+ await controllerBrowserAction(controllerHome, workId, 'configure', { enabled: true });
127
+ }
128
+
129
+ function stringField(value: unknown): string | undefined {
130
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
131
+ }
132
+
133
+ interface BrowserQueryMatch {
134
+ text?: string;
135
+ selectorHint?: string;
136
+ }
137
+
138
+ function queryMatches(result: Record<string, unknown> | undefined): BrowserQueryMatch[] {
139
+ if (!result || !Array.isArray(result.matches)) return [];
140
+ return result.matches.filter((value): value is BrowserQueryMatch => Boolean(value) && typeof value === 'object');
141
+ }
142
+
143
+ function matchText(value: BrowserQueryMatch): string {
144
+ return typeof value.text === 'string' ? value.text.trim() : '';
145
+ }
146
+
147
+ function matchSelector(value: BrowserQueryMatch | undefined): string | undefined {
148
+ return typeof value?.selectorHint === 'string' && value.selectorHint.trim() ? value.selectorHint.trim() : undefined;
149
+ }
150
+
151
+ function normalizeChatgptOutboundText(value: string): string {
152
+ return value.replace(/\s+/g, ' ').trim();
153
+ }
154
+
155
+ const CHATGPT_OUTBOUND_MESSAGE_UI_SUFFIXES = ['收起', 'Collapse', 'Show less'] as const;
156
+ const MAX_CHATGPT_OUTBOUND_VERIFICATION_CHARS = 100_000;
157
+ const MIN_TRUNCATED_CHATGPT_OUTBOUND_PREFIX_CHARS = 256;
158
+
159
+ export function chatgptOutboundMessageMatchesPrompt(
160
+ messageText: string,
161
+ prompt: string,
162
+ options: { truncated?: boolean } = {},
163
+ ): boolean {
164
+ const message = normalizeChatgptOutboundText(messageText);
165
+ const normalizedPrompt = normalizeChatgptOutboundText(prompt);
166
+ if (!message || !normalizedPrompt) return false;
167
+ if (message === normalizedPrompt) return true;
168
+ if (CHATGPT_OUTBOUND_MESSAGE_UI_SUFFIXES.some((suffix) => message === `${normalizedPrompt} ${suffix}`)) return true;
169
+ // Browser text extraction is deliberately bounded. A large ControllerRound
170
+ // prompt can exceed that bound, so requiring exact equality makes successful
171
+ // submissions mechanically unverifiable. Accept only an explicitly reported
172
+ // truncation of a substantial exact prefix; ordinary partial/mismatched text
173
+ // remains insufficient evidence.
174
+ return options.truncated === true
175
+ && message.length >= MIN_TRUNCATED_CHATGPT_OUTBOUND_PREFIX_CHARS
176
+ && normalizedPrompt.startsWith(message);
177
+ }
178
+
179
+ async function latestChatgptUserMessage(
180
+ controllerHome: string,
181
+ workId: string,
182
+ browserSessionId: string,
183
+ timeoutMs?: number,
184
+ ): Promise<{ selector?: string; preview: string; url?: string }> {
185
+ const result = await controllerBrowserAction(controllerHome, workId, 'query_all', {
186
+ session_id: browserSessionId,
187
+ selector: CHATGPT_USER_MESSAGE_SELECTOR,
188
+ limit: 1,
189
+ from_end: true,
190
+ timeout_ms: Math.min(timeoutMs ?? 3_000, 3_000),
191
+ }, timeoutMs);
192
+ const latest = queryMatches(result).at(-1);
193
+ return { selector: matchSelector(latest), preview: latest ? matchText(latest) : '', url: resultUrl(result) };
194
+ }
195
+
196
+ async function fullChatgptMessageText(
197
+ controllerHome: string,
198
+ workId: string,
199
+ browserSessionId: string,
200
+ message: { selector?: string; preview: string },
201
+ timeoutMs?: number,
202
+ ): Promise<{ text: string; truncated: boolean }> {
203
+ if (!message.selector) return { text: message.preview, truncated: false };
204
+ const result = await controllerBrowserAction(controllerHome, workId, 'get_text', {
205
+ session_id: browserSessionId,
206
+ selector: message.selector,
207
+ max_chars: MAX_CHATGPT_OUTBOUND_VERIFICATION_CHARS,
208
+ timeout_ms: Math.min(timeoutMs ?? 3_000, 3_000),
209
+ }, timeoutMs).catch(() => undefined);
210
+ return {
211
+ text: stringField(result?.text) ?? message.preview,
212
+ truncated: result?.truncated === true,
213
+ };
214
+ }
215
+
216
+ function chatgptSendControlUnavailable(error: unknown): boolean {
217
+ return error instanceof Error && (error.message.includes('Selector') && error.message.includes('not found'));
218
+ }
219
+
220
+ function normalizeExecutionControlLabel(label: string | undefined): string {
221
+ return (label ?? '').trim().toLowerCase().replace(/[\s_-]+/g, '');
222
+ }
223
+
224
+ export function chatgptAutomationReasoningLevelFromLabel(label: string | undefined): ChatgptAutomationReasoning | undefined {
225
+ const normalized = normalizeExecutionControlLabel(label);
226
+ if (!normalized) return undefined;
227
+
228
+ const exact = new Map<string, ChatgptAutomationReasoning>([
229
+ ['medium', 'medium'], ['中', 'medium'], ['中等', 'medium'],
230
+ ['high', 'high'], ['高', 'high'],
231
+ ['xhigh', 'xhigh'], ['extrahigh', 'xhigh'], ['超高', 'xhigh'], ['极高', 'xhigh'],
232
+ ]);
233
+ const exactLevel = exact.get(normalized);
234
+ if (exactLevel) return exactLevel;
235
+
236
+ const hasReasoningContext = ['reasoning', 'thinking', '推理', '思考']
237
+ .some((token) => normalized.includes(token));
238
+ if (!hasReasoningContext) return undefined;
239
+ if (['xhigh', 'extrahigh', '超高', '极高'].some((token) => normalized.includes(token))) return 'xhigh';
240
+ if (['medium', '中等'].some((token) => normalized.includes(token))) return 'medium';
241
+ if (['high', '高'].some((token) => normalized.includes(token))) return 'high';
242
+ return undefined;
243
+ }
244
+
245
+ function modelLabelMatches(label: string | undefined, model: string): boolean {
246
+ if (model !== DEFAULT_CHATGPT_AUTOMATION_MODEL || !label) return false;
247
+ const normalized = normalizeExecutionControlLabel(label);
248
+ return normalized.includes('5.6sol') || normalized.includes('gpt5.6sol');
249
+ }
250
+
251
+ export function chatgptAutomationModelFamilyMenuTrigger(
252
+ label: string | undefined,
253
+ ariaHasPopup: string | undefined,
254
+ ): boolean {
255
+ if (ariaHasPopup !== 'menu') return false;
256
+ const normalized = normalizeExecutionControlLabel(label);
257
+ // ChatGPT's composer can rotate model names independently of Forge releases.
258
+ // Locate the combined intelligence control by a bounded model-family prefix
259
+ // plus its menu-trigger role; do not treat arbitrary GPT prose/buttons as authority.
260
+ return /^gpt\d/.test(normalized);
261
+ }
262
+
263
+ function reasoningLabelMatches(label: string | undefined, reasoning: ChatgptAutomationReasoning): boolean {
264
+ return chatgptAutomationReasoningLevelFromLabel(label) === reasoning;
265
+ }
266
+
267
+ function isReasoningControlLabel(label: string | undefined): boolean {
268
+ return reasoningLabelMatches(label, 'medium')
269
+ || reasoningLabelMatches(label, 'high')
270
+ || reasoningLabelMatches(label, 'xhigh');
271
+ }
272
+
273
+ function reasoningSliderValue(reasoning: ChatgptAutomationReasoning): number {
274
+ if (reasoning === 'medium') return 2;
275
+ if (reasoning === 'high') return 3;
276
+ return 4;
277
+ }
278
+
279
+ async function findChatgptIntelligenceControl(
280
+ controllerHome: string,
281
+ workId: string,
282
+ browserSessionId: string,
283
+ timeoutMs?: number,
284
+ ): Promise<BrowserQueryMatch | undefined> {
285
+ for (const selector of CHATGPT_INTELLIGENCE_CONTROL_SELECTORS) {
286
+ const result = await controllerBrowserAction(controllerHome, workId, 'query_all', {
287
+ session_id: browserSessionId,
288
+ selector,
289
+ // Prefer the composer/main region so sidebar history cannot crowd the
290
+ // reasoning control out of a bounded query. Keep a larger global fallback
291
+ // for ChatGPT layouts that place the control outside <main>.
292
+ limit: chatgptAutomationControlQueryLimit(selector),
293
+ timeout_ms: timeoutMs ?? 60_000,
294
+ }, timeoutMs);
295
+ const candidates = queryMatches(result);
296
+ const exactMatch = candidates.find((candidate) => {
297
+ const label = matchText(candidate);
298
+ return modelLabelMatches(label, DEFAULT_CHATGPT_AUTOMATION_MODEL) || isReasoningControlLabel(label);
299
+ });
300
+ if (exactMatch) return exactMatch;
301
+ for (const candidate of candidates) {
302
+ const candidateSelector = matchSelector(candidate);
303
+ if (!candidateSelector || !/^gpt\d/.test(normalizeExecutionControlLabel(matchText(candidate)))) continue;
304
+ const popup = await controllerBrowserAction(controllerHome, workId, 'get_attribute', {
305
+ session_id: browserSessionId,
306
+ selector: candidateSelector,
307
+ attribute: 'aria-haspopup',
308
+ timeout_ms: timeoutMs ?? 60_000,
309
+ }, timeoutMs).catch(() => undefined);
310
+ if (chatgptAutomationModelFamilyMenuTrigger(matchText(candidate), stringField(popup?.value))) return candidate;
311
+ }
312
+ }
313
+ return undefined;
314
+ }
315
+
316
+ export function chatgptAutomationControlQueryLimit(selector: string): number {
317
+ // Long tool-heavy conversations can contribute well over 80 buttons before
318
+ // the composer controls. Keep the preferred <main> scan bounded but large
319
+ // enough that the current reasoning control is not crowded out by history.
320
+ return selector.startsWith('main ') ? 160 : 320;
321
+ }
322
+
323
+ export function chatgptAutomationControlWaitBudgets(timeoutMs?: number): { waitBudgetMs: number; probeTimeoutMs: number } {
324
+ const waitBudgetMs = Math.min(Math.max(timeoutMs ?? 30_000, 1_000), 30_000);
325
+ // Native Chrome attachment has a few seconds of Apple Events/DOM latency on
326
+ // long conversations. Keep each probe bounded while allowing a real query to
327
+ // complete; the total readiness window remains capped at 30 seconds.
328
+ return { waitBudgetMs, probeTimeoutMs: Math.min(waitBudgetMs, 5_000) };
329
+ }
330
+
331
+ async function waitForChatgptIntelligenceControl(
332
+ controllerHome: string,
333
+ workId: string,
334
+ browserSessionId: string,
335
+ timeoutMs?: number,
336
+ ): Promise<BrowserQueryMatch | undefined> {
337
+ const { waitBudgetMs, probeTimeoutMs } = chatgptAutomationControlWaitBudgets(timeoutMs);
338
+ const deadline = Date.now() + waitBudgetMs;
339
+ do {
340
+ const control = await findChatgptIntelligenceControl(controllerHome, workId, browserSessionId, probeTimeoutMs);
341
+ if (control) return control;
342
+ if (Date.now() >= deadline) break;
343
+ await new Promise((resolveWait) => setTimeout(resolveWait, 500));
344
+ } while (Date.now() < deadline);
345
+ return undefined;
346
+ }
347
+
348
+ export function chatgptAutomationPageFailure(
349
+ bodyText: string | undefined,
350
+ composerAvailable: boolean,
351
+ ): 'CHATGPT_AUTOMATION_LOGIN_REQUIRED' | 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE' | undefined {
352
+ if (composerAvailable) return undefined;
353
+ const normalized = (bodyText ?? '').toLowerCase();
354
+ const loginMarkers = [
355
+ 'log in', 'sign up', 'continue with google', 'continue with apple',
356
+ '登录', '登陆', '注册', '使用 google 继续', '使用 apple 继续',
357
+ ];
358
+ return loginMarkers.some((marker) => normalized.includes(marker))
359
+ ? 'CHATGPT_AUTOMATION_LOGIN_REQUIRED'
360
+ : 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE';
361
+ }
362
+
363
+ async function assertChatgptAutomationComposerReady(
364
+ controllerHome: string,
365
+ workId: string,
366
+ browserSessionId: string,
367
+ timeoutMs?: number,
368
+ ): Promise<void> {
369
+ const composerResult = await controllerBrowserAction(controllerHome, workId, 'query_all', {
370
+ session_id: browserSessionId,
371
+ selector: CHATGPT_PROMPT_SELECTOR,
372
+ limit: 1,
373
+ timeout_ms: Math.min(timeoutMs ?? 5_000, 5_000),
374
+ }, timeoutMs).catch(() => undefined);
375
+ const composerAvailable = queryMatches(composerResult).length > 0;
376
+ if (composerAvailable) return;
377
+
378
+ const bodyResult = await controllerBrowserAction(controllerHome, workId, 'get_text', {
379
+ session_id: browserSessionId,
380
+ selector: 'body',
381
+ max_chars: 4_000,
382
+ timeout_ms: Math.min(timeoutMs ?? 5_000, 5_000),
383
+ }, timeoutMs).catch(() => undefined);
384
+ const failure = chatgptAutomationPageFailure(stringField(bodyResult?.text), composerAvailable);
385
+ throw new Error(failure ?? 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE');
386
+ }
387
+
388
+ export async function ensureChatgptExecutionPreference(
389
+ controllerHome: string,
390
+ workId: string,
391
+ browserSessionId: string,
392
+ model: string,
393
+ reasoning: ChatgptAutomationReasoning,
394
+ timeoutMs?: number,
395
+ ): Promise<boolean> {
396
+ if (model !== DEFAULT_CHATGPT_AUTOMATION_MODEL) throw new Error(`CHATGPT_AUTOMATION_MODEL_UNSUPPORTED:${model}`);
397
+ let control = await waitForChatgptIntelligenceControl(controllerHome, workId, browserSessionId, timeoutMs);
398
+ if (!control) {
399
+ await assertChatgptAutomationComposerReady(controllerHome, workId, browserSessionId, timeoutMs);
400
+ throw new Error('CHATGPT_AUTOMATION_INTELLIGENCE_CONTROL_UNAVAILABLE');
401
+ }
402
+ // Current ChatGPT UI exposes the reasoning level (for example `高`) on the
403
+ // composer control instead of the model label. The model remains fail-closed
404
+ // through normalizeModel above; only the user-adjustable reasoning control is
405
+ // required to be observable in the page before unattended dispatch proceeds.
406
+ if (reasoningLabelMatches(matchText(control), reasoning)) return true;
407
+ let controlSelector = matchSelector(control);
408
+ if (!controlSelector) throw new Error('CHATGPT_AUTOMATION_INTELLIGENCE_CONTROL_UNAVAILABLE');
409
+
410
+ const expanded = await controllerBrowserAction(controllerHome, workId, 'get_attribute', {
411
+ session_id: browserSessionId,
412
+ selector: controlSelector,
413
+ attribute: 'aria-expanded',
414
+ timeout_ms: timeoutMs ?? 60_000,
415
+ }, timeoutMs).catch(() => undefined);
416
+ if (stringField(expanded?.value) !== 'true') {
417
+ await controllerBrowserAction(controllerHome, workId, 'press', {
418
+ session_id: browserSessionId,
419
+ selector: controlSelector,
420
+ key: 'ArrowDown',
421
+ timeout_ms: timeoutMs ?? 60_000,
422
+ post_action_wait_ms: 150,
423
+ }, timeoutMs);
424
+ }
425
+
426
+ const currentValueResult = await controllerBrowserAction(controllerHome, workId, 'get_attribute', {
427
+ session_id: browserSessionId,
428
+ selector: CHATGPT_CAPABILITY_SLIDER_SELECTOR,
429
+ attribute: 'aria-valuenow',
430
+ timeout_ms: timeoutMs ?? 60_000,
431
+ }, timeoutMs).catch(() => undefined);
432
+ const currentValue = Number(stringField(currentValueResult?.value));
433
+ const targetValue = reasoningSliderValue(reasoning);
434
+ if (!Number.isInteger(currentValue) || currentValue < 0 || currentValue > 4) {
435
+ throw new Error(`CHATGPT_AUTOMATION_REASONING_STATE_UNAVAILABLE:${reasoning}`);
436
+ }
437
+ const direction = targetValue > currentValue ? 'ArrowRight' : 'ArrowLeft';
438
+ for (let index = 0; index < Math.abs(targetValue - currentValue); index += 1) {
439
+ await controllerBrowserAction(controllerHome, workId, 'press', {
440
+ session_id: browserSessionId,
441
+ selector: CHATGPT_CAPABILITY_MENUITEM_SELECTOR,
442
+ key: direction,
443
+ timeout_ms: timeoutMs ?? 60_000,
444
+ post_action_wait_ms: 120,
445
+ }, timeoutMs);
446
+ }
447
+
448
+ const verifiedValueResult = await controllerBrowserAction(controllerHome, workId, 'get_attribute', {
449
+ session_id: browserSessionId,
450
+ selector: CHATGPT_CAPABILITY_SLIDER_SELECTOR,
451
+ attribute: 'aria-valuenow',
452
+ timeout_ms: timeoutMs ?? 60_000,
453
+ }, timeoutMs).catch(() => undefined);
454
+ if (Number(stringField(verifiedValueResult?.value)) !== targetValue) {
455
+ throw new Error(`CHATGPT_AUTOMATION_REASONING_NOT_VERIFIED:${reasoning}`);
456
+ }
457
+ control = await findChatgptIntelligenceControl(controllerHome, workId, browserSessionId, timeoutMs) ?? control;
458
+ if (isReasoningControlLabel(matchText(control)) && !reasoningLabelMatches(matchText(control), reasoning)) {
459
+ throw new Error(`CHATGPT_AUTOMATION_REASONING_NOT_VERIFIED:${reasoning}`);
460
+ }
461
+ controlSelector = matchSelector(control) ?? controlSelector;
462
+ await controllerBrowserAction(controllerHome, workId, 'press', {
463
+ session_id: browserSessionId,
464
+ selector: controlSelector,
465
+ key: 'Escape',
466
+ timeout_ms: timeoutMs ?? 60_000,
467
+ post_action_wait_ms: 100,
468
+ }, timeoutMs).catch(() => undefined);
469
+ return true;
470
+ }
471
+
472
+ function resultUrl(result: Record<string, unknown>): string | undefined {
473
+ return stringField(result.url)
474
+ ?? stringField((result.session as Record<string, unknown> | undefined)?.url);
475
+ }
476
+
477
+ function resultSessionId(result: Record<string, unknown>): string | undefined {
478
+ return stringField(result.session_id)
479
+ ?? stringField((result.session as Record<string, unknown> | undefined)?.sessionId);
480
+ }
481
+
482
+ interface ChatgptBrowserSessionInventoryItem {
483
+ sessionId?: unknown;
484
+ url?: unknown;
485
+ liveness?: unknown;
486
+ }
487
+
488
+ function chatgptUrlMatchesContinuationTarget(observedUrl: string, targetUrl: string): boolean {
489
+ try {
490
+ const observed = new URL(observedUrl);
491
+ const target = new URL(targetUrl);
492
+ const allowedHosts = new Set(['chatgpt.com', 'www.chatgpt.com', 'chat.openai.com']);
493
+ if (observed.protocol !== 'https:' || target.protocol !== 'https:' || !allowedHosts.has(observed.hostname) || !allowedHosts.has(target.hostname)) return false;
494
+ const targetConversation = /\/c\/([^/?#]+)/.exec(target.pathname)?.[1];
495
+ if (targetConversation) {
496
+ const observedConversation = /\/c\/([^/?#]+)/.exec(observed.pathname)?.[1];
497
+ return observedConversation === targetConversation;
498
+ }
499
+ return observed.pathname === target.pathname;
500
+ } catch {
501
+ return false;
502
+ }
503
+ }
504
+
505
+ function completeChatgptBrowserInventorySessions(
506
+ inventory: Record<string, unknown>,
507
+ ): ChatgptBrowserSessionInventoryItem[] | undefined {
508
+ if (stringField(inventory.nextCursor)) return undefined;
509
+ return Array.isArray(inventory.sessions)
510
+ ? inventory.sessions as ChatgptBrowserSessionInventoryItem[]
511
+ : [];
512
+ }
513
+
514
+ export function reconciledNewChatgptOpenPageSessionId(
515
+ beforeInventory: Record<string, unknown>,
516
+ afterInventory: Record<string, unknown>,
517
+ targetUrl: string,
518
+ ): string | undefined {
519
+ const beforeSessions = completeChatgptBrowserInventorySessions(beforeInventory);
520
+ const afterSessions = completeChatgptBrowserInventorySessions(afterInventory);
521
+ if (!beforeSessions || !afterSessions) return undefined;
522
+ const beforeIds = new Set(beforeSessions
523
+ .map((entry) => stringField(entry.sessionId))
524
+ .filter((value): value is string => Boolean(value)));
525
+ const matches = afterSessions.flatMap((entry) => {
526
+ const sessionId = stringField(entry.sessionId);
527
+ const url = stringField(entry.url);
528
+ return sessionId
529
+ && !beforeIds.has(sessionId)
530
+ && entry.liveness === 'live'
531
+ && url
532
+ && chatgptUrlMatchesContinuationTarget(url, targetUrl)
533
+ ? [sessionId]
534
+ : [];
535
+ });
536
+ return matches.length === 1 ? matches[0] : undefined;
537
+ }
538
+
539
+ function browserMutationOutcomeUnknown(error: unknown, actionId: string): boolean {
540
+ return error instanceof Error
541
+ && error.message.includes('PLUGIN_BROWSER_MUTATION_OUTCOME_UNKNOWN')
542
+ && error.message.includes(`Browser action ${actionId}`);
543
+ }
544
+
545
+ export function chatgptAutomationNavigationRequiresReplacement(error: unknown): boolean {
546
+ if (!(error instanceof Error)) return false;
547
+ return [
548
+ 'BROWSER_AUTOMATION_BACKGROUND_NAVIGATION_REQUIRES_REPLACEMENT',
549
+ 'PLUGIN_BROWSER_SESSION_STATE_LOST',
550
+ 'PLUGIN_SESSION_NOT_FOUND',
551
+ 'PLUGIN_BROWSER_NATIVE_TAB_IDENTITY_UNPROVEN',
552
+ ].some((marker) => error.message.includes(marker));
553
+ }
554
+
555
+ export async function closeChatgptAutomationTabAfterDispatch(
556
+ controllerHome: string,
557
+ workId: string,
558
+ browserSessionId: string,
559
+ timeoutMs?: number,
560
+ ): Promise<{ status: ChatgptAutomationTabCleanupStatus; error?: { code: string; message: string } }> {
561
+ try {
562
+ const closed = await controllerBrowserAction(controllerHome, workId, 'close_page', {
563
+ session_id: browserSessionId,
564
+ }, Math.min(timeoutMs ?? 15_000, 15_000));
565
+ if (closed.preservedUserOwnedTab === true) return { status: 'preserved_user_owned' };
566
+ if (closed.resourceClosed === true) return { status: 'closed' };
567
+ return { status: 'session_closed' };
568
+ } catch (error) {
569
+ const message = error instanceof Error ? error.message : String(error);
570
+ return {
571
+ status: 'failed',
572
+ error: {
573
+ code: error instanceof Error && error.message.includes(':') ? error.message.split(':', 1)[0] : 'CHATGPT_AUTOMATION_TAB_CLEANUP_FAILED',
574
+ message,
575
+ },
576
+ };
577
+ }
578
+ }
579
+
580
+ /**
581
+ * Settles the browser resource for one completed Work-bound ChatGPT round.
582
+ * Conversation identity is durable in the Work binding and intentionally
583
+ * survives this ephemeral tab/session cleanup. Browser ownership policy keeps
584
+ * user-owned native tabs intact.
585
+ */
586
+ export async function settleWorkChatgptAutomationTab(input: {
587
+ controllerHome: string;
588
+ workId: string;
589
+ browserSessionId: string;
590
+ timeoutMs?: number;
591
+ }): Promise<{ status: ChatgptAutomationTabCleanupStatus; error?: { code: string; message: string } }> {
592
+ if (input.browserSessionId.startsWith('forge-chatgpt-bridge-')) return { status: 'session_closed' };
593
+ return closeChatgptAutomationTabAfterDispatch(
594
+ input.controllerHome,
595
+ input.workId,
596
+ input.browserSessionId,
597
+ input.timeoutMs,
598
+ );
599
+ }
600
+
601
+ export function isChatgptConversationUrl(value: string): boolean {
602
+ try {
603
+ const url = new URL(value);
604
+ return url.protocol === 'https:'
605
+ && ['chatgpt.com', 'www.chatgpt.com', 'chat.openai.com'].includes(url.hostname)
606
+ && /\/c\/[^/?#]+/.test(url.pathname);
607
+ } catch {
608
+ return false;
609
+ }
610
+ }
611
+
612
+ export async function navigateWorkConversation(
613
+ controllerHome: string,
614
+ workId: string,
615
+ browserSessionId: string,
616
+ targetUrl: string,
617
+ timeoutMs?: number,
618
+ ): Promise<{ submissionTargetUrl: string; recoveredFromStaleBinding: boolean; browserSessionId: string }> {
619
+ const navigate = async (sessionId: string, url: string) => controllerBrowserAction(controllerHome, workId, 'navigate', {
620
+ session_id: sessionId,
621
+ url,
622
+ wait_until: 'domcontentloaded',
623
+ timeout_ms: timeoutMs ?? 60_000,
624
+ retries: 1,
625
+ }, timeoutMs);
626
+ const openReplacement = async (sessionId: string, url: string): Promise<string> => {
627
+ // Fresh ControllerRound transport already owns an explicit Browser session
628
+ // identity. Use create_session with that exact id so native Browser never has
629
+ // to guess among multiple reusable Forge-owned tabs. The Browser adapter still
630
+ // owns tab identity/replacement and can recover a stale saved tab behind this id.
631
+ try {
632
+ const opened = await controllerBrowserAction(controllerHome, workId, 'create_session', {
633
+ session_id: sessionId,
634
+ url,
635
+ wait_until: 'domcontentloaded',
636
+ timeout_ms: timeoutMs ?? 60_000,
637
+ retries: 1,
638
+ }, timeoutMs);
639
+ const replacementSessionId = resultSessionId(opened);
640
+ if (replacementSessionId !== sessionId) throw new Error('CHATGPT_AUTOMATION_REPLACEMENT_SESSION_NOT_CONFIRMED');
641
+ return replacementSessionId;
642
+ } catch (error) {
643
+ if (!browserMutationOutcomeUnknown(error, 'create_session')) throw error;
644
+ try {
645
+ const verified = await controllerBrowserAction(controllerHome, workId, 'verify_state', {
646
+ session_id: sessionId,
647
+ expected_url: url,
648
+ }, timeoutMs);
649
+ if (verified.matched === true && stringField(verified.sessionId) === sessionId) return sessionId;
650
+ } catch {
651
+ // Preserve the original create_session mutation-unknown evidence.
652
+ }
653
+ throw error;
654
+ }
655
+ };
656
+ try {
657
+ // Prove the exact saved Browser resource is still attachable before dispatching
658
+ // a navigation mutation. A stale native tab must be replaced before any write;
659
+ // discovering staleness only after mutation creates an avoidable outcome-unknown window.
660
+ try {
661
+ await controllerBrowserAction(controllerHome, workId, 'verify_state', {
662
+ session_id: browserSessionId,
663
+ expected_url: targetUrl,
664
+ }, timeoutMs);
665
+ } catch (preflightError) {
666
+ if (!chatgptAutomationNavigationRequiresReplacement(preflightError)) throw preflightError;
667
+ const replacementSessionId = await openReplacement(browserSessionId, targetUrl);
668
+ return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: true, browserSessionId: replacementSessionId };
669
+ }
670
+ await navigate(browserSessionId, targetUrl);
671
+ return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: false, browserSessionId };
672
+ } catch (error) {
673
+ if (browserMutationOutcomeUnknown(error, 'navigate')) {
674
+ try {
675
+ const verified = await controllerBrowserAction(controllerHome, workId, 'verify_state', {
676
+ session_id: browserSessionId,
677
+ expected_url: targetUrl,
678
+ }, timeoutMs);
679
+ if (verified.matched === true && stringField(verified.sessionId) === browserSessionId) {
680
+ return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: false, browserSessionId };
681
+ }
682
+ } catch {
683
+ // Preserve the original mutation-unknown evidence. Verification failure is
684
+ // not authority to replay navigate or to invent a replacement session.
685
+ }
686
+ throw error;
687
+ }
688
+ if (chatgptAutomationNavigationRequiresReplacement(error)) {
689
+ const replacementSessionId = await openReplacement(browserSessionId, targetUrl);
690
+ return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: false, browserSessionId: replacementSessionId };
691
+ }
692
+ if (!isChatgptConversationUrl(targetUrl)) throw error;
693
+ try {
694
+ await navigate(browserSessionId, 'https://chatgpt.com/');
695
+ return { submissionTargetUrl: 'https://chatgpt.com/', recoveredFromStaleBinding: true, browserSessionId };
696
+ } catch (fallbackError) {
697
+ if (!chatgptAutomationNavigationRequiresReplacement(fallbackError)) throw fallbackError;
698
+ const replacementSessionId = await openReplacement(browserSessionId, 'https://chatgpt.com/');
699
+ return { submissionTargetUrl: 'https://chatgpt.com/', recoveredFromStaleBinding: true, browserSessionId: replacementSessionId };
700
+ }
701
+ }
702
+ }
703
+
704
+ export async function submitChatgptPrompt(
705
+ controllerHome: string,
706
+ workId: string,
707
+ browserSessionId: string,
708
+ prompt: string,
709
+ targetUrl: string,
710
+ timeoutMs?: number,
711
+ ): Promise<string> {
712
+ const renderedPrompt = withForgePluginMention(prompt);
713
+ const before = await latestChatgptUserMessage(controllerHome, workId, browserSessionId, timeoutMs)
714
+ .catch((): { selector?: string; preview: string; url?: string } => ({ selector: undefined, preview: '', url: targetUrl }));
715
+ await controllerBrowserAction(controllerHome, workId, 'fill', {
716
+ session_id: browserSessionId,
717
+ selector: CHATGPT_PROMPT_SELECTOR,
718
+ text: renderedPrompt,
719
+ timeout_ms: timeoutMs ?? 60_000,
720
+ post_action_wait_ms: 100,
721
+ }, timeoutMs);
722
+
723
+ let observedUrl = targetUrl;
724
+ let submitOutcomeUnknown = false;
725
+ let observedNewOutbound = false;
726
+ try {
727
+ const sent = await controllerBrowserAction(controllerHome, workId, 'click', {
728
+ session_id: browserSessionId,
729
+ selector: CHATGPT_SEND_SELECTOR,
730
+ timeout_ms: timeoutMs ?? 60_000,
731
+ post_action_wait_ms: 250,
732
+ }, timeoutMs);
733
+ observedUrl = resultUrl(sent) ?? observedUrl;
734
+ } catch (error) {
735
+ if (browserMutationOutcomeUnknown(error, 'click')) {
736
+ // Never replay an outcome-unknown submit: semantic observation below decides
737
+ // whether the original click committed, preventing duplicate user messages.
738
+ submitOutcomeUnknown = true;
739
+ } else if (chatgptSendControlUnavailable(error)) {
740
+ const pressed = await controllerBrowserAction(controllerHome, workId, 'press', {
741
+ session_id: browserSessionId,
742
+ selector: CHATGPT_PROMPT_SELECTOR,
743
+ key: 'Enter',
744
+ timeout_ms: timeoutMs ?? 60_000,
745
+ post_action_wait_ms: 250,
746
+ }, timeoutMs);
747
+ observedUrl = resultUrl(pressed) ?? observedUrl;
748
+ } else {
749
+ throw error;
750
+ }
751
+ }
752
+
753
+ const deadline = Date.now() + Math.min(Math.max(timeoutMs ?? 10_000, 3_000), 10_000);
754
+ do {
755
+ const latest = await latestChatgptUserMessage(controllerHome, workId, browserSessionId, timeoutMs).catch(() => undefined);
756
+ if (latest) {
757
+ observedUrl = latest.url ?? observedUrl;
758
+ const isNewOutbound = Boolean(
759
+ (latest.selector && latest.selector !== before.selector)
760
+ || (!before.preview && latest.preview)
761
+ || latest.preview !== before.preview,
762
+ );
763
+ if (isNewOutbound) {
764
+ observedNewOutbound = true;
765
+ const fullText = await fullChatgptMessageText(controllerHome, workId, browserSessionId, latest, timeoutMs);
766
+ if (chatgptOutboundMessageMatchesPrompt(fullText.text, renderedPrompt, { truncated: fullText.truncated }) && /\/c\/[^/?#]+/.test(observedUrl)) {
767
+ return observedUrl;
768
+ }
769
+ }
770
+ }
771
+ await new Promise((resolveWait) => setTimeout(resolveWait, 150));
772
+ } while (Date.now() < deadline);
773
+ const hasConversationIdentity = /\/c\/[^/?#]+/.test(observedUrl);
774
+ const failureCode = submitOutcomeUnknown || (observedNewOutbound && hasConversationIdentity)
775
+ ? CHATGPT_AUTOMATION_SUBMISSION_OUTCOME_UNKNOWN
776
+ : 'CHATGPT_AUTOMATION_SUBMISSION_NOT_CONFIRMED';
777
+ throw new ChatgptProviderDeliveryError(
778
+ failureCode,
779
+ `${failureCode}:${observedUrl}`,
780
+ { conversationUrl: observedUrl },
781
+ );
782
+ }
783
+