@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
@@ -19,8 +19,7 @@ import {
19
19
  releaseControllerCheckSubscription,
20
20
  runControllerCheckAsync,
21
21
  } from '../../../cli/controller/check-runner';
22
- import { repositoryControllerRoot } from '../../../cli/repositories/controller-home';
23
- import type { RepositoryRecord } from '../../../cli/repositories/types';
22
+ import { commandExecutionScopeKey, type RepositoryCommandScopeTarget } from '../../../cli/repositories/command-scope';
24
23
  import { readJsonFile, sanitizeFileComponent, writeJsonAtomic } from '../../shared/json-files';
25
24
  import {
26
25
  defaultProcessIdentityProbe,
@@ -30,6 +29,17 @@ import {
30
29
  } from '../../shared/process-identity';
31
30
  import { terminateProcessTree } from '../../shared/process-tree';
32
31
  import { runBoundedProcess } from '../thin-harness/async-process';
32
+ import { recordProcessHandleIndexEntry } from './handle-index';
33
+ import {
34
+ FORGE_INSTANCE_PROCESS_SCOPE_KEY,
35
+ isWorkspaceProcessScopeKey,
36
+ legacyWorkspaceScopeRoot,
37
+ normalizeProcessScopeKey,
38
+ processScopeExecutionTarget,
39
+ processScopeFromKey,
40
+ processScopeKey,
41
+ processScopeRoot,
42
+ } from './process-scope';
33
43
  import { PROCESS_LOG_TAIL_BYTES, type ProcessHandle, type ProcessLogSlice, type WaitProcessOptions } from './types';
34
44
 
35
45
  const EMPTY_EFFECTS = {
@@ -66,7 +76,12 @@ interface LightweightExitObservation {
66
76
  interface LightweightEntry {
67
77
  processId: string;
68
78
  controllerHome: string;
69
- repoId: string;
79
+ /** Canonical Process scope partition key (`repo_*` repository id, or the ForgeInstance scope key). */
80
+ scopeKey: string;
81
+ /** Repository placement when this handle targets a repository; absent for ForgeInstance targets. */
82
+ repoId?: string;
83
+ /** Owning principal recorded from the invoking MCP/Runtime context. */
84
+ principalId?: string;
70
85
  workId?: string;
71
86
  commandId: string;
72
87
  requestFingerprint: string;
@@ -101,7 +116,9 @@ export function hasActiveLightweightProcesses(controllerHome: string): boolean {
101
116
 
102
117
  interface LightweightRunningReceipt {
103
118
  schemaVersion: 1;
104
- repoId: string;
119
+ /** Canonical scope partition key. Legacy receipts carry only `repoId`. */
120
+ scopeKey?: string;
121
+ repoId?: string;
105
122
  processId: string;
106
123
  commandId?: string;
107
124
  requestFingerprint?: string;
@@ -113,7 +130,9 @@ interface LightweightRunningReceipt {
113
130
 
114
131
  interface LightweightTerminalReceipt {
115
132
  schemaVersion: 1;
116
- repoId: string;
133
+ /** Canonical scope partition key. Legacy receipts carry only `repoId`. */
134
+ scopeKey?: string;
135
+ repoId?: string;
117
136
  processId: string;
118
137
  commandId?: string;
119
138
  requestFingerprint?: string;
@@ -121,20 +140,44 @@ interface LightweightTerminalReceipt {
121
140
  handle: ProcessHandle;
122
141
  }
123
142
 
124
- function runningReceiptRoot(controllerHome: string, repoId: string): string {
125
- return join(repositoryControllerRoot(controllerHome, repoId), 'process-runtime', 'lightweight-running');
143
+ function runningReceiptRoot(controllerHome: string, scopeKey: string): string {
144
+ return join(processScopeRoot(controllerHome, scopeKey), 'process-runtime', 'lightweight-running');
126
145
  }
127
146
 
128
- function runningReceiptPath(controllerHome: string, repoId: string, processId: string): string {
129
- return join(runningReceiptRoot(controllerHome, repoId), `${sanitizeFileComponent(processId)}.json`);
147
+ function runningReceiptPath(controllerHome: string, scopeKey: string, processId: string): string {
148
+ return join(runningReceiptRoot(controllerHome, scopeKey), `${sanitizeFileComponent(processId)}.json`);
130
149
  }
131
150
 
132
- function terminalReceiptRoot(controllerHome: string, repoId: string): string {
133
- return join(repositoryControllerRoot(controllerHome, repoId), 'process-runtime', 'lightweight-terminal');
151
+ function terminalReceiptRoot(controllerHome: string, scopeKey: string): string {
152
+ return join(processScopeRoot(controllerHome, scopeKey), 'process-runtime', 'lightweight-terminal');
134
153
  }
135
154
 
136
- function terminalReceiptPath(controllerHome: string, repoId: string, processId: string): string {
137
- return join(terminalReceiptRoot(controllerHome, repoId), `${sanitizeFileComponent(processId)}.json`);
155
+ function terminalReceiptPath(controllerHome: string, scopeKey: string, processId: string): string {
156
+ return join(terminalReceiptRoot(controllerHome, scopeKey), `${sanitizeFileComponent(processId)}.json`);
157
+ }
158
+
159
+ /**
160
+ * Bounded migration read: a workspace-scoped receipt written before the
161
+ * workspace partition existed still resolves, instead of reporting a running
162
+ * command as missing. Repository scopes never take this path.
163
+ */
164
+ function existingReceiptPath(
165
+ controllerHome: string,
166
+ scopeKey: string,
167
+ processId: string,
168
+ build: (controllerHome: string, scopeKey: string, processId: string) => string,
169
+ ): string {
170
+ const current = build(controllerHome, scopeKey, processId);
171
+ if (existsSync(current) || !isWorkspaceProcessScopeKey(scopeKey)) return current;
172
+ const legacyRoot = legacyWorkspaceScopeRoot(controllerHome, scopeKey);
173
+ if (!legacyRoot) return current;
174
+ const legacy = join(
175
+ legacyRoot,
176
+ 'process-runtime',
177
+ build === runningReceiptPath ? 'lightweight-running' : 'lightweight-terminal',
178
+ `${sanitizeFileComponent(processId)}.json`,
179
+ );
180
+ return existsSync(legacy) ? legacy : current;
138
181
  }
139
182
 
140
183
  function boundedAppend(current: string, chunk: string, maxBytes: number): string {
@@ -169,8 +212,8 @@ function captureProcessIdentity(pid: number): ExpectedProcessIdentity | undefine
169
212
  return { pid, processStartTime, executableFingerprint: executableFingerprint(command) };
170
213
  }
171
214
 
172
- function removeRunningReceipt(controllerHome: string, repoId: string, processId: string): void {
173
- try { rmSync(runningReceiptPath(controllerHome, repoId, processId), { force: true }); } catch { /* best effort */ }
215
+ function removeRunningReceipt(controllerHome: string, scopeKey: string, processId: string): void {
216
+ try { rmSync(runningReceiptPath(controllerHome, scopeKey, processId), { force: true }); } catch { /* best effort */ }
174
217
  }
175
218
 
176
219
  function persistRunningReceipt(entry: LightweightEntry, force = false): void {
@@ -182,12 +225,13 @@ function persistRunningReceipt(entry: LightweightEntry, force = false): void {
182
225
  if (!force && entry.runningReceiptUpdatedAtMs !== undefined
183
226
  && now - entry.runningReceiptUpdatedAtMs < RUNNING_RECEIPT_PERSIST_INTERVAL_MS) return;
184
227
  try {
185
- const root = runningReceiptRoot(entry.controllerHome, entry.repoId);
228
+ const root = runningReceiptRoot(entry.controllerHome, entry.scopeKey);
186
229
  mkdirSync(root, { recursive: true, mode: 0o700 });
187
- const path = runningReceiptPath(entry.controllerHome, entry.repoId, entry.processId);
230
+ const path = runningReceiptPath(entry.controllerHome, entry.scopeKey, entry.processId);
188
231
  const receipt: LightweightRunningReceipt = {
189
232
  schemaVersion: 1,
190
- repoId: entry.repoId,
233
+ scopeKey: entry.scopeKey,
234
+ ...(entry.repoId ? { repoId: entry.repoId } : {}),
191
235
  processId: entry.processId,
192
236
  commandId: entry.commandId,
193
237
  requestFingerprint: entry.requestFingerprint,
@@ -204,12 +248,12 @@ function persistRunningReceipt(entry: LightweightEntry, force = false): void {
204
248
  }
205
249
  }
206
250
 
207
- function readRunningReceipt(controllerHome: string, repoId: string, processId: string): LightweightRunningReceipt | undefined {
208
- const path = runningReceiptPath(controllerHome, repoId, processId);
251
+ function readRunningReceipt(controllerHome: string, scopeKey: string, processId: string): LightweightRunningReceipt | undefined {
252
+ const path = existingReceiptPath(controllerHome, scopeKey, processId, runningReceiptPath);
209
253
  if (!existsSync(path)) return undefined;
210
254
  try {
211
255
  const receipt = readJsonFile<LightweightRunningReceipt>(path);
212
- if (receipt.schemaVersion !== 1 || receipt.repoId !== repoId || receipt.processId !== processId) return undefined;
256
+ if (receipt.schemaVersion !== 1 || (receipt.scopeKey ?? receipt.repoId) !== scopeKey || receipt.processId !== processId) return undefined;
213
257
  if (receipt.handle?.processId !== processId || receipt.handle.completed) return undefined;
214
258
  return receipt;
215
259
  } catch {
@@ -261,18 +305,19 @@ function pruneTerminalReceipts(root: string, keepPath: string): void {
261
305
 
262
306
  function persistTerminalReceipt(controllerHome: string, entry: LightweightEntry): void {
263
307
  if (!entry.result || !entry.finishedAtMs) return;
264
- const existing = readTerminalReceipt(controllerHome, entry.repoId, entry.processId);
308
+ const existing = readTerminalReceipt(controllerHome, entry.scopeKey, entry.processId);
265
309
  if (existing) {
266
- removeRunningReceipt(controllerHome, entry.repoId, entry.processId);
310
+ removeRunningReceipt(controllerHome, entry.scopeKey, entry.processId);
267
311
  return;
268
312
  }
269
313
  try {
270
- const root = terminalReceiptRoot(controllerHome, entry.repoId);
314
+ const root = terminalReceiptRoot(controllerHome, entry.scopeKey);
271
315
  mkdirSync(root, { recursive: true, mode: 0o700 });
272
- const path = terminalReceiptPath(controllerHome, entry.repoId, entry.processId);
316
+ const path = terminalReceiptPath(controllerHome, entry.scopeKey, entry.processId);
273
317
  const receipt: LightweightTerminalReceipt = {
274
318
  schemaVersion: 1,
275
- repoId: entry.repoId,
319
+ scopeKey: entry.scopeKey,
320
+ ...(entry.repoId ? { repoId: entry.repoId } : {}),
276
321
  processId: entry.processId,
277
322
  commandId: entry.commandId,
278
323
  requestFingerprint: entry.requestFingerprint,
@@ -281,19 +326,19 @@ function persistTerminalReceipt(controllerHome: string, entry: LightweightEntry)
281
326
  };
282
327
  writeJsonAtomic(path, receipt);
283
328
  try { chmodSync(path, 0o600); } catch { /* Windows or restricted filesystem. */ }
284
- removeRunningReceipt(controllerHome, entry.repoId, entry.processId);
329
+ removeRunningReceipt(controllerHome, entry.scopeKey, entry.processId);
285
330
  pruneTerminalReceipts(root, path);
286
331
  } catch {
287
332
  // A receipt failure must never turn a completed local command into a failure.
288
333
  }
289
334
  }
290
335
 
291
- function readTerminalReceipt(controllerHome: string, repoId: string, processId: string): LightweightTerminalReceipt | undefined {
292
- const path = terminalReceiptPath(controllerHome, repoId, processId);
336
+ function readTerminalReceipt(controllerHome: string, scopeKey: string, processId: string): LightweightTerminalReceipt | undefined {
337
+ const path = existingReceiptPath(controllerHome, scopeKey, processId, terminalReceiptPath);
293
338
  if (!existsSync(path)) return undefined;
294
339
  try {
295
340
  const receipt = readJsonFile<LightweightTerminalReceipt>(path);
296
- if (receipt.schemaVersion !== 1 || receipt.repoId !== repoId || receipt.processId !== processId) return undefined;
341
+ if (receipt.schemaVersion !== 1 || (receipt.scopeKey ?? receipt.repoId) !== scopeKey || receipt.processId !== processId) return undefined;
297
342
  if (!receipt.handle?.completed || receipt.handle.processId !== processId) return undefined;
298
343
  const finishedAtMs = Date.parse(receipt.finishedAt);
299
344
  if (!Number.isFinite(finishedAtMs) || Date.now() - finishedAtMs > TERMINAL_RECEIPT_RETENTION_MS) {
@@ -323,24 +368,24 @@ function recoveredRequestMetrics(): LightweightCommandMetrics {
323
368
  */
324
369
  function findPersistedHandleForCommand(
325
370
  controllerHome: string,
326
- repoId: string,
371
+ scopeKey: string,
327
372
  commandId: string,
328
373
  requestFingerprint: string,
329
374
  ): ProcessHandle | undefined {
330
- const roots = [terminalReceiptRoot(controllerHome, repoId), runningReceiptRoot(controllerHome, repoId)];
375
+ const roots = [terminalReceiptRoot(controllerHome, scopeKey), runningReceiptRoot(controllerHome, scopeKey)];
331
376
  for (const root of roots) {
332
377
  if (!existsSync(root)) continue;
333
378
  for (const file of readdirSync(root)) {
334
379
  if (!file.endsWith('.json')) continue;
335
380
  try {
336
381
  const receipt = readJsonFile<LightweightRunningReceipt | LightweightTerminalReceipt>(join(root, file));
337
- if (receipt.schemaVersion !== 1 || receipt.repoId !== repoId || !receipt.processId) continue;
382
+ if (receipt.schemaVersion !== 1 || (receipt.scopeKey ?? receipt.repoId) !== scopeKey || !receipt.processId) continue;
338
383
  const persistedCommandId = receipt.commandId ?? receipt.handle?.commandId;
339
384
  if (persistedCommandId !== commandId) continue;
340
385
  if (!receipt.requestFingerprint || receipt.requestFingerprint !== requestFingerprint) {
341
386
  throw new Error(`PROCESS_REQUEST_CONFLICT: command id ${commandId} cannot be safely replayed after Runtime restart`);
342
387
  }
343
- return getLightweightProcessHandle(controllerHome, repoId, receipt.processId);
388
+ return getLightweightProcessHandle(controllerHome, scopeKey, receipt.processId);
344
389
  } catch (error) {
345
390
  if (error instanceof Error && error.message.startsWith('PROCESS_REQUEST_CONFLICT:')) throw error;
346
391
  // A corrupt or concurrently pruned receipt is not an execution result.
@@ -399,18 +444,21 @@ function persistRecoveredTerminalReceipt(
399
444
  receipt: LightweightRunningReceipt,
400
445
  handle: ProcessHandle,
401
446
  ): ProcessHandle {
402
- const existing = readTerminalReceipt(controllerHome, receipt.repoId, receipt.processId);
447
+ const scopeKey = receipt.scopeKey ?? receipt.repoId;
448
+ if (!scopeKey) return handle;
449
+ const existing = readTerminalReceipt(controllerHome, scopeKey, receipt.processId);
403
450
  if (existing) {
404
- removeRunningReceipt(controllerHome, receipt.repoId, receipt.processId);
451
+ removeRunningReceipt(controllerHome, scopeKey, receipt.processId);
405
452
  return existing.handle;
406
453
  }
407
454
  try {
408
- const root = terminalReceiptRoot(controllerHome, receipt.repoId);
455
+ const root = terminalReceiptRoot(controllerHome, scopeKey);
409
456
  mkdirSync(root, { recursive: true, mode: 0o700 });
410
- const path = terminalReceiptPath(controllerHome, receipt.repoId, receipt.processId);
457
+ const path = terminalReceiptPath(controllerHome, scopeKey, receipt.processId);
411
458
  const terminal: LightweightTerminalReceipt = {
412
459
  schemaVersion: 1,
413
- repoId: receipt.repoId,
460
+ scopeKey,
461
+ ...(receipt.repoId ? { repoId: receipt.repoId } : {}),
414
462
  processId: receipt.processId,
415
463
  commandId: receipt.commandId,
416
464
  requestFingerprint: receipt.requestFingerprint,
@@ -419,7 +467,7 @@ function persistRecoveredTerminalReceipt(
419
467
  };
420
468
  writeJsonAtomic(path, terminal);
421
469
  try { chmodSync(path, 0o600); } catch { /* Windows or restricted filesystem. */ }
422
- removeRunningReceipt(controllerHome, receipt.repoId, receipt.processId);
470
+ removeRunningReceipt(controllerHome, scopeKey, receipt.processId);
423
471
  pruneTerminalReceipts(root, path);
424
472
  } catch {
425
473
  // Recovery evidence persistence must not trigger command re-execution or unsafe signalling.
@@ -555,13 +603,16 @@ function entryHandle(entry: LightweightEntry): ProcessHandle {
555
603
 
556
604
  export interface StartLightweightCommandInput {
557
605
  controllerHome: string;
558
- repository: RepositoryRecord;
606
+ /** Repository identity is optional: an arbitrary workspace target carries a workspace scope key. */
607
+ repository: RepositoryCommandScopeTarget;
559
608
  execution: ExecuteRepositoryCommandInput;
560
609
  interactiveWaitMs: number;
561
610
  timeoutMs: number;
562
611
  maxOutputBytes?: number;
563
612
  workId?: string;
564
613
  commandId?: string;
614
+ /** Authenticated principal recorded with the handle for principal-bound attachment. */
615
+ principalId?: string;
565
616
  /** Start repository preparation on the next event-loop turn so the caller can receive a handle first. */
566
617
  deferStart?: boolean;
567
618
  /** Reuse an equivalent active local build/test even when a later caller has a different request id. */
@@ -588,10 +639,15 @@ export interface StartLightweightCheckInput {
588
639
  timeoutMs: number;
589
640
  workId?: string;
590
641
  commandId?: string;
642
+ /** Authenticated principal recorded with the handle for principal-bound attachment. */
643
+ principalId?: string;
591
644
  }
592
645
 
593
646
  export interface StartLightweightInternalProcessInput {
594
- repoId: string;
647
+ /** Canonical Process scope key. Defaults to `repoId` for repository-scoped internal processes. */
648
+ scopeKey?: string;
649
+ /** Repository placement when this process targets a repository; absent for ForgeInstance targets. */
650
+ repoId?: string;
595
651
  executable: string;
596
652
  args: readonly string[];
597
653
  cwd: string;
@@ -603,15 +659,21 @@ export interface StartLightweightInternalProcessInput {
603
659
  workId?: string;
604
660
  commandId?: string;
605
661
  signal?: AbortSignal;
662
+ /** Authenticated principal recorded with the handle for principal-bound attachment. */
663
+ principalId?: string;
606
664
  }
607
665
 
608
666
  export async function startLightweightRepositoryCommand(
609
667
  input: StartLightweightCommandInput,
610
668
  ): Promise<{ handle: ProcessHandle; metrics: LightweightCommandMetrics }> {
611
669
  sweep();
670
+ // Repository targets keep their repository partition; an arbitrary workspace
671
+ // target is scoped by its workspace id instead of a synthetic repository id.
672
+ const scope = processScopeFromKey(commandExecutionScopeKey(input.repository));
673
+ const scopeKey = processScopeKey(scope);
612
674
  const stableCommandId = input.commandId?.trim();
613
675
  const requestFingerprint = JSON.stringify({
614
- repoId: input.repository.repoId,
676
+ repoId: commandExecutionScopeKey(input.repository),
615
677
  command: input.execution.command,
616
678
  cwd: input.execution.cwd ?? '.',
617
679
  timeoutMs: input.timeoutMs,
@@ -620,7 +682,7 @@ export async function startLightweightRepositoryCommand(
620
682
  if (stableCommandId) {
621
683
  const existing = [...entries.values()].find((entry) => (
622
684
  entry.controllerHome === input.controllerHome
623
- && entry.repoId === input.repository.repoId
685
+ && entry.scopeKey === scopeKey
624
686
  && entry.commandId === stableCommandId
625
687
  ));
626
688
  if (existing) {
@@ -643,7 +705,7 @@ export async function startLightweightRepositoryCommand(
643
705
  }
644
706
  const recovered = findPersistedHandleForCommand(
645
707
  input.controllerHome,
646
- input.repository.repoId,
708
+ scopeKey,
647
709
  stableCommandId,
648
710
  requestFingerprint,
649
711
  );
@@ -651,7 +713,7 @@ export async function startLightweightRepositoryCommand(
651
713
  }
652
714
  if (input.reuseActiveEquivalent) {
653
715
  const existing = [...entries.values()].find((entry) => (
654
- entry.repoId === input.repository.repoId
716
+ entry.scopeKey === scopeKey
655
717
  && entry.requestFingerprint === requestFingerprint
656
718
  && entry.result === undefined
657
719
  ));
@@ -675,7 +737,9 @@ export async function startLightweightRepositoryCommand(
675
737
  const entry: LightweightEntry = {
676
738
  processId,
677
739
  controllerHome: input.controllerHome,
678
- repoId: input.repository.repoId,
740
+ scopeKey,
741
+ ...(scope.kind === 'repository' ? { repoId: commandExecutionScopeKey(input.repository) } : {}),
742
+ ...(input.principalId ? { principalId: input.principalId } : {}),
679
743
  workId: input.workId,
680
744
  commandId: stableCommandId || processId,
681
745
  requestFingerprint,
@@ -780,6 +844,16 @@ export async function startLightweightRepositoryCommand(
780
844
  })
781
845
  : startExecution();
782
846
  entries.set(processId, entry);
847
+ // Instance-level locator: a lightweight handle must be attachable from the
848
+ // returned processId alone, including after Runtime memory loss.
849
+ recordProcessHandleIndexEntry(input.controllerHome, {
850
+ processId,
851
+ lane: 'lightweight',
852
+ target: processScopeExecutionTarget(input.controllerHome, processScopeFromKey(entry.scopeKey)),
853
+ principalId: entry.principalId,
854
+ workId: entry.workId,
855
+ commandId: entry.commandId,
856
+ });
783
857
  persistRunningReceipt(entry, true);
784
858
 
785
859
  if (entry.interactiveWaitMs > 0) {
@@ -811,18 +885,31 @@ export async function startLightweightInternalProcess(
811
885
  input: StartLightweightInternalProcessInput,
812
886
  ): Promise<{ handle: ProcessHandle; metrics: LightweightCommandMetrics }> {
813
887
  sweep();
888
+ const repoId = input.repoId?.trim() || undefined;
889
+ const scopeKey = normalizeProcessScopeKey(input.scopeKey?.trim() || repoId || '');
890
+ const scope = processScopeFromKey(scopeKey);
814
891
  const stableCommandId = input.commandId?.trim();
892
+ // Retry identity is commandId + fingerprint(scope target, action, principal).
893
+ // An instance-scope request id is never a naked global namespace: a different
894
+ // principal presenting the same request id fails closed as a request conflict
895
+ // instead of attaching to another principal's process.
896
+ const principalId = input.principalId?.trim() || null;
815
897
  const requestFingerprint = JSON.stringify({
816
- repoId: input.repoId,
898
+ scopeKey,
899
+ repoId: repoId ?? null,
817
900
  executable: input.executable,
818
901
  args: input.args,
819
902
  cwd: input.cwd,
820
903
  timeoutMs: input.timeoutMs,
904
+ // Only a principal-bound lane (the ForgeInstance host command lane) adds the
905
+ // principal to retry identity, so existing repository/plugin fingerprints
906
+ // keep their historical value instead of failing closed after an upgrade.
907
+ ...(principalId ? { principalId } : {}),
821
908
  });
822
909
  if (stableCommandId) {
823
910
  const existing = [...entries.values()].find((entry) => (
824
911
  entry.controllerHome === input.controllerHome
825
- && entry.repoId === input.repoId
912
+ && entry.scopeKey === scopeKey
826
913
  && entry.commandId === stableCommandId
827
914
  ));
828
915
  if (existing) {
@@ -845,7 +932,7 @@ export async function startLightweightInternalProcess(
845
932
  }
846
933
  const recovered = findPersistedHandleForCommand(
847
934
  input.controllerHome,
848
- input.repoId,
935
+ scopeKey,
849
936
  stableCommandId,
850
937
  requestFingerprint,
851
938
  );
@@ -858,7 +945,9 @@ export async function startLightweightInternalProcess(
858
945
  const entry: LightweightEntry = {
859
946
  processId,
860
947
  controllerHome: input.controllerHome,
861
- repoId: input.repoId,
948
+ scopeKey,
949
+ ...(repoId ? { repoId } : {}),
950
+ ...(input.principalId?.trim() ? { principalId: input.principalId.trim() } : {}),
862
951
  workId: input.workId,
863
952
  commandId: stableCommandId || processId,
864
953
  requestFingerprint,
@@ -906,6 +995,14 @@ export async function startLightweightInternalProcess(
906
995
  return result;
907
996
  });
908
997
  entries.set(processId, entry);
998
+ recordProcessHandleIndexEntry(entry.controllerHome, {
999
+ processId,
1000
+ lane: 'lightweight',
1001
+ target: processScopeExecutionTarget(entry.controllerHome, processScopeFromKey(entry.scopeKey)),
1002
+ principalId: entry.principalId,
1003
+ workId: entry.workId,
1004
+ commandId: entry.commandId,
1005
+ });
909
1006
  persistRunningReceipt(entry, true);
910
1007
 
911
1008
  if (entry.interactiveWaitMs > 0) {
@@ -933,6 +1030,68 @@ export async function startLightweightInternalProcess(
933
1030
  };
934
1031
  }
935
1032
 
1033
+ export interface StartLightweightHostCommandInput {
1034
+ controllerHome: string;
1035
+ /** Typed argv command; `command[0]` is the executable. */
1036
+ command?: readonly string[];
1037
+ /** Explicit broad host-shell form. Authorized by the same host process grant as argv. */
1038
+ shellCommand?: string;
1039
+ cwd: string;
1040
+ env?: NodeJS.ProcessEnv;
1041
+ interactiveWaitMs: number;
1042
+ timeoutMs: number;
1043
+ maxOutputBytes?: number;
1044
+ workId?: string;
1045
+ commandId?: string;
1046
+ signal?: AbortSignal;
1047
+ principalId?: string;
1048
+ }
1049
+
1050
+ /** Platform shell used for the explicit `shell_command` form. Mirrors the Process Runner convention. */
1051
+ export function hostShellInvocation(shellCommand: string): readonly string[] {
1052
+ const command = shellCommand.trim();
1053
+ if (!command) throw new Error('HOST_COMMAND_SHELL_STRING_REQUIRED');
1054
+ return process.platform === 'win32'
1055
+ ? ['cmd.exe', '/c', command]
1056
+ : ['/bin/bash', '-lc', command];
1057
+ }
1058
+
1059
+ function hostCommandArgv(input: StartLightweightHostCommandInput): readonly string[] {
1060
+ const shellCommand = input.shellCommand?.trim();
1061
+ if (shellCommand) return hostShellInvocation(shellCommand);
1062
+ const argv = (input.command ?? []).map((entry) => String(entry));
1063
+ if (argv.length === 0 || !argv[0]) throw new Error('HOST_COMMAND_ARGV_REQUIRED');
1064
+ return argv;
1065
+ }
1066
+
1067
+ /**
1068
+ * Mode-free host-local command lane. The target is the ForgeInstance, `cwd` is
1069
+ * an execution argument, and no repository registration or checkout identity is
1070
+ * required. Authorization is the caller's responsibility: this lane performs no
1071
+ * command parsing and never becomes a path sandbox.
1072
+ */
1073
+ export async function startLightweightHostCommand(
1074
+ input: StartLightweightHostCommandInput,
1075
+ ): Promise<{ handle: ProcessHandle; metrics: LightweightCommandMetrics }> {
1076
+ const argv = hostCommandArgv(input);
1077
+ const executable = argv[0]!;
1078
+ return startLightweightInternalProcess({
1079
+ controllerHome: input.controllerHome,
1080
+ scopeKey: FORGE_INSTANCE_PROCESS_SCOPE_KEY,
1081
+ executable,
1082
+ args: argv.slice(1),
1083
+ cwd: input.cwd,
1084
+ env: input.env,
1085
+ interactiveWaitMs: input.interactiveWaitMs,
1086
+ timeoutMs: input.timeoutMs,
1087
+ maxOutputBytes: input.maxOutputBytes,
1088
+ workId: input.workId,
1089
+ commandId: input.commandId,
1090
+ signal: input.signal,
1091
+ principalId: input.principalId,
1092
+ });
1093
+ }
1094
+
936
1095
  export async function startLightweightControllerCheck(
937
1096
  input: StartLightweightCheckInput,
938
1097
  ): Promise<{ handle: ProcessHandle; metrics: LightweightCommandMetrics }> {
@@ -947,7 +1106,7 @@ export async function startLightweightControllerCheck(
947
1106
  if (stableCommandId) {
948
1107
  const existing = [...entries.values()].find((entry) => (
949
1108
  entry.controllerHome === input.controllerHome
950
- && entry.repoId === input.repoId
1109
+ && entry.scopeKey === input.repoId
951
1110
  && entry.commandId === stableCommandId
952
1111
  ));
953
1112
  if (existing) {
@@ -982,7 +1141,9 @@ export async function startLightweightControllerCheck(
982
1141
  const entry: LightweightEntry = {
983
1142
  processId,
984
1143
  controllerHome: input.controllerHome,
1144
+ scopeKey: input.repoId,
985
1145
  repoId: input.repoId,
1146
+ ...(input.principalId ? { principalId: input.principalId } : {}),
986
1147
  workId: input.workId,
987
1148
  commandId: stableCommandId || processId,
988
1149
  requestFingerprint,
@@ -1031,6 +1192,14 @@ export async function startLightweightControllerCheck(
1031
1192
  return result;
1032
1193
  });
1033
1194
  entries.set(processId, entry);
1195
+ recordProcessHandleIndexEntry(entry.controllerHome, {
1196
+ processId,
1197
+ lane: 'lightweight',
1198
+ target: processScopeExecutionTarget(entry.controllerHome, processScopeFromKey(entry.scopeKey)),
1199
+ principalId: entry.principalId,
1200
+ workId: entry.workId,
1201
+ commandId: entry.commandId,
1202
+ });
1034
1203
  persistRunningReceipt(entry, true);
1035
1204
 
1036
1205
  if (entry.interactiveWaitMs > 0) {
@@ -1058,10 +1227,10 @@ export async function startLightweightControllerCheck(
1058
1227
  };
1059
1228
  }
1060
1229
 
1061
- function requireEntry(repoId: string, processId: string): LightweightEntry {
1230
+ function requireEntry(scopeKey: string, processId: string): LightweightEntry {
1062
1231
  const entry = entries.get(processId);
1063
1232
  if (!entry) throw new Error(`PROCESS_NOT_FOUND: ${processId}`);
1064
- if (entry.repoId !== repoId) throw new Error(`PROCESS_REPO_MISMATCH: process ${processId} belongs to ${entry.repoId}, not ${repoId}`);
1233
+ if (entry.scopeKey !== scopeKey) throw new Error(`PROCESS_SCOPE_MISMATCH: process ${processId} belongs to ${entry.scopeKey}, not ${scopeKey}`);
1065
1234
  return entry;
1066
1235
  }
1067
1236
 
@@ -1069,17 +1238,27 @@ export function isLightweightProcessId(processId: string): boolean {
1069
1238
  return processId.startsWith('lightweight:');
1070
1239
  }
1071
1240
 
1241
+ /** Repository placement of a lightweight handle this Runtime still owns in memory. */
1242
+ export function lightweightProcessRepositoryId(processId: string): string | undefined {
1243
+ return entries.get(processId)?.repoId;
1244
+ }
1245
+
1246
+ /** Canonical Process scope key of a lightweight handle this Runtime still owns in memory. */
1247
+ export function lightweightProcessScopeKey(processId: string): string | undefined {
1248
+ return entries.get(processId)?.scopeKey;
1249
+ }
1250
+
1072
1251
  /** Test seam for proving terminal receipt recovery across Runtime-memory loss. */
1073
1252
  export function clearLightweightProcessMemoryForTest(): void {
1074
1253
  entries.clear();
1075
1254
  }
1076
1255
 
1077
- export function getLightweightProcessHandle(controllerHome: string, repoId: string, processId: string): ProcessHandle | undefined {
1256
+ export function getLightweightProcessHandle(controllerHome: string, scopeKey: string, processId: string): ProcessHandle | undefined {
1078
1257
  const entry = entries.get(processId);
1079
- if (entry?.repoId === repoId) return entryHandle(entry);
1080
- const terminal = readTerminalReceipt(controllerHome, repoId, processId);
1258
+ if (entry?.scopeKey === scopeKey) return entryHandle(entry);
1259
+ const terminal = readTerminalReceipt(controllerHome, scopeKey, processId);
1081
1260
  if (terminal) return terminal.handle;
1082
- const running = readRunningReceipt(controllerHome, repoId, processId);
1261
+ const running = readRunningReceipt(controllerHome, scopeKey, processId);
1083
1262
  if (!running) return undefined;
1084
1263
  if (running.exitObservation) {
1085
1264
  return persistRecoveredTerminalReceipt(controllerHome, running, completedObservedExitHandle(running));
@@ -1094,20 +1273,20 @@ export function getLightweightProcessHandle(controllerHome: string, repoId: stri
1094
1273
 
1095
1274
  export async function waitForLightweightProcess(
1096
1275
  controllerHome: string,
1097
- repoId: string,
1276
+ scopeKey: string,
1098
1277
  processId: string,
1099
1278
  options: WaitProcessOptions = {},
1100
1279
  ): Promise<ProcessHandle> {
1101
1280
  const entry = entries.get(processId);
1102
1281
  if (!entry) {
1103
- const terminal = readTerminalReceipt(controllerHome, repoId, processId);
1282
+ const terminal = readTerminalReceipt(controllerHome, scopeKey, processId);
1104
1283
  if (terminal) return terminal.handle;
1105
- let running = readRunningReceipt(controllerHome, repoId, processId);
1284
+ let running = readRunningReceipt(controllerHome, scopeKey, processId);
1106
1285
  if (!running) throw new Error(`PROCESS_NOT_FOUND: ${processId}`);
1107
1286
  const timeoutMs = Math.max(1, options.timeoutMs ?? 15_000);
1108
1287
  const deadline = Date.now() + timeoutMs;
1109
1288
  while (true) {
1110
- const recoveredTerminal = readTerminalReceipt(controllerHome, repoId, processId);
1289
+ const recoveredTerminal = readTerminalReceipt(controllerHome, scopeKey, processId);
1111
1290
  if (recoveredTerminal) return recoveredTerminal.handle;
1112
1291
  if (running.exitObservation) {
1113
1292
  return persistRecoveredTerminalReceipt(controllerHome, running, completedObservedExitHandle(running));
@@ -1124,10 +1303,10 @@ export async function waitForLightweightProcess(
1124
1303
  const timer = setTimeout(resolve, Math.min(50, Math.max(1, deadline - Date.now())));
1125
1304
  timer.unref?.();
1126
1305
  });
1127
- running = readRunningReceipt(controllerHome, repoId, processId) ?? running;
1306
+ running = readRunningReceipt(controllerHome, scopeKey, processId) ?? running;
1128
1307
  }
1129
1308
  }
1130
- if (entry.repoId !== repoId) throw new Error(`PROCESS_REPO_MISMATCH: process ${processId} belongs to ${entry.repoId}, not ${repoId}`);
1309
+ if (entry.scopeKey !== scopeKey) throw new Error(`PROCESS_SCOPE_MISMATCH: process ${processId} belongs to ${entry.scopeKey}, not ${scopeKey}`);
1131
1310
  if (entry.result) return entryHandle(entry);
1132
1311
  const timeoutMs = Math.max(1, options.timeoutMs ?? 15_000);
1133
1312
  await Promise.race([
@@ -1151,12 +1330,12 @@ export async function cancelAllLightweightProcesses(controllerHome: string): Pro
1151
1330
  return active.length;
1152
1331
  }
1153
1332
 
1154
- export async function cancelLightweightProcess(controllerHome: string, repoId: string, processId: string): Promise<ProcessHandle> {
1333
+ export async function cancelLightweightProcess(controllerHome: string, scopeKey: string, processId: string): Promise<ProcessHandle> {
1155
1334
  const entry = entries.get(processId);
1156
1335
  if (!entry) {
1157
- const terminal = readTerminalReceipt(controllerHome, repoId, processId);
1336
+ const terminal = readTerminalReceipt(controllerHome, scopeKey, processId);
1158
1337
  if (terminal) return terminal.handle;
1159
- const running = readRunningReceipt(controllerHome, repoId, processId);
1338
+ const running = readRunningReceipt(controllerHome, scopeKey, processId);
1160
1339
  if (!running) throw new Error(`PROCESS_NOT_FOUND: ${processId}`);
1161
1340
  if (running.exitObservation) {
1162
1341
  return persistRecoveredTerminalReceipt(controllerHome, running, completedObservedExitHandle(running));
@@ -1171,7 +1350,7 @@ export async function cancelLightweightProcess(controllerHome: string, repoId: s
1171
1350
  await terminateProcessTree(pid, { gracePeriodMs: 200, killAfterMs: 1_000, pollIntervalMs: 25 });
1172
1351
  return persistRecoveredTerminalReceipt(controllerHome, running, cancelledRecoveredHandle(running));
1173
1352
  }
1174
- if (entry.repoId !== repoId) throw new Error(`PROCESS_REPO_MISMATCH: process ${processId} belongs to ${entry.repoId}, not ${repoId}`);
1353
+ if (entry.scopeKey !== scopeKey) throw new Error(`PROCESS_SCOPE_MISMATCH: process ${processId} belongs to ${entry.scopeKey}, not ${scopeKey}`);
1175
1354
  if (!entry.result) {
1176
1355
  entry.abort.abort();
1177
1356
  await entry.promise;
@@ -1181,13 +1360,13 @@ export async function cancelLightweightProcess(controllerHome: string, repoId: s
1181
1360
 
1182
1361
  export function readLightweightProcessLogs(
1183
1362
  controllerHome: string,
1184
- repoId: string,
1363
+ scopeKey: string,
1185
1364
  processId: string,
1186
1365
  maxBytes = 32 * 1024,
1187
1366
  ): ProcessLogSlice | undefined {
1188
1367
  const entry = entries.get(processId);
1189
1368
  if (!entry) {
1190
- const terminal = readTerminalReceipt(controllerHome, repoId, processId);
1369
+ const terminal = readTerminalReceipt(controllerHome, scopeKey, processId);
1191
1370
  if (terminal) {
1192
1371
  const stdout = capProcessOutput(terminal.handle.stdout ?? '', maxBytes);
1193
1372
  const stderr = capProcessOutput(terminal.handle.stderr ?? '', maxBytes);
@@ -1201,7 +1380,7 @@ export function readLightweightProcessLogs(
1201
1380
  || Buffer.byteLength(terminal.handle.stderr ?? '', 'utf8') > maxBytes,
1202
1381
  };
1203
1382
  }
1204
- const running = readRunningReceipt(controllerHome, repoId, processId);
1383
+ const running = readRunningReceipt(controllerHome, scopeKey, processId);
1205
1384
  if (!running) return undefined;
1206
1385
  const stdout = capProcessOutput(running.handle.stdoutTail ?? '', maxBytes);
1207
1386
  const stderr = capProcessOutput(running.handle.stderrTail ?? '', maxBytes);
@@ -1215,7 +1394,7 @@ export function readLightweightProcessLogs(
1215
1394
  || Buffer.byteLength(running.handle.stderrTail ?? '', 'utf8') > maxBytes,
1216
1395
  };
1217
1396
  }
1218
- if (entry.repoId !== repoId) return undefined;
1397
+ if (entry.scopeKey !== scopeKey) return undefined;
1219
1398
  const rawStdout = entry.result?.stdout ?? entry.stdout;
1220
1399
  const rawStderr = entry.result?.stderr ?? entry.stderr;
1221
1400
  const visibleStdout = visibleOutput(rawStdout, maxBytes);