@moretea-labs/forge 1.8.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (528) hide show
  1. package/AGENTS.md +70 -7
  2. package/CHANGELOG.md +14 -0
  3. package/CLAUDE.md +17 -2
  4. package/CONTRIBUTING.md +3 -2
  5. package/THIRD_PARTY_NOTICES.md +1 -1
  6. package/adapters/chatgpt/browser-delivery-host.ts +7 -1
  7. package/adapters/chatgpt/browser-delivery-runtime.ts +529 -56
  8. package/adapters/chatgpt/controller-binding-store.ts +101 -22
  9. package/adapters/chatgpt/controller-round-host.ts +15 -25
  10. package/adapters/chatgpt/controller-round-settlement-store.ts +58 -4
  11. package/adapters/chatgpt/provider-delivery.ts +228 -1
  12. package/adapters/chatgpt/work-conversation-binding-store.ts +79 -53
  13. package/adapters/computer/chatgpt-extension-target.ts +463 -0
  14. package/adapters/computer/chatgpt-target-router.ts +103 -0
  15. package/adapters/computer/chrome-extension/background.js +385 -0
  16. package/adapters/computer/chrome-extension/content.js +378 -0
  17. package/adapters/computer/chrome-extension/core.js +24 -0
  18. package/adapters/computer/chrome-extension/manifest.json +30 -0
  19. package/adapters/computer/desktop-operator-discovery.ts +11 -31
  20. package/adapters/computer/interaction-target-authority.ts +142 -18
  21. package/adapters/mcp/auth.ts +0 -1
  22. package/adapters/mcp/instructions.ts +5 -4
  23. package/adapters/mcp/multi-repository.ts +3 -19
  24. package/adapters/mcp/oauth.ts +73 -0
  25. package/adapters/mcp/paths.ts +4 -1
  26. package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +87 -0
  27. package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
  28. package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
  29. package/adapters/mcp/runtime-gateway/context-adapter.ts +411 -73
  30. package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
  31. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +20 -5
  32. package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
  33. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +10 -27
  34. package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
  35. package/adapters/mcp/runtime-gateway/execution-tools.ts +8 -42
  36. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +2 -1
  37. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +117 -45
  38. package/adapters/mcp/runtime-gateway/process-tools.ts +190 -22
  39. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +16 -4
  40. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +69 -52
  41. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +4 -35
  42. package/adapters/mcp/runtime-gateway/result-adapter.ts +10 -4
  43. package/adapters/mcp/runtime-gateway/router.ts +18 -504
  44. package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
  45. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +14 -65
  46. package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
  47. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +113 -173
  48. package/adapters/mcp/runtime-gateway/runtime-tools.ts +15 -5
  49. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +1 -0
  50. package/adapters/mcp/runtime-gateway/shared-adapter.ts +20 -0
  51. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +358 -266
  52. package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
  53. package/adapters/mcp/runtime-gateway/work-adapter.ts +261 -3122
  54. package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
  55. package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
  56. package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
  57. package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
  58. package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
  59. package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
  60. package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
  61. package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
  62. package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
  63. package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
  64. package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
  65. package/adapters/mcp/server.ts +67 -23
  66. package/adapters/mcp/setup.ts +1 -3
  67. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +44 -170
  68. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +4 -9
  69. package/adapters/mcp/tool-mapping/repository-tools.ts +133 -142
  70. package/adapters/mcp/toolset-names.ts +5 -0
  71. package/adapters/mcp/toolset.ts +6 -5
  72. package/adapters/mcp/transports/http-observation.ts +365 -0
  73. package/adapters/mcp/transports/http.ts +154 -677
  74. package/adapters/mcp/transports/oauth-http.ts +322 -0
  75. package/adapters/mcp/transports/session-registry.ts +6 -0
  76. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +89 -5
  77. package/assets/plugin-registry.v1.json +6 -4
  78. package/assets/reference-configs/agentic-development-flow.md +25 -0
  79. package/assets/reference-configs/global-working-rules.md +19 -0
  80. package/assets/skill-version.json +2 -2
  81. package/docs/architecture/CURRENT.md +111 -41
  82. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
  83. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
  84. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +2 -0
  85. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +19 -4
  86. package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
  87. package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
  88. package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
  89. package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
  90. package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
  91. package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
  92. package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
  93. package/docs/architecture/domains/verification.md +1 -0
  94. package/docs/architecture/domains/workflow-engine.md +6 -0
  95. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  96. package/docs/forge-plugin-management.md +2 -2
  97. package/docs/operations/controller-performance-and-502.md +17 -0
  98. package/docs/operations/controller-reliability-runbook.md +2 -2
  99. package/docs/operations/features.md +1 -1
  100. package/docs/operations/features.zh-CN.md +1 -1
  101. package/docs/operations/homebrew.md +2 -2
  102. package/docs/operations/mcp-tool-exposure.md +2 -0
  103. package/docs/operations/releasing.md +2 -0
  104. package/docs/operations/standalone-disaster-recovery.md +14 -6
  105. package/docs/wiki/Implementation.md +1 -1
  106. package/package.json +25 -14
  107. package/packages/kernel/cognition/api/index.ts +6 -0
  108. package/packages/kernel/cognition/application/activation-service.ts +451 -0
  109. package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
  110. package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
  111. package/packages/kernel/cognition/application/memory-service.ts +64 -0
  112. package/packages/kernel/cognition/domain/learning.ts +106 -0
  113. package/packages/kernel/cognition/domain/memory.ts +254 -0
  114. package/packages/kernel/controller/api/index.ts +1 -0
  115. package/packages/kernel/controller/application/continuation-service.ts +281 -0
  116. package/packages/kernel/controller/application/controller-round-service.ts +12 -0
  117. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +242 -21
  118. package/packages/kernel/controller/domain/controller-round.ts +4 -1
  119. package/packages/kernel/controller/domain/execution-quality.ts +3 -1
  120. package/packages/kernel/controller/infrastructure/controller-round-store.ts +689 -103
  121. package/packages/kernel/controller/infrastructure/controller-session-store.ts +297 -71
  122. package/packages/kernel/controller/ports/controller-host.ts +2 -0
  123. package/packages/kernel/identity/api/index.ts +10 -0
  124. package/packages/kernel/identity/domain/activity-projection.ts +43 -0
  125. package/packages/kernel/identity/domain/capability-broker.ts +60 -0
  126. package/packages/kernel/identity/domain/grant.ts +72 -0
  127. package/packages/kernel/identity/domain/owned-resource.ts +116 -0
  128. package/packages/kernel/identity/domain/user-request.ts +93 -0
  129. package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
  130. package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
  131. package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
  132. package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
  133. package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
  134. package/packages/kernel/scheduler/api/index.ts +0 -2
  135. package/packages/kernel/scheduler/application/schedule-service.ts +3 -0
  136. package/packages/kernel/scheduler/application/settlement.ts +0 -15
  137. package/packages/kernel/scheduler/domain/schedule.ts +87 -0
  138. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +153 -133
  139. package/packages/kernel/work/api/index.ts +3 -2
  140. package/packages/kernel/work/application/work-service.ts +17 -42
  141. package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
  142. package/packages/kernel/work/domain/engineering-design.ts +10 -5
  143. package/packages/kernel/work/domain/engineering-profile.ts +8 -12
  144. package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
  145. package/packages/kernel/work/domain/execution-concurrency.ts +19 -26
  146. package/packages/kernel/work/domain/execution-snapshot.ts +0 -31
  147. package/packages/kernel/work/domain/implementation-review.ts +14 -331
  148. package/packages/kernel/work/domain/objective-graph.ts +119 -0
  149. package/packages/kernel/work/domain/project-engineering-contract.ts +23 -1
  150. package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
  151. package/packages/kernel/work/domain/state-machine.ts +25 -63
  152. package/packages/kernel/work/domain/types.ts +57 -51
  153. package/packages/kernel/work/infrastructure/work-contract-store.ts +819 -728
  154. package/packages/kernel/work/ports/work-contract-store.ts +5 -2
  155. package/packages/plugin-runtime/browser/session-authority.ts +2 -1
  156. package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
  157. package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
  158. package/packages/plugin-runtime/computer/index.ts +2 -0
  159. package/packages/plugin-runtime/computer/target-authority.ts +26 -1
  160. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +142 -18
  161. package/packages/protocols/computer/contract.ts +27 -1
  162. package/scripts/activate-source-baseline.ts +7 -9
  163. package/scripts/canary-controller-semantic-wait.ts +12 -24
  164. package/scripts/check-mcp-compatibility.ts +48 -136
  165. package/scripts/check-runtime-architecture.mjs +622 -217
  166. package/scripts/forge-local-recovery-helper.mjs +93 -15
  167. package/scripts/lib/project-init-lib.sh +3 -0
  168. package/scripts/open-source-audit-allowlist.txt +1 -2
  169. package/scripts/run-bounded-command.mjs +5 -0
  170. package/scripts/run-governed-gate.ts +0 -1
  171. package/scripts/run-mobile-monitor-bridge.ts +0 -1
  172. package/scripts/smoke-control-plane-failure-domain.ts +94 -0
  173. package/scripts/smoke-mcp-http-runtime.ts +5 -0
  174. package/scripts/smoke-runtime-recovery.ts +12 -43
  175. package/scripts/smoke-schedule-engine.ts +23 -15
  176. package/scripts/smoke-unattended-transport-recovery.ts +223 -0
  177. package/scripts/smoke-user-request-continuation-restart.ts +232 -0
  178. package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
  179. package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
  180. package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
  181. package/scripts/smoke-workflow-supervisor.ts +103 -0
  182. package/scripts/verify-forge-runtime.sh +2 -1
  183. package/src/cli/agent-jobs/executor-health.ts +11 -34
  184. package/src/cli/agent-jobs/integration.ts +18 -49
  185. package/src/cli/agent-jobs/job-manager.ts +7 -3
  186. package/src/cli/commands/brain-assistant.ts +27 -13
  187. package/src/cli/commands/brain.ts +13 -15
  188. package/src/cli/commands/chatgpt.ts +1 -163
  189. package/src/cli/commands/recovery.ts +135 -121
  190. package/src/cli/commands/runtime.ts +3 -3
  191. package/src/cli/commands/setup-profile.ts +8 -0
  192. package/src/cli/controller/check-runner.ts +13 -2
  193. package/src/cli/controller/completion-backlog.ts +3 -3
  194. package/src/cli/controller/completion-orchestrator.ts +4 -43
  195. package/src/cli/controller/completion-target.ts +3 -72
  196. package/src/cli/controller/context/source-materializer.ts +81 -100
  197. package/src/cli/controller/context-pack.ts +10 -8
  198. package/src/cli/controller/execution-completion.ts +4 -7
  199. package/src/cli/controller/execution-policy.ts +36 -153
  200. package/src/cli/controller/governance.ts +2 -3
  201. package/src/cli/controller/issue-store.ts +4 -99
  202. package/src/cli/controller/launch-agents.ts +59 -18
  203. package/src/cli/controller/operational-plan.ts +24 -51
  204. package/src/cli/controller/runtime-config.ts +5 -1
  205. package/src/cli/controller/stuck-state-migration.ts +2 -3
  206. package/src/cli/controller/systemd-user.ts +39 -1
  207. package/src/cli/editing/edit-session.ts +1 -7
  208. package/src/cli/local-bridge/console-automations.ts +18 -13
  209. package/src/cli/local-bridge/console-view-models.ts +3 -12
  210. package/src/cli/local-bridge/facade-api.ts +64 -191
  211. package/src/cli/local-bridge/server.ts +24 -147
  212. package/src/cli/local-bridge/types.ts +0 -1
  213. package/src/cli/mcp/oauth-client-retirement.ts +74 -0
  214. package/src/cli/repositories/command-executor.ts +29 -23
  215. package/src/cli/repositories/command-scope.ts +35 -16
  216. package/src/cli/repositories/controller-home.ts +48 -0
  217. package/src/cli/repositories/ephemeral-workspace.ts +20 -30
  218. package/src/cli/repositories/git-command-executor.ts +2 -2
  219. package/src/cli/repositories/registry.ts +77 -11
  220. package/src/cli/repositories/structured-git.ts +62 -2
  221. package/src/cli/repository/session-cache.ts +92 -6
  222. package/src/runtime/context/assistant-context.ts +20 -7
  223. package/src/runtime/context/assistant-work-context.ts +89 -27
  224. package/src/runtime/context/automatic-learning.ts +565 -0
  225. package/src/runtime/context/cognitive-attention.ts +173 -0
  226. package/src/runtime/context/cognitive-skill-canary.ts +158 -0
  227. package/src/runtime/context/context-closure.ts +46 -9
  228. package/src/runtime/context/context-pack-process.ts +103 -0
  229. package/src/runtime/context/context-plane.ts +1 -9
  230. package/src/runtime/context/direct-controller-learning.ts +298 -0
  231. package/src/runtime/context/semantic-navigation-contract.ts +9 -0
  232. package/src/runtime/context/semantic-navigation.ts +6 -34
  233. package/src/runtime/context/typescript-navigation-process.ts +145 -0
  234. package/src/runtime/context/typescript-navigation.ts +89 -3
  235. package/src/runtime/control-plane/cleanup-artifact-retention.ts +212 -9
  236. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +9 -47
  237. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +34 -4
  238. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +29 -20
  239. package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
  240. package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
  241. package/src/runtime/control-plane/execution/retained-work-resume.ts +206 -199
  242. package/src/runtime/control-plane/execution/session-store.ts +31 -1
  243. package/src/runtime/control-plane/execution/validation.ts +6 -8
  244. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +13 -42
  245. package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
  246. package/src/runtime/control-plane/execution/work-completion-authority.ts +30 -85
  247. package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
  248. package/src/runtime/control-plane/execution/work-evidence-policy.ts +12 -50
  249. package/src/runtime/control-plane/execution/work-execution-support.ts +50 -149
  250. package/src/runtime/control-plane/execution/work-handle-authority.ts +188 -109
  251. package/src/runtime/control-plane/execution/work-handle-store.ts +19 -43
  252. package/src/runtime/control-plane/execution/work-head-settlement.ts +53 -20
  253. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +52 -29
  254. package/src/runtime/control-plane/execution/work-operation-service.ts +43 -27
  255. package/src/runtime/control-plane/execution/work-preparation-service.ts +126 -266
  256. package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
  257. package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
  258. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +173 -186
  259. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -84
  260. package/src/runtime/control-plane/execution/work-verification-service.ts +406 -50
  261. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
  262. package/src/runtime/control-plane/facade/capability-registry.ts +362 -37
  263. package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
  264. package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
  265. package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
  266. package/src/runtime/control-plane/facade/goal-workloop-access.ts +1 -20
  267. package/src/runtime/control-plane/facade/goal-workloop.ts +281 -1753
  268. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +12 -25
  269. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +256 -52
  270. package/src/runtime/control-plane/facade/index.ts +0 -1
  271. package/src/runtime/control-plane/facade/operation-digest.ts +12 -15
  272. package/src/runtime/control-plane/facade/plan-contract-store.ts +451 -991
  273. package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
  274. package/src/runtime/control-plane/facade/repository-work-admission.ts +12 -22
  275. package/src/runtime/control-plane/facade/requirement-authority.ts +102 -199
  276. package/src/runtime/control-plane/facade/requirement-board.ts +3 -3
  277. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +51 -14
  278. package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
  279. package/src/runtime/control-plane/facade/suggested-actions.ts +15 -18
  280. package/src/runtime/control-plane/facade/types.ts +49 -108
  281. package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
  282. package/src/runtime/control-plane/generated-cache-retention.ts +97 -1
  283. package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
  284. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +1 -1
  285. package/src/runtime/control-plane/global-scheduler/maintenance.ts +239 -112
  286. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +2 -1
  287. package/src/runtime/control-plane/global-scheduler/scheduler.ts +66 -40
  288. package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
  289. package/src/runtime/control-plane/governance/access-policy.ts +19 -2
  290. package/src/runtime/control-plane/governance/authorization.ts +20 -3
  291. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +63 -300
  292. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +28 -300
  293. package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
  294. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +5 -2
  295. package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -240
  296. package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
  297. package/src/runtime/control-plane/persistence/experience-store.ts +162 -29
  298. package/src/runtime/control-plane/persistence/requirement-store.ts +216 -74
  299. package/src/runtime/control-plane/persistence/sqlite-store.ts +113 -6
  300. package/src/runtime/control-plane/release-retention.ts +135 -47
  301. package/src/runtime/control-plane/repository-namespace-retention.ts +2 -2
  302. package/src/runtime/control-plane/runtime-cleanup.ts +290 -21
  303. package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
  304. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +19 -5
  305. package/src/runtime/control-plane/workspace/workspace-store.ts +169 -0
  306. package/src/runtime/diagnostics/incident-repair.ts +313 -146
  307. package/src/runtime/diagnostics/mcp-timing.ts +114 -0
  308. package/src/runtime/evidence/artifact-store.ts +158 -40
  309. package/src/runtime/evidence/evidence-store.ts +112 -8
  310. package/src/runtime/evidence/output-handle-index.ts +225 -0
  311. package/src/runtime/evidence/result-store.ts +158 -44
  312. package/src/runtime/execution/jobs/store.ts +3 -4
  313. package/src/runtime/execution/managed-workspace.ts +57 -0
  314. package/src/runtime/execution/process-runtime/check-facade.ts +1 -1
  315. package/src/runtime/execution/process-runtime/check-result-retention.ts +2 -2
  316. package/src/runtime/execution/process-runtime/check-result.ts +105 -1
  317. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +18 -8
  318. package/src/runtime/execution/process-runtime/command-facade.ts +79 -27
  319. package/src/runtime/execution/process-runtime/gc.ts +15 -2
  320. package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
  321. package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
  322. package/src/runtime/execution/process-runtime/index.ts +3 -0
  323. package/src/runtime/execution/process-runtime/lightweight-managed.ts +251 -72
  324. package/src/runtime/execution/process-runtime/persisted-check.ts +3 -3
  325. package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
  326. package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
  327. package/src/runtime/execution/process-runtime/resource-claims.ts +65 -61
  328. package/src/runtime/execution/process-runtime/runtime.ts +35 -2
  329. package/src/runtime/execution/process-runtime/store.ts +30 -2
  330. package/src/runtime/execution/process-runtime/types.ts +6 -1
  331. package/src/runtime/execution/workers/executor.ts +2 -6
  332. package/src/runtime/platform/service-inventory.ts +130 -0
  333. package/src/runtime/platform/service-manager.ts +8 -0
  334. package/src/runtime/plugins/action-application.ts +85 -28
  335. package/src/runtime/plugins/app-store-connect-adapter.ts +38 -14
  336. package/src/runtime/plugins/browser-adapter.ts +757 -34
  337. package/src/runtime/plugins/browser-automation-service.ts +23 -3
  338. package/src/runtime/plugins/browser-macos-bridge.ts +321 -29
  339. package/src/runtime/plugins/browser-manifest-surface.ts +45 -11
  340. package/src/runtime/plugins/browser-native-session-service.ts +63 -13
  341. package/src/runtime/plugins/browser-registration.ts +16 -0
  342. package/src/runtime/plugins/browser-runtime.ts +14 -1
  343. package/src/runtime/plugins/browser-session-authority.ts +2 -2
  344. package/src/runtime/plugins/browser-session-store.ts +46 -76
  345. package/src/runtime/plugins/capability-authorization-grants.ts +258 -62
  346. package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
  347. package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
  348. package/src/runtime/plugins/computer-registration.ts +222 -11
  349. package/src/runtime/plugins/desktop-operator-external-policy.ts +36 -4
  350. package/src/runtime/plugins/desktop-operator-registration.ts +12 -4
  351. package/src/runtime/plugins/external-adapter.ts +42 -6
  352. package/src/runtime/plugins/external-registration.ts +21 -1
  353. package/src/runtime/plugins/gmail-adapter.ts +23 -2
  354. package/src/runtime/plugins/google-calendar-adapter.ts +2 -2
  355. package/src/runtime/plugins/google-shared.ts +14 -7
  356. package/src/runtime/plugins/google-tasks-adapter.ts +2 -2
  357. package/src/runtime/plugins/ios-adapter.ts +1 -1
  358. package/src/runtime/plugins/lightweight-action.ts +208 -2
  359. package/src/runtime/plugins/local-system-adapter.ts +12 -2
  360. package/src/runtime/plugins/managed-process-adapter.ts +12 -5
  361. package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
  362. package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
  363. package/src/runtime/plugins/store.ts +602 -428
  364. package/src/runtime/plugins/types.ts +20 -0
  365. package/src/runtime/plugins/xiaohongshu-publish.ts +16 -0
  366. package/src/runtime/projections/invalidation.ts +7 -3
  367. package/src/runtime/projections/materialized-view.ts +80 -36
  368. package/src/runtime/recovery/actions.ts +3 -3
  369. package/src/runtime/recovery/capability-action-application.ts +46 -45
  370. package/src/runtime/recovery/lifecycle-inventory.ts +29 -29
  371. package/src/runtime/recovery/maintenance-executor.ts +374 -123
  372. package/src/runtime/recovery/store.ts +29 -11
  373. package/src/runtime/release/release-coordinator.ts +296 -0
  374. package/src/runtime/release/release-session.ts +716 -0
  375. package/src/runtime/release/release-state-migration.ts +25 -0
  376. package/src/runtime/resources/leases/store.ts +56 -25
  377. package/src/runtime/root/computer-composition.ts +191 -6
  378. package/src/runtime/root/controller-progression-composition.ts +192 -0
  379. package/src/runtime/root/controller-round-composition.ts +20 -1
  380. package/src/runtime/root/deployment-topology.ts +106 -0
  381. package/src/runtime/root/entry.ts +4 -3
  382. package/src/runtime/root/known-good-recovery.ts +184 -0
  383. package/src/runtime/root/local-bridge.ts +0 -1
  384. package/src/runtime/root/mcp-transport.ts +111 -18
  385. package/src/runtime/root/ownership.ts +261 -9
  386. package/src/runtime/root/package-connector-service.ts +105 -19
  387. package/src/runtime/root/package-runtime-release.ts +117 -18
  388. package/src/runtime/root/package-runtime-service.ts +17 -11
  389. package/src/runtime/root/release-execution-canary.ts +44 -12
  390. package/src/runtime/root/release-loader.ts +90 -8
  391. package/src/runtime/root/release-manifest.ts +80 -6
  392. package/src/runtime/root/release-materialize.ts +372 -28
  393. package/src/runtime/root/release-store.ts +395 -36
  394. package/src/runtime/root/runtime-lane.ts +191 -0
  395. package/src/runtime/root/runtime.ts +159 -27
  396. package/src/runtime/root/scheduled-controller-composition.ts +28 -3
  397. package/src/runtime/root/service-runner.ts +2 -2
  398. package/src/runtime/root/service.ts +36 -33
  399. package/src/runtime/root/status.ts +48 -0
  400. package/src/runtime/root/types.ts +32 -3
  401. package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
  402. package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
  403. package/src/runtime/root/write-fence.ts +50 -2
  404. package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
  405. package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
  406. package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
  407. package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
  408. package/src/runtime/shared/process-environment.ts +20 -9
  409. package/src/runtime/shared/response-budget.ts +1 -3
  410. package/src/runtime/standalone-recovery/controller-home-migration.ts +16 -24
  411. package/src/runtime/standalone-recovery/core.ts +3702 -342
  412. package/src/runtime/standalone-recovery/entry.ts +469 -39
  413. package/src/runtime/standalone-recovery/installer.ts +262 -148
  414. package/src/runtime/standalone-recovery/mcp-server.ts +29 -193
  415. package/src/runtime/standalone-recovery/operation-lock.ts +1 -1
  416. package/src/runtime/standalone-recovery/performance.ts +54 -16
  417. package/src/runtime/standalone-recovery/release.ts +53 -5
  418. package/src/runtime/standalone-recovery/service-labels.ts +5 -2
  419. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
  420. package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
  421. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +10 -3
  422. package/src/runtime/workflow/schedules/engine.ts +138 -172
  423. package/src/runtime/workflow/schedules/work-continuation.ts +44 -5
  424. package/src/runtime/workflows/runtime.ts +15 -19
  425. package/src/testing/test-governance.ts +5 -1
  426. package/supervisor/chatgpt-conversation.ts +23 -0
  427. package/supervisor/client.ts +201 -0
  428. package/supervisor/control-plane.ts +868 -0
  429. package/supervisor/entry.ts +15 -0
  430. package/supervisor/forge-validators.ts +80 -0
  431. package/supervisor/index.ts +5 -0
  432. package/supervisor/native-browser-adapter.ts +786 -0
  433. package/supervisor/native-messaging/host.ts +39 -0
  434. package/supervisor/native-messaging/manifest.ts +26 -0
  435. package/supervisor/native-messaging/protocol.ts +27 -0
  436. package/supervisor/paths.ts +18 -0
  437. package/supervisor/protocol.ts +202 -0
  438. package/supervisor/server.ts +404 -0
  439. package/supervisor/store.ts +1451 -0
  440. package/supervisor/tsconfig.json +8 -0
  441. package/supervisor/types.ts +255 -0
  442. package/adapters/chatgpt/controller-host.ts +0 -119
  443. package/adapters/mcp/controller-round-compatibility.ts +0 -184
  444. package/adapters/mcp/frozen-client-semantic-compatibility.ts +0 -292
  445. package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +0 -502
  446. package/packages/kernel/progression/api/index.ts +0 -2
  447. package/packages/kernel/progression/application/projector.ts +0 -211
  448. package/packages/kernel/progression/domain/types.ts +0 -116
  449. package/packages/kernel/progression/index.ts +0 -1
  450. package/packages/kernel/scheduler/application/continuation-service.ts +0 -212
  451. package/packages/kernel/scheduler/domain/continuation.ts +0 -38
  452. package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +0 -38
  453. package/packages/kernel/work/domain/admission-policy.ts +0 -34
  454. package/scripts/benchmark-background-check-overlap.ts +0 -228
  455. package/scripts/benchmark-computer-hotpath.ts +0 -255
  456. package/scripts/benchmark-controller-hotpath.ts +0 -212
  457. package/scripts/benchmark-controller-round2.ts +0 -191
  458. package/scripts/benchmark-ios-agent-device.ts +0 -511
  459. package/scripts/benchmark-mcp-host.sh +0 -117
  460. package/scripts/benchmark-mcp-transport.ts +0 -169
  461. package/scripts/benchmark-operational-memory-activation.ts +0 -235
  462. package/scripts/benchmark-operational-shadow.ts +0 -28
  463. package/scripts/benchmark-quality-harness.ts +0 -146
  464. package/scripts/benchmark-route-session-concurrency.ts +0 -915
  465. package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
  466. package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
  467. package/scripts/benchmark-thin-harness.ts +0 -251
  468. package/scripts/benchmark-typescript-navigation.ts +0 -186
  469. package/scripts/benchmark-work-efficiency.ts +0 -334
  470. package/scripts/build-controller-ui.ts +0 -28
  471. package/scripts/check-npm-release.sh +0 -32
  472. package/scripts/check-public-release-surface.sh +0 -19
  473. package/scripts/check-release-published.sh +0 -57
  474. package/scripts/check-release-readiness.sh +0 -37
  475. package/scripts/check-release-version.mjs +0 -74
  476. package/scripts/check-tarball-install-smoke.sh +0 -150
  477. package/scripts/package-source-archive.sh +0 -147
  478. package/scripts/public-release-files.txt +0 -39
  479. package/scripts/public-release.ts +0 -174
  480. package/scripts/publish-release-tarball.sh +0 -27
  481. package/scripts/route-nl-vs-ts-eval.ts +0 -679
  482. package/scripts/stage-runtime-release.ts +0 -44
  483. package/src/cli/controller/progress.ts +0 -656
  484. package/src/cli/controller/work-mode.ts +0 -181
  485. package/src/cli/local-bridge/console-assets.ts +0 -17
  486. package/src/cli/local-bridge/dashboard.ts +0 -17
  487. package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
  488. package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
  489. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
  490. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
  491. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
  492. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
  493. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
  494. package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
  495. package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
  496. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
  497. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
  498. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
  499. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
  500. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
  501. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
  502. package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
  503. package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
  504. package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
  505. package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
  506. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
  507. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
  508. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
  509. package/src/cli/local-bridge/frontend/src/styles.css +0 -28
  510. package/src/cli/local-bridge/frontend/src/types.ts +0 -15
  511. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
  512. package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
  513. package/src/cli/local-bridge/ui-dist/app.css +0 -1
  514. package/src/cli/local-bridge/ui-dist/app.js +0 -11228
  515. package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +0 -169
  516. package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +0 -85
  517. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -655
  518. package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -2850
  519. package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
  520. package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -468
  521. package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -3
  522. package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -142
  523. package/src/runtime/control-plane/facade/work-implementation-review.ts +0 -2
  524. package/src/runtime/control-plane/facade/work-state-machine.ts +0 -2
  525. package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
  526. package/src/runtime/control-plane/routing/index.ts +0 -1
  527. package/src/runtime/control-plane/routing/route-policy.ts +0 -401
  528. package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
