@moretea-labs/forge 1.5.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +46 -0
  3. package/CLAUDE.md +2 -0
  4. package/DESIGN.md +7 -2
  5. package/README.md +4 -4
  6. package/README.zh-CN.md +2 -2
  7. package/SKILL.md +2 -2
  8. package/THIRD_PARTY_NOTICES.md +1 -1
  9. package/agents/openai.yaml +1 -1
  10. package/assets/hooks/hook-input.sh +11 -8
  11. package/assets/hooks/lib/session-state.sh +10 -7
  12. package/assets/hooks/lib/workflow-state.sh +13 -177
  13. package/assets/hooks/post-tool-observer.sh +8 -2
  14. package/assets/hooks/pre-edit-guard.sh +8 -39
  15. package/assets/hooks/prompt-guard.sh +7 -83
  16. package/assets/hooks/session-start-context.sh +56 -1
  17. package/assets/partials/05-workflow.partial.md +1 -1
  18. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  19. package/assets/plugin-registry.v1.json +54 -3
  20. package/assets/reference-configs/document-generation.md +1 -1
  21. package/assets/reference-configs/harness-overview.md +1 -1
  22. package/assets/reference-configs/hook-operations.md +1 -1
  23. package/assets/reference-configs/sprint-contracts.md +3 -3
  24. package/assets/skill-version.json +2 -2
  25. package/assets/templates/contract.template.md +2 -7
  26. package/assets/templates/helpers/capture-plan.sh +2 -2
  27. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  28. package/assets/templates/helpers/contract-worktree.sh +1 -21
  29. package/assets/templates/helpers/ensure-task-workflow.sh +13 -38
  30. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  31. package/assets/templates/helpers/new-plan.sh +1 -1
  32. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  33. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  34. package/assets/templates/helpers/sprint-backlog.sh +6 -6
  35. package/assets/templates/helpers/verify-contract.sh +1 -111
  36. package/assets/templates/helpers/verify-sprint.sh +3 -180
  37. package/assets/templates/plan.template.md +1 -1
  38. package/assets/templates/review.template.md +9 -34
  39. package/assets/workflow-contract.v1.json +10 -19
  40. package/docs/README.md +10 -10
  41. package/docs/architecture/CURRENT.md +170 -0
  42. package/docs/architecture/EVOLUTION.md +42 -0
  43. package/docs/architecture/current/README.md +4 -156
  44. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  45. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  46. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  47. package/docs/architecture/history/README.md +7 -0
  48. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  49. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  50. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  51. package/docs/architecture/history.md +3 -34
  52. package/docs/architecture/index.md +15 -98
  53. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  54. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  55. package/docs/architecture/versions/1.6.md +38 -0
  56. package/docs/architecture/versions/1.7.md +41 -0
  57. package/docs/forge-chatgpt-browser-engine.md +2 -2
  58. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  59. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  60. package/docs/forge-github-issue-launcher.md +1 -1
  61. package/docs/forge-runtime-self-healing-loop.md +1 -1
  62. package/docs/operations/controller-browser-plugin.md +38 -22
  63. package/docs/operations/controller-performance-and-502.md +124 -0
  64. package/docs/operations/controller-reliability-runbook.md +1 -1
  65. package/docs/operations/features.md +3 -3
  66. package/docs/operations/github-repository.md +1 -1
  67. package/docs/operations/homebrew.md +2 -2
  68. package/docs/operations/mcp-tool-exposure.md +5 -3
  69. package/docs/tutorials/01-install-and-start.md +1 -1
  70. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  71. package/docs/wiki/Architecture.md +6 -6
  72. package/docs/wiki/Implementation.md +2 -2
  73. package/docs/wiki/Installation.md +1 -1
  74. package/docs/wiki/Integrations.md +2 -2
  75. package/docs/wiki/Quick-Start.md +1 -1
  76. package/docs/wiki/Work-Lifecycle.md +1 -1
  77. package/package.json +29 -10
  78. package/scripts/activate-source-baseline.ts +17 -13
  79. package/scripts/benchmark-background-check-overlap.ts +228 -0
  80. package/scripts/benchmark-mcp-host.sh +117 -0
  81. package/scripts/benchmark-mcp-transport.ts +170 -0
  82. package/scripts/benchmark-quality-harness.ts +146 -0
  83. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  84. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  85. package/scripts/benchmark-typescript-navigation.ts +186 -0
  86. package/scripts/benchmark-work-efficiency.ts +334 -0
  87. package/scripts/capture-plan.sh +3 -3
  88. package/scripts/check-agent-tooling.sh +8 -35
  89. package/scripts/check-architecture-sync.sh +17 -86
  90. package/scripts/check-mcp-compatibility.ts +14 -0
  91. package/scripts/check-public-docs.sh +14 -1
  92. package/scripts/check-runtime-architecture.mjs +107 -64
  93. package/scripts/check-stable-baseline.ts +92 -0
  94. package/scripts/check-task-workflow.sh +4 -4
  95. package/scripts/contract-worktree.sh +1 -21
  96. package/scripts/ensure-task-workflow.sh +22 -55
  97. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  98. package/scripts/install-standalone-recovery.ts +24 -0
  99. package/scripts/lib/project-init-lib.sh +15 -44
  100. package/scripts/migrate-chatgpt-automation-schedules.ts +64 -0
  101. package/scripts/migrate-project-template.sh +23 -1179
  102. package/scripts/new-plan.sh +1 -1
  103. package/scripts/plan-to-todo.sh +11 -36
  104. package/scripts/run-governed-gate.ts +2 -1
  105. package/scripts/ship-worktrees.sh +2 -29
  106. package/scripts/smoke-ios-physical-device.ts +4 -2
  107. package/scripts/smoke-runtime-recovery.ts +1 -35
  108. package/scripts/smoke-schedule-engine.ts +1 -20
  109. package/scripts/sprint-backlog.sh +1 -1
  110. package/scripts/verify-contract.sh +1 -111
  111. package/scripts/verify-forge-runtime.sh +0 -1
  112. package/scripts/verify-sprint.sh +3 -180
  113. package/scripts/workflow-contract.ts +1 -5
  114. package/src/cli/commands/chatgpt.ts +31 -2
  115. package/src/cli/commands/init-hook.ts +3 -3
  116. package/src/cli/commands/mcp.ts +9 -5
  117. package/src/cli/commands/plugin.ts +37 -10
  118. package/src/cli/commands/recovery.ts +25 -3
  119. package/src/cli/commands/runtime.ts +55 -3
  120. package/src/cli/commands/security.ts +12 -1
  121. package/src/cli/commands/setup-profile.ts +31 -21
  122. package/src/cli/controller/check-runner.ts +106 -3
  123. package/src/cli/controller/composite-result.ts +1 -1
  124. package/src/cli/controller/context/focus.ts +51 -0
  125. package/src/cli/controller/context/instruction-context.ts +114 -0
  126. package/src/cli/controller/context/known-paths.ts +76 -0
  127. package/src/cli/controller/context/query-planning.ts +110 -0
  128. package/src/cli/controller/context/rule-resolution.ts +532 -0
  129. package/src/cli/controller/context/source-materializer.ts +278 -0
  130. package/src/cli/controller/context/structural.ts +16 -0
  131. package/src/cli/controller/context/types.ts +286 -0
  132. package/src/cli/controller/context-pack.ts +898 -430
  133. package/src/cli/controller/launch-agents.ts +77 -2
  134. package/src/cli/controller/operational-plan.ts +2 -2
  135. package/src/cli/controller/work-mode.ts +6 -4
  136. package/src/cli/editing/edit-session.ts +60 -7
  137. package/src/cli/github/github.ts +133 -0
  138. package/src/cli/hook/prompt-guard-decision.ts +17 -18
  139. package/src/cli/hook/prompt-intents.ts +1 -1
  140. package/src/cli/installer/managed-entries.ts +15 -1
  141. package/src/cli/local-bridge/console-automations.ts +225 -67
  142. package/src/cli/local-bridge/console-view-models.ts +3 -35
  143. package/src/cli/local-bridge/facade-api.ts +2 -217
  144. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  145. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +153 -3
  146. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  147. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +183 -3
  148. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +1 -1
  149. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +4 -3
  150. package/src/cli/local-bridge/frontend/src/lib/api.ts +18 -11
  151. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  152. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +1 -1
  153. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +4 -3
  154. package/src/cli/local-bridge/frontend/src/styles.css +27 -11
  155. package/src/cli/local-bridge/frontend/src/types.ts +8 -7
  156. package/src/cli/local-bridge/job-store.ts +1 -490
  157. package/src/cli/local-bridge/server.ts +29 -532
  158. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  159. package/src/cli/local-bridge/ui-dist/app.js +5101 -4967
  160. package/src/cli/mcp/auth.ts +5 -5
  161. package/src/cli/mcp/instructions.ts +3 -2
  162. package/src/cli/mcp/legacy-tool-service.ts +0 -189
  163. package/src/cli/mcp/multi-repository.ts +16 -5
  164. package/src/cli/mcp/oauth.ts +175 -26
  165. package/src/cli/mcp/paths.ts +8 -2
  166. package/src/cli/mcp/repository-tools.ts +356 -101
  167. package/src/cli/mcp/server.ts +625 -54
  168. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  169. package/src/cli/mcp/toolset-selection.ts +1 -1
  170. package/src/cli/mcp/toolset.ts +42 -5
  171. package/src/cli/mcp/transports/http.ts +125 -15
  172. package/src/cli/mcp/transports/session-registry.ts +67 -5
  173. package/src/cli/mcp/types.ts +3 -3
  174. package/src/cli/repositories/command-classifier.ts +209 -14
  175. package/src/cli/repositories/command-executor.ts +55 -521
  176. package/src/cli/repositories/command-process.ts +108 -0
  177. package/src/cli/repositories/command-scope.ts +99 -41
  178. package/src/cli/repositories/controller-home.ts +7 -8
  179. package/src/cli/repositories/git-command-executor.ts +2 -0
  180. package/src/cli/repositories/locks.ts +53 -16
  181. package/src/cli/repositories/repository-snapshot.ts +121 -0
  182. package/src/cli/repositories/safe-patch.ts +6 -4
  183. package/src/cli/repositories/selected-path-actions.ts +17 -9
  184. package/src/cli/repositories/structured-git.ts +110 -10
  185. package/src/cli/repository/inspector.ts +565 -62
  186. package/src/cli/repository/session-cache.ts +58 -17
  187. package/src/cli/v81-entry.ts +1 -4
  188. package/src/effects/fs-transaction.ts +85 -17
  189. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  190. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  191. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  192. package/src/runtime/context/lsp-client.ts +283 -0
  193. package/src/runtime/context/semantic-navigation.ts +275 -0
  194. package/src/runtime/context/swift-navigation.ts +565 -0
  195. package/src/runtime/context/typescript-navigation.ts +226 -0
  196. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  197. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +196 -2
  198. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  199. package/src/runtime/control-plane/execution/execution-identity.ts +65 -14
  200. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  201. package/src/runtime/control-plane/execution/validation.ts +15 -2
  202. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  203. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  204. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  205. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  206. package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
  207. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +175 -8
  208. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -21
  209. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +87 -9
  210. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  211. package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
  212. package/src/runtime/control-plane/facade/controller-session-store.ts +144 -14
  213. package/src/runtime/control-plane/facade/goal-workloop-access.ts +9 -2
  214. package/src/runtime/control-plane/facade/goal-workloop.ts +885 -105
  215. package/src/runtime/control-plane/facade/index.ts +1 -0
  216. package/src/runtime/control-plane/facade/plan-contract-store.ts +211 -26
  217. package/src/runtime/control-plane/facade/requirement-board.ts +24 -8
  218. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  219. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  220. package/src/runtime/control-plane/facade/types.ts +55 -11
  221. package/src/runtime/control-plane/facade/work-contract-store.ts +204 -73
  222. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  223. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  224. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  225. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  226. package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
  227. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  228. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  229. package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
  230. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  231. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  232. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  233. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  234. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  235. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  236. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  237. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  238. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  239. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  240. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
  241. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +642 -51
  242. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
  243. package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
  244. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  245. package/src/runtime/control-plane/persistence/requirement-store.ts +15 -11
  246. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  247. package/src/runtime/control-plane/release-retention.ts +62 -2
  248. package/src/runtime/control-plane/routing/route-policy.ts +7 -16
  249. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  250. package/src/runtime/diagnostics/mcp-timing.ts +19 -0
  251. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  252. package/src/runtime/diagnostics/performance.ts +24 -1
  253. package/src/runtime/evidence/event-ledger.ts +1 -1
  254. package/src/runtime/execution/jobs/store.ts +58 -4
  255. package/src/runtime/execution/jobs/types.ts +1 -1
  256. package/src/runtime/execution/managed-workspace.ts +203 -1
  257. package/src/runtime/execution/process-runtime/check-facade.ts +52 -14
  258. package/src/runtime/execution/process-runtime/check-result.ts +63 -2
  259. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  260. package/src/runtime/execution/process-runtime/command-facade.ts +346 -40
  261. package/src/runtime/execution/process-runtime/gc.ts +98 -12
  262. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
  263. package/src/runtime/execution/process-runtime/persisted-check.ts +57 -6
  264. package/src/runtime/execution/process-runtime/resource-claims.ts +97 -8
  265. package/src/runtime/execution/process-runtime/runtime.ts +59 -4
  266. package/src/runtime/execution/process-runtime/store.ts +7 -0
  267. package/src/runtime/execution/process-runtime/types.ts +2 -0
  268. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  269. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  270. package/src/runtime/execution/workers/executor.ts +0 -48
  271. package/src/runtime/gateway/mcp/execution-tools.ts +1219 -126
  272. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  273. package/src/runtime/gateway/mcp/router.ts +185 -83
  274. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
  275. package/src/runtime/gateway/mcp/runtime-tools.ts +2555 -1616
  276. package/src/runtime/model-clients/deepseek-adapter.ts +0 -32
  277. package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
  278. package/src/runtime/plugins/browser-adapter.ts +2925 -318
  279. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  280. package/src/runtime/plugins/browser-handoff-host.ts +6 -31
  281. package/src/runtime/plugins/browser-handoff.ts +92 -5
  282. package/src/runtime/plugins/browser-macos-bridge.ts +586 -45
  283. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  284. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  285. package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
  286. package/src/runtime/plugins/browser-runtime.ts +183 -0
  287. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  288. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  289. package/src/runtime/plugins/desktop-operator-registration.ts +15 -12
  290. package/src/runtime/plugins/external-adapter.ts +324 -3
  291. package/src/runtime/plugins/external-registration.ts +84 -5
  292. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  293. package/src/runtime/plugins/first-party-registry.ts +4 -2
  294. package/src/runtime/plugins/github-adapter.ts +125 -0
  295. package/src/runtime/plugins/gmail-adapter.ts +28 -2
  296. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  297. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  298. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  299. package/src/runtime/plugins/ios-adapter.ts +106 -16
  300. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  301. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  302. package/src/runtime/plugins/lightweight-action.ts +153 -0
  303. package/src/runtime/plugins/local-system-adapter.ts +125 -8
  304. package/src/runtime/plugins/macos-capability-broker.ts +55 -9
  305. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  306. package/src/runtime/plugins/plugin-management-adapter.ts +181 -0
  307. package/src/runtime/plugins/resend-adapter.ts +1 -1
  308. package/src/runtime/plugins/store.ts +366 -24
  309. package/src/runtime/plugins/types.ts +39 -2
  310. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  311. package/src/runtime/projections/git-status-sampler.ts +16 -16
  312. package/src/runtime/projections/materialized-view.ts +28 -10
  313. package/src/runtime/recovery/actions.ts +0 -10
  314. package/src/runtime/recovery/classifier.ts +0 -9
  315. package/src/runtime/recovery/maintenance-executor.ts +415 -18
  316. package/src/runtime/recovery/probe.ts +0 -1
  317. package/src/runtime/recovery/types.ts +0 -1
  318. package/src/runtime/root/mcp-transport.ts +180 -10
  319. package/src/runtime/root/package-connector-service.ts +186 -8
  320. package/src/runtime/root/package-runtime-release.ts +143 -17
  321. package/src/runtime/root/package-runtime-service.ts +430 -22
  322. package/src/runtime/root/release-manifest.ts +7 -2
  323. package/src/runtime/root/release-materialize.ts +162 -19
  324. package/src/runtime/root/release-store.ts +48 -11
  325. package/src/runtime/root/runtime.ts +23 -5
  326. package/src/runtime/root/service.ts +100 -27
  327. package/src/runtime/root/types.ts +2 -0
  328. package/src/runtime/safe-tooling/index.ts +0 -1
  329. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  330. package/src/runtime/safe-tooling/types.ts +0 -30
  331. package/src/runtime/shared/trusted-executable.ts +62 -0
  332. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  333. package/src/runtime/standalone-recovery/core.ts +702 -94
  334. package/src/runtime/standalone-recovery/entry.ts +99 -23
  335. package/src/runtime/standalone-recovery/installer.ts +19 -3
  336. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  337. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  338. package/src/runtime/workflow/schedules/browser-probe.ts +258 -0
  339. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +40 -0
  340. package/src/runtime/workflow/schedules/engine.ts +509 -80
  341. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  342. package/src/runtime/workflow/schedules/settlement.ts +48 -4
  343. package/src/runtime/workflow/schedules/store.ts +131 -10
  344. package/src/runtime/workflow/schedules/types.ts +6 -3
  345. package/src/runtime/workflow/schedules/work-continuation.ts +120 -34
  346. package/src/testing/test-governance.ts +8 -40
  347. package/assets/reference-configs/heartbeat-triage.md +0 -57
  348. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  349. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  350. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  351. package/bin/forge-desktop-helper.mjs +0 -204
  352. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  353. package/docs/architecture/current/architecture-invariants.md +0 -331
  354. package/docs/architecture/current/automation-and-schedule-engine.md +0 -453
  355. package/docs/architecture/current/codegraph-context-provider.md +0 -226
  356. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  357. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  358. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -354
  359. package/docs/architecture/current/entity-model.md +0 -592
  360. package/docs/architecture/current/external-plugin-governance.md +0 -396
  361. package/docs/architecture/current/failure-recovery.md +0 -447
  362. package/docs/architecture/current/governance.md +0 -213
  363. package/docs/architecture/current/human-interaction-plane.md +0 -87
  364. package/docs/architecture/current/implementation-status.md +0 -128
  365. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  366. package/docs/architecture/current/legacy-governance.md +0 -58
  367. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  368. package/docs/architecture/current/migration-roadmap.md +0 -105
  369. package/docs/architecture/current/multi-repository-execution.md +0 -371
  370. package/docs/architecture/current/operations-runbook.md +0 -45
  371. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  372. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  373. package/docs/architecture/current/repository-access-modes.md +0 -102
  374. package/docs/architecture/current/route-policy.md +0 -15
  375. package/docs/architecture/current/route-session-concurrency.md +0 -85
  376. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  377. package/docs/architecture/current/runtime-directory-map.md +0 -49
  378. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  379. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  380. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  381. package/docs/architecture/current/system-overview.md +0 -144
  382. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  383. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  384. package/docs/architecture/current/thin-harness-v1.md +0 -311
  385. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  386. package/docs/forge-autonomous-goal-loop.md +0 -9
  387. package/docs/forge-provider-configuration.md +0 -7
  388. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -83
  389. package/docs/operations/controller-desktop-plugin.md +0 -112
  390. package/docs/operations/gmail-assistant-routines.md +0 -186
  391. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  392. package/docs/operations/provider-configuration.md +0 -31
  393. package/scripts/harness-trace-grade.sh +0 -150
  394. package/scripts/heartbeat-triage.sh +0 -361
  395. package/scripts/maintenance-triage.sh +0 -167
  396. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  397. package/src/cli/repositories/goal-registry.ts +0 -239
  398. package/src/runtime/assistant/action-proposals.ts +0 -279
  399. package/src/runtime/assistant/intent.ts +0 -395
  400. package/src/runtime/assistant/model-provider.ts +0 -460
  401. package/src/runtime/assistant/openapi.ts +0 -223
  402. package/src/runtime/assistant/policy.ts +0 -89
  403. package/src/runtime/assistant/readiness.ts +0 -258
  404. package/src/runtime/assistant/routine-runtime.ts +0 -477
  405. package/src/runtime/assistant/schedule-binding.ts +0 -171
  406. package/src/runtime/assistant/standing-grants.ts +0 -375
  407. package/src/runtime/assistant/store.ts +0 -243
  408. package/src/runtime/assistant/types.ts +0 -135
  409. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  410. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  411. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  412. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  413. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  414. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  415. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  416. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  417. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  418. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  419. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  420. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  421. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  422. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  423. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  424. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  425. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  426. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  427. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  428. package/src/runtime/plugins/desktop-adapter.ts +0 -374
  429. package/src/runtime/safe-tooling/browser-safe.ts +0 -133
  430. package/src/runtime/workflow/findings/store.ts +0 -204
  431. package/src/runtime/workflow/findings/types.ts +0 -58
  432. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  433. package/src/runtime/workflow/portfolio/store.ts +0 -185
  434. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -68,7 +68,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
