@moretea-labs/forge 1.6.0 → 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 (396) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +26 -0
  3. package/CLAUDE.md +2 -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/03-orchestration.partial.md +1 -1
  17. package/assets/plugin-registry.v1.json +1 -1
  18. package/assets/reference-configs/document-generation.md +1 -1
  19. package/assets/reference-configs/harness-overview.md +1 -1
  20. package/assets/reference-configs/sprint-contracts.md +3 -3
  21. package/assets/skill-version.json +2 -2
  22. package/assets/templates/contract.template.md +2 -7
  23. package/assets/templates/helpers/capture-plan.sh +2 -2
  24. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  25. package/assets/templates/helpers/contract-worktree.sh +1 -21
  26. package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
  27. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  28. package/assets/templates/helpers/new-plan.sh +1 -1
  29. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  30. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  31. package/assets/templates/helpers/verify-contract.sh +1 -111
  32. package/assets/templates/helpers/verify-sprint.sh +3 -180
  33. package/assets/templates/plan.template.md +1 -1
  34. package/assets/templates/review.template.md +9 -34
  35. package/assets/workflow-contract.v1.json +10 -19
  36. package/docs/README.md +10 -10
  37. package/docs/architecture/CURRENT.md +170 -0
  38. package/docs/architecture/EVOLUTION.md +42 -0
  39. package/docs/architecture/current/README.md +4 -156
  40. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  41. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  42. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  43. package/docs/architecture/history/README.md +7 -0
  44. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  45. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  46. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  47. package/docs/architecture/history.md +3 -34
  48. package/docs/architecture/index.md +15 -98
  49. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  50. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  51. package/docs/architecture/versions/1.6.md +38 -0
  52. package/docs/architecture/versions/1.7.md +41 -0
  53. package/docs/forge-chatgpt-browser-engine.md +2 -2
  54. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  55. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  56. package/docs/forge-github-issue-launcher.md +1 -1
  57. package/docs/forge-runtime-self-healing-loop.md +1 -1
  58. package/docs/operations/controller-browser-plugin.md +26 -6
  59. package/docs/operations/controller-performance-and-502.md +124 -0
  60. package/docs/operations/controller-reliability-runbook.md +1 -1
  61. package/docs/operations/features.md +2 -2
  62. package/docs/operations/github-repository.md +1 -1
  63. package/docs/operations/homebrew.md +2 -2
  64. package/docs/operations/mcp-tool-exposure.md +5 -3
  65. package/docs/wiki/Architecture.md +6 -6
  66. package/docs/wiki/Implementation.md +2 -2
  67. package/docs/wiki/Integrations.md +2 -2
  68. package/docs/wiki/Work-Lifecycle.md +1 -1
  69. package/package.json +29 -10
  70. package/scripts/activate-source-baseline.ts +17 -13
  71. package/scripts/benchmark-background-check-overlap.ts +228 -0
  72. package/scripts/benchmark-mcp-host.sh +117 -0
  73. package/scripts/benchmark-mcp-transport.ts +170 -0
  74. package/scripts/benchmark-quality-harness.ts +146 -0
  75. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  76. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  77. package/scripts/benchmark-typescript-navigation.ts +186 -0
  78. package/scripts/benchmark-work-efficiency.ts +334 -0
  79. package/scripts/capture-plan.sh +3 -3
  80. package/scripts/check-agent-tooling.sh +8 -35
  81. package/scripts/check-architecture-sync.sh +17 -86
  82. package/scripts/check-mcp-compatibility.ts +14 -0
  83. package/scripts/check-runtime-architecture.mjs +107 -64
  84. package/scripts/check-stable-baseline.ts +92 -0
  85. package/scripts/check-task-workflow.sh +4 -4
  86. package/scripts/contract-worktree.sh +1 -21
  87. package/scripts/ensure-task-workflow.sh +22 -55
  88. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  89. package/scripts/install-standalone-recovery.ts +24 -0
  90. package/scripts/lib/project-init-lib.sh +15 -44
  91. package/scripts/migrate-project-template.sh +23 -1179
  92. package/scripts/new-plan.sh +1 -1
  93. package/scripts/plan-to-todo.sh +11 -36
  94. package/scripts/run-governed-gate.ts +2 -1
  95. package/scripts/ship-worktrees.sh +2 -29
  96. package/scripts/smoke-ios-physical-device.ts +4 -2
  97. package/scripts/smoke-runtime-recovery.ts +1 -35
  98. package/scripts/smoke-schedule-engine.ts +1 -20
  99. package/scripts/verify-contract.sh +1 -111
  100. package/scripts/verify-forge-runtime.sh +0 -1
  101. package/scripts/verify-sprint.sh +3 -180
  102. package/scripts/workflow-contract.ts +1 -5
  103. package/src/cli/commands/init-hook.ts +3 -3
  104. package/src/cli/commands/mcp.ts +9 -5
  105. package/src/cli/commands/plugin.ts +1 -1
  106. package/src/cli/commands/recovery.ts +25 -3
  107. package/src/cli/commands/runtime.ts +55 -3
  108. package/src/cli/commands/security.ts +12 -1
  109. package/src/cli/commands/setup-profile.ts +31 -21
  110. package/src/cli/controller/check-runner.ts +106 -3
  111. package/src/cli/controller/composite-result.ts +1 -1
  112. package/src/cli/controller/context/focus.ts +51 -0
  113. package/src/cli/controller/context/instruction-context.ts +114 -0
  114. package/src/cli/controller/context/known-paths.ts +76 -0
  115. package/src/cli/controller/context/query-planning.ts +110 -0
  116. package/src/cli/controller/context/rule-resolution.ts +532 -0
  117. package/src/cli/controller/context/source-materializer.ts +278 -0
  118. package/src/cli/controller/context/structural.ts +16 -0
  119. package/src/cli/controller/context/types.ts +286 -0
  120. package/src/cli/controller/context-pack.ts +785 -469
  121. package/src/cli/controller/launch-agents.ts +77 -2
  122. package/src/cli/controller/operational-plan.ts +2 -2
  123. package/src/cli/controller/work-mode.ts +6 -4
  124. package/src/cli/editing/edit-session.ts +60 -7
  125. package/src/cli/github/github.ts +133 -0
  126. package/src/cli/installer/managed-entries.ts +15 -1
  127. package/src/cli/local-bridge/console-automations.ts +54 -78
  128. package/src/cli/local-bridge/console-view-models.ts +1 -35
  129. package/src/cli/local-bridge/facade-api.ts +0 -217
  130. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  131. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
  132. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  133. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
  134. package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
  135. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  136. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
  137. package/src/cli/local-bridge/frontend/src/styles.css +1 -1
  138. package/src/cli/local-bridge/frontend/src/types.ts +5 -8
  139. package/src/cli/local-bridge/job-store.ts +1 -490
  140. package/src/cli/local-bridge/server.ts +0 -513
  141. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  142. package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
  143. package/src/cli/mcp/auth.ts +5 -5
  144. package/src/cli/mcp/instructions.ts +3 -2
  145. package/src/cli/mcp/legacy-tool-service.ts +0 -189
  146. package/src/cli/mcp/multi-repository.ts +16 -5
  147. package/src/cli/mcp/oauth.ts +175 -26
  148. package/src/cli/mcp/repository-tools.ts +247 -107
  149. package/src/cli/mcp/server.ts +537 -111
  150. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  151. package/src/cli/mcp/toolset-selection.ts +1 -1
  152. package/src/cli/mcp/toolset.ts +42 -5
  153. package/src/cli/mcp/transports/http.ts +101 -14
  154. package/src/cli/mcp/transports/session-registry.ts +43 -5
  155. package/src/cli/mcp/types.ts +3 -3
  156. package/src/cli/repositories/command-classifier.ts +101 -19
  157. package/src/cli/repositories/command-executor.ts +55 -521
  158. package/src/cli/repositories/command-process.ts +108 -0
  159. package/src/cli/repositories/command-scope.ts +99 -41
  160. package/src/cli/repositories/controller-home.ts +7 -8
  161. package/src/cli/repositories/git-command-executor.ts +2 -0
  162. package/src/cli/repositories/locks.ts +53 -16
  163. package/src/cli/repositories/repository-snapshot.ts +121 -0
  164. package/src/cli/repositories/selected-path-actions.ts +17 -9
  165. package/src/cli/repositories/structured-git.ts +61 -3
  166. package/src/cli/repository/inspector.ts +553 -63
  167. package/src/cli/repository/session-cache.ts +58 -17
  168. package/src/cli/v81-entry.ts +1 -4
  169. package/src/effects/fs-transaction.ts +85 -17
  170. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  171. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  172. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  173. package/src/runtime/context/lsp-client.ts +283 -0
  174. package/src/runtime/context/semantic-navigation.ts +275 -0
  175. package/src/runtime/context/swift-navigation.ts +565 -0
  176. package/src/runtime/context/typescript-navigation.ts +226 -0
  177. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  178. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +47 -8
  179. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  180. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  181. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  182. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  183. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  184. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  185. package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
  186. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +143 -8
  187. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
  188. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +85 -8
  189. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  190. package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
  191. package/src/runtime/control-plane/facade/controller-session-store.ts +132 -1
  192. package/src/runtime/control-plane/facade/goal-workloop-access.ts +2 -0
  193. package/src/runtime/control-plane/facade/goal-workloop.ts +686 -129
  194. package/src/runtime/control-plane/facade/index.ts +1 -0
  195. package/src/runtime/control-plane/facade/plan-contract-store.ts +185 -20
  196. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  197. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  198. package/src/runtime/control-plane/facade/types.ts +22 -5
  199. package/src/runtime/control-plane/facade/work-contract-store.ts +175 -73
  200. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  201. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  202. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  203. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  204. package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
  205. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  206. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  207. package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
  208. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  209. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  210. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  211. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  212. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  213. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  214. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  215. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  216. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  217. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  218. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
  219. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +355 -116
  220. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
  221. package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
  222. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  223. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  224. package/src/runtime/control-plane/release-retention.ts +62 -2
  225. package/src/runtime/control-plane/routing/route-policy.ts +7 -16
  226. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  227. package/src/runtime/diagnostics/mcp-timing.ts +13 -0
  228. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  229. package/src/runtime/diagnostics/performance.ts +24 -1
  230. package/src/runtime/evidence/event-ledger.ts +1 -1
  231. package/src/runtime/execution/jobs/store.ts +58 -4
  232. package/src/runtime/execution/jobs/types.ts +1 -1
  233. package/src/runtime/execution/managed-workspace.ts +203 -1
  234. package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
  235. package/src/runtime/execution/process-runtime/check-result.ts +53 -0
  236. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  237. package/src/runtime/execution/process-runtime/command-facade.ts +345 -40
  238. package/src/runtime/execution/process-runtime/gc.ts +38 -6
  239. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
  240. package/src/runtime/execution/process-runtime/persisted-check.ts +47 -4
  241. package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
  242. package/src/runtime/execution/process-runtime/runtime.ts +43 -3
  243. package/src/runtime/execution/process-runtime/store.ts +1 -0
  244. package/src/runtime/execution/process-runtime/types.ts +2 -0
  245. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  246. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  247. package/src/runtime/execution/workers/executor.ts +0 -48
  248. package/src/runtime/gateway/mcp/execution-tools.ts +1192 -114
  249. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  250. package/src/runtime/gateway/mcp/router.ts +184 -82
  251. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
  252. package/src/runtime/gateway/mcp/runtime-tools.ts +2224 -1589
  253. package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
  254. package/src/runtime/plugins/browser-adapter.ts +2901 -241
  255. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  256. package/src/runtime/plugins/browser-handoff.ts +92 -2
  257. package/src/runtime/plugins/browser-macos-bridge.ts +582 -49
  258. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  259. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  260. package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
  261. package/src/runtime/plugins/browser-runtime.ts +183 -0
  262. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  263. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  264. package/src/runtime/plugins/desktop-operator-registration.ts +8 -2
  265. package/src/runtime/plugins/external-adapter.ts +324 -3
  266. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  267. package/src/runtime/plugins/first-party-registry.ts +2 -0
  268. package/src/runtime/plugins/github-adapter.ts +125 -0
  269. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  270. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  271. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  272. package/src/runtime/plugins/ios-adapter.ts +106 -16
  273. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  274. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  275. package/src/runtime/plugins/lightweight-action.ts +153 -0
  276. package/src/runtime/plugins/local-system-adapter.ts +123 -6
  277. package/src/runtime/plugins/macos-capability-broker.ts +55 -9
  278. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  279. package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
  280. package/src/runtime/plugins/resend-adapter.ts +1 -1
  281. package/src/runtime/plugins/store.ts +269 -29
  282. package/src/runtime/plugins/types.ts +35 -2
  283. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  284. package/src/runtime/projections/git-status-sampler.ts +16 -16
  285. package/src/runtime/projections/materialized-view.ts +28 -10
  286. package/src/runtime/recovery/maintenance-executor.ts +393 -28
  287. package/src/runtime/root/mcp-transport.ts +180 -10
  288. package/src/runtime/root/package-connector-service.ts +186 -8
  289. package/src/runtime/root/package-runtime-release.ts +143 -17
  290. package/src/runtime/root/package-runtime-service.ts +430 -22
  291. package/src/runtime/root/release-manifest.ts +7 -2
  292. package/src/runtime/root/release-materialize.ts +19 -0
  293. package/src/runtime/root/release-store.ts +48 -11
  294. package/src/runtime/root/runtime.ts +23 -5
  295. package/src/runtime/root/service.ts +100 -29
  296. package/src/runtime/root/types.ts +2 -0
  297. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  298. package/src/runtime/shared/trusted-executable.ts +62 -0
  299. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  300. package/src/runtime/standalone-recovery/core.ts +700 -92
  301. package/src/runtime/standalone-recovery/entry.ts +99 -23
  302. package/src/runtime/standalone-recovery/installer.ts +19 -3
  303. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  304. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  305. package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
  306. package/src/runtime/workflow/schedules/engine.ts +287 -151
  307. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  308. package/src/runtime/workflow/schedules/settlement.ts +48 -4
  309. package/src/runtime/workflow/schedules/store.ts +131 -10
  310. package/src/runtime/workflow/schedules/types.ts +2 -4
  311. package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
  312. package/src/testing/test-governance.ts +8 -40
  313. package/assets/reference-configs/heartbeat-triage.md +0 -57
  314. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  315. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  316. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  317. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  318. package/docs/architecture/current/architecture-invariants.md +0 -331
  319. package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
  320. package/docs/architecture/current/codegraph-context-provider.md +0 -238
  321. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  322. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  323. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
  324. package/docs/architecture/current/entity-model.md +0 -612
  325. package/docs/architecture/current/external-plugin-governance.md +0 -396
  326. package/docs/architecture/current/failure-recovery.md +0 -447
  327. package/docs/architecture/current/governance.md +0 -213
  328. package/docs/architecture/current/human-interaction-plane.md +0 -87
  329. package/docs/architecture/current/implementation-status.md +0 -128
  330. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  331. package/docs/architecture/current/legacy-governance.md +0 -58
  332. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  333. package/docs/architecture/current/migration-roadmap.md +0 -105
  334. package/docs/architecture/current/multi-repository-execution.md +0 -371
  335. package/docs/architecture/current/operations-runbook.md +0 -45
  336. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  337. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  338. package/docs/architecture/current/repository-access-modes.md +0 -102
  339. package/docs/architecture/current/route-policy.md +0 -15
  340. package/docs/architecture/current/route-session-concurrency.md +0 -85
  341. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  342. package/docs/architecture/current/runtime-directory-map.md +0 -49
  343. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  344. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  345. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  346. package/docs/architecture/current/system-overview.md +0 -169
  347. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  348. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  349. package/docs/architecture/current/thin-harness-v1.md +0 -321
  350. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  351. package/docs/forge-autonomous-goal-loop.md +0 -9
  352. package/docs/forge-provider-configuration.md +0 -7
  353. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
  354. package/docs/operations/gmail-assistant-routines.md +0 -186
  355. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  356. package/docs/operations/provider-configuration.md +0 -31
  357. package/scripts/harness-trace-grade.sh +0 -150
  358. package/scripts/heartbeat-triage.sh +0 -361
  359. package/scripts/maintenance-triage.sh +0 -167
  360. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  361. package/src/cli/repositories/goal-registry.ts +0 -239
  362. package/src/runtime/assistant/action-proposals.ts +0 -279
  363. package/src/runtime/assistant/intent.ts +0 -395
  364. package/src/runtime/assistant/model-provider.ts +0 -460
  365. package/src/runtime/assistant/openapi.ts +0 -223
  366. package/src/runtime/assistant/policy.ts +0 -89
  367. package/src/runtime/assistant/readiness.ts +0 -258
  368. package/src/runtime/assistant/routine-runtime.ts +0 -477
  369. package/src/runtime/assistant/schedule-binding.ts +0 -171
  370. package/src/runtime/assistant/standing-grants.ts +0 -375
  371. package/src/runtime/assistant/store.ts +0 -243
  372. package/src/runtime/assistant/types.ts +0 -135
  373. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  374. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  375. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  376. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  377. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  378. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  379. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  380. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  381. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  382. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  383. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  384. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  385. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  386. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  387. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  388. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  389. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  390. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  391. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  392. package/src/runtime/workflow/findings/store.ts +0 -204
  393. package/src/runtime/workflow/findings/types.ts +0 -58
  394. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  395. package/src/runtime/workflow/portfolio/store.ts +0 -185
  396. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -1,570 +0,0 @@