@@ -0,0 +1,209 @@
1
+ import { once } from 'node:events';
2
+ import { WorkflowSupervisorControlPlane } from '../../../supervisor/control-plane';
3
+ import { forgeWorkflowSupervisorValidators } from '../../../supervisor/forge-validators';
4
+ import { forgeWorkflowSupervisorLifecycleHooks } from './workflow-supervisor-composition';
5
+ import { resolveWorkflowSupervisorForgeHome, workflowSupervisorSocketPath } from '../../../supervisor/paths';
6
+ import {
7
+ createWorkflowSupervisorServer,
8
+ reconcileWorkflowSupervisorSocket,
9
+ WorkflowSupervisorEphemeralDiscovery,
10
+ } from '../../../supervisor/server';
11
+ import { startWorkflowSupervisorNativeBrowserAdapter, type WorkflowSupervisorNativeBrowserHandle, type WorkflowSupervisorStallProjection, type WorkflowSupervisorTransportProjection } from '../../../supervisor/native-browser-adapter';
12
+ import type { WorkflowSupervisorConsumerStatus } from '../../../supervisor/types';
13
+ import { WorkflowSupervisorStore } from '../../../supervisor/store';
14
+ import { createComputerInteractionTargetAuthority } from '../../../adapters/computer/interaction-target-authority';
15
+ import { MacOsChatgptConversationTargetPort } from '../plugins/computer-chatgpt-macos-target';
16
+ import { ChromeExtensionChatgptConversationTargetPort } from '../../../adapters/computer/chatgpt-extension-target';
17
+ import { PreferredChatgptConversationTargetPort } from '../../../adapters/computer/chatgpt-target-router';
18
+ import { createRuntimeComputerTargetPersistence } from './computer-target-persistence';
19
+ import { listDirectActivities, listUserRequests, recordDirectActivity, recordUserRequest, resolveUserRequest } from '../../../packages/kernel/identity/api/index';
20
+ import { getRuntimeWriteClaim } from './write-fence';
21
+
22
+ export interface RuntimeWorkflowSupervisorHandle {
23
+ readonly done: Promise<void>;
24
+ close(): Promise<void>;
25
+ }
26
+
27
+ /**
28
+ * Workflow Supervisor is a distinct lifecycle authority, not a distinct OS
29
+ * service. Canonical Forge Runtime already owns the durable process lifecycle;
30
+ * this composition gives Supervisor one in-process server/writer while keeping
31
+ * its SQLite and Unix socket authority isolated under the exact Controller Home.
32
+ */
33
+ export function startWorkflowSupervisorRuntime(controllerHome: string): Promise<RuntimeWorkflowSupervisorHandle>;
34
+ export function startWorkflowSupervisorRuntime(
35
+ controllerHome: string,
36
+ options: { nativeBrowserAdapter?: boolean },
37
+ ): Promise<RuntimeWorkflowSupervisorHandle>;
38
+ export async function startWorkflowSupervisorRuntime(
39
+ controllerHome: string,
40
+ options: { nativeBrowserAdapter?: boolean } = {},
41
+ ): Promise<RuntimeWorkflowSupervisorHandle> {
42
+ const forgeHome = resolveWorkflowSupervisorForgeHome(controllerHome);
43
+ const socketPath = workflowSupervisorSocketPath(forgeHome);
44
+ const claim = getRuntimeWriteClaim();
45
+ const writer = claim && !claim.unmanaged
46
+ ? { runtimeInstanceId: claim.runtimeInstanceId, fencingGeneration: claim.fencingGeneration, pid: claim.ownerPid }
47
+ : undefined;
48
+ if (writer) await reconcileWorkflowSupervisorSocket({ socketPath, incoming: writer });
49
+ const store = new WorkflowSupervisorStore(forgeHome, {
50
+ activeReleaseId: () => getRuntimeWriteClaim()?.releaseId,
51
+ });
52
+ const controlPlane = new WorkflowSupervisorControlPlane(
53
+ store,
54
+ forgeWorkflowSupervisorValidators(),
55
+ forgeWorkflowSupervisorLifecycleHooks(controllerHome),
56
+ );
57
+ const discovery = new WorkflowSupervisorEphemeralDiscovery();
58
+ const browserAdapterEnabled = true;
59
+ const nativeBrowserAdapterEnabled = options.nativeBrowserAdapter !== false;
60
+ let nativeBrowser: WorkflowSupervisorNativeBrowserHandle | undefined;
61
+ const transportActivityId = (taskId?: string): string => `workflow-supervisor-transport-${(taskId ?? 'instance').replace(/[^a-zA-Z0-9._-]/g, '_')}`;
62
+ const reportTransportState = (projection: WorkflowSupervisorTransportProjection): void => {
63
+ const activityId = transportActivityId(projection.taskId);
64
+ const existing = listDirectActivities(controllerHome, 200).find((item) => item.activityId === activityId);
65
+ const recovered = projection.state === 'recovered';
66
+ recordDirectActivity(controllerHome, {
67
+ activityId,
68
+ capabilityId: 'workflow-supervisor.transport',
69
+ kind: 'direct_execution',
70
+ targetScope: projection.taskId ? `workflow-supervisor-task:${projection.taskId}` : 'workflow-supervisor:instance',
71
+ principalId: 'forge-runtime',
72
+ status: recovered ? 'completed' : 'running',
73
+ startedAt: existing?.startedAt ?? projection.firstFailureAt ?? projection.observedAt,
74
+ ...(recovered ? { completedAt: projection.observedAt } : {}),
75
+ summary: recovered
76
+ ? 'Workflow Supervisor transport recovered; autonomous continuation resumed without user action.'
77
+ : `Workflow Supervisor transport is temporarily degraded and auto-recovering${projection.code ? ` (${projection.code})` : ''}. Durable task/effect progress is preserved.`,
78
+ });
79
+ if (recovered && projection.taskId) {
80
+ const prefix = `workflow-supervisor.transport-human:${projection.taskId}:`;
81
+ for (const request of listUserRequests(controllerHome, 'pending').filter((item) => item.rootCauseKey.startsWith(prefix))) {
82
+ resolveUserRequest(controllerHome, { requestId: request.requestId, decision: 'transport_recovered', resolvedBy: 'forge-runtime' });
83
+ }
84
+ }
85
+ };
86
+ const stallActivityId = (taskId: string): string => `workflow-supervisor-stall-${taskId.replace(/[^a-zA-Z0-9._-]/g, '_')}`;
87
+ const reportTaskStall = (projection: WorkflowSupervisorStallProjection): void => {
88
+ const activityId = stallActivityId(projection.taskId);
89
+ const existing = listDirectActivities(controllerHome, 200).find((item) => item.activityId === activityId);
90
+ const exhausted = projection.state === 'exhausted';
91
+ if (!exhausted && !existing) return;
92
+ const budget = projection.generations !== undefined && projection.maxGenerations !== undefined
93
+ ? ` (${projection.generations}/${projection.maxGenerations})`
94
+ : '';
95
+ recordDirectActivity(controllerHome, {
96
+ activityId,
97
+ capabilityId: 'workflow-supervisor.continuation',
98
+ kind: 'direct_execution',
99
+ targetScope: `workflow-supervisor-task:${projection.taskId}`,
100
+ principalId: 'forge-runtime',
101
+ status: exhausted ? 'running' : 'completed',
102
+ startedAt: existing?.startedAt ?? projection.observedAt,
103
+ ...(!exhausted ? { completedAt: projection.observedAt } : {}),
104
+ summary: exhausted
105
+ ? projection.stallKind === 'provider_resume_exhausted'
106
+ ? 'Workflow Supervisor exhausted bounded provider-resume attempts. The committed effect is preserved; only read-only reconciliation for late receipt evidence continues, and no duplicate provider turn will be sent.'
107
+ : `Workflow Supervisor exhausted bounded automatic dispatch generations${budget}. The task/effect remains durable, but automatic sending is paused until explicit recovery or new authoritative evidence; no duplicate provider turn will be sent.`
108
+ : 'Workflow Supervisor continuation stall cleared; the durable task/effect can progress again.',
109
+ });
110
+ };
111
+ const requestHumanAction = (input: { taskId: string; effectId?: string; action: 'login' | 'grant_permission'; code: string }): void => {
112
+ const actionLabel = input.action === 'login' ? 'sign in to ChatGPT' : 'restore browser automation permission';
113
+ recordUserRequest(controllerHome, {
114
+ kind: 'user_action_request',
115
+ rootCauseKey: `workflow-supervisor.transport-human:${input.taskId}:${input.action}`,
116
+ title: input.action === 'login' ? 'ChatGPT sign-in required' : 'Browser permission required',
117
+ summary: `Workflow Supervisor cannot continue this task until you ${actionLabel}. The task and effect ledger are preserved and will resume automatically afterward.`,
118
+ actionRequired: input.action,
119
+ targetScope: { scopeKind: 'workflow_supervisor_task', scopeId: input.taskId },
120
+ presentation: {
121
+ severity: 'blocked',
122
+ creationReason: 'workflow_supervisor_transport_human_boundary',
123
+ reason: input.code,
124
+ currentState: { taskId: input.taskId, ...(input.effectId ? { effectId: input.effectId } : {}), transportCode: input.code },
125
+ },
126
+ });
127
+ };
128
+ const targetAuthority = createComputerInteractionTargetAuthority(createRuntimeComputerTargetPersistence());
129
+ const extensionTargetPort = new ChromeExtensionChatgptConversationTargetPort(controllerHome, targetAuthority);
130
+ const appleEventsTargetPort = new MacOsChatgptConversationTargetPort(controllerHome, targetAuthority);
131
+ const targetPort = new PreferredChatgptConversationTargetPort(extensionTargetPort, appleEventsTargetPort);
132
+ const browserConsumerStatus = (): WorkflowSupervisorConsumerStatus => nativeBrowser?.status() ?? {
133
+ enabled: browserAdapterEnabled,
134
+ running: false,
135
+ observedAt: new Date().toISOString(),
136
+ transportFailureStreak: 0,
137
+ providerBackpressureMs: 0,
138
+ stalled: false,
139
+ };
140
+ const server = createWorkflowSupervisorServer({
141
+ controlPlane,
142
+ socketPath,
143
+ discovery,
144
+ browserAdapterEnabled,
145
+ browserConsumerStatus,
146
+ computerExtensionBroker: extensionTargetPort,
147
+ ...(writer ? { writer } : {}),
148
+ });
149
+ const done = once(server, 'close').then(() => undefined);
150
+ await once(server, 'listening');
151
+ let closing = false;
152
+ let reconciliationInFlight: Promise<void> | undefined;
153
+ const reconcileCommittedContinuations = (): void => {
154
+ if (closing || reconciliationInFlight) return;
155
+ reconciliationInFlight = controlPlane.reconcileCommittedContinuations()
156
+ .then(() => undefined)
157
+ .catch((error) => { process.stderr.write(`[workflow-supervisor-reconcile] ${error instanceof Error ? error.message : String(error)}\\n`); })
158
+ .finally(() => { reconciliationInFlight = undefined; });
159
+ };
160
+ // Reconcile before browser polling starts so a Runtime restart can finish a
161
+ // previously committed CONTINUE without requiring another user/provider turn.
162
+ reconcileCommittedContinuations();
163
+ // Reconciliation is a core Runtime responsibility. Keep this timer referenced
164
+ // for the whole Runtime lifetime instead of treating continuation delivery as
165
+ // best-effort background work.
166
+ const reconciliationTimer = setInterval(reconcileCommittedContinuations, 2_000);
167
+ if (nativeBrowserAdapterEnabled) {
168
+ nativeBrowser = startWorkflowSupervisorNativeBrowserAdapter(controlPlane, discovery, {
169
+ targetPort,
170
+ nowMs: () => Date.now(),
171
+ // Liveness authority for one applied provider turn. Live evidence: these
172
+ // Supervisor turns run for 10-50 minutes, so any quiet window shorter than
173
+ // that reserved duplicate recovery prompts on turns that were still
174
+ // working (60s and then 3 minutes both fired in production). This is the
175
+ // existing bounded maximum, and the digest itself now tracks live turn
176
+ // activity, so the window only elapses on a genuinely silent surface. A
177
+ // real provider failure is classified from provider failure text
178
+ // independently of this threshold.
179
+ providerIdleGraceMs: 10 * 60_000,
180
+ providerScopeKey: controllerHome,
181
+ sleep: async (ms) => { await new Promise((resolve) => setTimeout(resolve, ms)); },
182
+ setInterval: (handler, ms) => setInterval(handler, ms),
183
+ clearInterval: (timer) => clearInterval(timer),
184
+ onError: (error) => { process.stderr.write(`[workflow-supervisor-computer-target] ${error instanceof Error ? error.message : String(error)}\\n`); },
185
+ reportTransportState,
186
+ reportTaskStall,
187
+ requestHumanAction,
188
+ });
189
+ }
190
+ return {
191
+ done,
192
+ async close(): Promise<void> {
193
+ if (closing) return;
194
+ closing = true;
195
+ clearInterval(reconciliationTimer);
196
+ try {
197
+ await reconciliationInFlight?.catch(() => undefined);
198
+ await nativeBrowser?.close().catch(() => undefined);
199
+ if (server.listening) {
200
+ await new Promise<void>((resolve, reject) => {
201
+ server.close((error) => error ? reject(error) : resolve());
202
+ });
203
+ }
204
+ } finally {
205
+ store.close();
206
+ }
207
+ },
208
+ };
209
+ }
@@ -1,7 +1,7 @@
1
1
  import { resolve } from 'path';