68
68
  {
69
69
  actionId: 'desktop_session_open',
70
70
  title: 'Open desktop session',
71
- description: 'Launch or locate one application, optionally activate it, and create a bounded interaction session.',
71
+ description: 'Launch or locate one application and create a bounded interaction session; stays in the background by default, or explicitly activates the target when activate=true.',
72
72
  readOnly: false,
73
73
  risk: 'workspace_write',
74
74
  confirmation: 'authorization',
@@ -83,7 +83,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
83
83
  bundle_id: { type: 'string' },
84
84
  app_name: { type: 'string' },
85
85
  launch: { type: 'boolean' },
86
- activate: { type: 'boolean' },
86
+ activate: { type: 'boolean', description: 'Explicitly activate the target application after launch/lookup. Defaults to false.' },
87
87
  },
88
88
  anyOf: [{ required: ['bundle_id'] }, { required: ['app_name'] }],
89
89
  additionalProperties: false,
@@ -119,7 +119,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
119
119
  {
120
120
  actionId: 'desktop_press',
121
121
  title: 'Press desktop element',
122
- description: 'Press one Accessibility element in an existing desktop interaction session.',
122
+ description: 'Press one Accessibility element semantically in the background; pointer/coordinate fallback is intentionally unavailable.',
123
123
  readOnly: false,
124
124
  risk: 'workspace_write',
125
125
  confirmation: 'authorization',
@@ -133,8 +133,11 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
133
133
  properties: {
134
134
  interaction_id: { type: 'string' },
135
135
  selector: SELECTOR_SCHEMA,
136
- coordinate_fallback: { type: 'boolean' },
137
- force_coordinate: { type: 'boolean' },
136
+ semantic_action: {
137
+ type: 'string',
138
+ enum: ['press', 'show_menu', 'pick', 'open', 'confirm'],
139
+ description: 'Bounded Accessibility action supported by the trusted Desktop Operator provider; defaults to press when omitted.',
140
+ },
138
141
  },
139
142
  required: ['interaction_id', 'selector'],
140
143
  additionalProperties: false,
@@ -143,7 +146,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
143
146
  {
144
147
  actionId: 'desktop_type_text',
145
148
  title: 'Type desktop text',
146
- description: 'Type text into one selected Accessibility element in an existing interaction session.',
149
+ description: 'Set text through Accessibility in the background without synthetic keyboard input or application activation.',
147
150
  readOnly: false,
148
151
  risk: 'workspace_write',
149
152
  confirmation: 'authorization',
@@ -167,7 +170,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
167
170
  {
168
171
  actionId: 'desktop_key',
169
172
  title: 'Press desktop keys',
170
- description: 'Send one bounded key chord, optionally activating an existing interaction session first.',
173
+ description: 'Send one bounded key chord only when the explicit target session is already foreground; the operator never activates it.',
171
174
  readOnly: false,
172
175
  risk: 'workspace_write',
173
176
  confirmation: 'authorization',
@@ -182,7 +185,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
182
185
  interaction_id: { type: 'string' },
183
186
  keys: { type: 'array', minItems: 1, maxItems: 8, items: { type: 'string' } },
184
187
  },
185
- required: ['keys'],
188
+ required: ['interaction_id', 'keys'],
186
189
  additionalProperties: false,
187
190
  },
188
191
  },
