@moretea-labs/forge 1.7.2 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (900) hide show
  1. package/AGENTS.md +121 -62
  2. package/CHANGELOG.md +40 -0
  3. package/CLAUDE.md +57 -62
  4. package/CONTRIBUTING.md +3 -2
  5. package/README.md +2 -2
  6. package/SKILL.md +0 -1
  7. package/THIRD_PARTY_NOTICES.md +5 -2
  8. package/adapters/browser/index.ts +2 -0
  9. package/adapters/chatgpt/browser-delivery-host.ts +77 -0
  10. package/adapters/chatgpt/browser-delivery-runtime.ts +1256 -0
  11. package/adapters/chatgpt/controller-binding-store.ts +134 -0
  12. package/adapters/chatgpt/controller-round-host.ts +68 -0
  13. package/adapters/chatgpt/controller-round-settlement-store.ts +99 -0
  14. package/adapters/chatgpt/provider-delivery.ts +340 -0
  15. package/adapters/chatgpt/work-conversation-binding-store.ts +218 -0
  16. package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
  17. package/adapters/computer/chatgpt-extension-target.ts +463 -0
  18. package/adapters/computer/chatgpt-target-router.ts +103 -0
  19. package/adapters/computer/chrome-extension/background.js +385 -0
  20. package/adapters/computer/chrome-extension/content.js +378 -0
  21. package/adapters/computer/chrome-extension/core.js +24 -0
  22. package/adapters/computer/chrome-extension/manifest.json +30 -0
  23. package/adapters/computer/desktop-operator-contract.ts +7 -0
  24. package/adapters/computer/desktop-operator-discovery.ts +181 -0
  25. package/adapters/computer/desktop-operator-negotiation.ts +174 -0
  26. package/adapters/computer/desktop-operator-provider.ts +312 -0
  27. package/adapters/computer/index.ts +1 -0
  28. package/adapters/computer/interaction-target-authority.ts +1068 -0
  29. package/adapters/controller-process/binding-store.ts +34 -0
  30. package/adapters/controller-process/controller-host.ts +26 -0
  31. package/adapters/mcp/access-mode.ts +132 -0
  32. package/adapters/mcp/audit.ts +32 -0
  33. package/adapters/mcp/auth.ts +406 -0
  34. package/adapters/mcp/instructions.ts +37 -0
  35. package/adapters/mcp/legacy-context.ts +18 -0
  36. package/adapters/mcp/multi-repository.ts +421 -0
  37. package/adapters/mcp/oauth.ts +464 -0
  38. package/adapters/mcp/paths.ts +143 -0
  39. package/adapters/mcp/policy.ts +232 -0
  40. package/adapters/mcp/proxy-env.ts +62 -0
  41. package/adapters/mcp/readiness-projection.ts +24 -0
  42. package/adapters/mcp/redaction.ts +21 -0
  43. package/adapters/mcp/repo.ts +24 -0
  44. package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
  45. package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +87 -0
  46. package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
  47. package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
  48. package/adapters/mcp/runtime-gateway/context-adapter.ts +1167 -0
  49. package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
  50. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +206 -0
  51. package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
  52. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +130 -0
  53. package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
  54. package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
  55. package/adapters/mcp/runtime-gateway/execution-tools.ts +173 -0
  56. package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
  57. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +412 -0
  58. package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
  59. package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
  60. package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
  61. package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
  62. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +380 -0
  63. package/adapters/mcp/runtime-gateway/process-tools.ts +457 -0
  64. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +88 -0
  65. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +285 -0
  66. package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
  67. package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
  68. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +79 -0
  69. package/adapters/mcp/runtime-gateway/result-adapter.ts +114 -0
  70. package/adapters/mcp/runtime-gateway/router.ts +242 -0
  71. package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
  72. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1200 -0
  73. package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
  74. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +574 -0
  75. package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
  76. package/adapters/mcp/runtime-gateway/runtime-tools.ts +78 -0
  77. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +187 -0
  78. package/adapters/mcp/runtime-gateway/shared-adapter.ts +45 -0
  79. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +962 -0
  80. package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
  81. package/adapters/mcp/runtime-gateway/work-adapter.ts +325 -0
  82. package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
  83. package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
  84. package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
  85. package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
  86. package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
  87. package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
  88. package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
  89. package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
  90. package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
  91. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
  92. package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
  93. package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
  94. package/adapters/mcp/server.ts +1092 -0
  95. package/adapters/mcp/setup.ts +789 -0
  96. package/adapters/mcp/tool-capability-cache.ts +87 -0
  97. package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
  98. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5139 -0
  99. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +145 -0
  100. package/adapters/mcp/tool-mapping/repository-tools.ts +1682 -0
  101. package/adapters/mcp/tool-mapping/tools.ts +9 -0
  102. package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
  103. package/adapters/mcp/toolset-names.ts +79 -0
  104. package/adapters/mcp/toolset-selection.ts +41 -0
  105. package/adapters/mcp/toolset.ts +337 -0
  106. package/adapters/mcp/transports/http-observation.ts +365 -0
  107. package/adapters/mcp/transports/http.ts +933 -0
  108. package/adapters/mcp/transports/oauth-http.ts +322 -0
  109. package/adapters/mcp/transports/session-registry.ts +519 -0
  110. package/adapters/mcp/transports/stdio.ts +8 -0
  111. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +223 -0
  112. package/adapters/mcp/types.ts +41 -0
  113. package/assets/AGENTS.md +2 -16
  114. package/assets/CLAUDE.md +2 -16
  115. package/assets/hooks/AGENTS.md +3 -14
  116. package/assets/hooks/CLAUDE.md +3 -14
  117. package/assets/hooks/hook-input.sh +9 -2
  118. package/assets/hooks/lib/workflow-state.sh +28 -10
  119. package/assets/hooks/prompt-guard.sh +63 -37
  120. package/assets/hooks/security-sentinel.sh +2 -1
  121. package/assets/hooks/session-start-context.sh +7 -5
  122. package/assets/hooks/stop-orchestrator.sh +13 -3
  123. package/assets/initializer-question-pack.v4.json +11 -7
  124. package/assets/partials/05-workflow.partial.md +18 -73
  125. package/assets/partials/07-footer.partial.md +1 -1
  126. package/assets/partials-agents/02-operating-mode.partial.md +1 -2
  127. package/assets/partials-agents/04-task-protocol.partial.md +14 -59
  128. package/assets/partials-agents/06-quality-safety.partial.md +11 -16
  129. package/assets/partials-agents/08-deep-docs.partial.md +1 -2
  130. package/assets/plan-map.json +11 -11
  131. package/assets/plugin-registry.v1.json +11 -3
  132. package/assets/plugin-trust-policy.v1.json +6 -0
  133. package/assets/recovery/ForgeRecovery.ps1 +100 -0
  134. package/assets/recovery/forge-wsl-rescue +271 -0
  135. package/assets/reference-configs/agentic-development-flow.md +25 -0
  136. package/assets/reference-configs/development-protocol.md +11 -15
  137. package/assets/reference-configs/document-generation.md +1 -1
  138. package/assets/reference-configs/global-working-rules.md +28 -1
  139. package/assets/reference-configs/harness-overview.md +22 -112
  140. package/assets/reference-configs/hook-operations.md +1 -1
  141. package/assets/reference-configs/sprint-contracts.md +2 -2
  142. package/assets/skill-version.json +2 -2
  143. package/assets/templates/helpers/architecture-event.ts +3 -34
  144. package/assets/templates/helpers/architecture-queue.sh +61 -10
  145. package/assets/templates/helpers/archive-workflow.sh +90 -180
  146. package/assets/templates/helpers/capability-config.ts +8 -11
  147. package/assets/templates/helpers/capability-resolver.ts +37 -19
  148. package/assets/templates/helpers/capture-plan.sh +2 -2
  149. package/assets/templates/helpers/check-architecture-sync.sh +1 -9
  150. package/assets/templates/helpers/check-task-sync.sh +82 -11
  151. package/assets/templates/helpers/check-task-workflow.sh +1 -3
  152. package/assets/templates/helpers/context-contract-sync.sh +1 -12
  153. package/assets/templates/helpers/contract-worktree.sh +26 -24
  154. package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
  155. package/assets/templates/helpers/inspect-project-state.ts +1 -1
  156. package/assets/templates/helpers/migrate-project-template.sh +1 -1
  157. package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
  158. package/assets/templates/helpers/new-plan.sh +2 -2
  159. package/assets/templates/helpers/plan-to-todo.sh +3 -31
  160. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  161. package/assets/templates/helpers/switch-plan.sh +1 -1
  162. package/assets/templates/helpers/workflow-contract.ts +1 -0
  163. package/assets/workflow-contract.v1.json +63 -171
  164. package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
  165. package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
  166. package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
  167. package/docs/architecture/CURRENT.md +233 -28
  168. package/docs/architecture/EVOLUTION.md +13 -0
  169. package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
  170. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
  171. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
  172. package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
  173. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
  174. package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
  175. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +37 -0
  176. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +311 -0
  177. package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
  178. package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
  179. package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
  180. package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
  181. package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
  182. package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
  183. package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
  184. package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
  185. package/docs/architecture/domains/verification.md +8 -0
  186. package/docs/architecture/domains/workflow-engine.md +7 -1
  187. package/docs/architecture/index.md +3 -3
  188. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
  189. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
  190. package/docs/forge-chatgpt-mcp-setup.md +5 -3
  191. package/docs/forge-plugin-management.md +20 -4
  192. package/docs/forge-runtime-self-healing-loop.md +2 -0
  193. package/docs/operations/benchmark-cross-version-freeze.md +38 -0
  194. package/docs/operations/controller-browser-plugin.md +3 -1
  195. package/docs/operations/controller-performance-and-502.md +17 -0
  196. package/docs/operations/controller-reliability-runbook.md +2 -2
  197. package/docs/operations/features.md +1 -1
  198. package/docs/operations/features.zh-CN.md +1 -1
  199. package/docs/operations/homebrew.md +2 -2
  200. package/docs/operations/mcp-tool-exposure.md +2 -0
  201. package/docs/operations/platform-support.md +16 -8
  202. package/docs/operations/platform-support.zh-CN.md +2 -2
  203. package/docs/operations/releasing.md +2 -0
  204. package/docs/operations/standalone-disaster-recovery.md +38 -5
  205. package/docs/operations/uu-remote-rescue.md +41 -0
  206. package/docs/operations/windows-wsl-host-rescue.md +133 -0
  207. package/docs/public-usage-guide.md +1 -1
  208. package/docs/public-usage-guide.zh-CN.md +1 -1
  209. package/docs/tutorials/01-install-and-start.md +1 -1
  210. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  211. package/docs/tutorials/02-connect-chatgpt.md +4 -4
  212. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
  213. package/docs/wiki/Implementation.md +1 -1
  214. package/package.json +46 -26
  215. package/packages/kernel/cognition/api/index.ts +6 -0
  216. package/packages/kernel/cognition/application/activation-service.ts +451 -0
  217. package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
  218. package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
  219. package/packages/kernel/cognition/application/memory-service.ts +64 -0
  220. package/packages/kernel/cognition/domain/learning.ts +106 -0
  221. package/packages/kernel/cognition/domain/memory.ts +254 -0
  222. package/packages/kernel/controller/api/index.ts +3 -0
  223. package/packages/kernel/controller/application/continuation-service.ts +281 -0
  224. package/packages/kernel/controller/application/controller-round-service.ts +41 -0
  225. package/packages/kernel/controller/application/controller-service.ts +30 -0
  226. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +529 -0
  227. package/packages/kernel/controller/domain/controller-round.ts +110 -0
  228. package/packages/kernel/controller/domain/execution-quality.ts +135 -0
  229. package/packages/kernel/controller/domain/types.ts +56 -0
  230. package/packages/kernel/controller/index.ts +1 -0
  231. package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
  232. package/packages/kernel/controller/infrastructure/controller-round-store.ts +1970 -0
  233. package/packages/kernel/controller/infrastructure/controller-session-store.ts +804 -0
  234. package/packages/kernel/controller/ports/controller-host.ts +18 -0
  235. package/packages/kernel/goal/api/index.ts +1 -0
  236. package/packages/kernel/goal/domain/types.ts +40 -0
  237. package/packages/kernel/goal/index.ts +1 -0
  238. package/packages/kernel/identity/api/index.ts +15 -0
  239. package/packages/kernel/identity/application/identity-service.ts +67 -0
  240. package/packages/kernel/identity/domain/activity-projection.ts +43 -0
  241. package/packages/kernel/identity/domain/capability-broker.ts +60 -0
  242. package/packages/kernel/identity/domain/grant.ts +72 -0
  243. package/packages/kernel/identity/domain/owned-resource.ts +116 -0
  244. package/packages/kernel/identity/domain/scope.ts +63 -0
  245. package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
  246. package/packages/kernel/identity/domain/types.ts +44 -0
  247. package/packages/kernel/identity/domain/user-request.ts +93 -0
  248. package/packages/kernel/identity/domain/workspace.ts +56 -0
  249. package/packages/kernel/identity/index.ts +1 -0
  250. package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
  251. package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
  252. package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
  253. package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
  254. package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
  255. package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
  256. package/packages/kernel/memory/api/index.ts +5 -0
  257. package/packages/kernel/memory/application/experience-service.ts +118 -0
  258. package/packages/kernel/memory/application/outcome-service.ts +77 -0
  259. package/packages/kernel/memory/domain/experience.ts +103 -0
  260. package/packages/kernel/memory/domain/operational-prior.ts +92 -0
  261. package/packages/kernel/memory/index.ts +1 -0
  262. package/packages/kernel/scheduler/api/index.ts +4 -0
  263. package/packages/kernel/scheduler/application/eligibility.ts +252 -0
  264. package/packages/kernel/scheduler/application/schedule-service.ts +27 -0
  265. package/packages/kernel/scheduler/application/settlement.ts +182 -0
  266. package/packages/kernel/scheduler/domain/schedule.ts +236 -0
  267. package/packages/kernel/scheduler/index.ts +1 -0
  268. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +805 -0
  269. package/packages/kernel/work/api/index.ts +16 -0
  270. package/packages/kernel/work/application/work-service.ts +123 -0
  271. package/packages/kernel/work/domain/check-receipt.ts +43 -0
  272. package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
  273. package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
  274. package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
  275. package/packages/kernel/work/domain/engineering-design.ts +286 -0
  276. package/packages/kernel/work/domain/engineering-profile.ts +238 -0
  277. package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
  278. package/packages/kernel/work/domain/execution-concurrency.ts +247 -0
  279. package/packages/kernel/work/domain/execution-snapshot.ts +2 -0
  280. package/packages/kernel/work/domain/implementation-review.ts +193 -0
  281. package/packages/kernel/work/domain/objective-graph.ts +119 -0
  282. package/packages/kernel/work/domain/project-engineering-contract.ts +224 -0
  283. package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
  284. package/{src/runtime/control-plane/facade/work-state-machine.ts → packages/kernel/work/domain/state-machine.ts} +37 -66
  285. package/packages/kernel/work/domain/types.ts +534 -0
  286. package/packages/kernel/work/index.ts +1 -0
  287. package/packages/kernel/work/infrastructure/work-contract-store.ts +1967 -0
  288. package/packages/kernel/work/ports/work-contract-store.ts +21 -0
  289. package/packages/plugin-runtime/browser/index.ts +3 -0
  290. package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
  291. package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
  292. package/packages/plugin-runtime/browser/session-authority.ts +62 -0
  293. package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
  294. package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
  295. package/packages/plugin-runtime/computer/index.ts +8 -0
  296. package/packages/plugin-runtime/computer/provider-error.ts +26 -0
  297. package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
  298. package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
  299. package/packages/plugin-runtime/computer/provider.ts +19 -0
  300. package/packages/plugin-runtime/computer/target-authority.ts +215 -0
  301. package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
  302. package/packages/plugin-runtime/external/index.ts +1 -0
  303. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +581 -0
  304. package/packages/plugin-runtime/index.ts +3 -0
  305. package/packages/protocols/browser/index.ts +1 -0
  306. package/packages/protocols/browser/session.ts +98 -0
  307. package/packages/protocols/computer/contract.ts +225 -0
  308. package/packages/protocols/computer/index.ts +1 -0
  309. package/packages/protocols/failure/contract.ts +158 -0
  310. package/packages/protocols/failure/index.ts +1 -0
  311. package/packages/protocols/handoff/index.ts +1 -0
  312. package/packages/protocols/handoff/status.ts +22 -0
  313. package/packages/protocols/mcp/execution-context.ts +11 -0
  314. package/packages/protocols/mcp/index.ts +2 -0
  315. package/packages/protocols/mcp/tool-contract.ts +42 -0
  316. package/packages/workflow-runtime/api/index.ts +2 -0
  317. package/packages/workflow-runtime/application/interpreter.ts +342 -0
  318. package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
  319. package/packages/workflow-runtime/index.ts +1 -0
  320. package/scripts/activate-source-baseline.ts +7 -9
  321. package/scripts/assemble-template.ts +1 -1
  322. package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
  323. package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
  324. package/scripts/canary-controller-semantic-wait.ts +183 -0
  325. package/scripts/canary-global-hook.sh +2 -2
  326. package/scripts/check-mcp-compatibility.ts +85 -7
  327. package/scripts/check-platform-support.mjs +28 -2
  328. package/scripts/check-public-docs.sh +1 -1
  329. package/scripts/check-repository-hygiene.mjs +92 -0
  330. package/scripts/check-runtime-architecture.mjs +1643 -127
  331. package/scripts/check-source-duplication.mjs +9 -1
  332. package/scripts/controller-ngrok-rotation.sh +5 -4
  333. package/scripts/create-project-dirs.sh +10 -74
  334. package/scripts/forge-local-recovery-helper.mjs +282 -0
  335. package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
  336. package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
  337. package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
  338. package/scripts/hook-shim.sh +2 -2
  339. package/scripts/init-project.sh +12 -70
  340. package/scripts/install-desktop-operator-registration.ts +4 -2
  341. package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
  342. package/scripts/install-standalone-recovery.ts +122 -10
  343. package/scripts/install-uu-remote-rescue-registration.ts +74 -0
  344. package/scripts/install-windows-host-recovery-registration.ts +22 -0
  345. package/scripts/lib/controller-home-env.sh +6 -3
  346. package/scripts/lib/project-init-lib.sh +42 -582
  347. package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
  348. package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
  349. package/scripts/open-source-audit-allowlist.txt +11 -1
  350. package/scripts/portability-bindings.mjs +70 -0
  351. package/scripts/run-bounded-command.mjs +60 -0
  352. package/scripts/run-bun-test-file.ts +22 -6
  353. package/scripts/run-governed-gate.ts +4 -2
  354. package/scripts/run-mobile-monitor-bridge.ts +0 -1
  355. package/scripts/smoke-control-plane-failure-domain.ts +94 -0
  356. package/scripts/smoke-mcp-http-runtime.ts +6 -1
  357. package/scripts/smoke-runtime-recovery.ts +13 -11
  358. package/scripts/smoke-schedule-engine.ts +24 -16
  359. package/scripts/smoke-unattended-transport-recovery.ts +223 -0
  360. package/scripts/smoke-user-request-continuation-restart.ts +232 -0
  361. package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
  362. package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
  363. package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
  364. package/scripts/smoke-workflow-supervisor.ts +103 -0
  365. package/scripts/test-governance.ts +51 -2
  366. package/scripts/verify-forge-runtime.sh +2 -1
  367. package/src/cli/agent-jobs/executor-health.ts +11 -34
  368. package/src/cli/agent-jobs/integration.ts +18 -49
  369. package/src/cli/agent-jobs/job-manager.ts +17 -4
  370. package/src/cli/chatgpt-browser/bind-server.ts +2 -1
  371. package/src/cli/chatgpt-browser/binding.ts +20 -10
  372. package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
  373. package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
  374. package/src/cli/chatgpt-browser/engine.ts +10 -11
  375. package/src/cli/chatgpt-browser/native-provider.ts +24 -83
  376. package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
  377. package/src/cli/chatgpt-browser/types.ts +1 -0
  378. package/src/cli/commands/bootstrap-control.ts +334 -0
  379. package/src/cli/commands/brain-assistant.ts +52 -0
  380. package/src/cli/commands/brain.ts +191 -40
  381. package/src/cli/commands/chatgpt.ts +32 -67
  382. package/src/cli/commands/computer.ts +437 -0
  383. package/src/cli/commands/doctor.ts +18 -1
  384. package/src/cli/commands/init-hook.ts +71 -61
  385. package/src/cli/commands/init.ts +3 -1
  386. package/src/cli/commands/mcp.ts +16 -10
  387. package/src/cli/commands/plugin.ts +38 -12
  388. package/src/cli/commands/recovery.ts +507 -80
  389. package/src/cli/commands/runtime.ts +12 -7
  390. package/src/cli/commands/setup-profile.ts +97 -39
  391. package/src/cli/commands/status.ts +2 -1
  392. package/src/cli/controller/check-runner.ts +500 -100
  393. package/src/cli/controller/completion-backlog.ts +3 -3
  394. package/src/cli/controller/completion-orchestrator.ts +4 -43
  395. package/src/cli/controller/completion-target.ts +3 -72
  396. package/src/cli/controller/context/source-materializer.ts +81 -100
  397. package/src/cli/controller/context/types.ts +7 -1
  398. package/src/cli/controller/context-pack.ts +28 -6
  399. package/src/cli/controller/execution-completion.ts +4 -7
  400. package/src/cli/controller/execution-policy.ts +36 -153
  401. package/src/cli/controller/governance.ts +2 -3
  402. package/src/cli/controller/issue-store.ts +4 -99
  403. package/src/cli/controller/launch-agents.ts +59 -18
  404. package/src/cli/controller/operational-plan.ts +24 -51
  405. package/src/cli/controller/runtime-config.ts +5 -1
  406. package/src/cli/controller/stuck-state-migration.ts +2 -3
  407. package/src/cli/controller/systemd-user.ts +148 -0
  408. package/src/cli/controller/types.ts +10 -70
  409. package/src/cli/controller/worklog.ts +67 -13
  410. package/src/cli/editing/edit-operation-contract.ts +92 -0
  411. package/src/cli/editing/edit-session.ts +290 -20
  412. package/src/cli/github/plugin.ts +17 -12
  413. package/src/cli/hook/route-registry.ts +3 -5
  414. package/src/cli/hook/runtime.ts +49 -3
  415. package/src/cli/index.ts +3 -0
  416. package/src/cli/local-bridge/connector-freshness.ts +2 -2
  417. package/src/cli/local-bridge/console-automations.ts +20 -15
  418. package/src/cli/local-bridge/console-view-models.ts +3 -12
  419. package/src/cli/local-bridge/facade-api.ts +105 -205
  420. package/src/cli/local-bridge/job-store.ts +1 -1
  421. package/src/cli/local-bridge/server.ts +48 -174
  422. package/src/cli/local-bridge/types.ts +0 -1
  423. package/src/cli/mcp/access-mode.ts +2 -132
  424. package/src/cli/mcp/access-tools.ts +2 -387
  425. package/src/cli/mcp/audit.ts +2 -32
  426. package/src/cli/mcp/auth.ts +2 -404
  427. package/src/cli/mcp/instructions.ts +2 -34
  428. package/src/cli/mcp/legacy-context.ts +2 -16
  429. package/src/cli/mcp/legacy-tool-service.ts +2 -5308
  430. package/src/cli/mcp/multi-repository.ts +2 -436
  431. package/src/cli/mcp/oauth-client-retirement.ts +74 -0
  432. package/src/cli/mcp/oauth.ts +2 -375
  433. package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
  434. package/src/cli/mcp/paths.ts +2 -140
  435. package/src/cli/mcp/policy.ts +2 -232
  436. package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
  437. package/src/cli/mcp/proxy-env.ts +2 -62
  438. package/src/cli/mcp/readiness-projection.ts +2 -24
  439. package/src/cli/mcp/redaction.ts +2 -21
  440. package/src/cli/mcp/repo.ts +2 -24
  441. package/src/cli/mcp/repository-tools.ts +2 -1613
  442. package/src/cli/mcp/server.ts +2 -951
  443. package/src/cli/mcp/setup.ts +2 -737
  444. package/src/cli/mcp/tool-capability-cache.ts +2 -87
  445. package/src/cli/mcp/tool-contract.ts +2 -14
  446. package/src/cli/mcp/tools.ts +2 -9
  447. package/src/cli/mcp/toolset-names.ts +2 -70
  448. package/src/cli/mcp/toolset-selection.ts +2 -41
  449. package/src/cli/mcp/toolset.ts +2 -336
  450. package/src/cli/mcp/transports/AGENTS.md +1 -10
  451. package/src/cli/mcp/transports/CLAUDE.md +1 -10
  452. package/src/cli/mcp/transports/http.ts +2 -1414
  453. package/src/cli/mcp/transports/session-registry.ts +2 -490
  454. package/src/cli/mcp/transports/stdio.ts +2 -8
  455. package/src/cli/mcp/types.ts +2 -41
  456. package/src/cli/mcp/umbrella-tools.ts +2 -117
  457. package/src/cli/repositories/command-executor.ts +91 -39
  458. package/src/cli/repositories/command-scope.ts +139 -4
  459. package/src/cli/repositories/controller-home.ts +167 -1
  460. package/src/cli/repositories/ephemeral-workspace.ts +20 -30
  461. package/src/cli/repositories/git-command-executor.ts +2 -2
  462. package/src/cli/repositories/identity.ts +13 -0
  463. package/src/cli/repositories/registry.ts +145 -60
  464. package/src/cli/repositories/repository-snapshot.ts +53 -7
  465. package/src/cli/repositories/runtime-storage.ts +27 -2
  466. package/src/cli/repositories/safe-patch.ts +34 -38
  467. package/src/cli/repositories/selected-path-actions.ts +22 -2
  468. package/src/cli/repositories/structured-git.ts +167 -27
  469. package/src/cli/repository/session-cache.ts +92 -6
  470. package/src/cli/runtime/helper-runner.ts +7 -0
  471. package/src/cli/runtime-invocation.ts +8 -1
  472. package/src/cli/tools/codegraph.ts +44 -25
  473. package/src/core/adoption/manifest-templates.ts +1 -1
  474. package/src/core/adoption/plan.ts +3 -26
  475. package/src/core/adoption/workflow-contract-plan.ts +17 -9
  476. package/src/effects/process-runner.ts +8 -14
  477. package/src/runtime/context/assistant-context.ts +167 -0
  478. package/src/runtime/context/assistant-work-context.ts +209 -0
  479. package/src/runtime/context/automatic-learning.ts +565 -0
  480. package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
  481. package/src/runtime/context/codegraph-read-provider.ts +55 -22
  482. package/src/runtime/context/codegraph-sidecar.cjs +8 -1
  483. package/src/runtime/context/cognitive-attention.ts +173 -0
  484. package/src/runtime/context/cognitive-skill-canary.ts +158 -0
  485. package/src/runtime/context/context-closure.ts +305 -0
  486. package/src/runtime/context/context-pack-process.ts +103 -0
  487. package/src/runtime/context/context-plane.ts +203 -0
  488. package/src/runtime/context/direct-controller-learning.ts +298 -0
  489. package/src/runtime/context/project-engineering-contract.ts +60 -0
  490. package/src/runtime/context/semantic-navigation-contract.ts +9 -0
  491. package/src/runtime/context/semantic-navigation.ts +6 -34
  492. package/src/runtime/context/typescript-navigation-process.ts +145 -0
  493. package/src/runtime/context/typescript-navigation.ts +89 -3
  494. package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
  495. package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
  496. package/src/runtime/control-plane/bootstrap/index.ts +5 -0
  497. package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
  498. package/src/runtime/control-plane/bootstrap/store.ts +21 -0
  499. package/src/runtime/control-plane/bootstrap/types.ts +87 -0
  500. package/src/runtime/control-plane/cleanup-artifact-retention.ts +531 -0
  501. package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
  502. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +282 -0
  503. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +380 -0
  504. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +113 -16
  505. package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
  506. package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
  507. package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
  508. package/src/runtime/control-plane/execution/retained-work-resume.ts +439 -102
  509. package/src/runtime/control-plane/execution/session-store.ts +31 -1
  510. package/src/runtime/control-plane/execution/validation.ts +34 -11
  511. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +87 -0
  512. package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
  513. package/src/runtime/control-plane/execution/work-completion-authority.ts +31 -44
  514. package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
  515. package/src/runtime/control-plane/execution/work-evidence-policy.ts +46 -53
  516. package/src/runtime/control-plane/execution/work-execution-support.ts +85 -164
  517. package/src/runtime/control-plane/execution/work-handle-authority.ts +349 -73
  518. package/src/runtime/control-plane/execution/work-handle-store.ts +55 -2
  519. package/src/runtime/control-plane/execution/work-head-settlement.ts +108 -0
  520. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +173 -28
  521. package/src/runtime/control-plane/execution/work-operation-service.ts +92 -40
  522. package/src/runtime/control-plane/execution/work-preparation-service.ts +145 -269
  523. package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
  524. package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
  525. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +543 -225
  526. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +59 -45
  527. package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
  528. package/src/runtime/control-plane/execution/work-verification-service.ts +662 -52
  529. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +205 -16
  530. package/src/runtime/control-plane/facade/capability-registry.ts +380 -44
  531. package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
  532. package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
  533. package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
  534. package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
  535. package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -22
  536. package/src/runtime/control-plane/facade/goal-workloop.ts +565 -1202
  537. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +145 -0
  538. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +266 -52
  539. package/src/runtime/control-plane/facade/index.ts +4 -3
  540. package/src/runtime/control-plane/facade/operation-digest.ts +34 -73
  541. package/src/runtime/control-plane/facade/plan-contract-store.ts +671 -245
  542. package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
  543. package/src/runtime/control-plane/facade/repository-work-admission.ts +31 -23
  544. package/src/runtime/control-plane/facade/requirement-authority.ts +121 -0
  545. package/src/runtime/control-plane/facade/requirement-board.ts +9 -4
  546. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +85 -0
  547. package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
  548. package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
  549. package/src/runtime/control-plane/facade/suggested-actions.ts +15 -15
  550. package/src/runtime/control-plane/facade/types.ts +218 -620
  551. package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
  552. package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
  553. package/src/runtime/control-plane/generated-cache-retention.ts +336 -19
  554. package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
  555. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +13 -2
  556. package/src/runtime/control-plane/global-scheduler/maintenance.ts +274 -84
  557. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
  558. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
  559. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -2
  560. package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
  561. package/src/runtime/control-plane/global-scheduler/scheduler.ts +227 -109
  562. package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
  563. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
  564. package/src/runtime/control-plane/governance/access-policy.ts +19 -2
  565. package/src/runtime/control-plane/governance/authorization.ts +20 -3
  566. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +160 -0
  567. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
  568. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +139 -670
  569. package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
  570. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +25 -6
  571. package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -194
  572. package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
  573. package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
  574. package/src/runtime/control-plane/persistence/experience-store.ts +302 -0
  575. package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
  576. package/src/runtime/control-plane/persistence/requirement-store.ts +230 -80
  577. package/src/runtime/control-plane/persistence/sqlite-store.ts +654 -78
  578. package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
  579. package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
  580. package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
  581. package/src/runtime/control-plane/release-retention.ts +154 -26
  582. package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
  583. package/src/runtime/control-plane/runtime-cleanup.ts +844 -38
  584. package/src/runtime/control-plane/runtime-generation.ts +6 -0
  585. package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
  586. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +479 -0
  587. package/src/runtime/control-plane/workspace/workspace-store.ts +249 -0
  588. package/src/runtime/diagnostics/incident-repair.ts +573 -0
  589. package/src/runtime/diagnostics/mcp-timing.ts +239 -6
  590. package/src/runtime/diagnostics/performance.ts +6 -14
  591. package/src/runtime/diagnostics/read-only-tool.ts +1 -1
  592. package/src/runtime/evidence/artifact-store.ts +216 -35
  593. package/src/runtime/evidence/evidence-store.ts +112 -8
  594. package/src/runtime/evidence/operational-shadow.ts +38 -0
  595. package/src/runtime/evidence/output-handle-index.ts +225 -0
  596. package/src/runtime/evidence/process-check-receipt.ts +5 -43
  597. package/src/runtime/evidence/result-store.ts +158 -44
  598. package/src/runtime/evidence/sensitive-output.ts +4 -4
  599. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
  600. package/src/runtime/execution/jobs/store.ts +124 -6
  601. package/src/runtime/execution/managed-workspace.ts +112 -5
  602. package/src/runtime/execution/process-runtime/canary.ts +19 -5
  603. package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
  604. package/src/runtime/execution/process-runtime/check-facade.ts +24 -10
  605. package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
  606. package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
  607. package/src/runtime/execution/process-runtime/check-result.ts +177 -2
  608. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +58 -9
  609. package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
  610. package/src/runtime/execution/process-runtime/command-facade.ts +117 -114
  611. package/src/runtime/execution/process-runtime/gc.ts +15 -2
  612. package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
  613. package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
  614. package/src/runtime/execution/process-runtime/index.ts +4 -0
  615. package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
  616. package/src/runtime/execution/process-runtime/lightweight-managed.ts +283 -73
  617. package/src/runtime/execution/process-runtime/persisted-check.ts +130 -23
  618. package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
  619. package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
  620. package/src/runtime/execution/process-runtime/resource-claims.ts +125 -64
  621. package/src/runtime/execution/process-runtime/runtime.ts +167 -17
  622. package/src/runtime/execution/process-runtime/store.ts +30 -2
  623. package/src/runtime/execution/process-runtime/types.ts +27 -0
  624. package/src/runtime/execution/workers/executor.ts +18 -19
  625. package/src/runtime/execution/workers/worker-entry.ts +1 -1
  626. package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
  627. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
  628. package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
  629. package/src/runtime/gateway/mcp/process-tools.ts +2 -289
  630. package/src/runtime/gateway/mcp/router.ts +2 -1125
  631. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
  632. package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6655
  633. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
  634. package/src/runtime/maintenance/cleanup.ts +38 -24
  635. package/src/runtime/platform/browser-product-discovery.ts +174 -0
  636. package/src/runtime/platform/computer-platform.ts +14 -0
  637. package/src/runtime/platform/executable-discovery.ts +54 -0
  638. package/src/runtime/platform/fetch-http-server.ts +90 -0
  639. package/src/runtime/platform/service-inventory.ts +130 -0
  640. package/src/runtime/platform/service-manager.ts +146 -0
  641. package/src/runtime/platform/windows-host-discovery.ts +118 -0
  642. package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
  643. package/src/runtime/plugins/action-application.ts +192 -0
  644. package/src/runtime/plugins/app-store-connect-adapter.ts +47 -35
  645. package/src/runtime/plugins/browser-adapter.ts +898 -135
  646. package/src/runtime/plugins/browser-automation-service.ts +33 -34
  647. package/src/runtime/plugins/browser-macos-bridge.ts +386 -46
  648. package/src/runtime/plugins/browser-manifest-surface.ts +75 -17
  649. package/src/runtime/plugins/browser-native-session-service.ts +64 -14
  650. package/src/runtime/plugins/browser-provider-registry.ts +2 -109
  651. package/src/runtime/plugins/browser-registration.ts +32 -0
  652. package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
  653. package/src/runtime/plugins/browser-runtime.ts +55 -10
  654. package/src/runtime/plugins/browser-session-authority.ts +101 -291
  655. package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
  656. package/src/runtime/plugins/browser-session-store.ts +243 -80
  657. package/src/runtime/plugins/browser-session-types.ts +2 -98
  658. package/src/runtime/plugins/capability-authorization-grants.ts +272 -59
  659. package/src/runtime/plugins/catalog-trust.ts +82 -0
  660. package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
  661. package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
  662. package/src/runtime/plugins/computer-registration.ts +1050 -0
  663. package/src/runtime/plugins/config-store.ts +119 -0
  664. package/src/runtime/plugins/desktop-operator-external-policy.ts +964 -0
  665. package/src/runtime/plugins/desktop-operator-registration.ts +123 -12
  666. package/src/runtime/plugins/errors.ts +8 -1
  667. package/src/runtime/plugins/external-adapter.ts +82 -323
  668. package/src/runtime/plugins/external-provider-policy.ts +48 -0
  669. package/src/runtime/plugins/external-registration.ts +30 -2
  670. package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
  671. package/src/runtime/plugins/external-unix-socket.ts +64 -193
  672. package/src/runtime/plugins/first-party-external-registration.ts +163 -0
  673. package/src/runtime/plugins/first-party-registry.ts +3 -1
  674. package/src/runtime/plugins/github-adapter.ts +1 -2
  675. package/src/runtime/plugins/gmail-adapter.ts +29 -8
  676. package/src/runtime/plugins/google-calendar-adapter.ts +8 -8
  677. package/src/runtime/plugins/google-shared.ts +45 -53
  678. package/src/runtime/plugins/google-tasks-adapter.ts +8 -8
  679. package/src/runtime/plugins/ios-adapter.ts +2 -3
  680. package/src/runtime/plugins/lightweight-action.ts +208 -2
  681. package/src/runtime/plugins/local-system-adapter.ts +192 -2
  682. package/src/runtime/plugins/macos-capability-broker.ts +15 -105
  683. package/src/runtime/plugins/managed-process-adapter.ts +12 -5
  684. package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
  685. package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
  686. package/src/runtime/plugins/resend-adapter.ts +15 -24
  687. package/src/runtime/plugins/store.ts +1003 -387
  688. package/src/runtime/plugins/types.ts +28 -1
  689. package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
  690. package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
  691. package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
  692. package/src/runtime/plugins/xiaohongshu-publish.ts +102 -430
  693. package/src/runtime/projections/controller-context.ts +12 -6
  694. package/src/runtime/projections/git-status-sampler.ts +73 -22
  695. package/src/runtime/projections/invalidation.ts +7 -3
  696. package/src/runtime/projections/materialized-view.ts +93 -43
  697. package/src/runtime/recovery/actions.ts +3 -3
  698. package/src/runtime/recovery/capability-action-application.ts +229 -0
  699. package/src/runtime/recovery/index.ts +3 -0
  700. package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
  701. package/src/runtime/recovery/local-jobs-repair.ts +14 -14
  702. package/src/runtime/recovery/maintenance-executor.ts +701 -168
  703. package/src/runtime/recovery/quarantine-retention.ts +256 -0
  704. package/src/runtime/recovery/store.ts +29 -11
  705. package/src/runtime/release/identity.ts +6 -0
  706. package/src/runtime/release/release-coordinator.ts +296 -0
  707. package/src/runtime/release/release-session.ts +716 -0
  708. package/src/runtime/release/release-state-migration.ts +25 -0
  709. package/src/runtime/resources/leases/store.ts +56 -25
  710. package/src/runtime/root/active-execution-power-assertion.ts +139 -0
  711. package/src/runtime/root/assistant-learning-loop.ts +45 -0
  712. package/src/runtime/root/browser-session-composition.ts +59 -0
  713. package/src/runtime/root/computer-composition.ts +376 -0
  714. package/src/runtime/root/computer-target-composition.ts +16 -0
  715. package/src/runtime/root/computer-target-persistence.ts +39 -0
  716. package/src/runtime/root/controller-progression-composition.ts +192 -0
  717. package/src/runtime/root/controller-round-composition.ts +126 -0
  718. package/src/runtime/root/controller-services.ts +2 -0
  719. package/src/runtime/root/deployment-topology.ts +106 -0
  720. package/src/runtime/root/entry.ts +8 -6
  721. package/src/runtime/root/gateway-adapter.ts +3 -3
  722. package/src/runtime/root/known-good-recovery.ts +184 -0
  723. package/src/runtime/root/local-bridge.ts +5 -3
  724. package/src/runtime/root/mcp-transport.ts +116 -23
  725. package/src/runtime/root/ownership.ts +261 -9
  726. package/src/runtime/root/package-connector-service.ts +212 -74
  727. package/src/runtime/root/package-runtime-release.ts +199 -33
  728. package/src/runtime/root/package-runtime-service.ts +77 -108
  729. package/src/runtime/root/release-execution-canary.ts +103 -0
  730. package/src/runtime/root/release-loader.ts +150 -0
  731. package/src/runtime/root/release-manifest.ts +185 -7
  732. package/src/runtime/root/release-materialize.ts +474 -31
  733. package/src/runtime/root/release-store.ts +425 -37
  734. package/src/runtime/root/runtime-lane.ts +191 -0
  735. package/src/runtime/root/runtime.ts +379 -32
  736. package/src/runtime/root/scheduled-controller-composition.ts +76 -0
  737. package/src/runtime/root/scheduler.ts +12 -4
  738. package/src/runtime/root/service-runner.ts +3 -3
  739. package/src/runtime/root/service.ts +73 -45
  740. package/src/runtime/root/status.ts +49 -0
  741. package/src/runtime/root/types.ts +55 -3
  742. package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
  743. package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
  744. package/src/runtime/root/write-fence.ts +50 -2
  745. package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
  746. package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
  747. package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
  748. package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
  749. package/src/runtime/shared/local-bridge-surface.ts +1 -1
  750. package/src/runtime/shared/process-environment.ts +36 -10
  751. package/src/runtime/shared/response-budget.ts +1 -3
  752. package/src/runtime/shared/storage-capacity.ts +68 -0
  753. package/src/runtime/standalone-recovery/controller-home-migration.ts +1108 -0
  754. package/src/runtime/standalone-recovery/core.ts +4609 -495
  755. package/src/runtime/standalone-recovery/entry.ts +642 -81
  756. package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
  757. package/src/runtime/standalone-recovery/installer.ts +399 -177
  758. package/src/runtime/standalone-recovery/mcp-server.ts +79 -0
  759. package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
  760. package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
  761. package/src/runtime/standalone-recovery/performance.ts +173 -0
  762. package/src/runtime/standalone-recovery/release.ts +58 -5
  763. package/src/runtime/standalone-recovery/service-labels.ts +5 -2
  764. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
  765. package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
  766. package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
  767. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +11 -4
  768. package/src/runtime/workflow/schedules/engine.ts +298 -484
  769. package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
  770. package/src/runtime/workflow/schedules/settlement.ts +2 -191
  771. package/src/runtime/workflow/schedules/store.ts +2 -607
  772. package/src/runtime/workflow/schedules/types.ts +2 -127
  773. package/src/runtime/workflow/schedules/work-continuation.ts +220 -16
  774. package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
  775. package/src/runtime/workflows/runtime.ts +482 -0
  776. package/src/testing/test-governance.ts +95 -30
  777. package/supervisor/chatgpt-conversation.ts +23 -0
  778. package/supervisor/client.ts +201 -0
  779. package/supervisor/control-plane.ts +868 -0
  780. package/supervisor/entry.ts +15 -0
  781. package/supervisor/forge-validators.ts +80 -0
  782. package/supervisor/index.ts +5 -0
  783. package/supervisor/native-browser-adapter.ts +786 -0
  784. package/supervisor/native-messaging/host.ts +39 -0
  785. package/supervisor/native-messaging/manifest.ts +26 -0
  786. package/supervisor/native-messaging/protocol.ts +27 -0
  787. package/supervisor/paths.ts +18 -0
  788. package/supervisor/protocol.ts +202 -0
  789. package/supervisor/server.ts +404 -0
  790. package/supervisor/store.ts +1451 -0
  791. package/supervisor/tsconfig.json +8 -0
  792. package/supervisor/types.ts +255 -0
  793. package/assets/templates/helpers/archive-architecture-request.sh +0 -244
  794. package/docs/architecture/history/README.md +0 -7
  795. package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
  796. package/docs/architecture/history/global-hook-runtime.md +0 -166
  797. package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
  798. package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  799. package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
  800. package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
  801. package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
  802. package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
  803. package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  804. package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  805. package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  806. package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  807. package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
  808. package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  809. package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  810. package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  811. package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  812. package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  813. package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  814. package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  815. package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  816. package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  817. package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
  818. package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
  819. package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
  820. package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  821. package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  822. package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  823. package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  824. package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
  825. package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
  826. package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
  827. package/docs/architecture/requests/archive/2026/root.md +0 -68
  828. package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
  829. package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
  830. package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
  831. package/scripts/archive-architecture-request.sh +0 -4
  832. package/scripts/benchmark-background-check-overlap.ts +0 -228
  833. package/scripts/benchmark-controller-hotpath.ts +0 -212
  834. package/scripts/benchmark-controller-round2.ts +0 -139
  835. package/scripts/benchmark-ios-agent-device.ts +0 -514
  836. package/scripts/benchmark-mcp-host.sh +0 -117
  837. package/scripts/benchmark-mcp-transport.ts +0 -170
  838. package/scripts/benchmark-quality-harness.ts +0 -146
  839. package/scripts/benchmark-route-session-concurrency.ts +0 -915
  840. package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
  841. package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
  842. package/scripts/benchmark-thin-harness.ts +0 -251
  843. package/scripts/benchmark-typescript-navigation.ts +0 -186
  844. package/scripts/benchmark-work-efficiency.ts +0 -334
  845. package/scripts/build-controller-ui.ts +0 -28
  846. package/scripts/check-npm-release.sh +0 -32
  847. package/scripts/check-public-release-surface.sh +0 -19
  848. package/scripts/check-release-published.sh +0 -57
  849. package/scripts/check-release-readiness.sh +0 -37
  850. package/scripts/check-release-version.mjs +0 -74
  851. package/scripts/check-tarball-install-smoke.sh +0 -67
  852. package/scripts/package-source-archive.sh +0 -147
  853. package/scripts/public-release-files.txt +0 -39
  854. package/scripts/public-release.ts +0 -174
  855. package/scripts/publish-release-tarball.sh +0 -27
  856. package/scripts/route-nl-vs-ts-eval.ts +0 -679
  857. package/scripts/run-skill-evals.ts +0 -1022
  858. package/scripts/stage-runtime-release.ts +0 -39
  859. package/src/cli/controller/progress.ts +0 -656
  860. package/src/cli/controller/work-mode.ts +0 -181
  861. package/src/cli/local-bridge/console-assets.ts +0 -17
  862. package/src/cli/local-bridge/dashboard.ts +0 -17
  863. package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
  864. package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
  865. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
  866. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
  867. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
  868. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
  869. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
  870. package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
  871. package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
  872. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
  873. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
  874. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
  875. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
  876. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
  877. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
  878. package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
  879. package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
  880. package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
  881. package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
  882. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
  883. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
  884. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
  885. package/src/cli/local-bridge/frontend/src/styles.css +0 -28
  886. package/src/cli/local-bridge/frontend/src/types.ts +0 -15
  887. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
  888. package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
  889. package/src/cli/local-bridge/ui-dist/app.css +0 -1
  890. package/src/cli/local-bridge/ui-dist/app.js +0 -11228
  891. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -385
  892. package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -1913
  893. package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
  894. package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -449
  895. package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -1118
  896. package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -93
  897. package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
  898. package/src/runtime/control-plane/routing/index.ts +0 -1
  899. package/src/runtime/control-plane/routing/route-policy.ts +0 -382
  900. package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