2
2
  import { resolveControllerHome } from '../../cli/repositories/controller-home';
3
3
  import { isProcessAlive } from '../shared/process-tree';
4
- import { readRuntimeOwner, type RuntimeOwnerRecord } from './ownership';
4
+ import { readRuntimeIncarnation, readRuntimeOwner, type RuntimeOwnerRecord } from './ownership';
5
5
  import {
6
6
  readRuntimeReleaseAuthority,
7
7
  type RuntimeReleaseAuthority,
@@ -29,6 +29,7 @@ export type RuntimeWriteAction = (typeof RUNTIME_WRITE_ACTIONS)[number];
29
29
  export interface RuntimeWriteClaim {
30
30
  controllerHome: string;
31
31
  runtimeInstanceId: string;
32
+ fencingGeneration: number;
32
33
  ownerPid: number;
33
34
  releaseAuthorityRevision: number;
34
35
  releaseFencingToken: string;
@@ -50,6 +51,7 @@ export interface RuntimeWriteFenceCheck {
50
51
  export const RUNTIME_WRITE_CLAIM_ENV = {
51
52
  controllerHome: 'FORGE_CONTROLLER_HOME',
52
53
  runtimeInstanceId: 'FORGE_RUNTIME_INSTANCE_ID',
54
+ fencingGeneration: 'FORGE_RUNTIME_INCARNATION_GENERATION',
53
55
  ownerPid: 'FORGE_RUNTIME_OWNER_PID',
54
56
  releaseAuthorityRevision: 'FORGE_RELEASE_AUTHORITY_REVISION',
55
57
  releaseFencingToken: 'FORGE_RELEASE_FENCING_TOKEN',
@@ -75,6 +77,7 @@ function claimFromAuthority(
75
77
  return {
76
78
  controllerHome: resolveControllerHome(controllerHome),
77
79
  runtimeInstanceId: owner.runtimeInstanceId,
80
+ fencingGeneration: owner.fencingGeneration ?? 0,
78
81
  ownerPid: owner.pid,
79
82
  releaseAuthorityRevision: authority.revision,
80
83
  releaseFencingToken: authority.fencingToken,
@@ -85,11 +88,22 @@ function claimFromAuthority(
85
88
  };
86
89
  }
87
90
 
91
+ export function currentRuntimeWriteClaimEnvironment(controllerHome: string): Record<string, string> {
92
+ const home = resolveControllerHome(controllerHome);
93
+ const owner = readRuntimeOwner(home);
94
+ const authority = readRuntimeReleaseAuthority(home);
95
+ if (!owner || !authority) {
96
+ throw new Error('RUNTIME_WRITE_CLAIM_BIND_FAILED: Runtime owner or release authority is missing');
97
+ }
98
+ return runtimeWriteClaimEnvironment(claimFromAuthority(home, owner, authority));
99
+ }
100
+
88
101
  export function runtimeWriteClaimEnvironment(claim: RuntimeWriteClaim): Record<string, string> {
89
102
  if (claim.unmanaged) return {};
90
103
  return {
91
104
  [RUNTIME_WRITE_CLAIM_ENV.controllerHome]: claim.controllerHome,
92
105
  [RUNTIME_WRITE_CLAIM_ENV.runtimeInstanceId]: claim.runtimeInstanceId,
106
+ [RUNTIME_WRITE_CLAIM_ENV.fencingGeneration]: String(claim.fencingGeneration),
93
107
  [RUNTIME_WRITE_CLAIM_ENV.ownerPid]: String(claim.ownerPid),
94
108
  [RUNTIME_WRITE_CLAIM_ENV.releaseAuthorityRevision]: String(claim.releaseAuthorityRevision),
95
109
  [RUNTIME_WRITE_CLAIM_ENV.releaseFencingToken]: claim.releaseFencingToken,
@@ -130,6 +144,7 @@ export const clearRuntimeWriteClaimForTests = clearRuntimeWriteClaim;
130
144
  export function bindRuntimeWriteClaim(input: {
131
145
  controllerHome: string;
132
146
  runtimeInstanceId?: string;
147
+ fencingGeneration?: number;
133
148
  ownerPid?: number;
134
149
  releaseAuthorityRevision?: number;
135
150
  releaseFencingToken?: string;
@@ -146,6 +161,7 @@ export function bindRuntimeWriteClaim(input: {
146
161
 
147
162
  const explicit = {
148
163
  runtimeInstanceId: input.runtimeInstanceId?.trim(),
164
+ fencingGeneration: input.fencingGeneration,
149
165
  ownerPid: input.ownerPid,
150
166
  releaseAuthorityRevision: input.releaseAuthorityRevision,
151
167
  releaseFencingToken: input.releaseFencingToken?.trim(),
@@ -157,6 +173,7 @@ export function bindRuntimeWriteClaim(input: {
157
173
  const hasAnyExplicit = explicitValues.some((value) => value !== undefined && value !== '');
158
174
  const hasFullExplicit = Boolean(
159
175
  explicit.runtimeInstanceId
176
+ && Number.isInteger(explicit.fencingGeneration) && explicit.fencingGeneration! > 0
160
177
  && Number.isInteger(explicit.ownerPid) && explicit.ownerPid! > 0
161
178
  && Number.isInteger(explicit.releaseAuthorityRevision) && explicit.releaseAuthorityRevision! > 0
162
179
  && explicit.releaseFencingToken
@@ -169,6 +186,7 @@ export function bindRuntimeWriteClaim(input: {
169
186
  return installClaim({
170
187
  controllerHome: home,
171
188
  runtimeInstanceId: explicit.runtimeInstanceId!,
189
+ fencingGeneration: explicit.fencingGeneration!,
172
190
  ownerPid: explicit.ownerPid!,
173
191
  releaseAuthorityRevision: explicit.releaseAuthorityRevision!,
174
192
  releaseFencingToken: explicit.releaseFencingToken!,
@@ -190,6 +208,7 @@ export function bindRuntimeWriteClaim(input: {
190
208
  return installClaim({
191
209
  controllerHome: home,
192
210
  runtimeInstanceId: `unmanaged-${process.pid}`,
211
+ fencingGeneration: 0,
193
212
  ownerPid: process.pid,
194
213
  releaseAuthorityRevision: 0,
195
214
  releaseFencingToken: 'unmanaged',
@@ -220,6 +239,7 @@ export function bindInheritedRuntimeWriteClaimFromEnvironment(
220
239
  return bindRuntimeWriteClaim({
221
240
  controllerHome,
222
241
  runtimeInstanceId,
242
+ fencingGeneration: positiveInteger(env[RUNTIME_WRITE_CLAIM_ENV.fencingGeneration]),
223
243
  ownerPid: positiveInteger(env[RUNTIME_WRITE_CLAIM_ENV.ownerPid]),
224
244
  releaseAuthorityRevision: positiveInteger(env[RUNTIME_WRITE_CLAIM_ENV.releaseAuthorityRevision]),
225
245
  releaseFencingToken: env[RUNTIME_WRITE_CLAIM_ENV.releaseFencingToken]?.trim(),
@@ -256,6 +276,7 @@ export function assertRuntimeMayWrite(
256
276
  const home = explicitHome ?? claim?.controllerHome;
257
277
  if (!home) return { allowed: true, reason: 'unbound_no_controller_home' };
258
278
  const owner = readRuntimeOwner(home);
279
+ const incarnation = readRuntimeIncarnation(home);
259
280
  const authority = readRuntimeReleaseAuthority(home);
260
281
  if (!claim) {
261
282
  if (!owner && !authority) return { allowed: true, reason: 'unbound_no_runtime_authority' };
@@ -276,9 +297,18 @@ export function assertRuntimeMayWrite(
276
297
  }
277
298
  if (!owner) return { allowed: false, reason: 'runtime_owner_missing', authority };
278
299
  if (!isProcessAlive(owner.pid)) return { allowed: false, reason: 'runtime_owner_dead', owner, authority };
300
+ if (!incarnation) return { allowed: false, reason: 'runtime_incarnation_missing', owner, authority };
301
+ if (
302
+ incarnation.runtimeInstanceId !== owner.runtimeInstanceId
303
+ || incarnation.pid !== owner.pid
304
+ || incarnation.fencingGeneration !== (owner.fencingGeneration ?? 0)
305
+ ) return { allowed: false, reason: 'runtime_incarnation_authority_mismatch', owner, authority };
279
306
  if (owner.runtimeInstanceId !== claim.runtimeInstanceId) {
280
307
  return { allowed: false, reason: 'runtime_instance_fenced', owner, authority };
281
308
  }
309
+ if (incarnation.fencingGeneration !== claim.fencingGeneration) {
310
+ return { allowed: false, reason: 'runtime_incarnation_generation_fenced', owner, authority };
311
+ }
282
312
  if (owner.pid !== claim.ownerPid) return { allowed: false, reason: 'runtime_owner_pid_fenced', owner, authority };
283
313
  if (!authority) return { allowed: false, reason: 'runtime_release_authority_missing', owner };
284
314
  if (authority.revision !== claim.releaseAuthorityRevision) {
@@ -299,11 +329,29 @@ export function assertRuntimeMayWrite(
299
329
  return { allowed: true, owner, authority };
300
330
  }
301
331
 
332
+ export class RuntimeWriteFenceError extends Error {
333
+ readonly code = 'WRITER_FENCED' as const;
334
+
335
+ constructor(
336
+ readonly action: RuntimeWriteAction,
337
+ readonly reason: string,
338
+ ) {
339
+ super(`WRITER_FENCED:${action}:${reason}`);
340
+ this.name = 'RuntimeWriteFenceError';
341
+ }
342
+ }
343
+
344
+ export function isRuntimeWriteFenceError(error: unknown): error is RuntimeWriteFenceError {
345
+ return Boolean(error
346
+ && typeof error === 'object'
347
+ && (error as { code?: unknown }).code === 'WRITER_FENCED');
348
+ }
349
+
302
350
  export function assertRuntimeMayWriteOrThrow(
303
351
  action: RuntimeWriteAction,
304
352
  controllerHomeOverride?: string,
305
353
  ): RuntimeReleaseAuthority | undefined {
306
354
  const check = assertRuntimeMayWrite(action, controllerHomeOverride);
307
- if (!check.allowed) throw new Error(`WRITER_FENCED:${action}:${check.reason ?? 'denied'}`);
355
+ if (!check.allowed) throw new RuntimeWriteFenceError(action, check.reason ?? 'denied');
308
356
  return check.authority;
309
357
  }
@@ -1,30 +1,38 @@
1
1
  import { execFileSync } from 'child_process';
2
2
 
3
3
  export type StoredGoogleService = 'gmail' | 'calendar' | 'tasks' | 'google-workspace';
4
+ export type StoredGoogleCredentialKind = 'refresh-token' | 'client-secret';
4
5
 
5
6
  export interface GoogleCredentialStoreAdapter {
6
7
  available(): boolean;
7
- read(service: StoredGoogleService): string | undefined;
8
- write(service: StoredGoogleService, refreshToken: string): void;
8
+ read(service: StoredGoogleService, kind?: StoredGoogleCredentialKind): string | undefined;
9
+ write(service: StoredGoogleService, value: string, kind?: StoredGoogleCredentialKind): void;
9
10
  }
10
11
 
11
12
  const KEYCHAIN_SERVICE_PREFIX = 'forge.google-oauth';
12
- const KEYCHAIN_ACCOUNT = 'refresh-token';
13
- const memoryCache = new Map<StoredGoogleService, string>();
13
+ const KEYCHAIN_ACCOUNT: Record<StoredGoogleCredentialKind, string> = {
14
+ 'refresh-token': 'refresh-token',
15
+ 'client-secret': 'client-secret',
16
+ };
17
+ const memoryCache = new Map<string, string>();
14
18
 
15
19
  function keychainService(service: StoredGoogleService): string {
16
20
  return `${KEYCHAIN_SERVICE_PREFIX}.${service}`;
17
21
  }
18
22
 
23
+ function cacheKey(service: StoredGoogleService, kind: StoredGoogleCredentialKind): string {
24
+ return `${service}:${kind}`;
25
+ }
26
+
19
27
  const macKeychainAdapter: GoogleCredentialStoreAdapter = {
20
28
  available: () => process.platform === 'darwin',
21
- read(service) {
29
+ read(service, kind = 'refresh-token') {
22
30
  if (process.platform !== 'darwin') return undefined;
23
31
  try {
24
32
  const value = execFileSync('/usr/bin/security', [
25
33
  'find-generic-password',
26
34
  '-s', keychainService(service),
27
- '-a', KEYCHAIN_ACCOUNT,
35
+ '-a', KEYCHAIN_ACCOUNT[kind],
28
36
  '-w',
29
37
  ], {
30
38
  encoding: 'utf-8',
@@ -36,7 +44,7 @@ const macKeychainAdapter: GoogleCredentialStoreAdapter = {
36
44
  return undefined;
37
45
  }
38
46
  },
39
- write(service, refreshToken) {
47
+ write(service, value, kind = 'refresh-token') {
40
48
  if (process.platform !== 'darwin') {
41
49
  throw new Error('GOOGLE_CREDENTIAL_STORE_UNAVAILABLE: macOS Keychain is required for local OAuth credential persistence');
42
50
  }
@@ -44,8 +52,8 @@ const macKeychainAdapter: GoogleCredentialStoreAdapter = {
44
52
  'add-generic-password',
45
53
  '-U',
46
54
  '-s', keychainService(service),
47
- '-a', KEYCHAIN_ACCOUNT,
48
- '-w', refreshToken,
55
+ '-a', KEYCHAIN_ACCOUNT[kind],
56
+ '-w', value,
49
57
  ], {
50
58
  encoding: 'utf-8',
51
59
  stdio: ['ignore', 'ignore', 'pipe'],
@@ -67,29 +75,76 @@ export function googleCredentialStoreStatus(): Record<string, unknown> {
67
75
  available: adapter.available(),
68
76
  repositoryPersistence: false,
69
77
  controllerStatePersistence: false,
78
+ credentialKinds: ['refresh-token', 'client-secret'],
70
79
  };
71
80
  }
72
81
 
73
- export function readStoredGoogleRefreshToken(service: StoredGoogleService): { token: string; source: string } | undefined {
82
+ function readStoredGoogleCredential(
83
+ service: StoredGoogleService,
84
+ kind: StoredGoogleCredentialKind,
85
+ ): { value: string; source: string } | undefined {
74
86
  const candidates: StoredGoogleService[] = service === 'google-workspace'
75
87
  ? ['google-workspace']
76
88
  : [service, 'google-workspace'];
77
89
  for (const candidate of candidates) {
78
- const cached = memoryCache.get(candidate);
79
- if (cached) return { token: cached, source: `keychain:${candidate}` };
90
+ const key = cacheKey(candidate, kind);
91
+ const cached = memoryCache.get(key);
92
+ if (cached) return { value: cached, source: `keychain:${candidate}:${kind}` };
80
93
  if (!adapter.available()) continue;
81
- const token = adapter.read(candidate)?.trim();
82
- if (!token) continue;
83
- memoryCache.set(candidate, token);
84
- return { token, source: `keychain:${candidate}` };
94
+ const value = adapter.read(candidate, kind)?.trim();
95
+ if (!value) continue;
96
+ memoryCache.set(key, value);
97
+ return { value, source: `keychain:${candidate}:${kind}` };
85
98
  }
86
99
  return undefined;
87
100
  }
88
101
 
89
- export function writeStoredGoogleRefreshToken(service: StoredGoogleService, refreshToken: string): void {
90
- const token = refreshToken.trim();
91
- if (!token) throw new Error('GOOGLE_REFRESH_TOKEN_REQUIRED');
102
+ function writeStoredGoogleCredential(service: StoredGoogleService, kind: StoredGoogleCredentialKind, value: string): void {
103
+ const normalized = value.trim();
104
+ if (!normalized) throw new Error(kind === 'refresh-token' ? 'GOOGLE_REFRESH_TOKEN_REQUIRED' : 'GOOGLE_CLIENT_SECRET_REQUIRED');
92
105
  if (!adapter.available()) throw new Error('GOOGLE_CREDENTIAL_STORE_UNAVAILABLE');
93
- adapter.write(service, token);
94
- memoryCache.set(service, token);
106
+ adapter.write(service, normalized, kind);
107
+ memoryCache.set(cacheKey(service, kind), normalized);
108
+ }
109
+
110
+ export function readStoredGoogleRefreshToken(service: StoredGoogleService): { token: string; source: string } | undefined {
111
+ const stored = readStoredGoogleCredential(service, 'refresh-token');
112
+ return stored ? { token: stored.value, source: stored.source } : undefined;
113
+ }
114
+
115
+ export function writeStoredGoogleRefreshToken(service: StoredGoogleService, refreshToken: string): void {
116
+ writeStoredGoogleCredential(service, 'refresh-token', refreshToken);
117
+ }
118
+
119
+ export function readStoredGoogleClientSecret(service: StoredGoogleService): { secret: string; source: string } | undefined {
120
+ const stored = readStoredGoogleCredential(service, 'client-secret');
121
+ return stored ? { secret: stored.value, source: stored.source } : undefined;
122
+ }
123
+
124
+ export function writeStoredGoogleClientSecret(service: StoredGoogleService, clientSecret: string): void {
125
+ writeStoredGoogleCredential(service, 'client-secret', clientSecret);
126
+ }
127
+
128
+ export function importStoredGoogleClientSecretFromClipboard(service: StoredGoogleService): Record<string, unknown> {
129
+ if (process.platform !== 'darwin' || !adapter.available()) throw new Error('GOOGLE_CREDENTIAL_STORE_UNAVAILABLE');
130
+ const secret = execFileSync('/usr/bin/pbpaste', [], {
131
+ encoding: 'utf-8',
132
+ stdio: ['ignore', 'pipe', 'ignore'],
133
+ timeout: 2_000,
134
+ }).trim();
135
+ if (!secret || secret.length > 1024 || /\s/.test(secret)) throw new Error('GOOGLE_CLIENT_SECRET_CLIPBOARD_INVALID');
136
+ writeStoredGoogleClientSecret(service, secret);
137
+ execFileSync('/usr/bin/pbcopy', [], {
138
+ encoding: 'utf-8',
139
+ input: '',
140
+ stdio: ['pipe', 'ignore', 'ignore'],
141
+ timeout: 2_000,
142
+ });
143
+ return {
144
+ stored: true,
145
+ service,
146
+ backend: 'macos-keychain',
147
+ clipboardCleared: true,
148
+ credentialMaterialReturned: false,
149
+ };
95
150
  }
@@ -5,6 +5,7 @@ import { URLSearchParams } from 'url';
5
5
  import { installGoogleAccessToken } from '../plugins/google-shared';
6
6
  import {
7
7
  googleCredentialStoreStatus,
8
+ readStoredGoogleClientSecret,
8
9
  type StoredGoogleService,
9
10
  writeStoredGoogleRefreshToken,
10
11
  } from './google-credential-store';
@@ -138,16 +139,33 @@ function base64Url(value: Buffer): string {
138
139
  return value.toString('base64url');
139
140
  }
140
141
 
141
- function clientId(): string | undefined {
142
- return process.env.FORGE_GOOGLE_WORKSPACE_CLIENT_ID?.trim()
142
+ function clientId(service: GoogleOAuthService): string | undefined {
143
+ const serviceSpecific = service === 'gmail'
144
+ ? process.env.FORGE_GMAIL_CLIENT_ID?.trim()
145
+ : service === 'calendar'
146
+ ? process.env.FORGE_GOOGLE_CALENDAR_CLIENT_ID?.trim()
147
+ : service === 'tasks'
148
+ ? process.env.FORGE_GOOGLE_TASKS_CLIENT_ID?.trim()
149
+ : undefined;
150
+ return serviceSpecific
151
+ || process.env.FORGE_GOOGLE_WORKSPACE_CLIENT_ID?.trim()
143
152
  || process.env.FORGE_GOOGLE_CLIENT_ID?.trim()
144
153
  || process.env.GOOGLE_CLIENT_ID?.trim();
145
154
  }
146
155
 
147
- function clientSecret(): string | undefined {
148
- return process.env.FORGE_GOOGLE_WORKSPACE_CLIENT_SECRET?.trim()
156
+ function clientSecret(service: GoogleOAuthService): string | undefined {
157
+ const serviceSpecific = service === 'gmail'
158
+ ? process.env.FORGE_GMAIL_CLIENT_SECRET?.trim()
159
+ : service === 'calendar'
160
+ ? process.env.FORGE_GOOGLE_CALENDAR_CLIENT_SECRET?.trim()
161
+ : service === 'tasks'
162
+ ? process.env.FORGE_GOOGLE_TASKS_CLIENT_SECRET?.trim()
163
+ : undefined;
164
+ return serviceSpecific
165
+ || process.env.FORGE_GOOGLE_WORKSPACE_CLIENT_SECRET?.trim()
149
166
  || process.env.FORGE_GOOGLE_CLIENT_SECRET?.trim()
150
- || process.env.GOOGLE_CLIENT_SECRET?.trim();
167
+ || process.env.GOOGLE_CLIENT_SECRET?.trim()
168
+ || readStoredGoogleClientSecret(service)?.secret;
151
169
  }
152
170
 
153
171
  export function prepareGoogleOAuthLogin(
@@ -157,7 +175,7 @@ export function prepareGoogleOAuthLogin(
157
175
  pruneOAuthRequests(controllerHome);
158
176
  const redirectUri = validateRedirectUri(input.redirectUri);
159
177
  const scopes = validateScopes(input.service, input.scopes);
160
- const configuredClientId = clientId();
178
+ const configuredClientId = clientId(input.service);
161
179
  if (!configuredClientId) {
162
180
  return {
163
181
  schemaVersion: 1,
@@ -236,7 +254,7 @@ export async function completeGoogleOAuthLogin(
236
254
  }
237
255
  const code = input.code?.trim();
238
256
  if (!code) throw new Error('GOOGLE_OAUTH_CODE_REQUIRED');
239
- const configuredSecret = clientSecret();
257
+ const configuredSecret = clientSecret(selected.record.service);
240
258
  if (!configuredSecret) throw new Error('GOOGLE_OAUTH_CLIENT_SECRET_REQUIRED');
241
259
  const body = new URLSearchParams({
242
260
  grant_type: 'authorization_code',
@@ -88,10 +88,10 @@ export function prepareWorkspaceAuthLogin(
88
88
  controllerHomeOrInput: string | WorkspaceAuthLoginInput = {},
89
89
  maybeInput: WorkspaceAuthLoginInput = {},
90
90
  ): Record<string, unknown> {
91
- bootstrapManagedRuntimeEnv();
92
91
  const controllerHome = typeof controllerHomeOrInput === 'string'
93
92
  ? controllerHomeOrInput
94
93
  : process.env.FORGE_CONTROLLER_HOME?.trim() || process.cwd();
94
+ bootstrapManagedRuntimeEnv({ controllerHome });
95
95
  const input = typeof controllerHomeOrInput === 'string' ? maybeInput : controllerHomeOrInput;
96
96
  const service = normalizeService(input.service);
97
97
  const redirectUri = input.redirectUri || process.env.FORGE_GOOGLE_REDIRECT_URI || 'http://127.0.0.1:8766/oauth/google/callback';
@@ -39,6 +39,8 @@ export interface BoundedChildRunOptions {
39
39
  env?: NodeJS.ProcessEnv;
40
40
  timeoutMs: number;
41
41
  maxOutputBytes?: number;
42
+ /** Optional bounded stdin payload for request/response sidecars. */
43
+ input?: string | Buffer;
42
44
  stdio?: 'capture' | 'inherit';
43
45
  forwardSignals?: boolean;
44
46
  onSpawn?: (pid: number) => void;
@@ -120,7 +122,7 @@ export async function runBoundedChild(
120
122
  const maxOutputBytes = options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
121
123
  const stdio: StdioOptions = options.stdio === 'inherit'
122
124
  ? ['ignore', 'inherit', 'inherit']
123
- : ['ignore', 'pipe', 'pipe'];
125
+ : [options.input === undefined ? 'ignore' : 'pipe', 'pipe', 'pipe'];
124
126
  let child: ChildProcess;
125
127
  try {
126
128
  child = spawn(command, [...args], {
@@ -147,6 +149,12 @@ export async function runBoundedChild(
147
149
 
148
150
  const pid = child.pid;
149
151
  if (pid) options.onSpawn?.(pid);
152
+ if (options.input !== undefined && child.stdin) {
153
+ // A canary or validation failure may close stdin before the parent writes.
154
+ // The child outcome is authoritative; a late EPIPE must not crash the host.
155
+ child.stdin.on('error', () => undefined);
156
+ child.stdin.end(options.input);
157
+ }
150
158
  let stdout = '';
151
159
  let stderr = '';
152
160
  child.stdout?.on('data', (chunk) => { stdout = appendBounded(stdout, chunk, maxOutputBytes); });