1
- import { mkdirSync, rmSync } from 'fs';
2
- import { join } from 'path';
3
- import { resolveControllerHome } from '../../../cli/repositories/controller-home';
4
- import { readJsonFile, writeJsonAtomic } from '../../shared/json-files';
5
- import type {
6
- ExecutorRoutingConfigFile,
7
- GoalLoopPolicyConfigFile,
8
- LocalToolConfigFile,
9
- LocalToolPreference,
10
- ProviderConfigFile,
11
- ProviderPreference,
12
- ProviderSecretEntry,
13
- ProviderSecretsFile,
14
- RoutingIntentKey,
15
- } from './config-types';
16
-
17
- /** Default remote API endpoints / models (non-secret). */
18
- export const REMOTE_API_DEFAULTS: Record<string, { baseUrl: string; model: string; envVars: string[] }> = {
19
- grok_api: {
20
- baseUrl: 'https://api.x.ai/v1',
21
- model: 'grok-3',
22
- envVars: ['XAI_API_KEY', 'FORGE_XAI_API_KEY', 'GROK_API_KEY'],
23
- },
24
- openai_api: {
25
- baseUrl: 'https://api.openai.com/v1',
26
- model: 'gpt-4o',
27
- envVars: ['OPENAI_API_KEY', 'FORGE_OPENAI_API_KEY'],
28
- },
29
- deepseek_api: {
30
- baseUrl: 'https://api.deepseek.com',
31
- model: 'deepseek-chat',
32
- envVars: ['DEEPSEEK_API_KEY', 'FORGE_DEEPSEEK_API_KEY'],
33
- },
34
- };
35
-
36
- export const REMOTE_API_PROVIDER_IDS = Object.keys(REMOTE_API_DEFAULTS);
37
- export interface GoalLoopConfigLocation {
38
- controllerHome?: string;
39
- root?: string;
40
- }
41
-
42
- function nowIso(): string {
43
- return new Date().toISOString();
44
- }
45
-
46
- export function globalConfigRoot(location: GoalLoopConfigLocation): string {
47
- if (location.root) {
48
- mkdirSync(location.root, { recursive: true });
49
- return location.root;
50
- }
51
- if (!location.controllerHome) {
52
- throw new Error('goal-loop config requires controllerHome or root');
53
- }
54
- const root = join(resolveControllerHome(location.controllerHome), 'global');
55
- mkdirSync(root, { recursive: true });
56
- return root;
57
- }
58
-
59
- export function defaultProviderConfig(updatedAt = nowIso()): ProviderConfigFile {
60
- const defaults: Array<Omit<ProviderPreference, 'updatedAt'>> = [
61
- { providerId: 'direct_edit', enabled: true, priority: 10 },
62
- { providerId: 'codex_cli', enabled: true, priority: 100 },
63
- { providerId: 'grok_cli', enabled: true, priority: 100 },
64
- { providerId: 'claude_cli', enabled: true, priority: 100 },
65
- {
66
- providerId: 'grok_api',
67
- enabled: true,
68
- priority: 200,
69
- credentialEnvVars: REMOTE_API_DEFAULTS.grok_api!.envVars,
70
- baseUrl: REMOTE_API_DEFAULTS.grok_api!.baseUrl,
71
- model: REMOTE_API_DEFAULTS.grok_api!.model,
72
- },
73
- {
74
- providerId: 'openai_api',
75
- enabled: true,
76
- priority: 200,
77
- credentialEnvVars: REMOTE_API_DEFAULTS.openai_api!.envVars,
78
- baseUrl: REMOTE_API_DEFAULTS.openai_api!.baseUrl,
79
- model: REMOTE_API_DEFAULTS.openai_api!.model,
80
- },
81
- {
82
- providerId: 'deepseek_api',
83
- enabled: true,
84
- priority: 200,
85
- credentialEnvVars: REMOTE_API_DEFAULTS.deepseek_api!.envVars,
86
- baseUrl: REMOTE_API_DEFAULTS.deepseek_api!.baseUrl,
87
- model: REMOTE_API_DEFAULTS.deepseek_api!.model,
88
- },
89
- { providerId: 'github_copilot_cloud', enabled: true, priority: 300 },
90
- {
91
- providerId: 'chatgpt_handoff',
92
- enabled: true,
93
- priority: 900,
94
- notes: 'Handoff-only supervisor; never direct-invokable.',
95
- },
96
- ];
97
- return {
98
- schemaVersion: 1,
99
- updatedAt,
100
- preferLiveModelProviders: false,
101
- goalLoopEnabled: true,
102
- providers: defaults.map((entry) => ({ ...entry, updatedAt })),
103
- };
104
- }
105
-
106
- export function defaultLocalToolConfig(updatedAt = nowIso()): LocalToolConfigFile {
107
- const ids = [
108
- 'direct_edit',
109
- 'codex_cli',
110
- 'grok_cli',
111
- 'claude_cli',
112
- 'git',
113
- 'gh',
114
- 'bun',
115
- 'npm',
116
- 'xcodebuild',
117
- 'xcrun',
118
- 'simctl',
119
- 'playwright',
120
- 'app_store_connect',
121
- 'gmail_workspace',
122
- ];
123
- return {
124
- schemaVersion: 1,
125
- updatedAt,
126
- tools: ids.map((toolId) => ({ toolId, enabled: true, updatedAt })),
127
- };
128
- }
129
-
130
- function neutralProviderOrder(providerConfig: ProviderConfigFile): string[] {
131
- return providerConfig.providers
132
- .filter((provider) => provider.enabled)
133
- .filter((provider) => !['direct_edit', 'chatgpt_handoff'].includes(provider.providerId))
134
- .sort((left, right) => {
135
- if (left.priority !== right.priority) return left.priority - right.priority;
136
- return left.providerId.localeCompare(right.providerId);
137
- })
138
- .map((provider) => provider.providerId);
139
- }
140
-
141
- /**
142
- * Fresh-install routing is vendor-neutral: execution kind first, then user
143
- * priority, then provider id as a deterministic tie-breaker. Persisted routing
144
- * files remain authoritative and are never rewritten by a read.
145
- */
146
- export function defaultRoutingConfig(
147
- updatedAt = nowIso(),
148
- providerConfig = defaultProviderConfig(updatedAt),
149
- ): ExecutorRoutingConfigFile {
150
- const executors = neutralProviderOrder(providerConfig);
151
- const withHandoff = [...executors, 'chatgpt_handoff'];
152
- const orders: Record<RoutingIntentKey, string[]> = {
153
- deterministic_edit: ['direct_edit', ...withHandoff],
154
- implementation: [...withHandoff],
155
- repair: [...withHandoff],
156
- planning: [...withHandoff],
157
- review: [...withHandoff],
158
- browser_planning: [...withHandoff],
159
- ios_analysis: [...withHandoff],
160
- fallback: ['direct_edit', ...withHandoff],
161
- };
162
- return {
163
- schemaVersion: 1,
164
- updatedAt,
165
- orders,
166
- };
167
- }
168
-
169
- export function defaultGoalLoopPolicy(updatedAt = nowIso()): GoalLoopPolicyConfigFile {
170
- return {
171
- schemaVersion: 1,
172
- updatedAt,
173
- requireApprovalForExternalWrites: true,
174
- requireApprovalForDestructiveChanges: true,
175
- requireApprovalForBroadRefactors: true,
176
- requireApprovalForBrowserFormSubmit: true,
177
- requireApprovalForGmailSendOrTrash: true,
178
- requireApprovalForAppStoreConnectWrites: true,
179
- requireApprovalBeforeFinalMerge: true,
180
- maxChangedFilesWithoutConfirmation: 40,
181
- maxChangedLinesWithoutConfirmation: 2_000,
182
- defaultRetryBudget: 5,
183
- maxRepairAttemptsPerProvider: 3,
184
- };
185
- }
186
-
187
- function providerConfigPath(location: GoalLoopConfigLocation): string {
188
- return join(globalConfigRoot(location), 'provider-config.json');
189
- }
190
- function providerSecretsPath(location: GoalLoopConfigLocation): string {
191
- return join(globalConfigRoot(location), 'provider-secrets.json');
192
- }
193
- function localToolConfigPath(location: GoalLoopConfigLocation): string {
194
- return join(globalConfigRoot(location), 'local-tool-config.json');
195
- }
196
- function routingConfigPath(location: GoalLoopConfigLocation): string {
197
- return join(globalConfigRoot(location), 'executor-routing.json');
198
- }
199
- function policyConfigPath(location: GoalLoopConfigLocation): string {
200
- return join(globalConfigRoot(location), 'goal-loop-policy.json');
201
- }
202
-
203
- function sanitizeBaseUrl(value: unknown): string | undefined {
204
- if (typeof value !== 'string') return undefined;
205
- const trimmed = value.trim().replace(/\/+$/, '');
206
- if (!trimmed) return undefined;
207
- if (!/^https?:\/\//i.test(trimmed)) return undefined;
208
- if (trimmed.length > 500) return undefined;
209
- return trimmed;
210
- }
211
-
212
- function sanitizeModel(value: unknown): string | undefined {
213
- if (typeof value !== 'string') return undefined;
214
- const trimmed = value.trim();
215
- if (!trimmed || trimmed.length > 200) return undefined;
216
- if (!/^[a-zA-Z0-9._:/-]+$/.test(trimmed)) return undefined;
217
- return trimmed;
218
- }
219
-
220
- function stripSecretFields(pref: ProviderPreference): ProviderPreference {
221
- const clone = { ...pref } as ProviderPreference & Record<string, unknown>;
222
- for (const key of Object.keys(clone)) {
223
- if (/api[_-]?key|token|secret|password|authorization|bearer/i.test(key)) {
224
- delete clone[key];
225
- }
226
- }
227
- return clone;
228
- }
229
-
230
- function mergeProviderDefaults(stored: ProviderConfigFile | undefined): ProviderConfigFile {
231
- const base = defaultProviderConfig(stored?.updatedAt ?? nowIso());
232
- if (!stored) return base;
233
- const byId = new Map(stored.providers.map((p) => [p.providerId, p]));
234
- const providers = base.providers.map((def) => {
235
- const existing = byId.get(def.providerId);
236
- if (!existing) return def;
237
- const defaults = REMOTE_API_DEFAULTS[def.providerId];
238
- return stripSecretFields({
239
- ...def,
240
- ...existing,
241
- providerId: def.providerId,
242
- // ChatGPT can never be "disabled" from handoff role; enabled only controls whether packets are preferred.
243
- enabled: def.providerId === 'chatgpt_handoff' ? true : existing.enabled !== false,
244
- priority: typeof existing.priority === 'number' ? existing.priority : def.priority,
245
- baseUrl: sanitizeBaseUrl(existing.baseUrl) ?? def.baseUrl ?? defaults?.baseUrl,
246
- model: sanitizeModel(existing.model) ?? def.model ?? defaults?.model,
247
- credentialEnvVars: existing.credentialEnvVars ?? def.credentialEnvVars ?? defaults?.envVars,
248
- });
249
- });
250
- // Keep any unknown custom provider prefs (forward-compat).
251
- for (const entry of stored.providers) {
252
- if (!providers.some((p) => p.providerId === entry.providerId)) {
253
- providers.push(entry);
254
- }
255
- }
256
- return {
257
- schemaVersion: 1,
258
- updatedAt: stored.updatedAt ?? base.updatedAt,
259
- preferLiveModelProviders: stored.preferLiveModelProviders === true,
260
- goalLoopEnabled: stored.goalLoopEnabled !== false,
261
- providers: providers.sort((a, b) => a.priority - b.priority),
262
- };
263
- }
264
-
265
- function mergeToolDefaults(stored: LocalToolConfigFile | undefined): LocalToolConfigFile {
266
- const base = defaultLocalToolConfig(stored?.updatedAt ?? nowIso());
267
- if (!stored) return base;
268
- const byId = new Map(stored.tools.map((t) => [t.toolId, t]));
269
- const tools: LocalToolPreference[] = base.tools.map((def) => {
270
- const existing = byId.get(def.toolId);
271
- if (!existing) return def;
272
- return { ...def, ...existing, toolId: def.toolId, enabled: existing.enabled !== false };
273
- });
274
- for (const entry of stored.tools) {
275
- if (!tools.some((t) => t.toolId === entry.toolId)) tools.push(entry);
276
- }
277
- return { schemaVersion: 1, updatedAt: stored.updatedAt ?? base.updatedAt, tools };
278
- }
279
-
280
- function mergeRoutingDefaults(
281
- stored: ExecutorRoutingConfigFile | undefined,
282
- providerConfig: ProviderConfigFile = defaultProviderConfig(stored?.updatedAt ?? nowIso()),
283
- ): ExecutorRoutingConfigFile {
284
- const base = defaultRoutingConfig(stored?.updatedAt ?? nowIso(), providerConfig);
285
- if (!stored) return base;
286
- const orders = { ...base.orders };
287
- for (const key of Object.keys(base.orders) as RoutingIntentKey[]) {
288
- if (Array.isArray(stored.orders?.[key]) && stored.orders[key]!.length > 0) {
289
- orders[key] = stored.orders[key]!.map(String);
290
- }
291
- }
292
- return {
293
- schemaVersion: 1,
294
- updatedAt: stored.updatedAt ?? base.updatedAt,
295
- orders,
296
- defaultImplementationProvider: stored.defaultImplementationProvider ?? base.defaultImplementationProvider,
297
- defaultRepairProvider: stored.defaultRepairProvider ?? base.defaultRepairProvider,
298
- defaultPlanningProvider: stored.defaultPlanningProvider ?? base.defaultPlanningProvider,
299
- defaultReviewProvider: stored.defaultReviewProvider ?? base.defaultReviewProvider,
300
- defaultBrowserPlanningProvider: stored.defaultBrowserPlanningProvider ?? base.defaultBrowserPlanningProvider,
301
- defaultIosAnalysisProvider: stored.defaultIosAnalysisProvider ?? base.defaultIosAnalysisProvider,
302
- };
303
- }
304
-
305
- function mergePolicyDefaults(stored: GoalLoopPolicyConfigFile | undefined): GoalLoopPolicyConfigFile {
306
- const base = defaultGoalLoopPolicy(stored?.updatedAt ?? nowIso());
307
- if (!stored) return base;
308
- return {
309
- ...base,
310
- ...stored,
311
- schemaVersion: 1,
312
- // Never allow a "disable all safety" collapse via missing fields.
313
- requireApprovalForExternalWrites: stored.requireApprovalForExternalWrites !== false,
314
- requireApprovalForDestructiveChanges: stored.requireApprovalForDestructiveChanges !== false,
315
- requireApprovalForBroadRefactors: stored.requireApprovalForBroadRefactors !== false,
316
- requireApprovalForBrowserFormSubmit: stored.requireApprovalForBrowserFormSubmit !== false,
317
- requireApprovalForGmailSendOrTrash: stored.requireApprovalForGmailSendOrTrash !== false,
318
- requireApprovalForAppStoreConnectWrites: stored.requireApprovalForAppStoreConnectWrites !== false,
319
- requireApprovalBeforeFinalMerge: stored.requireApprovalBeforeFinalMerge !== false,
320
- maxChangedFilesWithoutConfirmation: Math.max(1, Number(stored.maxChangedFilesWithoutConfirmation ?? base.maxChangedFilesWithoutConfirmation)),
321
- maxChangedLinesWithoutConfirmation: Math.max(1, Number(stored.maxChangedLinesWithoutConfirmation ?? base.maxChangedLinesWithoutConfirmation)),
322
- defaultRetryBudget: Math.max(0, Number(stored.defaultRetryBudget ?? base.defaultRetryBudget)),
323
- maxRepairAttemptsPerProvider: Math.max(1, Number(stored.maxRepairAttemptsPerProvider ?? base.maxRepairAttemptsPerProvider)),
324
- };
325
- }
326
-
327
- export function readProviderConfig(location: GoalLoopConfigLocation): ProviderConfigFile {
328
- const raw = readJsonFile<ProviderConfigFile | undefined>(providerConfigPath(location), undefined);
329
- return mergeProviderDefaults(raw);
330
- }
331
-
332
- export function writeProviderConfig(location: GoalLoopConfigLocation, config: ProviderConfigFile): ProviderConfigFile {
333
- const at = nowIso();
334
- const next: ProviderConfigFile = {
335
- ...mergeProviderDefaults(config),
336
- updatedAt: at,
337
- providers: config.providers.map((p) => {
338
- const cleaned = stripSecretFields(p);
339
- const defaults = REMOTE_API_DEFAULTS[cleaned.providerId];
340
- return {
341
- ...cleaned,
342
- // Never persist secret-like fields if a buggy client sent them.
343
- credentialEnvVars: (cleaned.credentialEnvVars ?? defaults?.envVars ?? [])
344
- .filter((name) => !/value|token|secret|password/i.test(name)),
345
- baseUrl: sanitizeBaseUrl(cleaned.baseUrl) ?? defaults?.baseUrl,
346
- model: sanitizeModel(cleaned.model) ?? defaults?.model,
347
- updatedAt: at,
348
- };
349
- }),
350
- };
351
- writeJsonAtomic(providerConfigPath(location), next);
352
- return next;
353
- }
354
-
355
- export function emptyProviderSecrets(updatedAt = nowIso()): ProviderSecretsFile {
356
- return { schemaVersion: 1, updatedAt, providers: {} };
357
- }
358
-
359
- export function readProviderSecrets(location: GoalLoopConfigLocation): ProviderSecretsFile {
360
- const raw = readJsonFile<ProviderSecretsFile | undefined>(providerSecretsPath(location), undefined);
361
- if (!raw || typeof raw !== 'object') return emptyProviderSecrets();
362
- const providers: Record<string, ProviderSecretEntry> = {};
363
- if (raw.providers && typeof raw.providers === 'object') {
364
- for (const [id, entry] of Object.entries(raw.providers)) {
365
- if (!entry || typeof entry !== 'object') continue;
366
- const apiKey = typeof entry.apiKey === 'string' ? entry.apiKey.trim() : undefined;
367
- if (apiKey) providers[id] = { apiKey, updatedAt: entry.updatedAt };
368
- }
369
- }
370
- return { schemaVersion: 1, updatedAt: raw.updatedAt ?? nowIso(), providers };
371
- }
372
-
373
- export function writeProviderSecrets(location: GoalLoopConfigLocation, secrets: ProviderSecretsFile): ProviderSecretsFile {
374
- const at = nowIso();
375
- const next: ProviderSecretsFile = {
376
- schemaVersion: 1,
377
- updatedAt: at,
378
- providers: {},
379
- };
380
- for (const [id, entry] of Object.entries(secrets.providers ?? {})) {
381
- if (!REMOTE_API_PROVIDER_IDS.includes(id)) continue;
382
- const apiKey = typeof entry?.apiKey === 'string' ? entry.apiKey.trim() : '';
383
- if (!apiKey) continue;
384
- if (apiKey.length > 8_000) continue;
385
- next.providers[id] = { apiKey, updatedAt: at };
386
- }
387
- writeJsonAtomic(providerSecretsPath(location), next);
388
- return next;
389
- }
390
-
391
- export function getStoredProviderApiKey(location: GoalLoopConfigLocation, providerId: string): string | undefined {
392
- const secrets = readProviderSecrets(location);
393
- const key = secrets.providers[providerId]?.apiKey?.trim();
394
- return key || undefined;
395
- }
396
-
397
- export function hasStoredProviderApiKey(location: GoalLoopConfigLocation, providerId: string): boolean {
398
- return Boolean(getStoredProviderApiKey(location, providerId));
399
- }
400
-
401
- /** Last 4 chars only — safe for GUI status. */
402
- export function maskApiKeyHint(apiKey: string | undefined): string | undefined {
403
- if (!apiKey || apiKey.length < 4) return apiKey ? '••••' : undefined;
404
- return `…${apiKey.slice(-4)}`;
405
- }
406
-
407
- export function setStoredProviderApiKey(
408
- location: GoalLoopConfigLocation,
409
- providerId: string,
410
- apiKey: string | null | undefined,
411
- ): ProviderSecretsFile {
412
- if (!REMOTE_API_PROVIDER_IDS.includes(providerId)) {
413
- throw new Error(`PROVIDER_NOT_REMOTE_API: ${providerId} does not support stored API keys`);
414
- }
415
- const secrets = readProviderSecrets(location);
416
- if (apiKey === null || apiKey === undefined || String(apiKey).trim() === '') {
417
- delete secrets.providers[providerId];
418
- } else {
419
- secrets.providers[providerId] = { apiKey: String(apiKey).trim(), updatedAt: nowIso() };
420
- }
421
- return writeProviderSecrets(location, secrets);
422
- }
423
-
424
- /**
425
- * Update non-secret API settings (baseUrl, model) on a remote provider preference.
426
- */
427
- export function updateProviderApiPreference(
428
- location: GoalLoopConfigLocation,
429
- providerId: string,
430
- patch: { baseUrl?: string; model?: string },
431
- ): ProviderConfigFile {
432
- if (!REMOTE_API_PROVIDER_IDS.includes(providerId)) {
433
- throw new Error(`PROVIDER_NOT_REMOTE_API: ${providerId}`);
434
- }
435
- const config = readProviderConfig(location);
436
- const defaults = REMOTE_API_DEFAULTS[providerId]!;
437
- const providers = config.providers.map((p) => {
438
- if (p.providerId !== providerId) return p;
439
- return {
440
- ...p,
441
- baseUrl: patch.baseUrl !== undefined
442
- ? (sanitizeBaseUrl(patch.baseUrl) ?? defaults.baseUrl)
443
- : (p.baseUrl ?? defaults.baseUrl),
444
- model: patch.model !== undefined
445
- ? (sanitizeModel(patch.model) ?? defaults.model)
446
- : (p.model ?? defaults.model),
447
- updatedAt: nowIso(),
448
- };
449
- });
450
- if (!providers.some((p) => p.providerId === providerId)) {
451
- providers.push({
452
- providerId,
453
- enabled: true,
454
- priority: 50,
455
- baseUrl: sanitizeBaseUrl(patch.baseUrl) ?? defaults.baseUrl,
456
- model: sanitizeModel(patch.model) ?? defaults.model,
457
- credentialEnvVars: defaults.envVars,
458
- updatedAt: nowIso(),
459
- });
460
- }
461
- return writeProviderConfig(location, { ...config, providers });
462
- }
463
-
464
- export function resolveProviderAuthPresent(
465
- location: GoalLoopConfigLocation | undefined,
466
- providerId: string,
467
- env: NodeJS.ProcessEnv,
468
- envVarNames: string[],
469
- ): { envAuth: boolean; storedAuth: boolean; authPresent: boolean; apiKey?: string } {
470
- const envAuth = envVarNames.some((name) => Boolean(env[name]?.trim()));
471
- let storedKey: string | undefined;
472
- if (location) {
473
- try {
474
- storedKey = getStoredProviderApiKey(location, providerId);
475
- } catch {
476
- storedKey = undefined;
477
- }
478
- }
479
- const storedAuth = Boolean(storedKey);
480
- // Prefer env key when both present (ops convention).
481
- const apiKey = envVarNames.map((n) => env[n]?.trim()).find(Boolean) || storedKey;
482
- return { envAuth, storedAuth, authPresent: envAuth || storedAuth, apiKey };
483
- }
484
-
485
- export function readLocalToolConfig(location: GoalLoopConfigLocation): LocalToolConfigFile {
486
- return mergeToolDefaults(readJsonFile(localToolConfigPath(location), undefined));
487
- }
488
-
489
- export function writeLocalToolConfig(location: GoalLoopConfigLocation, config: LocalToolConfigFile): LocalToolConfigFile {
490
- const at = nowIso();
491
- const next = { ...mergeToolDefaults(config), updatedAt: at };
492
- writeJsonAtomic(localToolConfigPath(location), next);
493
- return next;
494
- }
495
-
496
- export function readRoutingConfig(location: GoalLoopConfigLocation): ExecutorRoutingConfigFile {
497
- return mergeRoutingDefaults(
498
- readJsonFile(routingConfigPath(location), undefined),
499
- readProviderConfig(location),
500
- );
501
- }
502
-
503
- export function writeRoutingConfig(location: GoalLoopConfigLocation, config: ExecutorRoutingConfigFile): ExecutorRoutingConfigFile {
504
- const at = nowIso();
505
- // Strip handoff-only from non-terminal direct slots? Allow handoff only at end — validated by facade.
506
- const next = { ...mergeRoutingDefaults(config, readProviderConfig(location)), updatedAt: at };
507
- writeJsonAtomic(routingConfigPath(location), next);
508
- return next;
509
- }
510
-
511
- /** Remove explicit routing overrides and resume generated provider-priority routing. */
512
- export function resetRoutingConfig(location: GoalLoopConfigLocation): ExecutorRoutingConfigFile {
513
- rmSync(routingConfigPath(location), { force: true });
514
- return readRoutingConfig(location);
515
- }
516
-
517
- export function readGoalLoopPolicyConfig(location: GoalLoopConfigLocation): GoalLoopPolicyConfigFile {
518
- return mergePolicyDefaults(readJsonFile(policyConfigPath(location), undefined));
519
- }
520
-
521
- export function writeGoalLoopPolicyConfig(location: GoalLoopConfigLocation, config: GoalLoopPolicyConfigFile): GoalLoopPolicyConfigFile {
522
- const at = nowIso();
523
- const next = { ...mergePolicyDefaults(config), updatedAt: at };
524
- writeJsonAtomic(policyConfigPath(location), next);
525
- return next;
526
- }
527
-
528
- export function isProviderEnabledInConfig(config: ProviderConfigFile, providerId: string): boolean {
529
- const entry = config.providers.find((p) => p.providerId === providerId);
530
- if (!entry) return true;
531
- return entry.enabled !== false;
532
- }
533
-
534
- export function isLocalToolEnabledInConfig(config: LocalToolConfigFile, toolId: string): boolean {
535
- const entry = config.tools.find((t) => t.toolId === toolId);
536
- if (!entry) return true;
537
- return entry.enabled !== false;
538
- }
539
-
540
- export function providerPriorityMap(config: ProviderConfigFile): Map<string, number> {
541
- return new Map(config.providers.map((p) => [p.providerId, p.priority]));
542
- }
543
-
544
- export function sortProvidersByPriority<T extends { providerId: string }>(
545
- providers: T[],
546
- config: ProviderConfigFile,
547
- ): T[] {
548
- const priorities = providerPriorityMap(config);
549
- return [...providers].sort((a, b) => {
550
- const pa = priorities.get(a.providerId) ?? 500;
551
- const pb = priorities.get(b.providerId) ?? 500;
552
- if (pa !== pb) return pa - pb;
553
- return a.providerId.localeCompare(b.providerId);
554
- });
555
- }
556
-
557
- /** Effective live mode: env flag AND GUI preference. */
558
- export function isLiveModelProvidersEffective(
559
- config: ProviderConfigFile,
560
- env: NodeJS.ProcessEnv = process.env,
561
- ): boolean {
562
- const envEnabled = env.FORGE_ENABLE_LIVE_MODEL_PROVIDERS === '1'
563
- || env.FORGE_ENABLE_LIVE_MODEL_PROVIDERS === 'true';
564
- return envEnabled && config.preferLiveModelProviders === true;
565
- }
566
-
567
- export function liveModelProvidersEnvEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
568
- return env.FORGE_ENABLE_LIVE_MODEL_PROVIDERS === '1'
569
- || env.FORGE_ENABLE_LIVE_MODEL_PROVIDERS === 'true';
570
- }