@moretea-labs/forge 1.6.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/AGENTS.md +4 -0
  2. package/CHANGELOG.md +39 -0
  3. package/CLAUDE.md +4 -0
  4. package/README.md +2 -2
  5. package/README.zh-CN.md +1 -1
  6. package/SKILL.md +2 -2
  7. package/THIRD_PARTY_NOTICES.md +1 -1
  8. package/agents/openai.yaml +1 -1
  9. package/assets/hooks/hook-input.sh +11 -8
  10. package/assets/hooks/lib/session-state.sh +10 -7
  11. package/assets/hooks/lib/workflow-state.sh +13 -177
  12. package/assets/hooks/post-tool-observer.sh +8 -2
  13. package/assets/hooks/prompt-guard.sh +5 -81
  14. package/assets/hooks/session-start-context.sh +56 -1
  15. package/assets/partials/05-workflow.partial.md +1 -1
  16. package/assets/partials-agents/02-operating-mode.partial.md +2 -0
  17. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  18. package/assets/plugin-registry.v1.json +3 -3
  19. package/assets/reference-configs/document-generation.md +1 -1
  20. package/assets/reference-configs/harness-overview.md +1 -1
  21. package/assets/reference-configs/sprint-contracts.md +3 -3
  22. package/assets/skill-version.json +2 -2
  23. package/assets/templates/contract.template.md +2 -7
  24. package/assets/templates/helpers/capture-plan.sh +2 -2
  25. package/assets/templates/helpers/check-architecture-sync.sh +17 -0
  26. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  27. package/assets/templates/helpers/contract-worktree.sh +1 -21
  28. package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
  29. package/assets/templates/helpers/inspect-project-state.ts +8 -6
  30. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  31. package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
  32. package/assets/templates/helpers/new-plan.sh +1 -1
  33. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  34. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  35. package/assets/templates/helpers/verify-contract.sh +1 -111
  36. package/assets/templates/helpers/verify-sprint.sh +3 -180
  37. package/assets/templates/helpers/workflow-contract.ts +5 -1
  38. package/assets/templates/plan.template.md +1 -1
  39. package/assets/templates/review.template.md +9 -34
  40. package/assets/workflow-contract.v1.json +10 -19
  41. package/docs/README.md +10 -10
  42. package/docs/architecture/CURRENT.md +178 -0
  43. package/docs/architecture/EVOLUTION.md +42 -0
  44. package/docs/architecture/current/README.md +4 -156
  45. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  46. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  47. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  48. package/docs/architecture/history/README.md +7 -0
  49. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  50. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  51. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  52. package/docs/architecture/history.md +3 -34
  53. package/docs/architecture/index.md +15 -98
  54. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  55. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  56. package/docs/architecture/versions/1.6.md +38 -0
  57. package/docs/architecture/versions/1.7.md +41 -0
  58. package/docs/forge-chatgpt-browser-engine.md +2 -2
  59. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  60. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  61. package/docs/forge-github-issue-launcher.md +1 -1
  62. package/docs/forge-plugin-management.md +1 -1
  63. package/docs/forge-runtime-self-healing-loop.md +1 -1
  64. package/docs/operations/controller-browser-plugin.md +26 -6
  65. package/docs/operations/controller-performance-and-502.md +143 -0
  66. package/docs/operations/controller-reliability-runbook.md +1 -1
  67. package/docs/operations/features.md +2 -2
  68. package/docs/operations/github-repository.md +1 -1
  69. package/docs/operations/homebrew.md +2 -2
  70. package/docs/operations/mcp-tool-exposure.md +5 -3
  71. package/docs/wiki/Architecture.md +6 -6
  72. package/docs/wiki/Implementation.md +2 -2
  73. package/docs/wiki/Integrations.md +2 -2
  74. package/docs/wiki/Work-Lifecycle.md +1 -1
  75. package/package.json +33 -11
  76. package/scripts/activate-source-baseline.ts +17 -13
  77. package/scripts/architecture-event.ts +2 -820
  78. package/scripts/architecture-queue.sh +2 -645
  79. package/scripts/archive-architecture-request.sh +2 -242
  80. package/scripts/archive-workflow.sh +1 -291
  81. package/scripts/benchmark-background-check-overlap.ts +228 -0
  82. package/scripts/benchmark-mcp-host.sh +117 -0
  83. package/scripts/benchmark-mcp-transport.ts +170 -0
  84. package/scripts/benchmark-quality-harness.ts +146 -0
  85. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  86. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  87. package/scripts/benchmark-typescript-navigation.ts +186 -0
  88. package/scripts/benchmark-work-efficiency.ts +334 -0
  89. package/scripts/capability-config.ts +2 -392
  90. package/scripts/capability-resolver.ts +2 -503
  91. package/scripts/capture-plan.sh +1 -332
  92. package/scripts/check-agent-tooling.sh +2 -1558
  93. package/scripts/check-architecture-sync.sh +2 -371
  94. package/scripts/check-brain-manifest.sh +2 -253
  95. package/scripts/check-context-files.sh +2 -47
  96. package/scripts/check-deploy-sql-order.sh +2 -115
  97. package/scripts/check-mcp-compatibility.ts +14 -0
  98. package/scripts/check-runtime-architecture.mjs +349 -79
  99. package/scripts/check-skill-version.ts +3 -191
  100. package/scripts/check-source-duplication.mjs +61 -0
  101. package/scripts/check-stable-baseline.ts +92 -0
  102. package/scripts/check-task-sync.sh +2 -63
  103. package/scripts/check-task-workflow.sh +2 -1000
  104. package/scripts/codex-handoff-resume.sh +2 -267
  105. package/scripts/context-contract-sync.sh +2 -514
  106. package/scripts/contract-run.ts +2 -463
  107. package/scripts/contract-worktree.sh +1 -722
  108. package/scripts/ensure-task-workflow.sh +1 -1225
  109. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  110. package/scripts/inspect-project-state.ts +4 -279
  111. package/scripts/install-standalone-recovery.ts +24 -0
  112. package/scripts/lib/project-init-lib.sh +22 -46
  113. package/scripts/lib/run-forge-helper.sh +19 -0
  114. package/scripts/lib/run-forge-helper.ts +26 -0
  115. package/scripts/migrate-project-template.sh +1 -1189
  116. package/scripts/migrate-workflow-docs.ts +4 -412
  117. package/scripts/new-plan.sh +1 -180
  118. package/scripts/new-spec.sh +1 -47
  119. package/scripts/new-sprint.sh +1 -46
  120. package/scripts/plan-to-todo.sh +1 -796
  121. package/scripts/prepare-codex-handoff.sh +2 -201
  122. package/scripts/prepare-handoff.sh +1 -78
  123. package/scripts/refresh-current-status.sh +2 -467
  124. package/scripts/run-governed-gate.ts +5 -1
  125. package/scripts/select-agent-context-blocks.sh +2 -64
  126. package/scripts/ship-worktrees.sh +1 -558
  127. package/scripts/smoke-ios-physical-device.ts +4 -2
  128. package/scripts/smoke-runtime-recovery.ts +1 -35
  129. package/scripts/smoke-schedule-engine.ts +1 -20
  130. package/scripts/sprint-backlog.sh +1 -834
  131. package/scripts/stage-runtime-release.ts +2 -1
  132. package/scripts/summarize-failures.sh +2 -123
  133. package/scripts/switch-plan.sh +1 -150
  134. package/scripts/sync-brain-docs.sh +2 -379
  135. package/scripts/sync-generated-authority.mjs +175 -0
  136. package/scripts/verify-contract.sh +2 -703
  137. package/scripts/verify-forge-runtime.sh +0 -1
  138. package/scripts/verify-sprint.sh +1 -677
  139. package/scripts/workflow-contract.ts +4 -205
  140. package/scripts/workstream-sync.sh +2 -261
  141. package/src/cli/agent-jobs/executor-health.ts +1 -1
  142. package/src/cli/commands/init-hook.ts +3 -3
  143. package/src/cli/commands/mcp.ts +9 -5
  144. package/src/cli/commands/plugin.ts +12 -3
  145. package/src/cli/commands/recovery.ts +25 -3
  146. package/src/cli/commands/runtime.ts +55 -3
  147. package/src/cli/commands/security.ts +12 -1
  148. package/src/cli/commands/setup-profile.ts +31 -21
  149. package/src/cli/controller/check-runner.ts +113 -5
  150. package/src/cli/controller/composite-result.ts +1 -1
  151. package/src/cli/controller/context/focus.ts +51 -0
  152. package/src/cli/controller/context/instruction-context.ts +114 -0
  153. package/src/cli/controller/context/known-paths.ts +76 -0
  154. package/src/cli/controller/context/query-planning.ts +110 -0
  155. package/src/cli/controller/context/rule-resolution.ts +532 -0
  156. package/src/cli/controller/context/source-materializer.ts +278 -0
  157. package/src/cli/controller/context/structural.ts +16 -0
  158. package/src/cli/controller/context/types.ts +286 -0
  159. package/src/cli/controller/context-pack.ts +828 -487
  160. package/src/cli/controller/launch-agents.ts +77 -2
  161. package/src/cli/controller/operational-plan.ts +2 -2
  162. package/src/cli/controller/work-mode.ts +6 -4
  163. package/src/cli/editing/edit-session.ts +60 -7
  164. package/src/cli/github/contracts.ts +16 -0
  165. package/src/cli/github/github.ts +135 -16
  166. package/src/cli/github/session.ts +2 -1
  167. package/src/cli/installer/managed-entries.ts +15 -1
  168. package/src/cli/local-bridge/console-automations.ts +54 -78
  169. package/src/cli/local-bridge/console-view-models.ts +1 -35
  170. package/src/cli/local-bridge/facade-api.ts +37 -264
  171. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  172. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
  173. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  174. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
  175. package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
  176. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  177. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
  178. package/src/cli/local-bridge/frontend/src/styles.css +1 -1
  179. package/src/cli/local-bridge/frontend/src/types.ts +5 -8
  180. package/src/cli/local-bridge/job-store.ts +1 -490
  181. package/src/cli/local-bridge/server.ts +2 -515
  182. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  183. package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
  184. package/src/cli/mcp/auth.ts +5 -5
  185. package/src/cli/mcp/instructions.ts +3 -2
  186. package/src/cli/mcp/legacy-tool-service.ts +10 -222
  187. package/src/cli/mcp/multi-repository.ts +16 -5
  188. package/src/cli/mcp/oauth.ts +175 -26
  189. package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
  190. package/src/cli/mcp/repository-tools.ts +282 -146
  191. package/src/cli/mcp/server.ts +586 -116
  192. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  193. package/src/cli/mcp/tool-contract.ts +14 -0
  194. package/src/cli/mcp/toolset-selection.ts +1 -1
  195. package/src/cli/mcp/toolset.ts +44 -7
  196. package/src/cli/mcp/transports/http.ts +124 -14
  197. package/src/cli/mcp/transports/session-registry.ts +43 -5
  198. package/src/cli/mcp/types.ts +3 -3
  199. package/src/cli/repositories/command-classifier.ts +103 -21
  200. package/src/cli/repositories/command-executor.ts +78 -526
  201. package/src/cli/repositories/command-process.ts +108 -0
  202. package/src/cli/repositories/command-scope.ts +99 -41
  203. package/src/cli/repositories/controller-home.ts +7 -8
  204. package/src/cli/repositories/git-command-executor.ts +2 -0
  205. package/src/cli/repositories/locks.ts +53 -16
  206. package/src/cli/repositories/repository-snapshot.ts +124 -0
  207. package/src/cli/repositories/selected-path-actions.ts +17 -9
  208. package/src/cli/repositories/structured-git.ts +118 -6
  209. package/src/cli/repository/inspector.ts +563 -65
  210. package/src/cli/repository/session-cache.ts +58 -17
  211. package/src/cli/v81-entry.ts +1 -4
  212. package/src/effects/fs-transaction.ts +85 -17
  213. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  214. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  215. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  216. package/src/runtime/context/lsp-client.ts +283 -0
  217. package/src/runtime/context/semantic-navigation-contract.ts +61 -0
  218. package/src/runtime/context/semantic-navigation.ts +223 -0
  219. package/src/runtime/context/swift-navigation.ts +565 -0
  220. package/src/runtime/context/typescript-navigation.ts +226 -0
  221. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  222. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
  223. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  224. package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
  225. package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
  226. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  227. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  228. package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
  229. package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
  230. package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
  231. package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
  232. package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
  233. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  234. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  235. package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
  236. package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
  237. package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
  238. package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
  239. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  240. package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
  241. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
  242. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
  243. package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
  244. package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
  245. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
  246. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  247. package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
  248. package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
  249. package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
  250. package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
  251. package/src/runtime/control-plane/facade/index.ts +3 -0
  252. package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
  253. package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
  254. package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
  255. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  256. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  257. package/src/runtime/control-plane/facade/types.ts +90 -10
  258. package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
  259. package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
  260. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  261. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  262. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  263. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  264. package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
  265. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  266. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  267. package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
  268. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  269. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  270. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  271. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  272. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  273. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  274. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  275. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  276. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  277. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  278. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
  279. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
  280. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
  281. package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
  282. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  283. package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
  284. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  285. package/src/runtime/control-plane/release-retention.ts +62 -2
  286. package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
  287. package/src/runtime/control-plane/routing/route-policy.ts +24 -21
  288. package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
  289. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  290. package/src/runtime/diagnostics/mcp-timing.ts +13 -0
  291. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  292. package/src/runtime/diagnostics/performance.ts +24 -1
  293. package/src/runtime/evidence/event-ledger.ts +1 -1
  294. package/src/runtime/evidence/process-check-execution.ts +18 -0
  295. package/src/runtime/execution/jobs/child-reference.ts +6 -2
  296. package/src/runtime/execution/jobs/store.ts +58 -4
  297. package/src/runtime/execution/jobs/types.ts +1 -1
  298. package/src/runtime/execution/managed-workspace.ts +203 -1
  299. package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
  300. package/src/runtime/execution/process-runtime/check-result.ts +88 -0
  301. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
  302. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  303. package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
  304. package/src/runtime/execution/process-runtime/gc.ts +38 -6
  305. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
  306. package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
  307. package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
  308. package/src/runtime/execution/process-runtime/runtime.ts +53 -3
  309. package/src/runtime/execution/process-runtime/store.ts +1 -0
  310. package/src/runtime/execution/process-runtime/types.ts +4 -12
  311. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  312. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  313. package/src/runtime/execution/work-activity.ts +21 -0
  314. package/src/runtime/execution/workers/executor.ts +18 -61
  315. package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
  316. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
  317. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  318. package/src/runtime/gateway/mcp/router.ts +186 -84
  319. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
  320. package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
  321. package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
  322. package/src/runtime/plugins/browser-adapter.ts +2662 -829
  323. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  324. package/src/runtime/plugins/browser-handoff-host.ts +2 -25
  325. package/src/runtime/plugins/browser-handoff.ts +110 -5
  326. package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
  327. package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
  328. package/src/runtime/plugins/browser-native-session-service.ts +222 -0
  329. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  330. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  331. package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
  332. package/src/runtime/plugins/browser-runtime.ts +193 -0
  333. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  334. package/src/runtime/plugins/browser-session-store.ts +121 -0
  335. package/src/runtime/plugins/browser-session-types.ts +98 -0
  336. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  337. package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
  338. package/src/runtime/plugins/external-adapter.ts +358 -3
  339. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  340. package/src/runtime/plugins/first-party-registry.ts +2 -0
  341. package/src/runtime/plugins/github-adapter.ts +125 -0
  342. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  343. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  344. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  345. package/src/runtime/plugins/ios-adapter.ts +106 -16
  346. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  347. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  348. package/src/runtime/plugins/lightweight-action.ts +153 -0
  349. package/src/runtime/plugins/local-system-adapter.ts +123 -6
  350. package/src/runtime/plugins/macos-capability-broker.ts +47 -9
  351. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  352. package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
  353. package/src/runtime/plugins/resend-adapter.ts +1 -1
  354. package/src/runtime/plugins/store.ts +330 -33
  355. package/src/runtime/plugins/types.ts +38 -3
  356. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  357. package/src/runtime/projections/git-status-sampler.ts +16 -16
  358. package/src/runtime/projections/materialized-view.ts +28 -10
  359. package/src/runtime/recovery/maintenance-executor.ts +426 -25
  360. package/src/runtime/root/mcp-transport.ts +180 -10
  361. package/src/runtime/root/package-connector-service.ts +230 -10
  362. package/src/runtime/root/package-runtime-release.ts +143 -17
  363. package/src/runtime/root/package-runtime-service.ts +461 -22
  364. package/src/runtime/root/release-manifest.ts +144 -18
  365. package/src/runtime/root/release-materialize.ts +125 -32
  366. package/src/runtime/root/release-store.ts +48 -11
  367. package/src/runtime/root/runtime.ts +23 -5
  368. package/src/runtime/root/service.ts +100 -29
  369. package/src/runtime/root/types.ts +12 -0
  370. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  371. package/src/runtime/shared/trusted-executable.ts +62 -0
  372. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  373. package/src/runtime/standalone-recovery/core.ts +1079 -240
  374. package/src/runtime/standalone-recovery/entry.ts +100 -24
  375. package/src/runtime/standalone-recovery/installer.ts +20 -4
  376. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  377. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  378. package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
  379. package/src/runtime/workflow/schedules/engine.ts +364 -202
  380. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  381. package/src/runtime/workflow/schedules/settlement.ts +64 -17
  382. package/src/runtime/workflow/schedules/store.ts +209 -19
  383. package/src/runtime/workflow/schedules/types.ts +2 -4
  384. package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
  385. package/src/testing/test-governance.ts +8 -40
  386. package/assets/reference-configs/heartbeat-triage.md +0 -57
  387. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  388. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  389. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  390. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  391. package/docs/architecture/current/architecture-invariants.md +0 -331
  392. package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
  393. package/docs/architecture/current/codegraph-context-provider.md +0 -238
  394. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  395. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  396. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
  397. package/docs/architecture/current/entity-model.md +0 -612
  398. package/docs/architecture/current/external-plugin-governance.md +0 -396
  399. package/docs/architecture/current/failure-recovery.md +0 -447
  400. package/docs/architecture/current/governance.md +0 -213
  401. package/docs/architecture/current/human-interaction-plane.md +0 -87
  402. package/docs/architecture/current/implementation-status.md +0 -128
  403. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  404. package/docs/architecture/current/legacy-governance.md +0 -58
  405. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  406. package/docs/architecture/current/migration-roadmap.md +0 -105
  407. package/docs/architecture/current/multi-repository-execution.md +0 -371
  408. package/docs/architecture/current/operations-runbook.md +0 -45
  409. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  410. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  411. package/docs/architecture/current/repository-access-modes.md +0 -102
  412. package/docs/architecture/current/route-policy.md +0 -15
  413. package/docs/architecture/current/route-session-concurrency.md +0 -85
  414. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  415. package/docs/architecture/current/runtime-directory-map.md +0 -49
  416. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  417. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  418. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  419. package/docs/architecture/current/system-overview.md +0 -169
  420. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  421. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  422. package/docs/architecture/current/thin-harness-v1.md +0 -321
  423. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  424. package/docs/forge-autonomous-goal-loop.md +0 -9
  425. package/docs/forge-provider-configuration.md +0 -7
  426. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
  427. package/docs/operations/gmail-assistant-routines.md +0 -186
  428. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  429. package/docs/operations/provider-configuration.md +0 -31
  430. package/scripts/harness-trace-grade.sh +0 -150
  431. package/scripts/heartbeat-triage.sh +0 -361
  432. package/scripts/maintenance-triage.sh +0 -167
  433. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  434. package/src/cli/repositories/goal-registry.ts +0 -239
  435. package/src/runtime/assistant/action-proposals.ts +0 -279
  436. package/src/runtime/assistant/intent.ts +0 -395
  437. package/src/runtime/assistant/model-provider.ts +0 -460
  438. package/src/runtime/assistant/openapi.ts +0 -223
  439. package/src/runtime/assistant/policy.ts +0 -89
  440. package/src/runtime/assistant/readiness.ts +0 -258
  441. package/src/runtime/assistant/routine-runtime.ts +0 -477
  442. package/src/runtime/assistant/schedule-binding.ts +0 -171
  443. package/src/runtime/assistant/standing-grants.ts +0 -375
  444. package/src/runtime/assistant/store.ts +0 -243
  445. package/src/runtime/assistant/types.ts +0 -135
  446. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  447. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  448. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  449. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  450. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  451. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  452. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  453. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  454. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  455. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  456. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  457. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  458. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  459. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  460. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  461. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  462. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  463. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  464. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  465. package/src/runtime/workflow/findings/store.ts +0 -204
  466. package/src/runtime/workflow/findings/types.ts +0 -58
  467. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  468. package/src/runtime/workflow/portfolio/store.ts +0 -185
  469. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -1,9 +1,11 @@