@@ -222,7 +225,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
222
225
  {
223
226
  actionId: 'desktop_copy',
224
227
  title: 'Copy from desktop session',
225
- description: 'Activate one explicit desktop session and send Command+C. This foregrounds the target application by design.',
228
+ description: 'Send Command+C only when the explicit target session is already foreground; the operator never activates it.',
226
229
  readOnly: false,
227
230
  risk: 'workspace_write',
228
231
  confirmation: 'authorization',
@@ -241,7 +244,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
241
244
  {
242
245
  actionId: 'desktop_paste',
243
246
  title: 'Paste into desktop session',
244
- description: 'Activate one explicit desktop session and send Command+V. This foregrounds the target application by design.',
247
+ description: 'Send Command+V only when the explicit target session is already foreground; the operator never activates it.',
245
248
  readOnly: false,
246
249
  risk: 'workspace_write',
247
250
  confirmation: 'authorization',
@@ -260,7 +263,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
260
263
  {
261
264
  actionId: 'desktop_open_url',
262
265
  title: 'Open desktop URL',
263
- description: 'Open one absolute URL through macOS application routing.',
266
+ description: 'Open one absolute URL through macOS background routing without activating the destination application.',
264
267
  readOnly: false,
265
268
  risk: 'workspace_write',
266
269
  confirmation: 'authorization',
@@ -407,7 +410,7 @@ export function createDesktopOperatorRegistrationInput(
407
410
  { capabilityId: 'desktop.permissions', title: 'Desktop permissions', description: 'Request the official macOS permissions required by Desktop Operator capabilities.', scopes: ['desktop.permissions'], actions: ['desktop_permissions_request'] },
408
411
  { capabilityId: 'desktop.session', title: 'Desktop sessions', description: 'Create and close application interaction sessions.', scopes: ['desktop.session'], actions: ['desktop_session_open', 'desktop_session_close'] },
409
412
  { capabilityId: 'desktop.observe', title: 'Desktop observation', description: 'Read bounded Accessibility and window state.', scopes: ['desktop.observe'], actions: ['desktop_observe'] },
410
- { capabilityId: 'desktop.interact', title: 'Desktop interaction', description: 'Press, type, send keys, and open URLs.', scopes: ['desktop.interact'], actions: ['desktop_press', 'desktop_type_text', 'desktop_key', 'desktop_open_url'] },
413
+ { capabilityId: 'desktop.interact', title: 'Desktop interaction', description: 'Press controls, type text, send keys, and open URLs.', scopes: ['desktop.interact'], actions: ['desktop_press', 'desktop_type_text', 'desktop_key', 'desktop_open_url'] },
411
414
  { capabilityId: 'desktop.capture', title: 'Desktop capture', description: 'Capture authorized screenshots.', scopes: ['desktop.capture'], actions: ['desktop_screenshot'] },
412
415
  { capabilityId: 'desktop.clipboard', title: 'Desktop clipboard', description: 'Read or replace plain-text clipboard content and issue explicit copy/paste in a bound session.', scopes: ['desktop.clipboard'], actions: ['desktop_clipboard_read', 'desktop_clipboard_write', 'desktop_copy', 'desktop_paste'] },
413
416
  { capabilityId: 'desktop.batch', title: 'Desktop batch', description: 'Run bounded desktop action batches.', scopes: ['desktop.batch'], actions: ['desktop_batch'] },
@@ -1,9 +1,10 @@
1
+ import { createHash } from 'crypto';
1
2
  import { AssistantPluginError } from './errors';
2
3
  import { getExternalPluginRegistration, listExternalPluginRegistrations, type ExternalPluginRegistration } from './external-registration';
3
4
  import { callExternalUnixSocket, probeExternalUnixSocketSync, type ExternalUnixSocketCallOptions } from './external-unix-socket';
4
5
  import { executeManagedPluginProcess, executeManagedPluginProcessSync, type ManagedPluginProcessSpec } from './managed-process-adapter';
5
- import { restartVerifiedUserLaunchAgent, startVerifiedUserLaunchAgent, stopVerifiedUserLaunchAgent } from './local-system-adapter';
6
- import type { AssistantPluginActionDescriptor, AssistantPluginAdapter, AssistantPluginCapability, AssistantPluginHealth, AssistantPluginManifest, AssistantPluginPermissionScope } from './types';
6
+ import { activateAndVerifyFrontmostApplication, restartVerifiedUserLaunchAgent, startVerifiedUserLaunchAgent, stopVerifiedUserLaunchAgent } from './local-system-adapter';
7
+ import type { AssistantPluginActionDescriptor, AssistantPluginAdapter, AssistantPluginAuthorizationContext, AssistantPluginCapability, AssistantPluginHealth, AssistantPluginManifest, AssistantPluginPermissionScope } from './types';
7
8
 
8
9
  interface ProviderManifest {
9
10
  id: string;
@@ -31,6 +32,7 @@ export interface ExternalPluginAdapterDependencies {
31
32
  startVerifiedUserLaunchAgent?: (label: unknown, expectedProgram: unknown) => Record<string, unknown>;
32
33
  stopVerifiedUserLaunchAgent?: (label: unknown, expectedProgram: unknown) => Record<string, unknown>;
33
34
  restartVerifiedUserLaunchAgent?: (label: unknown, expectedProgram: unknown) => Record<string, unknown>;
35
+ activateAndVerifyFrontmostApplication?: typeof activateAndVerifyFrontmostApplication;
34
36
  now?: () => Date;
35
37
  }
36
38
 
@@ -59,7 +61,10 @@ function parseProviderManifest(value: Record<string, unknown>, registration: Ext
59
61
  if (manifest.protocolVersion !== registration.protocolVersion) throw providerError('EXTERNAL_PLUGIN_PROTOCOL_MISMATCH', `Expected provider protocol ${registration.protocolVersion}, received ${manifest.protocolVersion || 'unknown'}.`);
60
62
  if (manifest.scope !== registration.scope) throw providerError('EXTERNAL_PLUGIN_SCOPE_MISMATCH', `Expected provider scope ${registration.scope}, received ${manifest.scope || 'unknown'}.`);
61
63
  if (manifest.provider !== registration.provider) throw providerError('EXTERNAL_PLUGIN_PROVIDER_MISMATCH', `Expected provider ${registration.provider}, received ${manifest.provider || 'unknown'}.`);
62
- const missingActions = registration.actions.map((action) => action.actionId).filter((actionId) => !manifest.actions.includes(actionId));
64
+ const missingActions = registration.actions
65
+ .map((action) => action.actionId)
66
+ .filter((actionId) => !(registration.pluginId === 'desktop_operator' && ['desktop_pointer_click', 'desktop_foreground_pointer_click'].includes(actionId)))
67
+ .filter((actionId) => !manifest.actions.includes(actionId));
63
68
  if (missingActions.length > 0) throw providerError('EXTERNAL_PLUGIN_ACTION_MISMATCH', `Provider is missing registered actions: ${missingActions.join(', ')}.`);
64
69
  return manifest;
65
70
  }
@@ -189,6 +194,225 @@ async function callProvider(
189
194
  });
190
195
  }
191
196
 
197
+ function externalProviderAuthorizationContext(registration: ExternalPluginRegistration): AssistantPluginAuthorizationContext {
198
+ return {
199
+ target: {
200
+ kind: 'external-provider',
201
+ id: registration.pluginId,
202
+ identityFingerprint: registration.registrationFingerprint,
203
+ },
204
+ expiresInMinutes: 30 * 24 * 60,
205
+ };
206
+ }
207
+
208
+ function desktopApplicationAuthorizationContext(
209
+ registration: ExternalPluginRegistration,
210
+ stableApplicationId: string,
211
+ ): AssistantPluginAuthorizationContext {
212
+ const normalized = stableApplicationId.trim();
213
+ return {
214
+ target: {
215
+ kind: 'desktop-application',
216
+ id: normalized,
217
+ identityFingerprint: createHash('sha256')
218
+ .update(`${registration.registrationFingerprint}\0${normalized}`)
219
+ .digest('hex'),
220
+ },
221
+ expiresInMinutes: 30 * 24 * 60,
222
+ };
223
+ }
224
+
225
+ function recordArray(value: unknown): Record<string, unknown>[] {
226
+ return Array.isArray(value)
227
+ ? value.filter((entry): entry is Record<string, unknown> => Boolean(entry) && typeof entry === 'object' && !Array.isArray(entry))
228
+ : [];
229
+ }
230
+
231
+ function firstString(record: Record<string, unknown>, ...keys: string[]): string {
232
+ for (const key of keys) {
233
+ const value = record[key];
234
+ if (typeof value === 'string' && value.trim()) return value.trim();
235
+ }
236
+ return '';
237
+ }
238
+
239
+ function firstNumber(record: Record<string, unknown>, ...keys: string[]): number | undefined {
240
+ for (const key of keys) {
241
+ const value = record[key];
242
+ if (typeof value === 'number' && Number.isFinite(value)) return value;
243
+ }
244
+ return undefined;
245
+ }
246
+
247
+ function desktopSession(status: Record<string, unknown>, interactionId: string): Record<string, unknown> | undefined {
248
+ return recordArray(status.sessions).find((session) => firstString(session, 'interactionId', 'interaction_id') === interactionId);
249
+ }
250
+
251
+ function desktopApplicationIsActive(status: Record<string, unknown>, bundleId: string, appName: string): boolean {
252
+ return recordArray(status.applications).some((application) => {
253
+ if (application.active !== true || application.terminated === true) return false;
254
+ const candidateBundleId = firstString(application, 'bundle_id', 'bundleIdentifier');
255
+ const candidateName = firstString(application, 'name', 'appName');
256
+ return bundleId ? candidateBundleId === bundleId : Boolean(appName) && candidateName === appName;
257
+ });
258
+ }
259
+
260
+ async function openVerifiedDesktopSession(
261
+ registration: ExternalPluginRegistration,
262
+ requestId: string,
263
+ args: Record<string, unknown>,
264
+ timeoutMs: number | undefined,
265
+ signal: AbortSignal | undefined,
266
+ dependencies: ExternalPluginAdapterDependencies,
267
+ ): Promise<Record<string, unknown>> {
268
+ const bundleId = typeof args.bundle_id === 'string' ? args.bundle_id.trim() : '';
269
+ const appName = typeof args.app_name === 'string' ? args.app_name.trim() : '';
270
+ if (!bundleId && !appName) {
271
+ throw providerError('DESKTOP_ACTIVATION_TARGET_UNAVAILABLE', 'Activated desktop sessions require an exact bundle id or application name.');
272
+ }
273
+
274
+ let result: Record<string, unknown>;
275
+ let fallbackVerified = false;
276
+ try {
277
+ result = await callProvider(
278
+ registration,
279
+ requestId,
280
+ 'desktop_session_open',
281
+ args,
282
+ timeoutMs,
283
+ signal,
284
+ dependencies,
285
+ );
286
+ } catch (error) {
287
+ if (!(error instanceof AssistantPluginError) || error.code !== 'APP_ACTIVATION_FAILED') throw error;
288
+
289
+ result = await callProvider(
290
+ registration,
291
+ `${requestId}:fallback-session`,
292
+ 'desktop_session_open',
293
+ { ...(bundleId ? { bundle_id: bundleId } : { app_name: appName }), launch: false, activate: false },
294
+ timeoutMs,
295
+ signal,
296
+ dependencies,
297
+ );
298
+
299
+ const activateFallback = dependencies.activateAndVerifyFrontmostApplication ?? activateAndVerifyFrontmostApplication;
300
+ try {
301
+ const fallbackProof = await activateFallback({ bundleId: bundleId || undefined, appName: appName || undefined });
302
+ const proofBundleId = typeof fallbackProof.bundleId === 'string' ? fallbackProof.bundleId.trim() : '';
303
+ const proofAppName = typeof fallbackProof.appName === 'string' ? fallbackProof.appName.trim() : '';
304
+ const proofMatches = bundleId ? proofBundleId === bundleId : proofAppName === appName;
305
+ if (fallbackProof.activated !== true || fallbackProof.verified !== true || !proofMatches) {
306
+ throw new AssistantPluginError(
307
+ 'DESKTOP_ACTIVATION_FALLBACK_IDENTITY_MISMATCH',
308
+ `Forge fallback did not prove the exact requested application ${bundleId || appName} as frontmost.`,
309
+ { retryable: true, details: { target: { bundleId, appName }, observed: { bundleId: proofBundleId, appName: proofAppName } } },
310
+ );
311
+ }
312
+ fallbackVerified = true;
313
+ } catch (fallbackError) {
314
+ if (fallbackError instanceof AssistantPluginError) {
315
+ throw new AssistantPluginError(
316
+ 'DESKTOP_ACTIVATION_FALLBACK_FAILED',
317
+ `Desktop Operator activation failed and Forge could not independently verify ${bundleId || appName} as frontmost.`,
318
+ {
319
+ retryable: fallbackError.retryable,
320
+ details: {
321
+ providerErrorCode: error.code,
322
+ fallbackErrorCode: fallbackError.code,
323
+ fallbackErrorMessage: fallbackError.message,
324
+ target: bundleId || appName,
325
+ },
326
+ },
327
+ );
328
+ }
329
+ throw new AssistantPluginError(
330
+ 'DESKTOP_ACTIVATION_FALLBACK_FAILED',
331
+ `Desktop Operator activation failed and Forge fallback failed for ${bundleId || appName}.`,
332
+ { retryable: true, details: { providerErrorCode: error.code, fallbackErrorMessage: String(fallbackError), target: bundleId || appName } },
333
+ );
334
+ }
335
+ }
336
+
337
+ if (!fallbackVerified) {
338
+ const activeStatus = await callProvider(
339
+ registration,
340
+ `${requestId}:active-status`,
341
+ 'desktop_status',
342
+ { limit: 500 },
343
+ timeoutMs,
344
+ signal,
345
+ dependencies,
346
+ );
347
+ if (!desktopApplicationIsActive(activeStatus, bundleId, appName)) {
348
+ throw providerError('DESKTOP_ACTIVATION_NOT_CONFIRMED', `Desktop Operator did not confirm ${bundleId || appName} as the frontmost application after activation.`, true);
349
+ }
350
+ }
351
+ return result;
352
+ }
353
+
354
+ async function resolveExternalAuthorizationContext(
355
+ registration: ExternalPluginRegistration,
356
+ input: Parameters<NonNullable<AssistantPluginAdapter['resolveAuthorizationContext']>>[0],
357
+ dependencies: ExternalPluginAdapterDependencies,
358
+ ): Promise<AssistantPluginAuthorizationContext | undefined> {
359
+ if (EXTERNAL_PROVIDER_LIFECYCLE_ACTIONS.has(input.actionId)) {
360
+ return externalProviderAuthorizationContext(registration);
361
+ }
362
+ if (registration.pluginId !== 'desktop_operator') return undefined;
363
+ if (input.actionId === 'desktop_permissions_request') {
364
+ return externalProviderAuthorizationContext(registration);
365
+ }
366
+ if (input.actionId === 'desktop_session_open') {
367
+ const bundleId = typeof input.args.bundle_id === 'string' ? input.args.bundle_id.trim() : '';
368
+ return bundleId ? desktopApplicationAuthorizationContext(registration, bundleId) : undefined;
369
+ }
370
+
371
+ const interactionId = typeof input.args.interaction_id === 'string' ? input.args.interaction_id.trim() : '';
372
+ if (!interactionId) return undefined;
373
+
374
+ const providerManifest = await callProvider(
375
+ registration,
376
+ `${input.requestId}:authorization-target:manifest`,
377
+ 'manifest',
378
+ {},
379
+ undefined,
380
+ input.signal,
381
+ dependencies,
382
+ );
383
+ parseProviderManifest(providerManifest, registration);
384
+ const status = await callProvider(
385
+ registration,
386
+ `${input.requestId}:authorization-target:desktop-status`,
387
+ 'desktop_status',
388
+ { limit: 500 },
389
+ input.timeoutMs,
390
+ input.signal,
391
+ dependencies,
392
+ );
393
+ const sessions = Array.isArray(status.sessions) ? status.sessions : [];
394
+ const session = sessions.find((entry) => {
395
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;
396
+ const record = entry as Record<string, unknown>;
397
+ const id = typeof record.interactionId === 'string' ? record.interactionId : typeof record.interaction_id === 'string' ? record.interaction_id : '';
398
+ return id === interactionId;
399
+ }) as Record<string, unknown> | undefined;
400
+ if (!session) {
401
+ throw providerError('EXTERNAL_PLUGIN_AUTHORIZATION_TARGET_UNAVAILABLE', `Desktop session ${interactionId} was not found while resolving its stable authorization target.`, false);
402
+ }
403
+ const bundleId = typeof session.bundleIdentifier === 'string' ? session.bundleIdentifier.trim()
404
+ : typeof session.bundle_identifier === 'string' ? session.bundle_identifier.trim()
405
+ : '';
406
+ const appName = typeof session.appName === 'string' ? session.appName.trim()
407
+ : typeof session.app_name === 'string' ? session.app_name.trim()
408
+ : '';
409
+ const stableApplicationId = bundleId || (appName ? `name:${appName.toLowerCase()}` : '');
410
+ if (!stableApplicationId) {
411
+ throw providerError('EXTERNAL_PLUGIN_AUTHORIZATION_TARGET_UNAVAILABLE', `Desktop session ${interactionId} has no stable application identity.`, false);
412
+ }
413
+ return desktopApplicationAuthorizationContext(registration, stableApplicationId);
414
+ }
415
+
192
416
  export function getExternalPluginAdapter(controllerHome: string, pluginId: string): AssistantPluginAdapter | undefined {
193
417
  const registration = getExternalPluginRegistration(controllerHome, pluginId);
194
418
  return registration ? createExternalPluginAdapter(registration) : undefined;
@@ -211,6 +435,7 @@ export function createExternalPluginAdapter(
211
435
  return {
212
436
  pluginId: registration.pluginId,
213
437
  scope: registration.scope,
438
+ resolveAuthorizationContext: (input) => resolveExternalAuthorizationContext(registration, input, dependencies),
214
439
  buildManifest(previousRevision = 0, previousUpdatedAt?: string): AssistantPluginManifest {
215
440
  const checkedAt = now().toISOString();
216
441
  if (!registration.enabled) {
@@ -305,6 +530,102 @@ export function createExternalPluginAdapter(
305
530
  );
306
531
  parseProviderManifest(providerManifest, registration);
307
532
  }
533
+ if (registration.pluginId === 'desktop_operator' && input.actionId === 'desktop_foreground_pointer_click') {
534
+ const sourceInteractionId = typeof input.args.interaction_id === 'string' ? input.args.interaction_id.trim() : '';
535
+ const requestedWindowId = typeof input.args.window_id === 'number' && Number.isInteger(input.args.window_id) ? input.args.window_id : undefined;
536
+ const x = typeof input.args.x === 'number' && Number.isFinite(input.args.x) ? input.args.x : undefined;
537
+ const y = typeof input.args.y === 'number' && Number.isFinite(input.args.y) ? input.args.y : undefined;
538
+ if (!sourceInteractionId || !requestedWindowId || x === undefined || y === undefined) {
539
+ throw providerError('DESKTOP_FOREGROUND_POINTER_ARGUMENT_INVALID', 'desktop_foreground_pointer_click requires interaction_id, window_id, x, and y.');
540
+ }
541
+
542
+ const sourceStatus = await callProvider(
543
+ registration,
544
+ `${input.requestId}:source-status`,
545
+ 'desktop_status',
546
+ { limit: 500 },
547
+ input.timeoutMs,
548
+ input.signal,
549
+ dependencies,
550
+ );
551
+ const sourceSession = desktopSession(sourceStatus, sourceInteractionId);
552
+ if (!sourceSession) {
553
+ throw providerError('DESKTOP_FOREGROUND_POINTER_SESSION_NOT_FOUND', `Desktop session ${sourceInteractionId} is no longer available.`, true);
554
+ }
555
+ const bundleId = firstString(sourceSession, 'bundleIdentifier', 'bundle_id');
556
+ const appName = firstString(sourceSession, 'appName', 'app_name');
557
+ if (!bundleId && !appName) {
558
+ throw providerError('DESKTOP_FOREGROUND_POINTER_TARGET_UNAVAILABLE', `Desktop session ${sourceInteractionId} has no stable application identity.`);
559
+ }
560
+
561
+ const activation = await openVerifiedDesktopSession(
562
+ registration,
563
+ `${input.requestId}:activate`,
564
+ { ...(bundleId ? { bundle_id: bundleId } : { app_name: appName }), launch: false, activate: true },
565
+ input.timeoutMs,
566
+ input.signal,
567
+ dependencies,
568
+ );
569
+ const activationInteractionId = firstString(activation, 'interactionId', 'interaction_id');
570
+ if (!activationInteractionId) {
571
+ throw providerError('DESKTOP_ACTIVATION_SESSION_MISSING', 'Desktop Operator activated the application without returning a bound interaction session.', true);
572
+ }
573
+
574
+ const screenshot = await callProvider(
575
+ registration,
576
+ `${input.requestId}:screenshot`,
577
+ 'desktop_screenshot',
578
+ {
579
+ interaction_id: activationInteractionId,
580
+ scope: 'window',
581
+ window_id: requestedWindowId,
582
+ ...(typeof input.args.label === 'string' && input.args.label.trim() ? { label: input.args.label.trim() } : {}),
583
+ },
584
+ input.timeoutMs,
585
+ input.signal,
586
+ dependencies,
587
+ );
588
+ const visualRevision = firstNumber(screenshot, 'visual_revision', 'visualRevision');
589
+ const capturedWindowId = firstNumber(screenshot, 'windowId', 'window_id') ?? requestedWindowId;
590
+ if (!visualRevision || !capturedWindowId) {
591
+ throw providerError('DESKTOP_FOREGROUND_CAPTURE_INVALID', 'Desktop Operator did not return a fresh visual revision for the foreground window.', true);
592
+ }
593
+
594
+ const click = await callProvider(
595
+ registration,
596
+ `${input.requestId}:click`,
597
+ 'desktop_pointer_click',
598
+ {
599
+ interaction_id: activationInteractionId,
600
+ window_id: capturedWindowId,
601
+ visual_revision: visualRevision,
602
+ x,
603
+ y,
604
+ },
605
+ input.timeoutMs,
606
+ input.signal,
607
+ dependencies,
608
+ );
609
+ return {
610
+ interactionId: activationInteractionId,
611
+ activationVerified: true,
612
+ windowId: capturedWindowId,
613
+ visualRevision,
614
+ screenshot,
615
+ click,
616
+ };
617
+ }
618
+
619
+ if (registration.pluginId === 'desktop_operator' && input.actionId === 'desktop_session_open' && input.args.activate === true) {
620
+ return await openVerifiedDesktopSession(
621
+ registration,
622
+ input.requestId,
623
+ input.args,
624
+ input.timeoutMs,
625
+ input.signal,
626
+ dependencies,
627
+ );
628
+ }
308
629
  return await callProvider(
309
630
  registration,
310
631
  input.requestId,
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'crypto';
2
- import { existsSync, mkdirSync, readdirSync } from 'fs';
2
+ import { existsSync, mkdirSync, readdirSync, rmSync } from 'fs';
3
3
  import { isAbsolute, join } from 'path';
4
4
  import { controllerSystemRoot, ensureControllerHome } from '../../cli/repositories/controller-home';
5
5
  import { readJsonFile, sanitizeFileComponent, writeJsonAtomic } from '../shared/json-files';
@@ -284,6 +284,34 @@ export function listExternalPluginRegistrations(controllerHome: string): Externa
284
284
  .sort((left, right) => left.pluginId.localeCompare(right.pluginId));
285
285
  }
286
286
 
287
+ export interface ExternalPluginRegistrationPreview {
288
+ pluginId: string;
289
+ currentRevision: number;
290
+ nextRevision: number;
291
+ registrationFingerprint: string;
292
+ currentFingerprint?: string;
293
+ wouldChange: boolean;
294
+ registration: Omit<ExternalPluginRegistration, 'revision' | 'registrationFingerprint' | 'installedAt' | 'updatedAt'>;
295
+ }
296
+
297
+ export function previewExternalPluginRegistration(
298
+ controllerHome: string,
299
+ input: ExternalPluginRegistrationInput,
300
+ ): ExternalPluginRegistrationPreview {
301
+ const normalized = normalizeRegistrationInput(input);
302
+ const existing = getExternalPluginRegistration(controllerHome, normalized.pluginId);
303
+ const fingerprint = registrationFingerprint(normalized);
304
+ return {
305
+ pluginId: normalized.pluginId,
306
+ currentRevision: existing?.revision ?? 0,
307
+ nextRevision: existing && existing.registrationFingerprint === fingerprint ? existing.revision : (existing?.revision ?? 0) + 1,
308
+ registrationFingerprint: fingerprint,
309
+ currentFingerprint: existing?.registrationFingerprint,
310
+ wouldChange: existing?.registrationFingerprint !== fingerprint,
311
+ registration: normalized,
312
+ };
313
+ }
314
+
287
315
  export function installExternalPluginRegistration(
288
316
  controllerHome: string,
289
317
  input: ExternalPluginRegistrationInput,
@@ -291,14 +319,19 @@ export function installExternalPluginRegistration(
291
319
  ): ExternalPluginRegistration {
292
320
  const normalized = normalizeRegistrationInput(input);
293
321
  const existing = getExternalPluginRegistration(controllerHome, normalized.pluginId);
294
- if (options.expectedRevision !== undefined && existing?.revision !== options.expectedRevision) {
295
- throw new Error(`EXTERNAL_PLUGIN_REGISTRATION_REVISION_CONFLICT: expected ${options.expectedRevision}, current ${existing?.revision ?? 0}`);
322
+ const fingerprint = registrationFingerprint(normalized);
323
+ // Identical desired state is idempotent, including a replay carrying the old
324
+ // expected revision after the first write committed.
325
+ if (existing?.registrationFingerprint === fingerprint) return existing;
326
+ const currentRevision = existing?.revision ?? 0;
327
+ if (options.expectedRevision !== undefined && currentRevision !== options.expectedRevision) {
328
+ throw new Error(`EXTERNAL_PLUGIN_REGISTRATION_REVISION_CONFLICT: expected ${options.expectedRevision}, current ${currentRevision}`);
296
329
  }
297
330
  const at = (options.now ?? new Date()).toISOString();
298
331
  const next: ExternalPluginRegistration = {
299
332
  ...normalized,
300
- revision: (existing?.revision ?? 0) + 1,
301
- registrationFingerprint: registrationFingerprint(normalized),
333
+ revision: currentRevision + 1,
334
+ registrationFingerprint: fingerprint,
302
335
  installedAt: existing?.installedAt ?? at,
303
336
  updatedAt: at,
304
337
  };
@@ -306,3 +339,49 @@ export function installExternalPluginRegistration(
306
339
  writeJsonAtomic(externalPluginRegistrationPath(controllerHome, next.pluginId), next);
307
340
  return next;
308
341
  }
342
+
343
+ function registrationInputFromStored(
344
+ registration: ExternalPluginRegistration,
345
+ patch: Partial<Pick<ExternalPluginRegistrationInput, 'enabled'>> = {},
346
+ ): ExternalPluginRegistrationInput {
347
+ return {
348
+ pluginId: registration.pluginId,
349
+ providerPluginId: registration.providerPluginId,
350
+ displayName: registration.displayName,
351
+ provider: registration.provider,
352
+ pluginVersion: registration.pluginVersion,
353
+ protocolVersion: registration.protocolVersion,
354
+ scope: registration.scope,
355
+ enabled: patch.enabled ?? registration.enabled,
356
+ transport: structuredClone(registration.transport),
357
+ lifecycle: registration.lifecycle ? structuredClone(registration.lifecycle) : undefined,
358
+ permissions: structuredClone(registration.permissions),
359
+ capabilities: structuredClone(registration.capabilities),
360
+ actions: structuredClone(registration.actions),
361
+ legacyIdentities: [...(registration.legacyIdentities ?? [])],
362
+ };
363
+ }
364
+
365
+ export function disableExternalPluginRegistration(
366
+ controllerHome: string,
367
+ pluginId: string,
368
+ options: { expectedRevision?: number; now?: Date } = {},
369
+ ): ExternalPluginRegistration {
370
+ const existing = getExternalPluginRegistration(controllerHome, pluginId);
371
+ if (!existing) throw new Error(`EXTERNAL_PLUGIN_REGISTRATION_NOT_FOUND: ${pluginId}`);
372
+ return installExternalPluginRegistration(controllerHome, registrationInputFromStored(existing, { enabled: false }), options);
373
+ }
374
+
375
+ export function removeExternalPluginRegistration(
376
+ controllerHome: string,
377
+ pluginId: string,
378
+ options: { expectedRevision?: number } = {},
379
+ ): ExternalPluginRegistration {
380
+ const existing = getExternalPluginRegistration(controllerHome, pluginId);
381
+ if (!existing) throw new Error(`EXTERNAL_PLUGIN_REGISTRATION_NOT_FOUND: ${pluginId}`);
382
+ if (options.expectedRevision !== undefined && existing.revision !== options.expectedRevision) {
383
+ throw new Error(`EXTERNAL_PLUGIN_REGISTRATION_REVISION_CONFLICT: expected ${options.expectedRevision}, current ${existing.revision}`);
384
+ }
385
+ rmSync(externalPluginRegistrationPath(controllerHome, existing.pluginId));
386
+ return existing;
387
+ }
@@ -1,7 +1,7 @@
1
1
  import { spawnSync } from 'child_process';
2
2
  import { existsSync } from 'fs';
3
3
  import { createConnection, type Socket } from 'net';
4
- import { dirname, isAbsolute, join } from 'path';
4
+ import { dirname, isAbsolute, join, resolve } from 'path';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { resolveBunExecutable } from '../shared/process-environment';
7
7
  import { AssistantPluginError } from './errors';
@@ -178,7 +178,13 @@ export async function callExternalUnixSocket(
178
178
  export function resolveExternalPluginProbeSidecarPath(
179
179
  execPath: string = process.execPath,
180
180
  moduleUrl: string = import.meta.url,
181
+ env: NodeJS.ProcessEnv = process.env,
181
182
  ): string {
183
+ const configuredReleaseRoot = env.FORGE_RELEASE_PATH?.trim();
184
+ if (configuredReleaseRoot) {
185
+ const releaseSidecar = join(resolve(configuredReleaseRoot), 'external-unix-socket-probe.cjs');
186
+ if (existsSync(releaseSidecar)) return releaseSidecar;
187
+ }
182
188
  const releaseSibling = join(dirname(execPath), 'external-unix-socket-probe.cjs');
183
189
  if (existsSync(releaseSibling)) return releaseSibling;
184
190
  const moduleSibling = fileURLToPath(new URL('./external-unix-socket-probe.cjs', moduleUrl));
@@ -1,13 +1,14 @@
1
1
  import { appStoreConnectPluginAdapter } from './app-store-connect-adapter';
2
2
  import { browserPluginAdapter } from './browser-adapter';
3
- import { desktopPluginAdapter } from './desktop-adapter';
4
3
  import { githubPluginAdapter } from './github-adapter';
5
4
  import { gmailPluginAdapter } from './gmail-adapter';
6
5
  import { googleCalendarPluginAdapter } from './google-calendar-adapter';
7
6
  import { googleTasksPluginAdapter } from './google-tasks-adapter';
8
7
  import { iosPluginAdapter } from './ios-adapter';
9
8
  import { localSystemPluginAdapter } from './local-system-adapter';
9
+ import { pluginManagementAdapter } from './plugin-management-adapter';
10
10
  import { resendPluginAdapter } from './resend-adapter';
11
+ import { xiaohongshuPluginAdapter } from './xiaohongshu-publish';
11
12
  import type { AssistantPluginAdapter } from './types';
12
13
 
13
14
  const FIRST_PARTY_PLUGIN_ADAPTERS: readonly AssistantPluginAdapter[] = [
@@ -19,8 +20,9 @@ const FIRST_PARTY_PLUGIN_ADAPTERS: readonly AssistantPluginAdapter[] = [
19
20
  googleCalendarPluginAdapter,
20
21
  googleTasksPluginAdapter,
21
22
  resendPluginAdapter,
23
+ xiaohongshuPluginAdapter,
22
24
  localSystemPluginAdapter,
23
- desktopPluginAdapter,
25
+ pluginManagementAdapter,
24
26
  ];
25
27
 
26
28
  export function listFirstPartyPluginAdapters(): readonly AssistantPluginAdapter[] {