@@ -0,0 +1,891 @@
1
+ import type {
2
+ ComputerChatgptBootstrapIdentity,
3
+ ComputerChatgptConversationIdentity,
4
+ ComputerChatgptConversationInventory,
5
+ ComputerChatgptConversationObservation,
6
+ ComputerChatgptConversationObservationOptions,
7
+ ComputerChatgptConversationTarget,
8
+ ComputerChatgptConversationTargetPort,
9
+ ComputerChatgptTargetIdentity,
10
+ ComputerChatgptTargetResult,
11
+ ComputerInteractionTargetAuthorityPort,
12
+ ComputerSurfaceProviderBinding,
13
+ ComputerSurfaceStableIdentity,
14
+ ComputerSurfaceTarget,
15
+ } from '../../../packages/plugin-runtime/computer';
16
+ import { parseCanonicalChatgptConversationIdentity } from '../../../packages/plugin-runtime/computer';
17
+ import { AssistantPluginError } from './errors';
18
+ import {
19
+ closeMacOsBrowserOwnedTab,
20
+ createMacOsBrowserOwnedPageForProduct,
21
+ discoverMacOsBrowserAttachment,
22
+ ensureMacOsBrowserApplicationRunning,
23
+ listMacOsBrowserTabs,
24
+ reattachMacOsBrowserOwnedPage,
25
+ type MacOsAppleEventsPage,
26
+ type MacOsBrowserProduct,
27
+ type MacOsBrowserTabInventoryEntry,
28
+ type MacOsBrowserTabRef,
29
+ } from './browser-macos-bridge';
30
+
31
+ const PROVIDER_ID = 'browser.macos-apple-events';
32
+ const PROVIDER_PRODUCTS: readonly MacOsBrowserProduct[] = ['chrome', 'vivaldi'];
33
+ const DEFAULT_TIMEOUT_MS = 10_000;
34
+ const MAX_LOCAL_OBSERVATION_ATTEMPTS = 3;
35
+ const MAX_PROVIDER_FAILURE_SCAN_CHARS = 250_000;
36
+ const MAX_PROVIDER_ACTIVITY_CHARS = 64 * 1024;
37
+ const OWNER_PREFIX = 'forge-computer-chatgpt:';
38
+ const LEGACY_SUPERVISOR_OWNER_PREFIX = 'forge-workflow-supervisor:';
39
+ const LEGACY_BROWSER_OWNER_PREFIX = 'forge-browser-owned:';
40
+ const BROWSER_PREFERENCE_RECORD = 'chatgpt.browser-preference';
41
+ const BROWSER_PREFERENCE_KEY = 'default';
42
+ const BROWSER_CREATE_COOLDOWN_MS = 60_000;
43
+
44
+ const BROWSER_PREFERENCE_IDENTITY = {
45
+ surfaceType: 'browser-page' as const,
46
+ ownership: 'provider_owned' as const,
47
+ resource: { namespace: 'chatgpt.browser-preference', key: BROWSER_PREFERENCE_KEY },
48
+ };
49
+
50
+ type TaggedTab = MacOsBrowserTabInventoryEntry & { browserProduct: MacOsBrowserProduct };
51
+ type TaggedRef = MacOsBrowserTabRef & { browserProduct: MacOsBrowserProduct };
52
+
53
+ interface ComputerChatgptNativePage {
54
+ evaluate<T>(expression: string | ((...args: unknown[]) => unknown), arg?: unknown): Promise<T>;
55
+ foregroundState?(): Promise<{ frontmost: boolean; active: boolean }>;
56
+ bringToFront?(): Promise<void>;
57
+ tabRef(): TaggedRef | undefined;
58
+ }
59
+
60
+ function taggedPage(page: MacOsAppleEventsPage, product: MacOsBrowserProduct): ComputerChatgptNativePage {
61
+ return {
62
+ evaluate: page.evaluate.bind(page),
63
+ foregroundState: page.foregroundState.bind(page),
64
+ bringToFront: page.bringToFront.bind(page),
65
+ tabRef: () => {
66
+ const ref = page.tabRef();
67
+ return ref ? { ...ref, browserProduct: product } : undefined;
68
+ },
69
+ };
70
+ }
71
+
72
+ function normalize(value: string): string { return value.replace(/\s+/g, ' ').trim(); }
73
+ function delayMs(attempt: number, baseMs: number, maxMs: number): number {
74
+ return Math.min(maxMs, baseMs * 2 ** Math.max(0, Math.min(8, Math.trunc(attempt) - 1)));
75
+ }
76
+ async function sleep(ms: number): Promise<void> { await new Promise((resolve) => setTimeout(resolve, ms)); }
77
+
78
+ function identityResource(identity: ComputerChatgptTargetIdentity): ComputerSurfaceStableIdentity {
79
+ return {
80
+ surfaceType: 'browser-tab',
81
+ ownership: 'provider_owned',
82
+ resource: identity.namespace === 'chatgpt.conversation'
83
+ ? { namespace: identity.namespace, key: identity.conversationId }
84
+ : { namespace: identity.namespace, key: identity.bootstrapKey },
85
+ };
86
+ }
87
+
88
+ function targetAlias(identity: ComputerChatgptTargetIdentity): string {
89
+ return identity.namespace === 'chatgpt.conversation'
90
+ ? `chatgpt:conversation:${identity.conversationId}`
91
+ : `chatgpt:bootstrap:${identity.bootstrapKey}`;
92
+ }
93
+
94
+ function ownerToken(targetId: string): string { return `${OWNER_PREFIX}${targetId}`; }
95
+ function providerBinding(
96
+ page: ComputerChatgptNativePage,
97
+ targetId: string,
98
+ ownership: 'provider_owned' | 'user_owned',
99
+ ownerMarkerObserved = false,
100
+ ): ComputerSurfaceProviderBinding {
101
+ const ref = page.tabRef();
102
+ if (!ref) throw new Error('COMPUTER_CHATGPT_TARGET_PROVIDER_BINDING_UNPROVEN');
103
+ return {
104
+ providerId: PROVIDER_ID,
105
+ observedAt: new Date().toISOString(),
106
+ browserProduct: ref.browserProduct,
107
+ windowId: ref.windowId,
108
+ tabId: ref.tabId,
109
+ ...(ownership === 'provider_owned' && ownerMarkerObserved ? { ownerToken: ownerToken(targetId) } : {}),
110
+ };
111
+ }
112
+
113
+ function productForBinding(binding: ComputerSurfaceProviderBinding | undefined): MacOsBrowserProduct | undefined {
114
+ return binding?.browserProduct === 'chrome' || binding?.browserProduct === 'vivaldi' ? binding.browserProduct : undefined;
115
+ }
116
+
117
+ function preferredBrowserFromRecord(record: ComputerSurfaceTarget): { product?: MacOsBrowserProduct; windowId?: string } {
118
+ const preference = record.compatibilityRecords.find((entry) => entry.namespace === BROWSER_PREFERENCE_RECORD)?.value;
119
+ return {
120
+ ...(preference?.browserProduct === 'chrome' || preference?.browserProduct === 'vivaldi' ? { product: preference.browserProduct } : {}),
121
+ ...(typeof preference?.windowId === 'string' && preference.windowId.trim() ? { windowId: preference.windowId.trim() } : {}),
122
+ };
123
+ }
124
+
125
+ function parseConversation(value: string): ComputerChatgptConversationIdentity | undefined {
126
+ try {
127
+ const parsed = parseCanonicalChatgptConversationIdentity(value);
128
+ return { namespace: 'chatgpt.conversation', conversationId: parsed.conversationId, canonicalUrl: parsed.conversationUrl };
129
+ } catch { return undefined; }
130
+ }
131
+
132
+ function sameConversation(value: string, identity: ComputerChatgptConversationIdentity): boolean {
133
+ return parseConversation(value)?.conversationId === identity.conversationId;
134
+ }
135
+
136
+ function macOsChatgptSessionUsable(observation: ComputerChatgptConversationObservation): boolean {
137
+ // A signed-in conversation renders a composer; a login wall renders neither a
138
+ // composer, nor generating state, nor any conversation text.
139
+ return observation.composerText !== undefined
140
+ || observation.isGenerating
141
+ || Boolean(observation.latestUserText.trim() || observation.latestAssistantResponse.trim() || observation.providerActivityText.trim());
142
+ }
143
+ function isChatgptUrl(value: string): boolean {
144
+ try {
145
+ const parsed = new URL(value);
146
+ return parsed.protocol === 'https:' && parsed.hostname === 'chatgpt.com';
147
+ } catch { return false; }
148
+ }
149
+ function projectMetadata(value: string): { projectTitle?: string; projectUrl?: string } {
150
+ try {
151
+ const parsed = new URL(value);
152
+ const match = /^\/g\/(g-p-[a-z0-9]+)(?:-([^/]+))?\/c\/[a-z0-9-]+\/?$/i.exec(parsed.pathname);
153
+ const slug = match?.[2]?.trim();
154
+ if (!match?.[1]) return {};
155
+ return {
156
+ ...(slug ? { projectTitle: slug.replace(/[-_]+/g, ' ').replace(/\s+/g, ' ').trim() } : {}),
157
+ projectUrl: `https://chatgpt.com/g/${match[1]}/project`,
158
+ };
159
+ } catch { return {}; }
160
+ }
161
+
162
+ function boundTabProvenGone(error: unknown): boolean {
163
+ return error instanceof AssistantPluginError
164
+ && error.code === 'PLUGIN_BROWSER_NATIVE_TAB_IDENTITY_UNPROVEN'
165
+ && error.details?.candidateCount === 0
166
+ && error.details?.inventoryTruncated === false;
167
+ }
168
+
169
+ function failure(error: unknown, fallback: string): ComputerChatgptTargetResult {
170
+ const code = error instanceof AssistantPluginError ? error.code : (() => {
171
+ const message = error instanceof Error ? error.message : String(error);
172
+ const candidate = message.split(':', 1)[0]?.trim() ?? '';
173
+ return /^[A-Z][A-Z0-9_]+$/.test(candidate) ? candidate : fallback;
174
+ })();
175
+ return {
176
+ state: 'unavailable',
177
+ failure: {
178
+ code,
179
+ retryable: !(error instanceof AssistantPluginError) || error.retryable !== false,
180
+ phase: 'pre_mutation',
181
+ ...(code === 'PLUGIN_BROWSER_JAVASCRIPT_PERMISSION_REQUIRED' ? { humanAction: 'grant_permission' as const } : {}),
182
+ },
183
+ };
184
+ }
185
+
186
+ export async function observeMacOsChatgptPage(
187
+ page: ComputerChatgptNativePage,
188
+ options: ComputerChatgptConversationObservationOptions = {},
189
+ ): Promise<ComputerChatgptConversationObservation> {
190
+ const includeUserHistory = options.includeUserHistory ?? true;
191
+ const includePageText = options.includePageText ?? true;
192
+ return await page.evaluate<ComputerChatgptConversationObservation>(`(() => {
193
+ const text = (node) => String(node?.innerText ?? node?.textContent ?? '').trim();
194
+ const root = Array.from(document.querySelectorAll('main')).filter(node => node.getClientRects().length).pop() || document;
195
+ const nodes = (selector) => root.querySelectorAll(selector);
196
+ const semanticRole = (node) => {
197
+ const explicit = node?.getAttribute?.('data-message-author-role');
198
+ if (explicit === 'user' || explicit === 'assistant') return explicit;
199
+ const key = String(node?.getAttribute?.('data-chatgpt-search-unit-key') || node?.getAttribute?.('data-content-search-unit-key') || '');
200
+ if (key.endsWith(':user')) return 'user';
201
+ if (key.endsWith(':assistant')) return 'assistant';
202
+ return undefined;
203
+ };
204
+ const messageText = (node) => {
205
+ if (!node) return '';
206
+ const semanticContent = node.querySelector?.('[data-chatgpt-selection-message-id]');
207
+ return text(semanticContent) || text(node);
208
+ };
209
+ const roleEntries = (role) => {
210
+ const selector = role === 'user'
211
+ ? '[data-message-author-role="user"], [data-chatgpt-search-unit-key$=":user"], [data-content-search-unit-key$=":user"]'
212
+ : '[data-message-author-role="assistant"], [data-chatgpt-search-unit-key$=":assistant"], [data-content-search-unit-key$=":assistant"]';
213
+ const seen = new Map(); const entries = [];
214
+ for (const node of Array.from(nodes(selector))) {
215
+ const semanticKey = String(node?.getAttribute?.('data-chatgpt-search-unit-key') || node?.getAttribute?.('data-content-search-unit-key') || '');
216
+ const messageIds = String(node?.getAttribute?.('data-chatgpt-search-message-ids') || node?.getAttribute?.('data-chatgpt-selection-message-id') || '');
217
+ if (semanticKey || messageIds) {
218
+ const key = semanticKey || messageIds;
219
+ if (seen.has(key)) { entries[seen.get(key)] = node; continue; }
220
+ seen.set(key, entries.length);
221
+ }
222
+ entries.push(node);
223
+ }
224
+ return entries;
225
+ };
226
+ const userEntries = roleEntries('user'); const assistantEntries = roleEntries('assistant');
227
+ const userTexts = ${JSON.stringify(includeUserHistory)} ? userEntries.map(messageText).filter(Boolean) : undefined;
228
+ const assistantTexts = ${JSON.stringify(includeUserHistory)} ? assistantEntries.map(messageText).filter(Boolean) : undefined;
229
+ const latest = (entries) => messageText(entries.length ? entries[entries.length - 1] : undefined);
230
+ const composer = ['[data-testid="composer-text-input"]','div#prompt-textarea[contenteditable="true"]','#prompt-textarea[contenteditable="true"]','textarea[name="prompt"]','div[role="textbox"][contenteditable="true"]']
231
+ .flatMap((selector) => Array.from(nodes(selector))).find((element) => Boolean(element && element.getClientRects && element.getClientRects().length));
232
+ const roleNodes = Array.from(nodes('[data-message-author-role="user"], [data-message-author-role="assistant"], [data-chatgpt-search-unit-key$=":user"], [data-chatgpt-search-unit-key$=":assistant"], [data-content-search-unit-key$=":user"], [data-content-search-unit-key$=":assistant"]'));
233
+ const latestRoleNode = roleNodes.length ? roleNodes[roleNodes.length - 1] : undefined;
234
+ const turns = nodes('[data-testid^="conversation-turn-"]');
235
+ const providerActivityText = turns.length ? text(turns[turns.length - 1]).slice(-${MAX_PROVIDER_ACTIVITY_CHARS}) : messageText(latestRoleNode).slice(-${MAX_PROVIDER_ACTIVITY_CHARS});
236
+ const liveStatusNodes = Array.from(nodes('[role="alert"], [role="status"], [aria-live="assertive"], [aria-live="polite"]'));
237
+ const retryErrorNodes = Array.from(nodes('button')).filter((node) => /(?:请重试|try again)/i.test(text(node))).map((node) => node.parentElement || node);
238
+ // Detect only visible, short provider chrome outside message turns.
239
+ // The length-limit banner can lack aria-live on macOS native surfaces.
240
+ const capacityNodes = Array.from(nodes('p, span, div'))
241
+ .filter((node) => node.getClientRects?.().length
242
+ && !node.closest?.('[data-testid^="conversation-turn-"], [data-message-author-role]'))
243
+ .filter((node) => {
244
+ const value = text(node);
245
+ return value.length <= 250 && /(?:你已达到此对话的长度上限|此对话已达到长度上限|you(?:'ve| have) reached the (?:maximum length for this conversation|conversation length limit)|this conversation has reached its maximum length)/i.test(value);
246
+ });
247
+ const liveProviderStatus = [...liveStatusNodes, ...retryErrorNodes, ...capacityNodes].map(text).filter(Boolean).slice(-8).join('\\n');
248
+ const result = {
249
+ url: String(location.href || ''), title: String(document.title || ''),
250
+ latestUserText: userTexts ? userTexts.join('\\n') : latest(userEntries),
251
+ latestAssistantResponse: latest(assistantEntries),
252
+ ...(userTexts ? { userMessages: userTexts } : {}), ...(assistantTexts ? { assistantMessages: assistantTexts } : {}),
253
+ ...(composer ? { composerText: String(('value' in composer ? composer.value : composer.innerText ?? composer.textContent ?? '') || '') } : {}),
254
+ providerActivityText, providerFailureText: liveProviderStatus.slice(-${MAX_PROVIDER_FAILURE_SCAN_CHARS}),
255
+ latestTurnRole: semanticRole(latestRoleNode),
256
+ isGenerating: Array.from(nodes('[data-testid="stop-button"], [data-testid*="stop-button"], button[aria-label="Stop"], button[aria-label="Stop generating"], button[aria-label="停止"], button[aria-label="停止生成"], [data-is-streaming="true"]')).some(node => Boolean(node.getClientRects().length)),
257
+ };
258
+ if (${JSON.stringify(includePageText)}) result.pageText = String(document.body?.innerText ?? document.body?.textContent ?? '').trim().slice(-${MAX_PROVIDER_FAILURE_SCAN_CHARS});
259
+ return result;
260
+ })()`);
261
+ }
262
+
263
+ export async function dispatchMacOsChatgptPrompt(
264
+ page: ComputerChatgptNativePage,
265
+ prompt: string,
266
+ options: { mode?: 'send' | 'resume' | 'recover'; reasoning?: 'medium' | 'high' | 'xhigh'; prepareOnly?: boolean } = {},
267
+ ): Promise<{ mutation: 'not_attempted'; reasonCode: string } | { mutation: 'prepared' } | { mutation: 'attempted'; confirmed?: boolean }> {
268
+ // Apple Events has no verified reasoning selector. Never silently submit
269
+ // a Supervisor effect under the unproven maximum-effort claim.
270
+ if (options.reasoning) return { mutation: 'not_attempted', reasonCode: 'COMPUTER_CHATGPT_REASONING_UNSUPPORTED_BY_PROVIDER' };
271
+ const supervisor = /^@forge\s+<<<FORGE_WORKFLOW_EFFECT_V1:fx_[a-zA-Z0-9_-]+>>>/.test(prompt.trim());
272
+ const submittedPrompt = supervisor ? prompt.trim().replace(/^@forge\s+/, '') : prompt;
273
+ const resume = options.mode === 'resume';
274
+ if (options.mode === 'recover') {
275
+ if (options.prepareOnly) {
276
+ const busy = await page.evaluate<boolean>(`(() => Array.from(document.querySelectorAll('[data-testid="stop-button"], [data-testid*="stop-button"]')).some(element => Boolean(element && element.getClientRects && element.getClientRects().length)))()`);
277
+ if (busy) return { mutation: 'not_attempted', reasonCode: 'provider_recovery_stop_pending' };
278
+ }
279
+ try {
280
+ const interrupted = await page.evaluate<boolean>(`(() => {
281
+ const visible = (element) => Boolean(element && element.getClientRects && element.getClientRects().length);
282
+ const stop = Array.from(document.querySelectorAll('[data-testid="stop-button"], [data-testid*="stop-button"], button[aria-label="Stop"], button[aria-label="Stop generating"], button[aria-label="停止"], button[aria-label="停止生成"]')).find(visible);
283
+ if (!(stop instanceof HTMLElement)) return false;
284
+ stop.click(); return true;
285
+ })()`);
286
+ if (interrupted) {
287
+ for (let attempt = 1; attempt <= MAX_LOCAL_OBSERVATION_ATTEMPTS; attempt += 1) {
288
+ await sleep(delayMs(attempt, 250, 1_000));
289
+ const generating = await page.evaluate<boolean>(`(() => Array.from(document.querySelectorAll('[data-testid="stop-button"], [data-testid*="stop-button"], button[aria-label="Stop"], button[aria-label="Stop generating"], button[aria-label="停止"], button[aria-label="停止生成"]')).some(element => Boolean(element && element.getClientRects && element.getClientRects().length)))()`);
290
+ if (!generating) break;
291
+ if (attempt >= MAX_LOCAL_OBSERVATION_ATTEMPTS) return { mutation: 'not_attempted', reasonCode: 'provider_recovery_stop_unconfirmed' };
292
+ }
293
+ }
294
+ } catch (error) {
295
+ const code = error instanceof AssistantPluginError ? error.code : String(error);
296
+ if (code === 'PLUGIN_BROWSER_JAVASCRIPT_PERMISSION_REQUIRED' || String(code).includes('PLUGIN_BROWSER_JAVASCRIPT_PERMISSION_REQUIRED')) {
297
+ return { mutation: 'not_attempted', reasonCode: 'PLUGIN_BROWSER_JAVASCRIPT_PERMISSION_REQUIRED' };
298
+ }
299
+ return { mutation: 'not_attempted', reasonCode: 'provider_recovery_stop_unavailable' };
300
+ }
301
+ }
302
+ let prepared: { prepared: boolean; reason?: string } | undefined;
303
+ for (let attempt = 1; attempt <= MAX_LOCAL_OBSERVATION_ATTEMPTS; attempt += 1) {
304
+ try {
305
+ prepared = await page.evaluate<{ prepared: boolean; reason?: string }>(`(() => {
306
+ const visible = (element) => Boolean(element && element.getClientRects && element.getClientRects().length);
307
+ const root = Array.from(document.querySelectorAll('main')).filter(visible).pop() || document;
308
+ const value = (element) => String((element?.innerText ?? element?.textContent ?? '') || '');
309
+ const norm = (input) => String(input || '').replace(/\\s+/g, ' ').trim();
310
+ const expected = ${JSON.stringify(submittedPrompt)}; const expectedNorm = norm(expected); const resume = ${JSON.stringify(resume)};
311
+ const draft = ${JSON.stringify(supervisor)} ? expected + String.fromCharCode(10) + '@' : expected;
312
+ const draftNorm = norm(draft);
313
+ const composer = ['div#prompt-textarea[contenteditable="true"]','#prompt-textarea[contenteditable="true"]','[data-testid="composer-text-input"][contenteditable="true"]','div[role="textbox"][contenteditable="true"]']
314
+ .flatMap((selector) => Array.from(root.querySelectorAll(selector))).find(visible);
315
+ if (!(composer instanceof HTMLElement) || !composer.isContentEditable) return { prepared: false, reason: 'composer_missing' };
316
+ const current = norm(value(composer));
317
+ const chips = Array.from(composer.querySelectorAll('[plugin-mention-name], [app-mention-name]'));
318
+ if (${JSON.stringify(supervisor)} && chips.length === 1
319
+ && chips[0].getAttribute('app-mention-name') === 'forge'
320
+ && chips[0].getAttribute('app-mention-path') === 'app://asdk_app_6ac09022b26081918f20ff868c773824'
321
+ && current === norm(expected + ' forge')) return { prepared: true };
322
+ // A *previous attempt of this exact effect* may have inserted this same
323
+ // prompt and then failed before clicking Send. That draft is this turn's
324
+ // own text, not a foreign user draft: reusing it is the same logical
325
+ // send. Treating it as occupied burned a generation without ever
326
+ // reaching the provider.
327
+ if (current && (current === expectedNorm || current === draftNorm)) return { prepared: true };
328
+ if (resume) { if (!current) return { prepared: false, reason: 'composer_resume_empty' }; if (current !== expectedNorm && current !== draftNorm) return { prepared: false, reason: 'composer_resume_mismatch' }; }
329
+ else {
330
+ if (current) return { prepared: false, reason: 'composer_not_empty' };
331
+ composer.focus({ preventScroll: true });
332
+ const selection = window.getSelection(); if (!selection) return { prepared: false, reason: 'composer_selection_unavailable' };
333
+ const range = document.createRange(); range.selectNodeContents(composer); range.deleteContents(); range.collapse(true); selection.removeAllRanges(); selection.addRange(range);
334
+ if (!document.execCommand('insertText', false, draft)) return { prepared: false, reason: 'composer_text_insertion_rejected' };
335
+ }
336
+ return norm(value(composer)) === draftNorm ? { prepared: true } : { prepared: false, reason: 'composer_text_unconfirmed' };
337
+ })()`);
338
+ } catch (error) {
339
+ const code = error instanceof AssistantPluginError ? error.code : String(error);
340
+ if (code === 'PLUGIN_BROWSER_JAVASCRIPT_PERMISSION_REQUIRED' || String(code).includes('PLUGIN_BROWSER_JAVASCRIPT_PERMISSION_REQUIRED')) {
341
+ return { mutation: 'not_attempted', reasonCode: 'PLUGIN_BROWSER_JAVASCRIPT_PERMISSION_REQUIRED' };
342
+ }
343
+ if (attempt >= MAX_LOCAL_OBSERVATION_ATTEMPTS) throw error;
344
+ await sleep(delayMs(attempt, 2_000, 4_000)); continue;
345
+ }
346
+ if (prepared.prepared) break;
347
+ if (prepared.reason !== 'composer_missing' || attempt >= MAX_LOCAL_OBSERVATION_ATTEMPTS) return { mutation: 'not_attempted', reasonCode: prepared.reason ?? 'composer_prepare_failed' };
348
+ await sleep(delayMs(attempt, 2_000, 4_000));
349
+ }
350
+ if (!prepared?.prepared) return { mutation: 'not_attempted', reasonCode: prepared?.reason ?? 'composer_prepare_failed' };
351
+ // A literal '@forge' is not an app invocation. Before Send, bind the exact
352
+ // main Forge chip on the existing tab and verify that no app was substituted.
353
+ if (supervisor) {
354
+ try {
355
+ const initial = await page.evaluate<{ bound: boolean; opened: boolean; reason?: string }>(`(() => {
356
+ const norm = (x) => String(x || '').replace(/\\s+/g, ' ').trim();
357
+ const expected = ${JSON.stringify(submittedPrompt)};
358
+ const composer = document.querySelector('[data-composer-markdown][role="textbox"][contenteditable="true"], #prompt-textarea[contenteditable="true"], div[role="textbox"][contenteditable="true"]');
359
+ if (!(composer instanceof HTMLElement)) return { bound: false, opened: false, reason: 'composer_missing' };
360
+ const chips = Array.from(composer.querySelectorAll('[plugin-mention-name], [app-mention-name]'));
361
+ if (chips.length === 1
362
+ && chips[0].getAttribute('app-mention-name') === 'forge'
363
+ && chips[0].getAttribute('app-mention-path') === 'app://asdk_app_6ac09022b26081918f20ff868c773824'
364
+ && norm(composer.innerText) === norm(expected + ' forge')) return { bound: true, opened: false };
365
+ const triggerDraft = norm(expected + String.fromCharCode(10) + '@');
366
+ const current = norm(composer.innerText);
367
+ if (chips.length || (current !== norm(expected) && current !== triggerDraft)) return { bound: false, opened: false, reason: 'composer_binding_mismatch' };
368
+ if (current === norm(expected)) {
369
+ composer.focus({ preventScroll: true });
370
+ const selection = window.getSelection();
371
+ if (!selection) return { bound: false, opened: false, reason: 'forge_plugin_selection_unavailable' };
372
+ const range = document.createRange(); range.selectNodeContents(composer); range.deleteContents(); range.collapse(true);
373
+ selection.removeAllRanges(); selection.addRange(range);
374
+ if (!document.execCommand('insertText', false, expected + String.fromCharCode(10) + '@')) return { bound: false, opened: false, reason: 'forge_plugin_selection_unavailable' };
375
+ if (norm(composer.innerText) !== triggerDraft) return { bound: false, opened: false, reason: 'composer_binding_mismatch' };
376
+ }
377
+ composer.focus({ preventScroll: true });
378
+ const selection = window.getSelection();
379
+ if (!selection) return { bound: false, opened: false, reason: 'forge_plugin_selection_unavailable' };
380
+ const caret = document.createRange(); caret.selectNodeContents(composer); caret.collapse(false);
381
+ selection.removeAllRanges(); selection.addRange(caret);
382
+ return { bound: false, opened: true };
383
+ })()`);
384
+ if (!initial.bound) {
385
+ if (!initial.opened) return { mutation: 'not_attempted', reasonCode: initial.reason ?? 'forge_plugin_selection_unavailable' };
386
+ await sleep(150);
387
+ const typed = await page.evaluate<boolean>(`(() => document.execCommand('insertText', false, 'forge'))()`);
388
+ if (!typed) return { mutation: 'not_attempted', reasonCode: 'COMPUTER_CHATGPT_FORGE_PLUGIN_SELECTION_UNAVAILABLE' };
389
+ await sleep(150);
390
+ const picked = await page.evaluate<boolean>(`(() => {
391
+ const norm = (x) => String(x || '').replace(/\\s+/g, ' ').trim();
392
+ const options = Array.from(document.querySelectorAll('[data-mention-section-items] > button'));
393
+ const matches = options.filter((node) => { const label = norm(node.innerText ?? node.textContent); return label === 'forge' || label.startsWith('forge Current Forge 1.8.1'); });
394
+ if (matches.length !== 1) return false;
395
+ matches[0].click(); return true;
396
+ })()`);
397
+ if (!picked) return { mutation: 'not_attempted', reasonCode: 'COMPUTER_CHATGPT_FORGE_PLUGIN_UNAVAILABLE' };
398
+ await sleep(150);
399
+ const verified = await page.evaluate<boolean>(`(() => {
400
+ const norm = (x) => String(x || '').replace(/\\s+/g, ' ').trim();
401
+ const expected = ${JSON.stringify(submittedPrompt)};
402
+ const composer = document.querySelector('[data-composer-markdown][role="textbox"][contenteditable="true"], #prompt-textarea[contenteditable="true"], div[role="textbox"][contenteditable="true"]');
403
+ if (!(composer instanceof HTMLElement)) return false;
404
+ const chips = Array.from(composer.querySelectorAll('[plugin-mention-name], [app-mention-name]'));
405
+ return chips.length === 1 && chips[0].getAttribute('app-mention-name') === 'forge'
406
+ && chips[0].getAttribute('app-mention-path') === 'app://asdk_app_6ac09022b26081918f20ff868c773824'
407
+ && norm(composer.innerText) === norm(expected + ' forge');
408
+ })()`);
409
+ if (!verified) return { mutation: 'not_attempted', reasonCode: 'COMPUTER_CHATGPT_FORGE_PLUGIN_CHIP_UNVERIFIED' };
410
+ }
411
+ } catch {
412
+ // No Send was clicked: an unavailable UI/Apple Events transport is a
413
+ // mechanical negative proof, not an outcome-unknown provider mutation.
414
+ return { mutation: 'not_attempted', reasonCode: 'COMPUTER_CHATGPT_FORGE_PLUGIN_BINDING_UNAVAILABLE' };
415
+ }
416
+ }
417
+ if (options.prepareOnly) {
418
+ const ready = await page.evaluate<boolean>(`(() => {
419
+ const root = Array.from(document.querySelectorAll('main')).filter(element => Boolean(element.getClientRects?.().length)).pop() || document;
420
+ return Array.from(root.querySelectorAll('[data-testid="send-button"], button[aria-label="Send"], button[aria-label="发送"], button[data-testid*="send"], button[type="submit"]'))
421
+ .some(element => Boolean(element.getClientRects?.().length) && !element.hasAttribute('disabled') && element.getAttribute('aria-disabled') !== 'true');
422
+ })()`);
423
+ return ready ? { mutation: 'prepared' } : { mutation: 'not_attempted', reasonCode: 'send_button_missing' };
424
+ }
425
+ for (let attempt = 1; attempt <= MAX_LOCAL_OBSERVATION_ATTEMPTS; attempt += 1) {
426
+ let result: { dispatched: boolean; reason?: string };
427
+ try {
428
+ result = await page.evaluate<{ dispatched: boolean; reason?: string }>(`(() => {
429
+ const visible = (element) => Boolean(element && element.getClientRects && element.getClientRects().length);
430
+ const root = Array.from(document.querySelectorAll('main')).filter(visible).pop() || document;
431
+ const value = (element) => String((element?.innerText ?? element?.textContent ?? '') || '');
432
+ const norm = (input) => String(input || '').replace(/\\s+/g, ' ').trim(); const expected = ${JSON.stringify(submittedPrompt)}; const supervisor = ${JSON.stringify(supervisor)};
433
+ const composer = Array.from(root.querySelectorAll('div#prompt-textarea[contenteditable="true"], #prompt-textarea[contenteditable="true"], [data-testid="composer-text-input"][contenteditable="true"], div[role="textbox"][contenteditable="true"]')).find(visible);
434
+ if (!(composer instanceof HTMLElement)) return { dispatched: false, reason: 'composer_submit_mismatch' };
435
+ if (supervisor) {
436
+ const chips = Array.from(composer.querySelectorAll('[plugin-mention-name], [app-mention-name]'));
437
+ if (chips.length !== 1 || chips[0].getAttribute('app-mention-name') !== 'forge' || chips[0].getAttribute('app-mention-path') !== 'app://asdk_app_6ac09022b26081918f20ff868c773824' || norm(value(composer)) !== norm(expected + ' forge')) return { dispatched: false, reason: 'COMPUTER_CHATGPT_FORGE_PLUGIN_CHIP_UNVERIFIED' };
438
+ } else if (norm(value(composer)) !== norm(expected)) return { dispatched: false, reason: 'composer_submit_mismatch' };
439
+ const sendButton = Array.from(root.querySelectorAll('[data-testid="send-button"], button[aria-label="Send"], button[aria-label="发送"], button[data-testid*="send"], button[type="submit"]')).find(visible);
440
+ if (!(sendButton instanceof HTMLElement) || sendButton.hasAttribute('disabled') || sendButton.getAttribute('aria-disabled') === 'true') return { dispatched: false, reason: 'send_button_missing' };
441
+ sendButton.click(); return { dispatched: true };
442
+ })()`);
443
+ } catch {
444
+ // The submit-stage Apple Events operation may have executed the click
445
+ // before its response transport failed. Conservatively treat it as a
446
+ // possible provider mutation so callers reconcile instead of resending.
447
+ return { mutation: 'attempted' };
448
+ }
449
+ if (result.dispatched) return { mutation: 'attempted' };
450
+ if (result.reason !== 'send_button_missing' || attempt >= MAX_LOCAL_OBSERVATION_ATTEMPTS) return { mutation: 'not_attempted', reasonCode: result.reason ?? 'send_failed' };
451
+ await sleep(delayMs(attempt, 250, 1_000));
452
+ }
453
+ return { mutation: 'not_attempted', reasonCode: 'send_button_missing' };
454
+ }
455
+
456
+ export class MacOsChatgptConversationTargetPort implements ComputerChatgptConversationTargetPort {
457
+ private readonly pages = new Map<string, ComputerChatgptNativePage>();
458
+ private readonly createInFlight = new Map<string, Promise<ComputerChatgptNativePage>>();
459
+ private readonly createCooldownUntil = new Map<string, number>();
460
+ constructor(
461
+ private readonly controllerHome: string,
462
+ private readonly authority: ComputerInteractionTargetAuthorityPort,
463
+ private readonly timeoutMs = DEFAULT_TIMEOUT_MS,
464
+ ) {}
465
+
466
+ private async listTabs(): Promise<{ entries: TaggedTab[]; unavailableProviders: string[] }> {
467
+ const inspected = await Promise.all(PROVIDER_PRODUCTS.map(async (product) => {
468
+ try {
469
+ // Exact conversation identity is proven from stable windowId/tabId/url
470
+ // inventory. Do not precede it with the optional foreground-tab probe:
471
+ // a blocked Vivaldi Apple Events front-window read would otherwise hold
472
+ // the global browser serialization lane and strand Supervisor recovery.
473
+ const inventory = await listMacOsBrowserTabs(product, this.timeoutMs, { correctFrontmost: false });
474
+ return { entries: inventory.tabs.map((tab): TaggedTab => ({ ...tab, browserProduct: product })) };
475
+ } catch (error) {
476
+ if (error instanceof AssistantPluginError && error.code === 'PLUGIN_BROWSER_NATIVE_APP_NOT_RUNNING') return { entries: [] as TaggedTab[] };
477
+ return { entries: [] as TaggedTab[], unavailableProvider: `${PROVIDER_ID}:${product}` };
478
+ }
479
+ }));
480
+ return {
481
+ entries: inspected.flatMap((item) => item.entries),
482
+ unavailableProviders: inspected.flatMap((item) => item.unavailableProvider ? [item.unavailableProvider] : []),
483
+ };
484
+ }
485
+
486
+ private async reattach(ref: TaggedRef): Promise<ComputerChatgptNativePage> {
487
+ return taggedPage((await reattachMacOsBrowserOwnedPage(ref.browserProduct, ref, this.timeoutMs)).page, ref.browserProduct);
488
+ }
489
+
490
+ /**
491
+ * Opening a *new* ChatGPT tab must land in the browser that already hosts the
492
+ * user's signed-in ChatGPT session. Selecting by "frontmost" does not express
493
+ * identity: it can open the conversation in a browser profile that is not
494
+ * signed in, which both fails and leaves a foreign logged-out window behind.
495
+ * When no browser hosts any ChatGPT conversation we fail closed with a
496
+ * specific code instead of guessing; the caller surfaces one durable blocker.
497
+ */
498
+ private async create(url: string, preferredProduct?: MacOsBrowserProduct, preferredWindowId?: string): Promise<ComputerChatgptNativePage> {
499
+ const inventory = await this.listTabs();
500
+ const conversationTabCounts = new Map<MacOsBrowserProduct, number>();
501
+ for (const entry of inventory.entries) {
502
+ if (!isChatgptUrl(entry.url)) continue;
503
+ conversationTabCounts.set(entry.browserProduct, (conversationTabCounts.get(entry.browserProduct) ?? 0) + 1);
504
+ }
505
+ const signedIn = [...conversationTabCounts.keys()];
506
+ // With no ChatGPT tab anywhere, stay on the browser this target was already
507
+ // bound to (it was the user's ChatGPT browser before); the post-create
508
+ // session check still closes it and blocks if that profile is signed out.
509
+ const candidates = signedIn.length > 0
510
+ ? (preferredProduct && signedIn.includes(preferredProduct) ? [preferredProduct] : signedIn)
511
+ : (preferredProduct ? [preferredProduct] : []);
512
+ if (candidates.length === 0) throw new Error('COMPUTER_CHATGPT_BROWSER_IDENTITY_UNAVAILABLE');
513
+ const ordered = preferredProduct && candidates.includes(preferredProduct)
514
+ ? [preferredProduct, ...candidates.filter((product) => product !== preferredProduct)]
515
+ : candidates;
516
+ let discovered = await discoverMacOsBrowserAttachment(ordered, this.timeoutMs);
517
+ if (!discovered.attachment && preferredProduct) {
518
+ // The product choice is durable target evidence, so starting that exact
519
+ // browser family is safe and avoids requiring the user to reopen it.
520
+ // Authentication and exact page usability remain separate postconditions.
521
+ await ensureMacOsBrowserApplicationRunning(preferredProduct, this.timeoutMs);
522
+ discovered = await discoverMacOsBrowserAttachment([preferredProduct], this.timeoutMs);
523
+ }
524
+ const attachment = discovered.attachment;
525
+ if (!attachment) throw new Error('COMPUTER_CHATGPT_BROWSER_UNAVAILABLE');
526
+ // The chosen browser must either host a ChatGPT session or be the product
527
+ // this target was already bound to.
528
+ if (signedIn.length > 0 && !signedIn.includes(attachment.metadata.product)) throw new Error('COMPUTER_CHATGPT_BROWSER_IDENTITY_UNAVAILABLE');
529
+ if (signedIn.length === 0 && preferredProduct && attachment.metadata.product !== preferredProduct) throw new Error('COMPUTER_CHATGPT_BROWSER_IDENTITY_UNAVAILABLE');
530
+ const windowId = preferredWindowId && inventory.entries.some((entry) => entry.browserProduct === attachment.metadata.product && entry.windowId === preferredWindowId)
531
+ ? preferredWindowId
532
+ : undefined;
533
+ return taggedPage((await createMacOsBrowserOwnedPageForProduct(attachment.metadata.product, url, attachment.attempts, windowId, this.timeoutMs)).page, attachment.metadata.product);
534
+ }
535
+
536
+ private async createSingleFlight(url: string, preferredProduct?: MacOsBrowserProduct, preferredWindowId?: string): Promise<ComputerChatgptNativePage> {
537
+ const key = `${preferredProduct ?? 'unknown'}:${url}`;
538
+ const existing = this.createInFlight.get(key);
539
+ if (existing) return existing;
540
+ const cooldownUntil = this.createCooldownUntil.get(key) ?? 0;
541
+ if (cooldownUntil > Date.now()) throw new Error('COMPUTER_CHATGPT_TARGET_CREATE_COOLDOWN');
542
+ const operation = this.create(url, preferredProduct, preferredWindowId)
543
+ .catch((error) => {
544
+ this.createCooldownUntil.set(key, Date.now() + BROWSER_CREATE_COOLDOWN_MS);
545
+ throw error;
546
+ })
547
+ .finally(() => { this.createInFlight.delete(key); });
548
+ this.createInFlight.set(key, operation);
549
+ return operation;
550
+ }
551
+
552
+ private preferredBrowser(): { product?: MacOsBrowserProduct; windowId?: string } {
553
+ const record = this.authority.findSurfaceByStableIdentity(this.controllerHome, BROWSER_PREFERENCE_IDENTITY);
554
+ return record ? preferredBrowserFromRecord(record) : {};
555
+ }
556
+
557
+ private rememberBrowser(binding: TaggedRef | undefined): void {
558
+ const product = binding?.browserProduct;
559
+ if (!product) return;
560
+ this.authority.upsertSurface(this.controllerHome, {
561
+ stableIdentity: BROWSER_PREFERENCE_IDENTITY,
562
+ visibility: 'controller',
563
+ compatibilityRecords: [{
564
+ namespace: BROWSER_PREFERENCE_RECORD,
565
+ schemaVersion: 1,
566
+ value: {
567
+ browserProduct: product,
568
+ providerId: PROVIDER_ID,
569
+ ...(binding.windowId ? { windowId: binding.windowId } : {}),
570
+ },
571
+ updatedAt: new Date().toISOString(),
572
+ }],
573
+ reactivate: true,
574
+ });
575
+ }
576
+
577
+ private upsert(identity: ComputerChatgptTargetIdentity, ownership: 'provider_owned' | 'user_owned', binding?: ComputerSurfaceProviderBinding): ComputerSurfaceTarget {
578
+ return this.authority.upsertSurface(this.controllerHome, {
579
+ stableIdentity: { ...identityResource(identity), ownership },
580
+ compatibilityAliases: [targetAlias(identity)],
581
+ visibility: 'controller',
582
+ providerBinding: binding,
583
+ compatibilityRecords: [{
584
+ namespace: 'chatgpt.target', schemaVersion: 1,
585
+ value: identity.namespace === 'chatgpt.conversation'
586
+ ? { namespace: identity.namespace, conversationId: identity.conversationId, canonicalUrl: identity.canonicalUrl }
587
+ : { namespace: identity.namespace, bootstrapKey: identity.bootstrapKey, projectUrl: identity.projectUrl },
588
+ updatedAt: new Date().toISOString(),
589
+ }, ...(binding?.browserProduct ? [{
590
+ namespace: BROWSER_PREFERENCE_RECORD,
591
+ schemaVersion: 1,
592
+ value: { browserProduct: binding.browserProduct, providerId: PROVIDER_ID, ...(binding.windowId ? { windowId: binding.windowId } : {}) },
593
+ updatedAt: new Date().toISOString(),
594
+ }] : [])],
595
+ reactivate: true,
596
+ }).target;
597
+ }
598
+
599
+ private async prepareProviderOwnedPage(record: ComputerSurfaceTarget, page: ComputerChatgptNativePage): Promise<void> {
600
+ if (record.stableIdentity.ownership !== 'provider_owned') return;
601
+ if (!page.foregroundState || !page.bringToFront) throw new Error('COMPUTER_CHATGPT_INTERACTION_READINESS_UNAVAILABLE');
602
+ const state = await page.foregroundState();
603
+ if (!state.frontmost || !state.active) await page.bringToFront();
604
+ }
605
+
606
+ private async observeTarget(
607
+ record: ComputerSurfaceTarget,
608
+ page: ComputerChatgptNativePage,
609
+ options: ComputerChatgptConversationObservationOptions = {},
610
+ ): Promise<ComputerChatgptConversationObservation> {
611
+ await this.prepareProviderOwnedPage(record, page);
612
+ return await observeMacOsChatgptPage(page, options);
613
+ }
614
+
615
+ private target(identity: ComputerChatgptTargetIdentity, record: ComputerSurfaceTarget, page: ComputerChatgptNativePage): ComputerChatgptConversationTarget {
616
+ return {
617
+ targetId: record.targetId,
618
+ identity,
619
+ observe: async (options) => await this.observeTarget(record, page, options),
620
+ prepare: async (prompt, options) => {
621
+ if (options?.reasoning) return { ready: false, reasonCode: 'COMPUTER_CHATGPT_REASONING_UNSUPPORTED_BY_PROVIDER' };
622
+ await this.prepareProviderOwnedPage(record, page);
623
+ const result = await dispatchMacOsChatgptPrompt(page, prompt, { ...options, prepareOnly: true });
624
+ return result.mutation === 'prepared' ? { ready: true } : { ready: false,
625
+ reasonCode: result.mutation === 'not_attempted' ? result.reasonCode : 'COMPUTER_CHATGPT_PREPARE_MUTATION_UNEXPECTED' };
626
+ },
627
+ dispatch: async (prompt, options) => {
628
+ if (options?.reasoning) return { mutation: 'not_attempted', reasonCode: 'COMPUTER_CHATGPT_REASONING_UNSUPPORTED_BY_PROVIDER' };
629
+ await this.prepareProviderOwnedPage(record, page);
630
+ const result = await dispatchMacOsChatgptPrompt(page, prompt, options);
631
+ return result.mutation === 'prepared'
632
+ ? { mutation: 'not_attempted', reasonCode: 'COMPUTER_CHATGPT_PREPARE_RESULT_IN_DISPATCH' } : result;
633
+ },
634
+ };
635
+ }
636
+
637
+ private async bind(identity: ComputerChatgptTargetIdentity, record: ComputerSurfaceTarget, page: ComputerChatgptNativePage, ownership: 'provider_owned' | 'user_owned'): Promise<ComputerSurfaceTarget> {
638
+ // Stable tab identity is the reconnect authority. Persist it before any
639
+ // fallible DOM/foreground work so a successfully created physical tab can
640
+ // never become an untracked ghost merely because Apple Events JS stalls.
641
+ let updated = this.upsert(identity, ownership, providerBinding(page, record.targetId, ownership));
642
+ this.pages.set(updated.targetId, page);
643
+ if (ownership === 'provider_owned') {
644
+ try {
645
+ await this.prepareProviderOwnedPage(updated, page);
646
+ await page.evaluate(`(() => { window.name = ${JSON.stringify(ownerToken(record.targetId))}; return window.name; })()`);
647
+ // ownerToken is rebuildable provider observation, not semantic
648
+ // identity. Record it only after the marker was actually observed.
649
+ updated = this.upsert(identity, ownership, providerBinding(page, record.targetId, ownership, true));
650
+ } catch {
651
+ // The durable exact-tab binding remains canonical. Interaction paths
652
+ // retry readiness later instead of creating a second provider-owned tab.
653
+ }
654
+ }
655
+ return updated;
656
+ }
657
+
658
+ private async clearBinding(record: ComputerSurfaceTarget): Promise<void> {
659
+ this.pages.delete(record.targetId);
660
+ await this.authority.withSurfaceLease(this.controllerHome, record.targetId, async (lease) => { lease.clearBinding(); });
661
+ }
662
+
663
+ async inventory(): Promise<ComputerChatgptConversationInventory> {
664
+ const inventory = await this.listTabs();
665
+ const conversations = [];
666
+ for (const entry of inventory.entries) {
667
+ const identity = parseConversation(entry.url); if (!identity) continue;
668
+ const metadata = projectMetadata(entry.url);
669
+ conversations.push({
670
+ conversationId: identity.conversationId, canonicalUrl: identity.canonicalUrl,
671
+ ...(entry.title.trim() ? { title: entry.title.trim().slice(0, 512) } : {}), ...metadata,
672
+ ...(entry.active && entry.frontmost === true ? { isCurrent: true } : {}),
673
+ });
674
+ }
675
+ return { conversations, complete: inventory.unavailableProviders.length === 0, unavailableProviders: inventory.unavailableProviders };
676
+ }
677
+
678
+ async ensureExact(identity: ComputerChatgptConversationIdentity): Promise<ComputerChatgptTargetResult> {
679
+ let record = this.upsert(identity, 'provider_owned');
680
+ const cached = this.pages.get(record.targetId);
681
+ if (cached) {
682
+ try {
683
+ const observation = await this.observeTarget(record, cached, { includeUserHistory: false, includePageText: false });
684
+ if (sameConversation(observation.url, identity)) return { state: 'ready', target: this.target(identity, record, cached), observation };
685
+ } catch (error) {
686
+ // Transient native observation failure cannot revoke durable tab identity.
687
+ if (!boundTabProvenGone(error)) return failure(error, 'COMPUTER_CHATGPT_TARGET_RESTORE_FAILED');
688
+ }
689
+ this.pages.delete(record.targetId);
690
+ }
691
+ const binding = record.providerBinding;
692
+ const preferredBrowser = preferredBrowserFromRecord(record);
693
+ const rememberedBrowser = this.preferredBrowser();
694
+ const product = (binding ? productForBinding(binding) : undefined) ?? preferredBrowser.product ?? rememberedBrowser.product;
695
+ const preferredWindowId = binding?.windowId ?? preferredBrowser.windowId ?? rememberedBrowser.windowId;
696
+ if (binding?.providerId === PROVIDER_ID && product && binding.windowId && binding.tabId) {
697
+ try {
698
+ const page = await this.reattach({ browserProduct: product, windowId: binding.windowId, tabId: binding.tabId });
699
+ const observation = await this.observeTarget(record, page, { includeUserHistory: false, includePageText: false });
700
+ if (sameConversation(observation.url, identity)) {
701
+ this.pages.set(record.targetId, page);
702
+ return { state: 'ready', target: this.target(identity, record, page), observation };
703
+ }
704
+ } catch (error) {
705
+ // Only confirmed absence (or a successfully observed URL mismatch)
706
+ // permits retiring a bound tab, never a busy/timeout DOM read.
707
+ if (!boundTabProvenGone(error)) return failure(error, 'COMPUTER_CHATGPT_TARGET_RESTORE_FAILED');
708
+ }
709
+ try { await this.clearBinding(record); }
710
+ catch (error) { return failure(error, 'COMPUTER_CHATGPT_TARGET_BINDING_CLEAR_FAILED'); }
711
+ record = this.upsert(identity, record.stableIdentity.ownership === 'user_owned' ? 'user_owned' : 'provider_owned');
712
+ }
713
+ const inventory = await this.listTabs();
714
+ const exact = inventory.entries.filter((entry) => sameConversation(entry.url, identity));
715
+ const inspected: Array<{ page: ComputerChatgptNativePage; entry: TaggedTab; owner: string }> = [];
716
+ for (const entry of exact) {
717
+ try {
718
+ const page = await this.reattach({ browserProduct: entry.browserProduct, windowId: entry.windowId, tabId: entry.tabId });
719
+ const observation = await observeMacOsChatgptPage(page, { includeUserHistory: false, includePageText: false });
720
+ if (!sameConversation(observation.url, identity)) continue;
721
+ const owner = await page.evaluate<string>('String(window.name || "")').catch(() => '');
722
+ inspected.push({ page, entry, owner });
723
+ } catch { /* unreadable exact target keeps absence unproven */ }
724
+ }
725
+ const owned = inspected.filter((candidate) => candidate.owner === ownerToken(record.targetId)
726
+ || candidate.owner.startsWith(LEGACY_SUPERVISOR_OWNER_PREFIX)
727
+ || candidate.owner.startsWith(LEGACY_BROWSER_OWNER_PREFIX));
728
+ const candidates = owned.length === 1 ? owned : inspected.length === 1 ? inspected : [];
729
+ if (candidates.length === 1) {
730
+ const selected = candidates[0]!;
731
+ const ownership = selected.owner ? 'provider_owned' : 'user_owned';
732
+ const updated = await this.bind(identity, record, selected.page, ownership);
733
+ const observation = await observeMacOsChatgptPage(selected.page, { includeUserHistory: false, includePageText: false });
734
+ return { state: 'ready', target: this.target(identity, updated, selected.page), observation };
735
+ }
736
+ if (exact.length > 0) return failure(new Error('COMPUTER_CHATGPT_EXACT_TARGET_UNPROVEN'), 'COMPUTER_CHATGPT_EXACT_TARGET_UNPROVEN');
737
+ if (inventory.unavailableProviders.length > 0) return failure(new Error('COMPUTER_CHATGPT_TARGET_INVENTORY_INCOMPLETE'), 'COMPUTER_CHATGPT_TARGET_INVENTORY_INCOMPLETE');
738
+ let created: ComputerChatgptNativePage | undefined;
739
+ let bound = false;
740
+ try {
741
+ created = await this.createSingleFlight(identity.canonicalUrl, product, preferredWindowId);
742
+ const updated = await this.bind(identity, record, created, 'provider_owned');
743
+ bound = true;
744
+ this.rememberBrowser(created.tabRef());
745
+ const observation = await this.observeTarget(updated, created, { includeUserHistory: false, includePageText: false });
746
+ if (!sameConversation(observation.url, identity)) throw new Error('COMPUTER_CHATGPT_RESTORED_TARGET_UNPROVEN');
747
+ // A created tab that renders a signed-out shell is not a usable surface.
748
+ // Close it instead of leaving a foreign logged-out window open, and let the
749
+ // caller report one durable identity blocker.
750
+ if (!macOsChatgptSessionUsable(observation)) throw new Error('COMPUTER_CHATGPT_BROWSER_IDENTITY_UNAVAILABLE');
751
+ return { state: 'ready', target: this.target(identity, updated, created), observation };
752
+ } catch (error) {
753
+ const code = error instanceof Error ? error.message.split(':', 1)[0] : '';
754
+ const provenInvalid = code === 'COMPUTER_CHATGPT_RESTORED_TARGET_UNPROVEN'
755
+ || code === 'COMPUTER_CHATGPT_BROWSER_IDENTITY_UNAVAILABLE';
756
+ // Once the new physical tab is bound, transient DOM failures retain
757
+ // its canonical identity for the next attempt, not a replacement tab.
758
+ if (!bound || provenInvalid) {
759
+ if (created?.tabRef()) {
760
+ const ref = created.tabRef()!;
761
+ try { await closeMacOsBrowserOwnedTab(ref.browserProduct, ref, this.timeoutMs); }
762
+ catch (closeError) { return failure(closeError, 'COMPUTER_CHATGPT_TARGET_RETIRE_FAILED'); }
763
+ }
764
+ try { await this.clearBinding(record); }
765
+ catch (clearError) { return failure(clearError, 'COMPUTER_CHATGPT_TARGET_BINDING_CLEAR_FAILED'); }
766
+ }
767
+ return failure(error, 'COMPUTER_CHATGPT_TARGET_RESTORE_FAILED');
768
+ }
769
+ }
770
+
771
+ async openBootstrap(projectUrl: string, bootstrapKey: string): Promise<ComputerChatgptTargetResult> {
772
+ const identity: ComputerChatgptBootstrapIdentity = { namespace: 'chatgpt.bootstrap', bootstrapKey, projectUrl };
773
+ let record = this.upsert(identity, 'provider_owned');
774
+ const cached = this.pages.get(record.targetId);
775
+ if (cached) return { state: 'ready', target: this.target(identity, record, cached) };
776
+
777
+ const binding = record.providerBinding;
778
+ const boundProduct = productForBinding(binding);
779
+ if (binding?.providerId === PROVIDER_ID && boundProduct && binding.windowId && binding.tabId) {
780
+ try {
781
+ const page = await this.reattach({ browserProduct: boundProduct, windowId: binding.windowId, tabId: binding.tabId });
782
+ this.pages.set(record.targetId, page);
783
+ return { state: 'ready', target: this.target(identity, record, page) };
784
+ } catch (error) {
785
+ if (!boundTabProvenGone(error)) {
786
+ return failure(error, 'COMPUTER_CHATGPT_BOOTSTRAP_TARGET_REATTACH_FAILED');
787
+ }
788
+ try { await this.clearBinding(record); }
789
+ catch (clearError) { return failure(clearError, 'COMPUTER_CHATGPT_TARGET_BINDING_CLEAR_FAILED'); }
790
+ record = this.upsert(identity, 'provider_owned');
791
+ }
792
+ }
793
+
794
+ try {
795
+ const preferred = this.preferredBrowser();
796
+ const page = await this.createSingleFlight(projectUrl, preferred.product, preferred.windowId);
797
+ record = await this.bind(identity, record, page, 'provider_owned');
798
+ this.rememberBrowser(page.tabRef());
799
+ return { state: 'ready', target: this.target(identity, record, page) };
800
+ } catch (error) { return failure(error, 'COMPUTER_CHATGPT_BOOTSTRAP_TARGET_UNAVAILABLE'); }
801
+ }
802
+
803
+ async findBySubmittedMarker(
804
+ marker: string,
805
+ bootstrapKey?: string,
806
+ betweenObservations?: () => Promise<void>,
807
+ ): Promise<ComputerChatgptTargetResult[]> {
808
+ const inventory = await this.listTabs();
809
+ const matches: ComputerChatgptTargetResult[] = [];
810
+ for (const entry of inventory.entries) {
811
+ try {
812
+ const parsed = new URL(entry.url); if (parsed.protocol !== 'https:' || parsed.hostname !== 'chatgpt.com') continue;
813
+ const page = await this.reattach({ browserProduct: entry.browserProduct, windowId: entry.windowId, tabId: entry.tabId });
814
+ await betweenObservations?.();
815
+ const observation = await observeMacOsChatgptPage(page, { includeUserHistory: true, includePageText: false });
816
+ await betweenObservations?.();
817
+ if (!observation.latestUserText.includes(marker) && observation.userMessages?.some((message) => message.includes(marker)) !== true) continue;
818
+ const conversation = parseConversation(observation.url);
819
+ const identity: ComputerChatgptTargetIdentity = conversation ?? (bootstrapKey ? { namespace: 'chatgpt.bootstrap', bootstrapKey, projectUrl: observation.url } : undefined as never);
820
+ if (!identity) continue;
821
+ let record = this.upsert(identity, 'user_owned');
822
+ record = await this.bind(identity, record, page, 'user_owned');
823
+ matches.push({ state: 'ready', target: this.target(identity, record, page), observation });
824
+ } catch { /* one unreadable tab is not causal proof */ }
825
+ }
826
+ return matches;
827
+ }
828
+
829
+ async promoteBootstrap(targetId: string, identity: ComputerChatgptConversationIdentity): Promise<ComputerChatgptTargetResult> {
830
+ const page = this.pages.get(targetId);
831
+ if (!page) return failure(new Error('COMPUTER_CHATGPT_BOOTSTRAP_PROVIDER_BINDING_MISSING'), 'COMPUTER_CHATGPT_BOOTSTRAP_PROVIDER_BINDING_MISSING');
832
+ const previous = this.authority.getSurface(this.controllerHome, targetId);
833
+ if (!previous) return failure(new Error('COMPUTER_CHATGPT_BOOTSTRAP_TARGET_MISSING'), 'COMPUTER_CHATGPT_BOOTSTRAP_TARGET_MISSING');
834
+ if (previous.providerBinding && previous.providerBinding.providerId !== PROVIDER_ID) {
835
+ return {
836
+ state: 'unavailable',
837
+ failure: { code: 'COMPUTER_CHATGPT_BOOTSTRAP_PROVIDER_MISMATCH', retryable: false, phase: 'pre_mutation', failoverSafe: true },
838
+ };
839
+ }
840
+ try {
841
+ await this.authority.withSurfaceLease(this.controllerHome, targetId, async (lease) => { lease.clearBinding(); });
842
+ let record = this.upsert(identity, 'provider_owned');
843
+ record = await this.bind(identity, record, page, 'provider_owned');
844
+ this.pages.delete(targetId);
845
+ this.authority.tombstoneSurface(this.controllerHome, targetId);
846
+ const observation = await this.observeTarget(record, page, { includeUserHistory: false, includePageText: false });
847
+ return { state: 'ready', target: this.target(identity, record, page), observation };
848
+ } catch (error) { return failure(error, 'COMPUTER_CHATGPT_BOOTSTRAP_PROMOTION_FAILED'); }
849
+ }
850
+
851
+ async cleanup(activeResourceKeys: readonly string[]): Promise<void> {
852
+ const active = new Set(activeResourceKeys);
853
+ for (const record of this.authority.listAllSurfaces(this.controllerHome)) {
854
+ const resource = record.stableIdentity.resource;
855
+ if (!resource || (resource.namespace !== 'chatgpt.conversation' && resource.namespace !== 'chatgpt.bootstrap')) continue;
856
+ const resourceKey = `${resource.namespace}:${resource.key}`;
857
+ if (active.has(resourceKey) || !record.providerBinding || record.providerBinding.providerId !== PROVIDER_ID) continue;
858
+ const product = productForBinding(record.providerBinding);
859
+ const page = this.pages.get(record.targetId);
860
+ let resolved = page;
861
+ if (!resolved && product && record.providerBinding.windowId && record.providerBinding.tabId) {
862
+ try { resolved = await this.reattach({ browserProduct: product, windowId: record.providerBinding.windowId, tabId: record.providerBinding.tabId }); } catch { /* stale binding */ }
863
+ }
864
+ if (resolved && record.stableIdentity.ownership === 'provider_owned') {
865
+ const ref = resolved.tabRef(); if (ref) await closeMacOsBrowserOwnedTab(ref.browserProduct, ref, this.timeoutMs).catch(() => undefined);
866
+ }
867
+ this.pages.delete(record.targetId);
868
+ await this.authority.withSurfaceLease(this.controllerHome, record.targetId, async (lease) => { lease.clearBinding(); }).catch(() => undefined);
869
+ if (resource.namespace === 'chatgpt.bootstrap') this.authority.tombstoneSurface(this.controllerHome, record.targetId);
870
+ }
871
+ }
872
+
873
+ async release(targetId: string): Promise<void> {
874
+ const record = this.authority.getSurface(this.controllerHome, targetId);
875
+ if (!record) { this.pages.delete(targetId); return; }
876
+ if (record.providerBinding && record.providerBinding.providerId !== PROVIDER_ID) return;
877
+ const page = this.pages.get(targetId);
878
+ this.pages.delete(targetId);
879
+ if (page && record.stableIdentity.ownership === 'provider_owned') {
880
+ const ref = page.tabRef(); if (ref) await closeMacOsBrowserOwnedTab(ref.browserProduct, ref, this.timeoutMs).catch(() => undefined);
881
+ }
882
+ await this.authority.withSurfaceLease(this.controllerHome, targetId, async (lease) => { lease.clearBinding(); }).catch(() => undefined);
883
+ }
884
+
885
+ async close(): Promise<void> {
886
+ // Runtime/adapter shutdown is not semantic target retirement. Keep the
887
+ // provider tab and durable provider binding intact so the next Runtime can
888
+ // reattach to the same exact conversation without manufacturing a tab.
889
+ this.pages.clear();
890
+ }
891
+ }