1
1
  import { createPrivateKey, sign } from 'crypto';
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
+ import { accessSync, constants as fsConstants, existsSync, mkdirSync, promises as fsPromises, readFileSync, statSync, writeFileSync } from 'fs';
3
3
  import { dirname, join } from 'path';
4
+ import { CONTROLLER_SCOPE_REPO_ID, controllerSystemRoot } from '../../cli/repositories/controller-home';
4
5
  import type {
5
6
  AssistantPluginActionDescriptor,
6
7
  AssistantPluginActionExecutionInput,
8
+ AssistantPluginBuildContext,
7
9
  AssistantPluginCapability,
8
10
  AssistantPluginHealth,
9
11
  AssistantPluginManifest,
@@ -17,6 +19,8 @@ const CONFIG_ROOT = '.forge/plugins';
17
19
  const LEGACY_CONFIG_ROOT = '.repo-harness/plugins';
18
20
  const API_BASE_URL = 'https://api.appstoreconnect.apple.com';
19
21
  const DEFAULT_TIMEOUT_MS = 60_000;
22
+ const PRIVATE_KEY_READ_MAX_ATTEMPTS = 4;
23
+ const PRIVATE_KEY_READ_RETRY_BASE_MS = 10;
20
24
 
21
25
  type AppStoreConnectProviderKind = 'mock' | 'app-store-connect-api';
22
26
 
@@ -45,6 +49,12 @@ interface AppStoreConnectAuthState {
45
49
  warnings: string[];
46
50
  }
47
51
 
52
+ export interface AppStoreConnectXcodeAuthenticationReference {
53
+ privateKeyPath: string;
54
+ keyId: string;
55
+ issuerId: string;
56
+ }
57
+
48
58
  function now(): string {
49
59
  return new Date().toISOString();
50
60
  }
@@ -70,6 +80,10 @@ function legacyConfigPath(repoRoot: string): string {
70
80
  return join(repoRoot, LEGACY_CONFIG_ROOT, 'app-store-connect.json');
71
81
  }
72
82
 
83
+ function globalConfigPath(controllerHome: string): string {
84
+ return join(controllerSystemRoot(controllerHome), 'plugins', 'profiles', 'app-store-connect.json');
85
+ }
86
+
73
87
  function normalizeConfig(raw: Partial<AppStoreConnectPluginConfig>): AppStoreConnectPluginConfig {
74
88
  return {
75
89
  schemaVersion: 1,
@@ -85,46 +99,139 @@ function normalizeConfig(raw: Partial<AppStoreConnectPluginConfig>): AppStoreCon
85
99
  };
86
100
  }
87
101
 
88
- function loadConfig(repoRoot: string): AppStoreConnectPluginConfig {
89
- // `.forge/plugins` is the current authority, but installations created before
90
- // the Forge config-root migration may still have the App Store Connect config
91
- // under `.repo-harness/plugins`. Read that legacy file only when the current
92
- // path is absent; the next explicit configure action writes the current path.
93
- const path = existsSync(configPath(repoRoot)) ? configPath(repoRoot) : legacyConfigPath(repoRoot);
94
- if (!existsSync(path)) return normalizeConfig({});
102
+ function readConfigFile(path: string): Partial<AppStoreConnectPluginConfig> | undefined {
103
+ if (!existsSync(path)) return undefined;
95
104
  try {
96
- return normalizeConfig(JSON.parse(readFileSync(path, 'utf-8')) as Partial<AppStoreConnectPluginConfig>);
105
+ const parsed = JSON.parse(readFileSync(path, 'utf-8')) as Partial<AppStoreConnectPluginConfig>;
106
+ return parsed && typeof parsed === 'object' ? parsed : undefined;
97
107
  } catch {
98
- return normalizeConfig({});
108
+ return undefined;
99
109
  }
100
110
  }
101
111
 
102
- function saveConfig(repoRoot: string, patch: Partial<AppStoreConnectPluginConfig>): AppStoreConnectPluginConfig {
103
- const next = normalizeConfig({ ...loadConfig(repoRoot), ...patch });
112
+ function repositoryConfig(repoRoot: string): { raw?: Partial<AppStoreConnectPluginConfig>; source?: 'current' | 'legacy' } {
113
+ const current = readConfigFile(configPath(repoRoot));
114
+ if (current) return { raw: current, source: 'current' };
115
+ const legacy = readConfigFile(legacyConfigPath(repoRoot));
116
+ return legacy ? { raw: legacy, source: 'legacy' } : {};
117
+ }
118
+
119
+ function loadConfig(repoRoot: string, controllerHome?: string, repoId?: string): AppStoreConnectPluginConfig {
120
+ const global = controllerHome ? readConfigFile(globalConfigPath(controllerHome)) : undefined;
121
+ if (repoId === CONTROLLER_SCOPE_REPO_ID) return normalizeConfig(global ?? {});
122
+ const local = repositoryConfig(repoRoot).raw;
123
+ return normalizeConfig({ ...(global ?? {}), ...(local ?? {}) });
124
+ }
125
+
126
+ function definedPatch(patch: Partial<AppStoreConnectPluginConfig>): Partial<AppStoreConnectPluginConfig> {
127
+ return Object.fromEntries(Object.entries(patch).filter(([, value]) => value !== undefined)) as Partial<AppStoreConnectPluginConfig>;
128
+ }
129
+
130
+ function saveConfig(
131
+ repoRoot: string,
132
+ patch: Partial<AppStoreConnectPluginConfig>,
133
+ controllerHome?: string,
134
+ repoId?: string,
135
+ ): AppStoreConnectPluginConfig {
136
+ const cleanPatch = definedPatch(patch);
137
+ if (controllerHome && repoId === CONTROLLER_SCOPE_REPO_ID) {
138
+ const path = globalConfigPath(controllerHome);
139
+ const next = normalizeConfig({ ...(readConfigFile(path) ?? {}), ...cleanPatch });
140
+ mkdirSync(dirname(path), { recursive: true });
141
+ writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, 'utf-8');
142
+ return next;
143
+ }
144
+
145
+ // Repository state is an overlay only. Do not materialize inherited global
146
+ // credential references into every repository just because configure ran.
147
+ const existing = repositoryConfig(repoRoot).raw ?? {};
148
+ const overlay = { schemaVersion: 1 as const, ...existing, ...cleanPatch };
104
149
  const path = configPath(repoRoot);
105
150
  mkdirSync(dirname(path), { recursive: true });
106
- writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, 'utf-8');
107
- return next;
151
+ writeFileSync(path, `${JSON.stringify(overlay, null, 2)}\n`, 'utf-8');
152
+ return loadConfig(repoRoot, controllerHome, repoId);
108
153
  }
109
154
 
110
155
  function envValue(name: string): string | undefined {
111
156
  return stringValue(process.env[name]);
112
157
  }
113
158
 
114
- function privateKeyMaterial(config: AppStoreConnectPluginConfig): { key?: string; source?: string; warning?: string } {
159
+ interface AppStoreConnectPrivateKeyReference {
160
+ available: boolean;
161
+ source?: string;
162
+ warning?: string;
163
+ }
164
+
165
+ function privateKeyPathReference(path: string, source: string): AppStoreConnectPrivateKeyReference {
166
+ if (!existsSync(path)) {
167
+ return { available: false, source, warning: 'Configured App Store Connect private key path does not exist.' };
168
+ }
169
+ try {
170
+ if (!statSync(path).isFile()) {
171
+ return { available: false, source, warning: 'Configured App Store Connect private key path is not a regular file.' };
172
+ }
173
+ accessSync(path, fsConstants.R_OK);
174
+ return { available: true, source };
175
+ } catch {
176
+ // Capability discovery and auth_status are metadata reads. They must never
177
+ // synchronously consume secret bytes just to decide whether a credential
178
+ // reference is usable; filesystem contention (including macOS EDEADLK)
179
+ // therefore degrades readiness instead of escaping as a raw read error.
180
+ return { available: false, source, warning: 'Configured App Store Connect private key path is not readable.' };
181
+ }
182
+ }
183
+
184
+ function privateKeyReference(config: AppStoreConnectPluginConfig): AppStoreConnectPrivateKeyReference {
115
185
  const inline = envValue('FORGE_ASC_PRIVATE_KEY');
116
- if (inline) return { key: inline.replace(/\\n/g, '\n'), source: 'env:FORGE_ASC_PRIVATE_KEY' };
186
+ if (inline) return { available: true, source: 'env:FORGE_ASC_PRIVATE_KEY' };
117
187
  const envKeyPath = envValue('FORGE_ASC_PRIVATE_KEY_PATH');
118
- if (envKeyPath) {
119
- return existsSync(envKeyPath)
120
- ? { key: readFileSync(envKeyPath, 'utf-8'), source: 'env:FORGE_ASC_PRIVATE_KEY_PATH' }
121
- : { source: 'env:FORGE_ASC_PRIVATE_KEY_PATH', warning: 'Configured App Store Connect private key path does not exist.' };
122
- }
123
- if (config.privateKeyPath) {
124
- return existsSync(config.privateKeyPath)
125
- ? { key: readFileSync(config.privateKeyPath, 'utf-8'), source: 'config:privateKeyPath' }
126
- : { source: 'config:privateKeyPath', warning: 'Configured App Store Connect private key path does not exist.' };
188
+ if (envKeyPath) return privateKeyPathReference(envKeyPath, 'env:FORGE_ASC_PRIVATE_KEY_PATH');
189
+ if (config.privateKeyPath) return privateKeyPathReference(config.privateKeyPath, 'config:privateKeyPath');
190
+ return { available: false };
191
+ }
192
+
193
+ function fsErrorCode(error: unknown): string | undefined {
194
+ if (!error || typeof error !== 'object' || !('code' in error)) return undefined;
195
+ const code = (error as { code?: unknown }).code;
196
+ return typeof code === 'string' ? code : undefined;
197
+ }
198
+
199
+ async function readPrivateKeyFile(path: string, source: string): Promise<{ key?: string; source: string; warning?: string }> {
200
+ for (let attempt = 1; attempt <= PRIVATE_KEY_READ_MAX_ATTEMPTS; attempt += 1) {
201
+ try {
202
+ return { key: await fsPromises.readFile(path, 'utf-8'), source };
203
+ } catch (error) {
204
+ const code = fsErrorCode(error);
205
+ if (code === 'ENOENT') {
206
+ return { source, warning: 'Configured App Store Connect private key path does not exist.' };
207
+ }
208
+ if (code === 'EDEADLK' && attempt < PRIVATE_KEY_READ_MAX_ATTEMPTS) {
209
+ await new Promise((resolve) => setTimeout(resolve, PRIVATE_KEY_READ_RETRY_BASE_MS * attempt));
210
+ continue;
211
+ }
212
+ if (code === 'EDEADLK') {
213
+ throw new AssistantPluginError(
214
+ 'PLUGIN_PROVIDER_UNAVAILABLE',
215
+ 'App Store Connect private key is temporarily unavailable because the filesystem is busy.',
216
+ { retryable: true, details: { credentialSource: source, code } },
217
+ );
218
+ }
219
+ throw new AssistantPluginError(
220
+ 'PLUGIN_AUTH_REQUIRED',
221
+ 'Configured App Store Connect private key could not be read.',
222
+ { retryable: false, details: { credentialSource: source, code } },
223
+ );
224
+ }
127
225
  }
226
+ throw new AssistantPluginError('PLUGIN_PROVIDER_UNAVAILABLE', 'App Store Connect private key is temporarily unavailable.', { retryable: true });
227
+ }
228
+
229
+ async function privateKeyMaterial(config: AppStoreConnectPluginConfig): Promise<{ key?: string; source?: string; warning?: string }> {
230
+ const inline = envValue('FORGE_ASC_PRIVATE_KEY');
231
+ if (inline) return { key: inline.replace(/\\n/g, '\n'), source: 'env:FORGE_ASC_PRIVATE_KEY' };
232
+ const envKeyPath = envValue('FORGE_ASC_PRIVATE_KEY_PATH');
233
+ if (envKeyPath) return readPrivateKeyFile(envKeyPath, 'env:FORGE_ASC_PRIVATE_KEY_PATH');
234
+ if (config.privateKeyPath) return readPrivateKeyFile(config.privateKeyPath, 'config:privateKeyPath');
128
235
  return {};
129
236
  }
130
237
 
@@ -145,12 +252,12 @@ function resolveAuth(config: AppStoreConnectPluginConfig): AppStoreConnectAuthSt
145
252
 
146
253
  const issuerId = envValue('FORGE_ASC_ISSUER_ID') ?? config.issuerId;
147
254
  const keyId = envValue('FORGE_ASC_KEY_ID') ?? config.keyId;
148
- const key = privateKeyMaterial(config);
255
+ const key = privateKeyReference(config);
149
256
  const errors: string[] = [];
150
257
  const warnings: string[] = key.warning ? [key.warning] : [];
151
258
  if (!issuerId) errors.push('Set FORGE_ASC_ISSUER_ID or configure issuer_id.');
152
259
  if (!keyId) errors.push('Set FORGE_ASC_KEY_ID or configure key_id.');
153
- if (!key.key) errors.push('Set FORGE_ASC_PRIVATE_KEY, FORGE_ASC_PRIVATE_KEY_PATH, or configure private_key_path.');
260
+ if (!key.available) errors.push('Set FORGE_ASC_PRIVATE_KEY, FORGE_ASC_PRIVATE_KEY_PATH, or configure private_key_path.');
154
261
 
155
262
  return {
156
263
  provider: 'app-store-connect-api',
@@ -165,6 +272,37 @@ function resolveAuth(config: AppStoreConnectPluginConfig): AppStoreConnectAuthSt
165
272
  };
166
273
  }
167
274
 
275
+ export function resolveAppStoreConnectXcodeAuthenticationReference(
276
+ repoRoot: string,
277
+ controllerHome: string,
278
+ repoId: string,
279
+ ): AppStoreConnectXcodeAuthenticationReference {
280
+ const config = loadConfig(repoRoot, controllerHome, repoId);
281
+ if (!config.enabled) {
282
+ throw new AssistantPluginError('PLUGIN_DISABLED', 'App Store Connect is disabled for this repository.', { retryable: false });
283
+ }
284
+ if (config.provider !== 'app-store-connect-api') {
285
+ throw new AssistantPluginError('PLUGIN_DEPENDENCY_MISSING', 'Xcode provisioning requires the App Store Connect API provider.', { retryable: false });
286
+ }
287
+ const privateKeyPath = envValue('FORGE_ASC_PRIVATE_KEY_PATH') ?? config.privateKeyPath;
288
+ const keyId = envValue('FORGE_ASC_KEY_ID') ?? config.keyId;
289
+ const issuerId = envValue('FORGE_ASC_ISSUER_ID') ?? config.issuerId;
290
+ if (!privateKeyPath) {
291
+ throw new AssistantPluginError(
292
+ 'PLUGIN_DEPENDENCY_MISSING',
293
+ 'Xcode provisioning requires a file-backed App Store Connect private key path; inline key material is not accepted for this path.',
294
+ { retryable: false },
295
+ );
296
+ }
297
+ if (!existsSync(privateKeyPath)) {
298
+ throw new AssistantPluginError('PLUGIN_DEPENDENCY_MISSING', 'Configured App Store Connect private key path does not exist.', { retryable: false });
299
+ }
300
+ if (!keyId || !issuerId) {
301
+ throw new AssistantPluginError('PLUGIN_DEPENDENCY_MISSING', 'App Store Connect key id and issuer id are required for Xcode provisioning.', { retryable: false });
302
+ }
303
+ return { privateKeyPath, keyId, issuerId };
304
+ }
305
+
168
306
  function readLength(buffer: Buffer, offset: number): { length: number; next: number } {
169
307
  const first = buffer[offset];
170
308
  if (first < 0x80) return { length: first, next: offset + 1 };
@@ -195,10 +333,10 @@ function derSignatureToJose(signature: Buffer): string {
195
333
  return Buffer.concat([derIntegerToJose(r), derIntegerToJose(s)]).toString('base64url');
196
334
  }
197
335
 
198
- function createJwt(config: AppStoreConnectPluginConfig): string {
336
+ async function createJwt(config: AppStoreConnectPluginConfig): Promise<string> {
199
337
  const issuerId = envValue('FORGE_ASC_ISSUER_ID') ?? config.issuerId;
200
338
  const keyId = envValue('FORGE_ASC_KEY_ID') ?? config.keyId;
201
- const key = privateKeyMaterial(config).key;
339
+ const key = (await privateKeyMaterial(config)).key;
202
340
  if (!issuerId || !keyId || !key) throw new AssistantPluginError('PLUGIN_AUTH_REQUIRED', 'App Store Connect API credentials are incomplete.', { retryable: false });
203
341
 
204
342
  const issuedAt = Math.floor(Date.now() / 1000);
@@ -224,7 +362,7 @@ async function apiRequest<T>(config: AppStoreConnectPluginConfig, options: {
224
362
  const response = await fetch(url, {
225
363
  method: options.method ?? 'GET',
226
364
  headers: {
227
- authorization: `Bearer ${createJwt(config)}`,
365
+ authorization: `Bearer ${await createJwt(config)}`,
228
366
  accept: 'application/json',
229
367
  ...(options.body === undefined ? {} : { 'content-type': 'application/json' }),
230
368
  },
@@ -308,6 +446,7 @@ function permission(scope: string, mode: 'read' | 'write', description: string,
308
446
  function permissions(ready: boolean): AssistantPluginPermissionScope[] {
309
447
  return [
310
448
  permission('appstoreconnect.apps.read', 'read', 'Read App Store Connect apps, versions, localizations, builds, and TestFlight groups.', ready),
449
+ permission('appstoreconnect.developer_resources.read', 'read', 'Read Apple Developer bundle IDs, capabilities, certificates, devices, and provisioning profiles.', ready),
311
450
  permission('appstoreconnect.metadata.write', 'write', 'Patch App Store Connect metadata after dry-run review and authorization.', ready),
312
451
  permission('appstoreconnect.testflight.write', 'write', 'Assign builds to TestFlight groups and prepare beta review submissions.', ready),
313
452
  permission('appstoreconnect.release.write', 'write', 'Create App Store versions and gated review submissions.', ready),
@@ -329,6 +468,13 @@ function capabilities(): AssistantPluginCapability[] {
329
468
  'list_beta_groups', 'list_beta_testers', 'list_review_submissions',
330
469
  ],
331
470
  },
471
+ {
472
+ capabilityId: 'developer-resources-read',
473
+ title: 'Apple Developer Resources',
474
+ description: 'Read Certificates, Identifiers & Profiles resources to diagnose provisioning authorization without mutating Apple Developer state.',
475
+ scopes: ['appstoreconnect.developer_resources.read'],
476
+ actions: ['list_bundle_ids', 'list_bundle_id_capabilities', 'list_certificates', 'list_devices', 'list_profiles'],
477
+ },
332
478
  {
333
479
  capabilityId: 'app-store-metadata',
334
480
  title: 'App Metadata Update',
@@ -369,11 +515,16 @@ function actions(): AssistantPluginActionDescriptor[] {
369
515
  return [
370
516
  {
371
517
  actionId: 'configure', title: 'Configure App Store Connect plugin', description: 'Enable official App Store Connect API access and save non-secret defaults.', readOnly: false, risk: 'workspace_write', confirmation: 'authorization', defaultTimeoutMs: 30_000, cancellable: true, idempotent: true,
372
- scopes: ['appstoreconnect.apps.read', 'appstoreconnect.metadata.write', 'appstoreconnect.testflight.write', 'appstoreconnect.release.write', 'appstoreconnect.xcodecloud.read', 'appstoreconnect.xcodecloud.write'], resourceClaims: [{ resource: 'repo-state', mode: 'write' }],
518
+ scopes: ['appstoreconnect.apps.read', 'appstoreconnect.developer_resources.read', 'appstoreconnect.metadata.write', 'appstoreconnect.testflight.write', 'appstoreconnect.release.write', 'appstoreconnect.xcodecloud.read', 'appstoreconnect.xcodecloud.write'], resourceClaims: [{ resource: 'repo-state', mode: 'write' }],
373
519
  argumentsSchema: { type: 'object', properties: { enabled: { type: 'boolean' }, provider: { type: 'string', enum: ['mock', 'app-store-connect-api'] }, issuer_id: { type: 'string' }, key_id: { type: 'string' }, private_key_path: { type: 'string' }, clear_private_key_path: { type: 'boolean' }, clear_api_identity: { type: 'boolean' }, team_id: { type: 'string' }, clear_team_id: { type: 'boolean' }, default_app_id: { type: 'string' }, clear_default_app_id: { type: 'boolean' }, default_locale: { type: 'string' }, default_timeout_ms: { type: 'number' } }, additionalProperties: false },
374
520
  },
375
521
  { actionId: 'auth_status', title: 'Check App Store Connect auth', description: 'Report API readiness without returning secrets.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 10_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.apps.read'], resourceClaims: [], argumentsSchema: { type: 'object', properties: {}, additionalProperties: false } },
376
522
  { actionId: 'list_apps', title: 'List apps', description: 'List App Store Connect apps, optionally filtered by bundle ID or name.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.apps.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { bundle_id: { type: 'string' }, name: { type: 'string' }, limit: { type: 'number' } }, additionalProperties: false } },
523
+ { actionId: 'list_bundle_ids', title: 'List Apple Developer bundle IDs', description: 'Read registered Bundle IDs, optionally filtered by exact identifier or name.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.developer_resources.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { identifier: { type: 'string' }, name: { type: 'string' }, limit: { type: 'number' } }, additionalProperties: false } },
524
+ { actionId: 'list_bundle_id_capabilities', title: 'List Bundle ID capabilities', description: 'Read enabled capabilities for one exact Apple Developer Bundle ID resource.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.developer_resources.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { bundle_id_resource_id: { type: 'string' }, limit: { type: 'number' } }, required: ['bundle_id_resource_id'], additionalProperties: false } },
525
+ { actionId: 'list_certificates', title: 'List Apple Developer certificates', description: 'Read certificates available to the current Apple Developer API identity.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.developer_resources.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { certificate_type: { type: 'string' }, limit: { type: 'number' } }, additionalProperties: false } },
526
+ { actionId: 'list_devices', title: 'List Apple Developer devices', description: 'Read registered devices, optionally filtered by UDID.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.developer_resources.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { udid: { type: 'string' }, limit: { type: 'number' } }, additionalProperties: false } },
527
+ { actionId: 'list_profiles', title: 'List provisioning profiles', description: 'Read Apple Developer provisioning profiles, optionally filtered by profile name or type.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.developer_resources.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { name: { type: 'string' }, profile_type: { type: 'string' }, limit: { type: 'number' } }, additionalProperties: false } },
377
528
  { actionId: 'list_app_store_versions', title: 'List App Store versions', description: 'List App Store versions for one app.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.apps.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { app_id: { type: 'string' }, limit: { type: 'number' } }, additionalProperties: false } },
378
529
  { actionId: 'list_app_store_version_localizations', title: 'List App Store version localizations', description: 'List localizations for one App Store version.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.apps.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { version_id: { type: 'string' }, limit: { type: 'number' } }, required: ['version_id'], additionalProperties: false } },
379
530
  { actionId: 'get_app_info', title: 'Get app info', description: 'Get App Info records and localizations for one app.', readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 45_000, cancellable: true, idempotent: true, scopes: ['appstoreconnect.apps.read'], resourceClaims: readRemote, argumentsSchema: { type: 'object', properties: { app_id: { type: 'string' } }, additionalProperties: false } },
@@ -579,6 +730,11 @@ function mockResponse(actionId: string, args: Record<string, unknown>, config: A
579
730
  }],
580
731
  };
581
732
  }
733
+ if (actionId === 'list_bundle_ids') return { data: [{ type: 'bundleIds', id: stableMockId('bundle_id', args), attributes: { identifier: stringValue(args.identifier) ?? 'com.example.app', name: stringValue(args.name) ?? 'Mock Bundle ID', platform: 'IOS' } }] };
734
+ if (actionId === 'list_bundle_id_capabilities') return { data: [{ type: 'bundleIdCapabilities', id: stableMockId('bundle_capability', args), attributes: { capabilityType: 'ICLOUD', settings: [] } }] };
735
+ if (actionId === 'list_certificates') return { data: [{ type: 'certificates', id: stableMockId('certificate', args), attributes: { certificateType: stringValue(args.certificate_type) ?? 'DEVELOPMENT', displayName: 'Mock Certificate' } }] };
736
+ if (actionId === 'list_devices') return { data: [{ type: 'devices', id: stableMockId('device', args), attributes: { udid: stringValue(args.udid) ?? 'MOCK-UDID', name: 'Mock iPhone', platform: 'IOS', status: 'ENABLED' } }] };
737
+ if (actionId === 'list_profiles') return { data: [{ type: 'profiles', id: stableMockId('profile', args), attributes: { name: stringValue(args.name) ?? 'Mock Profile', profileType: stringValue(args.profile_type) ?? 'IOS_APP_DEVELOPMENT', profileState: 'ACTIVE' } }] };
582
738
  if (actionId === 'list_xcode_cloud_products') return { data: [{ type: 'ciProducts', id: stableMockId('ci_product', { id }), attributes: { name: 'Mock Xcode Cloud Product', productType: 'APP' } }] };
583
739
  if (actionId === 'list_xcode_cloud_workflows') return { data: [{ type: 'ciWorkflows', id: stableMockId('ci_workflow', { product: args.product_id }), attributes: { name: 'CI', isEnabled: true, clean: false } }] };
584
740
  if (actionId === 'get_xcode_cloud_workflow') return { data: { type: 'ciWorkflows', id: requiredArg(args, 'workflow_id'), attributes: { name: 'CI', isEnabled: true, branchStartCondition: {} } } };
@@ -671,8 +827,9 @@ function mockResponse(actionId: string, args: Record<string, unknown>, config: A
671
827
  throw new AssistantPluginError('PLUGIN_ACTION_NOT_SUPPORTED', `app_store_connect/${actionId} is not supported.`, { retryable: false });
672
828
  }
673
829
 
674
- export function buildAppStoreConnectPluginManifest(previousRevision = 0, previousUpdatedAt?: string, repoRoot?: string): AssistantPluginManifest {
675
- const config = loadConfig(repoRoot ?? process.cwd());
830
+ export function buildAppStoreConnectPluginManifest(previousRevision = 0, previousUpdatedAt?: string, repoRoot?: string, context?: AssistantPluginBuildContext): AssistantPluginManifest {
831
+ const effectiveRepoRoot = repoRoot ?? context?.repoRoot ?? process.cwd();
832
+ const config = loadConfig(effectiveRepoRoot, context?.controllerHome, context?.repoId);
676
833
  const auth = resolveAuth(config);
677
834
  const state = pluginState(config, auth);
678
835
  return {
@@ -683,7 +840,7 @@ export function buildAppStoreConnectPluginManifest(previousRevision = 0, previou
683
840
  provider: 'apple',
684
841
  displayName: 'App Store Connect API Plugin',
685
842
  pluginVersion: '1.1.1',
686
- authority: { strategy: 'derived', duplicateStateAllowed: false, sourceOfTruth: [`repo-local:${CONFIG_ROOT}/app-store-connect.json`, `legacy-read-fallback:${LEGACY_CONFIG_ROOT}/app-store-connect.json`, 'env:FORGE_ASC_*'] },
843
+ authority: { strategy: 'derived', duplicateStateAllowed: false, sourceOfTruth: ['controller-global:system/plugins/profiles/app-store-connect.json', `repo-overlay:${CONFIG_ROOT}/app-store-connect.json`, `legacy-read-fallback:${LEGACY_CONFIG_ROOT}/app-store-connect.json`, 'env:FORGE_ASC_*'] },
687
844
  enabled: config.enabled,
688
845
  lifecycle: { state: state.lifecycleState, reason: !config.enabled ? 'App Store Connect plugin is disabled.' : auth.ready ? 'App Store Connect API credentials are ready.' : auth.errors[0] ?? auth.warnings[0] },
689
846
  health: state.health,
@@ -695,7 +852,7 @@ export function buildAppStoreConnectPluginManifest(previousRevision = 0, previou
695
852
  }
696
853
 
697
854
  export async function executeAppStoreConnectPluginAction(input: AssistantPluginActionExecutionInput): Promise<Record<string, unknown>> {
698
- const config = loadConfig(input.repoRoot);
855
+ const config = loadConfig(input.repoRoot, input.controllerHome, input.repoId);
699
856
  if (input.actionId === 'configure') {
700
857
  const args = input.args;
701
858
  const next = saveConfig(input.repoRoot, {
@@ -708,28 +865,39 @@ export async function executeAppStoreConnectPluginAction(input: AssistantPluginA
708
865
  defaultAppId: args.clear_default_app_id === true ? '' : stringValue(args.default_app_id),
709
866
  defaultLocale: stringValue(args.default_locale),
710
867
  defaultTimeoutMs: boundedTimeout(args.default_timeout_ms),
711
- });
868
+ }, input.controllerHome, input.repoId);
712
869
  return { config: next, auth: resolveAuth(next) };
713
870
  }
714
871
 
715
872
  if (config.provider === 'mock') return mockResponse(input.actionId, input.args, config);
716
873
  const auth = resolveAuth(config);
874
+ if (input.actionId === 'auth_status') {
875
+ return {
876
+ ready: config.enabled && auth.ready,
877
+ provider: auth.provider,
878
+ issuerId: auth.issuerId ? 'configured' : undefined,
879
+ keyId: auth.keyId ? 'configured' : undefined,
880
+ credentialSource: auth.credentialSource,
881
+ errors: auth.errors,
882
+ warnings: auth.warnings,
883
+ userFacingStatus: userFacingAscStatus(config, auth),
884
+ };
885
+ }
717
886
  if (!config.enabled || !auth.ready) throw new AssistantPluginError('PLUGIN_NOT_READY', 'App Store Connect plugin is not ready.', { retryable: false, details: { enabled: config.enabled, errors: auth.errors } });
718
887
 
719
888
  switch (input.actionId) {
720
- case 'auth_status':
721
- return {
722
- ready: auth.ready,
723
- provider: auth.provider,
724
- issuerId: auth.issuerId ? 'configured' : undefined,
725
- keyId: auth.keyId ? 'configured' : undefined,
726
- credentialSource: auth.credentialSource,
727
- errors: auth.errors,
728
- warnings: auth.warnings,
729
- userFacingStatus: userFacingAscStatus(config, auth),
730
- };
731
889
  case 'list_apps':
732
890
  return apiRequest(config, { path: '/v1/apps', query: { 'filter[bundleId]': stringValue(input.args.bundle_id), 'filter[name]': stringValue(input.args.name), limit: limit(input.args.limit) } });
891
+ case 'list_bundle_ids':
892
+ return apiRequest(config, { path: '/v1/bundleIds', query: { 'filter[identifier]': stringValue(input.args.identifier), 'filter[name]': stringValue(input.args.name), limit: limit(input.args.limit) } });
893
+ case 'list_bundle_id_capabilities':
894
+ return apiRequest(config, { path: `/v1/bundleIds/${encodeURIComponent(requiredArg(input.args, 'bundle_id_resource_id'))}/bundleIdCapabilities`, query: { limit: limit(input.args.limit) } });
895
+ case 'list_certificates':
896
+ return apiRequest(config, { path: '/v1/certificates', query: { 'filter[certificateType]': stringValue(input.args.certificate_type), limit: limit(input.args.limit) } });
897
+ case 'list_devices':
898
+ return apiRequest(config, { path: '/v1/devices', query: { 'filter[udid]': stringValue(input.args.udid), limit: limit(input.args.limit) } });
899
+ case 'list_profiles':
900
+ return apiRequest(config, { path: '/v1/profiles', query: { 'filter[name]': stringValue(input.args.name), 'filter[profileType]': stringValue(input.args.profile_type), limit: limit(input.args.limit) } });
733
901
  case 'list_app_store_versions':
734
902
  return apiRequest(config, { path: '/v1/appStoreVersions', query: { 'filter[app]': appId(input.args, config), limit: limit(input.args.limit) } });
735
903
  case 'list_app_store_version_localizations':
@@ -863,6 +1031,7 @@ export async function executeAppStoreConnectPluginAction(input: AssistantPluginA
863
1031
 
864
1032
  export const appStoreConnectPluginAdapter = {
865
1033
  pluginId: APP_STORE_CONNECT_PLUGIN_ID,
1034
+ scope: 'controller_with_repository_overlay' as const,
866
1035
  buildManifest: buildAppStoreConnectPluginManifest,
867
1036
  executeAction: executeAppStoreConnectPluginAction,
868
1037
  };