@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
@@ -1,11 +1,12 @@
1
1
  import { randomUUID } from 'crypto';
2
2
  import { existsSync, mkdirSync, realpathSync } from 'fs';
3
- import { isAbsolute, join } from 'path';
3
+ import { delimiter, dirname, isAbsolute, join } from 'path';
4
4
  import { spawnSync } from 'child_process';
5
5
  import { performance } from 'perf_hooks';
6
6
  import { repositoryControllerRoot } from '../../cli/repositories/controller-home';
7
7
  import { runBoundedProcess } from '../execution/thin-harness/async-process';
8
8
  import { readJsonFile, writeJsonAtomic } from '../shared/json-files';
9
+ import { resolveTrustedNodeExecutable } from '../shared/trusted-node-executable';
9
10
  import { AssistantPluginError, toAssistantPluginError } from './errors';
10
11
  import {
11
12
  interactionAutomationEngine,
@@ -169,16 +170,35 @@ export function resetIosAgentDeviceRuntimeHooksForTest(): void {
169
170
  interactionCommandTails.clear();
170
171
  }
171
172
 
172
- function executable(): string {
173
- return process.env.FORGE_AGENT_DEVICE_EXECUTABLE?.trim() || 'agent-device';
173
+ function configuredExecutable(): string | undefined {
174
+ return process.env.FORGE_AGENT_DEVICE_EXECUTABLE?.trim() || undefined;
174
175
  }
175
176
 
176
- function resolvedExecutable(): string {
177
- const configured = executable();
177
+ function repoLocalExecutable(repoRoot?: string): string | undefined {
178
+ if (!repoRoot) return undefined;
179
+ const candidates = [
180
+ join(repoRoot, 'node_modules', '.bin', 'agent-device'),
181
+ join(repoRoot, 'node_modules', 'agent-device', 'bin', 'agent-device.mjs'),
182
+ ];
183
+ for (const candidate of candidates) {
184
+ if (!existsSync(candidate)) continue;
185
+ try { return realpathSync(candidate); } catch { return candidate; }
186
+ }
187
+ return undefined;
188
+ }
189
+
190
+ function executable(repoRoot?: string): string {
191
+ const configured = configuredExecutable();
192
+ if (configured) return configured;
193
+ return repoLocalExecutable(repoRoot) ?? 'agent-device';
194
+ }
195
+
196
+ function resolvedExecutable(repoRoot?: string): string {
197
+ const configured = executable(repoRoot);
178
198
  if (isAbsolute(configured)) {
179
199
  try { return realpathSync(configured); } catch { return configured; }
180
200
  }
181
- for (const directory of (process.env.PATH ?? '').split(':').filter(Boolean)) {
201
+ for (const directory of (process.env.PATH ?? '').split(delimiter).filter(Boolean)) {
182
202
  const candidate = join(directory, configured);
183
203
  if (!existsSync(candidate)) continue;
184
204
  try { return realpathSync(candidate); } catch { return candidate; }
@@ -186,6 +206,13 @@ function resolvedExecutable(): string {
186
206
  return configured;
187
207
  }
188
208
 
209
+ function withTrustedNodePath(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
210
+ const node = resolveTrustedNodeExecutable(env).executable;
211
+ if (!node) return env;
212
+ const path = [dirname(node), env.PATH].filter((value): value is string => Boolean(value)).join(delimiter);
213
+ return { ...env, PATH: path };
214
+ }
215
+
189
216
  function timestamp(): string {
190
217
  return hooks.now().toISOString();
191
218
  }
@@ -194,28 +221,29 @@ function sanitize(value: string): string {
194
221
  return value.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'ios-agent-device';
195
222
  }
196
223
 
197
- function statusProbeCacheKey(): string {
224
+ function statusProbeCacheKey(repoRoot?: string): string {
198
225
  return [
199
226
  hooks.platform(),
200
- executable(),
201
- resolvedExecutable(),
227
+ repoRoot ?? '',
228
+ executable(repoRoot),
229
+ resolvedExecutable(repoRoot),
202
230
  configuredAgentDeviceBackendMode(),
203
231
  process.versions.node,
204
232
  process.env.PATH ?? '',
205
233
  ].join('|');
206
234
  }
207
235
 
208
- function probeStatus() {
236
+ function probeStatus(repoRoot?: string) {
209
237
  const backendMode = configuredAgentDeviceBackendMode();
210
- const typedClient = typedAgentDeviceIdentity();
238
+ const typedClient = typedAgentDeviceIdentity({ repoRoot });
211
239
  if (hooks.platform() !== 'darwin') {
212
240
  return {
213
241
  available: false,
214
242
  expectedVersion: IOS_AGENT_DEVICE_VERSION,
215
243
  supportedVersionPolicy: '>=0.19.3 <0.21.0 with reviewed command contract',
216
244
  detectedVersion: undefined,
217
- executable: executable(),
218
- resolvedExecutable: resolvedExecutable(),
245
+ executable: executable(repoRoot),
246
+ resolvedExecutable: resolvedExecutable(repoRoot),
219
247
  platform: hooks.platform(),
220
248
  capabilityProfile: detectAgentDeviceCapabilities('', {}),
221
249
  backendMode,
@@ -223,14 +251,15 @@ function probeStatus() {
223
251
  reason: 'agent-device iOS support requires macOS.',
224
252
  };
225
253
  }
226
- const result = hooks.runCommand(executable(), ['--version'], { timeoutMs: 3_000 });
254
+ const runtimeEnv = withTrustedNodePath({ ...process.env });
255
+ const result = hooks.runCommand(executable(repoRoot), ['--version'], { cwd: repoRoot, env: runtimeEnv, timeoutMs: 3_000 });
227
256
  const detectedVersion = result.ok ? result.stdout.trim() : undefined;
228
257
  const help: AgentDeviceHelpContract = {};
229
258
  if (result.ok && detectedVersion) {
230
- const rootHelp = hooks.runCommand(executable(), ['help'], { timeoutMs: 3_000 });
259
+ const rootHelp = hooks.runCommand(executable(repoRoot), ['help'], { cwd: repoRoot, env: runtimeEnv, timeoutMs: 3_000 });
231
260
  if (rootHelp.ok) help.root = rootHelp.stdout || rootHelp.stderr;
232
261
  for (const topic of ['snapshot', 'press', 'fill', 'batch', 'keyboard'] as const) {
233
- const topicResult = hooks.runCommand(executable(), ['help', topic], { timeoutMs: 3_000 });
262
+ const topicResult = hooks.runCommand(executable(repoRoot), ['help', topic], { cwd: repoRoot, env: runtimeEnv, timeoutMs: 3_000 });
234
263
  if (topicResult.ok) help[topic] = topicResult.stdout || topicResult.stderr;
235
264
  }
236
265
  }
@@ -251,8 +280,8 @@ function probeStatus() {
251
280
  expectedVersion: IOS_AGENT_DEVICE_VERSION,
252
281
  supportedVersionPolicy: '>=0.19.3 <0.21.0 with reviewed command contract',
253
282
  detectedVersion,
254
- executable: executable(),
255
- resolvedExecutable: resolvedExecutable(),
283
+ executable: executable(repoRoot),
284
+ resolvedExecutable: resolvedExecutable(repoRoot),
256
285
  platform: hooks.platform(),
257
286
  capabilityProfile,
258
287
  backendMode,
@@ -267,20 +296,105 @@ function probeStatus() {
267
296
  };
268
297
  }
269
298
 
270
- export function iosAgentDeviceStatus(options: { forceRefresh?: boolean } = {}) {
299
+ function unprobedAgentDeviceStatus(repoRoot?: string): ReturnType<typeof probeStatus> {
300
+ const backendMode = configuredAgentDeviceBackendMode();
301
+ const typedClient = typedAgentDeviceIdentity({ repoRoot });
302
+ return {
303
+ available: false,
304
+ expectedVersion: IOS_AGENT_DEVICE_VERSION,
305
+ supportedVersionPolicy: '>=0.19.3 <0.21.0 with reviewed command contract',
306
+ detectedVersion: undefined,
307
+ executable: executable(repoRoot),
308
+ resolvedExecutable: resolvedExecutable(repoRoot),
309
+ platform: hooks.platform(),
310
+ capabilityProfile: detectAgentDeviceCapabilities('', {}),
311
+ backendMode,
312
+ typedClient: { ...typedClient, cliVersion: undefined, cliVersionCompatible: false },
313
+ reason: 'agent-device readiness has not been probed by an explicit asynchronous action in this runtime yet.',
314
+ };
315
+ }
316
+
317
+ export function iosAgentDeviceStatus(options: { forceRefresh?: boolean; repoRoot?: string } = {}) {
271
318
  const nowMs = hooks.now().getTime();
272
- const cacheKey = statusProbeCacheKey();
319
+ const cacheKey = statusProbeCacheKey(options.repoRoot);
273
320
  if (!options.forceRefresh
274
321
  && statusCache
275
322
  && statusCache.cacheKey === cacheKey
276
323
  && statusCache.expiresAt > nowMs) return statusCache.value;
277
- const value = probeStatus();
324
+ if (hooks.platform() !== 'darwin') return probeStatus(options.repoRoot);
325
+ return unprobedAgentDeviceStatus(options.repoRoot);
326
+ }
327
+
328
+ async function probeStatusAsync(input: Pick<AssistantPluginActionExecutionInput, 'repoRoot' | 'signal'>): Promise<ReturnType<typeof probeStatus>> {
329
+ const repoRoot = input.repoRoot;
330
+ const backendMode = configuredAgentDeviceBackendMode();
331
+ const typedClient = typedAgentDeviceIdentity({ repoRoot });
332
+ if (hooks.platform() !== 'darwin') return probeStatus(repoRoot);
333
+ const runtimeEnv = withTrustedNodePath({ ...process.env });
334
+ const result = await hooks.runCommandAsync(executable(repoRoot), ['--version'], {
335
+ cwd: repoRoot,
336
+ env: runtimeEnv,
337
+ timeoutMs: 3_000,
338
+ signal: input.signal,
339
+ });
340
+ const detectedVersion = result.ok ? result.stdout.trim() : undefined;
341
+ const help: AgentDeviceHelpContract = {};
342
+ if (result.ok && detectedVersion) {
343
+ const rootHelp = await hooks.runCommandAsync(executable(repoRoot), ['help'], {
344
+ cwd: repoRoot, env: runtimeEnv, timeoutMs: 3_000, signal: input.signal,
345
+ });
346
+ if (rootHelp.ok) help.root = rootHelp.stdout || rootHelp.stderr;
347
+ for (const topic of ['snapshot', 'press', 'fill', 'batch', 'keyboard'] as const) {
348
+ const topicResult = await hooks.runCommandAsync(executable(repoRoot), ['help', topic], {
349
+ cwd: repoRoot, env: runtimeEnv, timeoutMs: 3_000, signal: input.signal,
350
+ });
351
+ if (topicResult.ok) help[topic] = topicResult.stdout || topicResult.stderr;
352
+ }
353
+ }
354
+ const capabilityProfile = detectAgentDeviceCapabilities(detectedVersion ?? '', help);
355
+ const typedClientWithCompatibility = {
356
+ ...typedClient,
357
+ cliVersion: detectedVersion,
358
+ cliVersionCompatible: agentDeviceProviderVersionsMatch(typedClient.version, detectedVersion),
359
+ };
360
+ const available = result.ok
361
+ && capabilityProfile.versionSupported
362
+ && Boolean(capabilityProfile.snapshot.interactiveFlag)
363
+ && capabilityProfile.press.supported
364
+ && capabilityProfile.fill.supported
365
+ && capabilityProfile.batch.supported;
366
+ return {
367
+ available,
368
+ expectedVersion: IOS_AGENT_DEVICE_VERSION,
369
+ supportedVersionPolicy: '>=0.19.3 <0.21.0 with reviewed command contract',
370
+ detectedVersion,
371
+ executable: executable(repoRoot),
372
+ resolvedExecutable: resolvedExecutable(repoRoot),
373
+ platform: hooks.platform(),
374
+ capabilityProfile,
375
+ backendMode,
376
+ typedClient: typedClientWithCompatibility,
377
+ reason: !result.ok
378
+ ? (result.stderr || result.stdout || 'agent-device is not installed.')
379
+ : !capabilityProfile.versionSupported
380
+ ? `Unsupported agent-device version ${detectedVersion || 'unknown'}; expected the reviewed >=0.19.3 <0.21.0 contract.`
381
+ : !available
382
+ ? 'The installed agent-device command contract is missing required snapshot, press, fill, or batch capabilities.'
383
+ : undefined,
384
+ };
385
+ }
386
+
387
+ async function iosAgentDeviceActionStatus(input: AssistantPluginActionExecutionInput): Promise<ReturnType<typeof probeStatus>> {
388
+ const nowMs = hooks.now().getTime();
389
+ const cacheKey = statusProbeCacheKey(input.repoRoot);
390
+ if (statusCache && statusCache.cacheKey === cacheKey && statusCache.expiresAt > nowMs) return statusCache.value;
391
+ const value = await probeStatusAsync(input);
278
392
  statusCache = { cacheKey, expiresAt: nowMs + STATUS_TTL_MS, value };
279
393
  return value;
280
394
  }
281
395
 
282
- function requireDependency(): ReturnType<typeof probeStatus> {
283
- const status = iosAgentDeviceStatus();
396
+ function requireDependency(repoRoot?: string): ReturnType<typeof probeStatus> {
397
+ const status = iosAgentDeviceStatus({ repoRoot });
284
398
  if (!status.available) {
285
399
  throw new AssistantPluginError('PLUGIN_DEPENDENCY_MISSING', status.reason ?? 'A compatible agent-device provider is unavailable.', {
286
400
  retryable: false,
@@ -290,8 +404,19 @@ function requireDependency(): ReturnType<typeof probeStatus> {
290
404
  return status;
291
405
  }
292
406
 
293
- function capabilityProfile(): AgentDeviceCapabilityProfile {
294
- return requireDependency().capabilityProfile;
407
+ async function requireDependencyAsync(input: AssistantPluginActionExecutionInput): Promise<ReturnType<typeof probeStatus>> {
408
+ const status = await iosAgentDeviceActionStatus(input);
409
+ if (!status.available) {
410
+ throw new AssistantPluginError('PLUGIN_DEPENDENCY_MISSING', status.reason ?? 'A compatible agent-device provider is unavailable.', {
411
+ retryable: false,
412
+ details: status,
413
+ });
414
+ }
415
+ return status;
416
+ }
417
+
418
+ function capabilityProfile(repoRoot?: string): AgentDeviceCapabilityProfile {
419
+ return requireDependency(repoRoot).capabilityProfile;
295
420
  }
296
421
 
297
422
  function controllerRoot(input: AssistantPluginActionExecutionInput): string {
@@ -308,6 +433,37 @@ function stateDir(input: AssistantPluginActionExecutionInput, interactionId: str
308
433
  return path;
309
434
  }
310
435
 
436
+ function physicalDeviceRuntimeRoot(input: AssistantPluginActionExecutionInput, targetId: string): string {
437
+ const path = join(
438
+ controllerRoot(input),
439
+ 'interactions',
440
+ 'ios-agent-device',
441
+ 'device-runtime',
442
+ sanitize(targetId),
443
+ );
444
+ mkdirSync(path, { recursive: true });
445
+ return path;
446
+ }
447
+
448
+ function physicalDeviceRuntimeStateDir(input: AssistantPluginActionExecutionInput, targetId: string): string {
449
+ const path = join(physicalDeviceRuntimeRoot(input, targetId), 'state');
450
+ mkdirSync(path, { recursive: true });
451
+ return path;
452
+ }
453
+
454
+ function runtimeStateDir(input: AssistantPluginActionExecutionInput, record: InteractionSessionRecord): string {
455
+ // A real iPhone must not get a fresh daemon/Runner cache for every logical
456
+ // interaction. One stable per-device runtime owns provider metadata and any
457
+ // explicitly prepared semantic Runner. Simulator sessions remain isolated.
458
+ return record.provider === DEVICE_PROVIDER
459
+ ? physicalDeviceRuntimeStateDir(input, record.targetId)
460
+ : stateDir(input, record.interactionId);
461
+ }
462
+
463
+ function targetRuntimeStateDir(input: AssistantPluginActionExecutionInput, target: AgentDeviceEntry): string | undefined {
464
+ return target.kind === 'device' ? physicalDeviceRuntimeStateDir(input, target.id) : undefined;
465
+ }
466
+
311
467
  function signingConfigPath(input: AssistantPluginActionExecutionInput, interactionId: string): string {
312
468
  return join(interactionRoot(input, interactionId), 'signing.json');
313
469
  }
@@ -344,27 +500,31 @@ function artifactDir(input: AssistantPluginActionExecutionInput, interactionId:
344
500
 
345
501
  function sessionEnv(input: AssistantPluginActionExecutionInput, record: InteractionSessionRecord): NodeJS.ProcessEnv {
346
502
  const config = readSigningConfig(input, record.interactionId);
347
- return {
503
+ return withTrustedNodePath({
348
504
  ...process.env,
349
505
  ...signingEnv(config),
350
- AGENT_DEVICE_STATE_DIR: stateDir(input, record.interactionId),
506
+ AGENT_DEVICE_STATE_DIR: runtimeStateDir(input, record),
351
507
  AGENT_DEVICE_SESSION: record.sessionId,
352
508
  AGENT_DEVICE_PLATFORM: 'ios',
353
509
  AGENT_DEVICE_SESSION_LOCK: 'reject',
354
- // Keep the per-interaction daemon and a healthy XCTest runner warm between
355
- // commands. The previous five-second/zero-retention policy forced repeated
356
- // cold starts during ordinary multi-step device workflows.
510
+ // Keep the daemon and an explicitly prepared Runner warm. Physical devices
511
+ // share one stable runtime state directory across logical interactions;
512
+ // Simulator interactions remain isolated.
357
513
  AGENT_DEVICE_DAEMON_IDLE_TIMEOUT_MS:
358
514
  process.env.FORGE_AGENT_DEVICE_DAEMON_IDLE_TIMEOUT_MS?.trim() || DEFAULT_AGENT_DEVICE_IDLE_MS,
359
515
  AGENT_DEVICE_IOS_RUNNER_IDLE_STOP_MS:
360
516
  process.env.FORGE_AGENT_DEVICE_IOS_RUNNER_IDLE_STOP_MS?.trim() || DEFAULT_AGENT_DEVICE_IDLE_MS,
361
- };
517
+ });
362
518
  }
363
519
 
364
- function probeEnv(input: AssistantPluginActionExecutionInput, config?: AgentDeviceSigningConfig): NodeJS.ProcessEnv {
365
- const path = join(controllerRoot(input), 'interactions', 'ios-agent-device', 'probe-state');
520
+ function probeEnv(
521
+ input: AssistantPluginActionExecutionInput,
522
+ config?: AgentDeviceSigningConfig,
523
+ requestedStateDir?: string,
524
+ ): NodeJS.ProcessEnv {
525
+ const path = requestedStateDir ?? join(controllerRoot(input), 'interactions', 'ios-agent-device', 'probe-state');
366
526
  mkdirSync(path, { recursive: true });
367
- return {
527
+ return withTrustedNodePath({
368
528
  ...process.env,
369
529
  ...signingEnv(config),
370
530
  AGENT_DEVICE_STATE_DIR: path,
@@ -373,7 +533,7 @@ function probeEnv(input: AssistantPluginActionExecutionInput, config?: AgentDevi
373
533
  process.env.FORGE_AGENT_DEVICE_DAEMON_IDLE_TIMEOUT_MS?.trim() || DEFAULT_AGENT_DEVICE_IDLE_MS,
374
534
  AGENT_DEVICE_IOS_RUNNER_IDLE_STOP_MS:
375
535
  process.env.FORGE_AGENT_DEVICE_IOS_RUNNER_IDLE_STOP_MS?.trim() || DEFAULT_AGENT_DEVICE_IDLE_MS,
376
- };
536
+ });
377
537
  }
378
538
 
379
539
  function bounded(value: unknown): unknown {
@@ -488,14 +648,15 @@ function runJson(
488
648
  options: {
489
649
  record?: InteractionSessionRecord;
490
650
  signing?: AgentDeviceSigningConfig;
651
+ stateDir?: string;
491
652
  timeoutMs?: number;
492
653
  failureCode: string;
493
654
  },
494
655
  ): Record<string, unknown> {
495
- const result = hooks.runCommand(executable(), args, {
656
+ const result = hooks.runCommand(executable(input.repoRoot), args, {
496
657
  cwd: input.repoRoot,
497
658
  timeoutMs: effectiveCommandTimeout(input, options.timeoutMs),
498
- env: options.record ? sessionEnv(input, options.record) : probeEnv(input, options.signing),
659
+ env: options.record ? sessionEnv(input, options.record) : probeEnv(input, options.signing, options.stateDir),
499
660
  signal: input.signal,
500
661
  });
501
662
  return parseJsonResult(result, options.failureCode);
@@ -507,14 +668,15 @@ async function runJsonAsync(
507
668
  options: {
508
669
  record?: InteractionSessionRecord;
509
670
  signing?: AgentDeviceSigningConfig;
671
+ stateDir?: string;
510
672
  timeoutMs?: number;
511
673
  failureCode: string;
512
674
  },
513
675
  ): Promise<Record<string, unknown>> {
514
- const result = await hooks.runCommandAsync(executable(), args, {
676
+ const result = await hooks.runCommandAsync(executable(input.repoRoot), args, {
515
677
  cwd: input.repoRoot,
516
678
  timeoutMs: effectiveCommandTimeout(input, options.timeoutMs),
517
- env: options.record ? sessionEnv(input, options.record) : probeEnv(input, options.signing),
679
+ env: options.record ? sessionEnv(input, options.record) : probeEnv(input, options.signing, options.stateDir),
518
680
  signal: input.signal,
519
681
  });
520
682
  return parseJsonResult(result, options.failureCode);
@@ -696,7 +858,7 @@ function providerSessionAlreadyAbsent(result: CommandResult): boolean {
696
858
  }
697
859
 
698
860
  function bestEffortClose(input: AssistantPluginActionExecutionInput, record: InteractionSessionRecord): boolean {
699
- const result = hooks.runCommand(executable(), ['close', '--session', record.sessionId, '--platform', 'ios', '--json'], {
861
+ const result = hooks.runCommand(executable(input.repoRoot), ['close', '--session', record.sessionId, '--platform', 'ios', '--json'], {
700
862
  cwd: input.repoRoot,
701
863
  timeoutMs: 30_000,
702
864
  env: sessionEnv(input, record),
@@ -716,7 +878,7 @@ async function closeProviderSession(
716
878
  record: InteractionSessionRecord,
717
879
  args: string[],
718
880
  ): Promise<{ result: Record<string, unknown>; providerAlreadyAbsent: boolean }> {
719
- const commandResult = await hooks.runCommandAsync(executable(), [
881
+ const commandResult = await hooks.runCommandAsync(executable(input.repoRoot), [
720
882
  ...args,
721
883
  '--session', record.sessionId,
722
884
  '--platform', 'ios',
@@ -985,8 +1147,8 @@ async function executeSessionSnapshotBackend(
985
1147
  let fallbackTypedVersion: string | undefined;
986
1148
  let fallbackCliVersion: string | undefined;
987
1149
  if (mode !== 'cli') {
988
- const dependency = requireDependency();
989
- const typed = new TypedAgentDeviceReadProvider();
1150
+ const dependency = requireDependency(input.repoRoot);
1151
+ const typed = new TypedAgentDeviceReadProvider(typedAgentDeviceIdentity({ repoRoot: input.repoRoot }));
990
1152
  const versionsMatch = agentDeviceProviderVersionsMatch(
991
1153
  typed.identity.version,
992
1154
  dependency.detectedVersion,
@@ -1013,9 +1175,8 @@ async function executeSessionSnapshotBackend(
1013
1175
  } else if (typed.identity.available || mode === 'typed') {
1014
1176
  try {
1015
1177
  return await typed.snapshot({
1016
- stateDir: stateDir(input, record.interactionId),
1178
+ stateDir: runtimeStateDir(input, record),
1017
1179
  session: record.sessionId,
1018
- device: record.targetId,
1019
1180
  platform: 'ios',
1020
1181
  requestId: input.requestId,
1021
1182
  cwd: input.repoRoot,
@@ -1052,7 +1213,7 @@ async function executeSessionSnapshotBackend(
1052
1213
  }
1053
1214
  }
1054
1215
 
1055
- const args = compileSnapshotCommand(capabilityProfile(), request);
1216
+ const args = compileSnapshotCommand(capabilityProfile(input.repoRoot), request);
1056
1217
  const result = await runJsonAsync(
1057
1218
  input,
1058
1219
  [...args, '--session', record.sessionId, '--platform', 'ios', '--json'],
@@ -1378,7 +1539,7 @@ async function runSessionBatch(
1378
1539
  let result: unknown = await runSessionCommand(
1379
1540
  input,
1380
1541
  record,
1381
- compileBatchCommand(capabilityProfile(), prepared.nativeSteps, MAX_BATCH_STEPS, { includeCost: true }),
1542
+ compileBatchCommand(capabilityProfile(input.repoRoot), prepared.nativeSteps, MAX_BATCH_STEPS, { includeCost: true }),
1382
1543
  'AGENT_DEVICE_BATCH_FAILED',
1383
1544
  timeoutMs,
1384
1545
  );
@@ -1395,7 +1556,7 @@ async function runSessionBatchAttempt(
1395
1556
  let result: unknown = await runSessionCommandAttempt(
1396
1557
  input,
1397
1558
  record,
1398
- compileBatchCommand(capabilityProfile(), prepared.nativeSteps, MAX_BATCH_STEPS, { includeCost: true }),
1559
+ compileBatchCommand(capabilityProfile(input.repoRoot), prepared.nativeSteps, MAX_BATCH_STEPS, { includeCost: true }),
1399
1560
  'AGENT_DEVICE_BATCH_FAILED',
1400
1561
  timeoutMs,
1401
1562
  );
@@ -1817,7 +1978,7 @@ async function executeJdSearch(input: AssistantPluginActionExecutionInput): Prom
1817
1978
  // The current batch schema supports only status/dismiss, so Return stays
1818
1979
  // a separate provider command, but only when the negotiated contract
1819
1980
  // explicitly advertises it.
1820
- if (!capabilityProfile().keyboard.returnSupported) {
1981
+ if (!capabilityProfile(input.repoRoot).keyboard.returnSupported) {
1821
1982
  return failWorkflowSession(input, record, new AssistantPluginError(
1822
1983
  'PLUGIN_ACTION_NOT_SUPPORTED',
1823
1984
  'The detected agent-device contract does not support keyboard Return.',
@@ -1921,7 +2082,7 @@ export function iosAgentDeviceCapabilities(): AssistantPluginCapability[] {
1921
2082
  {
1922
2083
  capabilityId: 'ios-agent-device-physical',
1923
2084
  title: 'agent-device physical iPhone',
1924
- description: 'Optional signed, capability-negotiated agent-device XCTest sessions for one exact connected physical iPhone.',
2085
+ description: 'Opt-in XCTest accessibility fallback for one exact physical iPhone. CoreDevice/RemoteXPC is the default physical-device path. Do not use agent-device for ordinary profile/form editing, Unicode text entry, screenshots, or known-coordinate interaction; use the physical-device provider and its batch fast path instead. Prepare XCTest only when semantic AX inspection is genuinely required.',
1925
2086
  scopes: ['ios.discover', 'ios.device'],
1926
2087
  actions,
1927
2088
  },
@@ -1935,7 +2096,7 @@ export function iosAgentDeviceActions(): AssistantPluginActionDescriptor[] {
1935
2096
  { resource: 'repo-state' as const, mode: 'write' as const },
1936
2097
  ];
1937
2098
  const interactionProperty = { interaction_id: { type: 'string' } };
1938
- return [
2099
+ const actions: AssistantPluginActionDescriptor[] = [
1939
2100
  {
1940
2101
  actionId: 'agent_device_status', title: 'agent-device status',
1941
2102
  description: 'Resolve the exact executable and verify a reviewed agent-device command contract.',
@@ -1950,8 +2111,8 @@ export function iosAgentDeviceActions(): AssistantPluginActionDescriptor[] {
1950
2111
  argumentsSchema: { type: 'object', properties: { app: { type: 'string' }, device: { type: 'string' }, team_id: { type: 'string' }, runner_bundle_id: { type: 'string' }, developer_dir: { type: 'string' } }, additionalProperties: false },
1951
2112
  },
1952
2113
  {
1953
- actionId: 'agent_device_prepare', title: 'Prepare signed iOS Runner',
1954
- description: 'Build, sign, install and health-check the agent-device XCTest Runner for one exact iOS target.',
2114
+ actionId: 'agent_device_prepare', title: 'Prepare semantic XCTest fallback',
2115
+ description: 'Explicitly build, sign, install and health-check the agent-device XCTest Runner. This is a heavy semantic fallback, not the normal physical-iPhone form-edit path. Physical-device preparation uses one stable per-device runtime so repeated semantic interactions can reuse it.',
1955
2116
  readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 10 * 60_000, cancellable: true, idempotent: false,
1956
2117
  scopes: ['ios.discover', 'ios.simulator', 'ios.device'], resourceClaims: write,
1957
2118
  argumentsSchema: {
@@ -1961,7 +2122,7 @@ export function iosAgentDeviceActions(): AssistantPluginActionDescriptor[] {
1961
2122
  },
1962
2123
  {
1963
2124
  actionId: 'agent_device_open', title: 'Open agent-device iOS session',
1964
- description: 'Open an app on one exact connected physical iPhone or already-booted iOS Simulator.',
2125
+ description: 'Open an agent-device semantic session on one exact physical iPhone or booted Simulator. On physical iPhone this may start/reconnect an XCTest Runner; prefer physical_device_open plus physical_device_batch for ordinary app navigation and profile/form edits.',
1965
2126
  readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 4 * 60_000, cancellable: true, idempotent: false,
1966
2127
  scopes: ['ios.simulator', 'ios.device'], resourceClaims: write,
1967
2128
  argumentsSchema: {
@@ -1971,7 +2132,7 @@ export function iosAgentDeviceActions(): AssistantPluginActionDescriptor[] {
1971
2132
  },
1972
2133
  {
1973
2134
  actionId: 'agent_device_batch', title: 'Run fast typed iOS action batch',
1974
- description: `Run up to ${MAX_BATCH_STEPS} allowlisted session steps in one agent-device process and one daemon request. Mutating steps always settle before the next step.`,
2135
+ description: `Run up to ${MAX_BATCH_STEPS} semantic AX steps in one agent-device process and one daemon request. Mutating steps settle before the next step. On a physical iPhone, use this only when accessibility semantics are required; known-coordinate/form workflows should use physical_device_batch to avoid XCTest lifecycle cost.`,
1975
2136
  readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 2 * 60_000, cancellable: true, idempotent: false,
1976
2137
  scopes: ['ios.simulator', 'ios.device'], resourceClaims: write,
1977
2138
  argumentsSchema: {
@@ -2023,7 +2184,7 @@ export function iosAgentDeviceActions(): AssistantPluginActionDescriptor[] {
2023
2184
  },
2024
2185
  {
2025
2186
  actionId: 'agent_device_press', title: 'Press agent-device target',
2026
- description: 'Press one ref, selector, or explicit coordinate pair and return a settled bounded diff.',
2187
+ description: 'Press one ref, selector, or explicit coordinate pair. Semantic targets return a settled bounded diff; explicit coordinates bypass accessibility settle for heavy-screen recovery.',
2027
2188
  readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 60_000, cancellable: true, idempotent: false,
2028
2189
  scopes: ['ios.simulator', 'ios.device'], resourceClaims: write,
2029
2190
  argumentsSchema: { type: 'object', properties: { ...interactionProperty, target: { type: 'string' }, x: { type: 'number' }, y: { type: 'number' } }, required: ['interaction_id'], additionalProperties: false },
@@ -2064,13 +2225,16 @@ export function iosAgentDeviceActions(): AssistantPluginActionDescriptor[] {
2064
2225
  argumentsSchema: { type: 'object', properties: { ...interactionProperty, shutdown_simulator: { type: 'boolean' } }, required: ['interaction_id'], additionalProperties: false },
2065
2226
  },
2066
2227
  ];
2228
+ return actions.map((action) => action.readOnly
2229
+ ? action
2230
+ : { ...action, executionMode: 'lightweight_process' });
2067
2231
  }
2068
2232
 
2069
2233
  export async function executeIosAgentDeviceAction(input: AssistantPluginActionExecutionInput): Promise<Record<string, unknown>> {
2070
2234
  if (input.deadlineAtMs === undefined && typeof input.timeoutMs === 'number' && Number.isFinite(input.timeoutMs)) {
2071
2235
  input = { ...input, deadlineAtMs: Date.now() + Math.max(1, Math.trunc(input.timeoutMs)) };
2072
2236
  }
2073
- if (input.actionId === 'agent_device_status') return { provider: 'agent-device', ...iosAgentDeviceStatus() };
2237
+ if (input.actionId === 'agent_device_status') return { provider: 'agent-device', ...await iosAgentDeviceActionStatus(input) };
2074
2238
  if (input.actionId === 'agent_device_close') {
2075
2239
  const interactionId = requireString(input.args.interaction_id, 'interaction_id');
2076
2240
  const existing = readAgentDeviceInteraction(input.repoRoot, interactionId);
@@ -2078,7 +2242,7 @@ export async function executeIosAgentDeviceAction(input: AssistantPluginActionEx
2078
2242
  return { provider: 'agent-device', interaction: existing, alreadyClosed: true };
2079
2243
  }
2080
2244
  }
2081
- const dependency = requireDependency();
2245
+ const dependency = await requireDependencyAsync(input);
2082
2246
 
2083
2247
  if (input.actionId === 'agent_device_jd_search') return executeJdSearch(input);
2084
2248
 
@@ -2096,6 +2260,7 @@ export async function executeIosAgentDeviceAction(input: AssistantPluginActionEx
2096
2260
  physicalDeviceSupported: selected.kind === 'device',
2097
2261
  result: bounded(await runJsonAsync(input, args, {
2098
2262
  signing: signingFromArgs(input.args),
2263
+ stateDir: targetRuntimeStateDir(input, selected),
2099
2264
  failureCode: 'AGENT_DEVICE_DOCTOR_FAILED',
2100
2265
  timeoutMs: 4 * 60_000,
2101
2266
  })),
@@ -2115,6 +2280,7 @@ export async function executeIosAgentDeviceAction(input: AssistantPluginActionEx
2115
2280
  'prepare', 'ios-runner', '--platform', 'ios', '--device', selected.name, '--timeout', '600000', '--json',
2116
2281
  ], {
2117
2282
  signing,
2283
+ stateDir: targetRuntimeStateDir(input, selected),
2118
2284
  failureCode: 'AGENT_DEVICE_PREPARE_FAILED',
2119
2285
  timeoutMs: 10 * 60_000,
2120
2286
  })),
@@ -2223,11 +2389,15 @@ export async function executeIosAgentDeviceAction(input: AssistantPluginActionEx
2223
2389
  if (record.provider === DEVICE_PROVIDER && target && SENSITIVE_SEMANTICS.test(target)) {
2224
2390
  throw new AssistantPluginError('IOS_DEVICE_SENSITIVE_ACTION_BLOCKED', 'Press targets involving credentials, verification, biometrics, checkout, purchase or payment require human interaction.', { retryable: false });
2225
2391
  }
2226
- const args = appendSettleFlag(
2227
- capabilityProfile(),
2228
- ['press', ...(target ? [target] : [String(input.args.x), String(input.args.y)])],
2229
- 'press',
2230
- );
2392
+ const baseArgs = ['press', ...(target ? [target] : [String(input.args.x), String(input.args.y)])];
2393
+ // Explicit coordinate presses are the bounded fallback for screens whose
2394
+ // accessibility tree is too heavy or animated to snapshot reliably.
2395
+ // Settling forces an AX snapshot after the mutation, defeating that
2396
+ // fallback and can recycle/wedge the XCTest runner. Keep semantic target
2397
+ // presses settled, but let explicit points complete without AX capture.
2398
+ const args = target
2399
+ ? appendSettleFlag(capabilityProfile(input.repoRoot), baseArgs, 'press')
2400
+ : baseArgs;
2231
2401
  return { provider: 'agent-device', interaction: record, result: bounded(await runSessionCommand(input, record, args, 'AGENT_DEVICE_PRESS_FAILED')) };
2232
2402
  }
2233
2403
  case 'agent_device_fill': {
@@ -2237,7 +2407,7 @@ export async function executeIosAgentDeviceAction(input: AssistantPluginActionEx
2237
2407
  if (record.provider === DEVICE_PROVIDER && (SENSITIVE_SEMANTICS.test(target) || SENSITIVE_SEMANTICS.test(text))) {
2238
2408
  throw new AssistantPluginError('IOS_DEVICE_SENSITIVE_ACTION_BLOCKED', 'Sensitive text and credential, verification, checkout, purchase or payment targets require human interaction.', { retryable: false });
2239
2409
  }
2240
- const profile = capabilityProfile();
2410
+ const profile = capabilityProfile(input.repoRoot);
2241
2411
  const args = appendSettleFlag(profile, ['fill', target, text]);
2242
2412
  if (typeof input.args.delay_ms === 'number') {
2243
2413
  if (!profile.fill.delayFlag) {