@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
@@ -1,5 +1,13 @@
1
+ import {
2
+ findActiveCanonicalGrant,
3
+ listCanonicalGrants,
4
+ reconcileCanonicalGrants,
5
+ recordCanonicalGrant,
6
+ revokeCanonicalGrant,
7
+ type Grant,
8
+ } from '../../../packages/kernel/identity/api/index';
1
9
  import { randomUUID } from 'crypto';
2
- import { existsSync, mkdirSync } from 'fs';
10
+ import { existsSync, renameSync } from 'fs';
3
11
  import { join } from 'path';
4
12
  import { controllerSystemRoot } from '../../cli/repositories/controller-home';
5
13
  import { ControllerLockContentionError, withControllerLock } from '../../cli/repositories/locks';
@@ -31,7 +39,7 @@ export interface PluginCapabilityAuthorizationGrant {
31
39
  schemaVersion: 1;
32
40
  grantId: string;
33
41
  ownerScope: string;
34
- repoId: string;
42
+ repoId?: string;
35
43
  pluginId: string;
36
44
  capabilityId: string;
37
45
  target: AssistantPluginAuthorizationTarget;
@@ -51,7 +59,7 @@ interface PluginCapabilityAuthorizationGrantStore {
51
59
 
52
60
  export interface PluginCapabilityAuthorizationQuery {
53
61
  ownerScope: string;
54
- repoId: string;
62
+ repoId?: string;
55
63
  pluginId: string;
56
64
  capabilityId: string;
57
65
  target: AssistantPluginAuthorizationTarget;
@@ -73,12 +81,19 @@ export interface RevokePluginCapabilityAuthorizationInput {
73
81
  now?: Date;
74
82
  }
75
83
 
76
- const RISK_RANK: Record<AssistantPluginActionRisk, number> = {
77
- readonly: 0,
78
- workspace_write: 1,
79
- remote_write: 2,
80
- destructive: 3,
81
- };
84
+ export interface ReconcilePluginCapabilityAuthorizationsInput {
85
+ retiredOwnerScopes?: readonly string[];
86
+ now?: Date;
87
+ }
88
+
89
+ export interface ReconcilePluginCapabilityAuthorizationsResult {
90
+ removedRetiredOwner: number;
91
+ removedRevoked: number;
92
+ removedExpired: number;
93
+ removedTotal: number;
94
+ remaining: number;
95
+ changed: boolean;
96
+ }
82
97
 
83
98
  const GENERIC_PLUGIN_AUTHORIZATION_ACTORS = new Set(['', 'anonymous', 'plugin_action_execute']);
84
99
 
@@ -156,7 +171,10 @@ function validatePersistedGrant(value: unknown, index: number): PluginCapability
156
171
  schemaVersion: 1,
157
172
  grantId: string('grantId'),
158
173
  ownerScope: string('ownerScope'),
159
- repoId: string('repoId'),
174
+ // Pre-canonical plugin grants could omit repoId for controller/account targets.
175
+ // Normalize that omission once during the one-way migration; authorization
176
+ // against the canonical Grant store is exact after this point.
177
+ repoId: typeof record.repoId === 'string' && record.repoId.trim() ? record.repoId.trim() : 'controller:global',
160
178
  pluginId: string('pluginId'),
161
179
  capabilityId: string('capabilityId'),
162
180
  target: normalizeTarget({
@@ -180,6 +198,41 @@ export function pluginCapabilityAuthorizationGrantStorePath(controllerHome: stri
180
198
  return join(controllerSystemRoot(controllerHome), 'plugin-capability-authorizations', 'grants.json');
181
199
  }
182
200
 
201
+ function legacyPluginGrantMigrationMarkerPath(controllerHome: string): string {
202
+ return join(controllerSystemRoot(controllerHome), 'plugin-capability-authorizations', 'migration.json');
203
+ }
204
+
205
+ /**
206
+ * Durable proof that this Controller Home already ran the one-way migration, so
207
+ * authorization reads stop consulting the legacy file at all.
208
+ */
209
+ interface LegacyPluginGrantMigrationMarker {
210
+ schemaVersion: 1;
211
+ migratedAt: string;
212
+ legacyGrantCount: number;
213
+ canonicalGrantCount: number;
214
+ }
215
+
216
+ function readLegacyPluginGrantMigrationMarker(controllerHome: string): LegacyPluginGrantMigrationMarker | undefined {
217
+ const path = legacyPluginGrantMigrationMarkerPath(controllerHome);
218
+ if (!existsSync(path)) return undefined;
219
+ try {
220
+ const raw = readJsonFile<Record<string, unknown>>(path);
221
+ if (raw?.schemaVersion !== 1
222
+ || typeof raw.migratedAt !== 'string'
223
+ || typeof raw.legacyGrantCount !== 'number'
224
+ || typeof raw.canonicalGrantCount !== 'number') {
225
+ throw new Error('migration marker schema is invalid');
226
+ }
227
+ return raw as unknown as LegacyPluginGrantMigrationMarker;
228
+ } catch (error) {
229
+ throw new PluginCapabilityAuthorizationGrantError(
230
+ 'PLUGIN_CAPABILITY_GRANT_STORE_CORRUPT',
231
+ `Plugin capability authorization migration marker is corrupt: ${error instanceof Error ? error.message : String(error)}`,
232
+ );
233
+ }
234
+ }
235
+
183
236
  function loadStore(controllerHome: string): PluginCapabilityAuthorizationGrantStore {
184
237
  const path = pluginCapabilityAuthorizationGrantStorePath(controllerHome);
185
238
  if (!existsSync(path)) return { schemaVersion: 1, grants: [] };
@@ -197,21 +250,123 @@ function loadStore(controllerHome: string): PluginCapabilityAuthorizationGrantSt
197
250
  }
198
251
  }
199
252
 
200
- function saveStore(controllerHome: string, store: PluginCapabilityAuthorizationGrantStore): void {
201
- const path = pluginCapabilityAuthorizationGrantStorePath(controllerHome);
202
- mkdirSync(join(controllerSystemRoot(controllerHome), 'plugin-capability-authorizations'), { recursive: true });
203
- writeJsonAtomic(path, store);
253
+ function pluginMetadataFromCanonicalGrant(grant: Grant): { pluginId: string; capabilityId: string } | undefined {
254
+ const constraints = grant.constraints;
255
+ if (!constraints || constraints.kind !== 'plugin_capability_authorization') return undefined;
256
+ const pluginId = typeof constraints.pluginId === 'string' ? constraints.pluginId.trim() : '';
257
+ const capabilityId = typeof constraints.capabilityId === 'string' ? constraints.capabilityId.trim() : '';
258
+ return pluginId && capabilityId ? { pluginId, capabilityId } : undefined;
259
+ }
260
+
261
+ function pluginGrantFromCanonical(
262
+ grant: Grant,
263
+ fallback?: { pluginId: string; capabilityId: string; repoId?: string; target?: AssistantPluginAuthorizationTarget },
264
+ ): PluginCapabilityAuthorizationGrant | undefined {
265
+ const metadata = pluginMetadataFromCanonicalGrant(grant)
266
+ ?? (fallback ? { pluginId: fallback.pluginId, capabilityId: fallback.capabilityId } : undefined);
267
+ if (!metadata || !grant.target) return undefined;
268
+ if (grant.riskCeiling === 'destructive') return undefined;
269
+ const ownerScope = grant.ownerScope?.trim() || grant.principalId.trim();
270
+ if (!ownerScope) return undefined;
271
+ const target: AssistantPluginAuthorizationTarget = {
272
+ kind: grant.target.kind,
273
+ id: grant.target.id,
274
+ ...(grant.target.identityFingerprint ? { identityFingerprint: grant.target.identityFingerprint } : fallback?.target?.identityFingerprint ? { identityFingerprint: fallback.target.identityFingerprint } : {}),
275
+ };
276
+ return {
277
+ schemaVersion: 1,
278
+ grantId: grant.grantId,
279
+ ownerScope,
280
+ repoId: grant.target.repoId ?? fallback?.repoId ?? 'controller:global',
281
+ pluginId: metadata.pluginId,
282
+ capabilityId: metadata.capabilityId,
283
+ target,
284
+ scopes: [...(grant.scopes ?? [])],
285
+ riskCeiling: grant.riskCeiling ?? 'readonly',
286
+ createdAt: grant.createdAt,
287
+ updatedAt: grant.updatedAt,
288
+ expiresAt: grant.expiresAt,
289
+ ...(grant.revokedAt ? { revokedAt: grant.revokedAt } : {}),
290
+ ...(grant.revokedReason ? { revokedReason: grant.revokedReason } : {}),
291
+ };
204
292
  }
205
293
 
206
- function targetMatches(left: AssistantPluginAuthorizationTarget, right: AssistantPluginAuthorizationTarget): boolean {
207
- return left.kind === right.kind
208
- && left.id === right.id
209
- && (left.identityFingerprint ?? '') === (right.identityFingerprint ?? '');
294
+ function recordCanonicalPluginGrant(
295
+ controllerHome: string,
296
+ grant: PluginCapabilityAuthorizationGrant,
297
+ now?: Date,
298
+ ): PluginCapabilityAuthorizationGrant {
299
+ const expiresInMinutes = Math.max(1, Math.ceil((Date.parse(grant.expiresAt) - (now ?? new Date()).getTime()) / 60_000));
300
+ const canonical = recordCanonicalGrant(controllerHome, {
301
+ grantId: grant.grantId,
302
+ principalId: grant.ownerScope,
303
+ ownerScope: grant.ownerScope,
304
+ capabilities: [`${grant.pluginId}:${grant.capabilityId}`, grant.capabilityId],
305
+ target: {
306
+ kind: grant.target.kind,
307
+ id: grant.target.id,
308
+ repoId: grant.repoId,
309
+ ...(grant.target.identityFingerprint ? { identityFingerprint: grant.target.identityFingerprint } : {}),
310
+ },
311
+ scopes: grant.scopes,
312
+ riskCeiling: grant.riskCeiling,
313
+ constraints: { kind: 'plugin_capability_authorization', pluginId: grant.pluginId, capabilityId: grant.capabilityId },
314
+ expiresInMinutes,
315
+ now,
316
+ });
317
+ return pluginGrantFromCanonical(canonical, grant)!;
210
318
  }
211
319
 
212
- function scopesContain(granted: readonly string[], requested: readonly string[]): boolean {
213
- const available = new Set(granted);
214
- return requested.every((scope) => available.has(scope));
320
+ /**
321
+ * One-way migration input. The legacy plugin grant file is read-only history:
322
+ * every still-authorizable legacy grant is copied into the canonical Grant
323
+ * authority exactly once, and this module never writes the legacy file again.
324
+ * A corrupt migration input still fails closed so un-migrated grants cannot be
325
+ * silently dropped.
326
+ *
327
+ * Removal trigger: once a released Runtime baseline has shipped with the
328
+ * `migration.json` marker (so every install has migrated at least once), delete
329
+ * this migration, `loadStore`/`validatePersistedGrant`, the legacy path getter
330
+ * and the PLUGIN_CAPABILITY_GRANT_STORE_CORRUPT error code in the same slice.
331
+ * The archived `grants.json.migrated-*` file is recoverable history, not input.
332
+ */
333
+ function migrateLegacyPluginCapabilityGrants(controllerHome: string, at = new Date()): void {
334
+ if (readLegacyPluginGrantMigrationMarker(controllerHome)) return;
335
+ const legacyGrants = loadStore(controllerHome).grants;
336
+ const canonicalById = new Map(listCanonicalGrants(controllerHome).map((grant) => [grant.grantId, grant]));
337
+ const atMs = at.getTime();
338
+ for (const grant of legacyGrants) {
339
+ if (grant.revokedAt) continue;
340
+ if (Date.parse(grant.expiresAt) <= atMs) continue;
341
+ const existing = canonicalById.get(grant.grantId);
342
+ if (existing) {
343
+ // A canonical row written before plugin metadata existed cannot be projected
344
+ // back into a plugin grant. Repair it in place instead of leaving a live grant
345
+ // that neither list nor by-id resolution can see.
346
+ if (existing.revokedAt || pluginMetadataFromCanonicalGrant(existing)) continue;
347
+ recordCanonicalPluginGrant(controllerHome, grant, new Date(grant.createdAt));
348
+ continue;
349
+ }
350
+ recordCanonicalPluginGrant(controllerHome, grant, new Date(grant.createdAt));
351
+ canonicalById.set(grant.grantId, { grantId: grant.grantId } as Grant);
352
+ }
353
+ // The migration is complete for this Controller Home: prove it durably, then
354
+ // move the legacy file aside so no authorization read can ever depend on it
355
+ // again. The archive is recoverable; a failed rename leaves an inert file.
356
+ writeJsonAtomic(legacyPluginGrantMigrationMarkerPath(controllerHome), {
357
+ schemaVersion: 1,
358
+ migratedAt: at.toISOString(),
359
+ legacyGrantCount: legacyGrants.length,
360
+ canonicalGrantCount: listCanonicalGrants(controllerHome).length,
361
+ } satisfies LegacyPluginGrantMigrationMarker);
362
+ const legacyPath = pluginCapabilityAuthorizationGrantStorePath(controllerHome);
363
+ if (existsSync(legacyPath)) {
364
+ try {
365
+ renameSync(legacyPath, `${legacyPath}.migrated-${at.getTime()}`);
366
+ } catch {
367
+ // Inert history; the marker already prevents any further legacy read.
368
+ }
369
+ }
215
370
  }
216
371
 
217
372
  export function findActivePluginCapabilityAuthorization(
@@ -220,22 +375,21 @@ export function findActivePluginCapabilityAuthorization(
220
375
  ): PluginCapabilityAuthorizationGrant | undefined {
221
376
  if (query.risk === 'destructive') return undefined;
222
377
  const ownerScope = required(query.ownerScope, 'ownerScope');
223
- const repoId = required(query.repoId, 'repoId');
378
+ const repoId = (query.repoId?.trim()) || 'controller:global';
224
379
  const pluginId = required(query.pluginId, 'pluginId');
225
380
  const capabilityId = required(query.capabilityId, 'capabilityId');
226
381
  const target = normalizeTarget(query.target);
227
382
  const scopes = normalizeScopes(query.scopes);
228
- const atMs = (query.at ?? new Date()).getTime();
229
- return loadStore(controllerHome).grants
230
- .filter((grant) => !grant.revokedAt && Date.parse(grant.expiresAt) > atMs)
231
- .filter((grant) => grant.ownerScope === ownerScope
232
- && grant.repoId === repoId
233
- && grant.pluginId === pluginId
234
- && grant.capabilityId === capabilityId
235
- && targetMatches(grant.target, target)
236
- && scopesContain(grant.scopes, scopes)
237
- && RISK_RANK[grant.riskCeiling] >= RISK_RANK[query.risk])
238
- .sort((left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt))[0];
383
+ migrateLegacyPluginCapabilityGrants(controllerHome, query.at ?? new Date());
384
+ const canonical = findActiveCanonicalGrant(controllerHome, {
385
+ ownerScope,
386
+ capability: `${pluginId}:${capabilityId}`,
387
+ target: { kind: target.kind, id: target.id, repoId, ...(target.identityFingerprint ? { identityFingerprint: target.identityFingerprint } : {}) },
388
+ scopes,
389
+ risk: query.risk,
390
+ at: query.at,
391
+ });
392
+ return canonical ? pluginGrantFromCanonical(canonical, { pluginId, capabilityId, repoId, target }) : undefined;
239
393
  }
240
394
 
241
395
  /**
@@ -250,9 +404,12 @@ export function findActivePluginCapabilityAuthorizationById(
250
404
  ): PluginCapabilityAuthorizationGrant | undefined {
251
405
  const normalizedGrantId = required(grantId, 'grantId');
252
406
  const atMs = at.getTime();
253
- const grant = loadStore(controllerHome).grants.find((entry) => entry.grantId === normalizedGrantId);
254
- if (!grant || grant.revokedAt || Date.parse(grant.expiresAt) <= atMs) return undefined;
255
- return structuredClone(grant);
407
+ migrateLegacyPluginCapabilityGrants(controllerHome, at);
408
+ const canonical = listCanonicalGrants(controllerHome).find((entry) => entry.grantId === normalizedGrantId);
409
+ if (canonical && !canonical.revokedAt && Date.parse(canonical.expiresAt) > atMs) {
410
+ return pluginGrantFromCanonical(canonical);
411
+ }
412
+ return undefined;
256
413
  }
257
414
 
258
415
  export function recordPluginCapabilityAuthorization(
@@ -267,7 +424,7 @@ export function recordPluginCapabilityAuthorization(
267
424
  }
268
425
  const now = input.now ?? new Date();
269
426
  const ownerScope = required(input.ownerScope, 'ownerScope');
270
- const repoId = required(input.repoId, 'repoId');
427
+ const repoId = (input.repoId?.trim()) || 'controller:global';
271
428
  const pluginId = required(input.pluginId, 'pluginId');
272
429
  const capabilityId = required(input.capabilityId, 'capabilityId');
273
430
  const target = normalizeTarget(input.target);
@@ -278,6 +435,7 @@ export function recordPluginCapabilityAuthorization(
278
435
  }
279
436
  const expiresInMinutes = Math.min(Math.max(1, Math.trunc(rawMinutes)), MAX_PLUGIN_CAPABILITY_GRANT_MINUTES);
280
437
  const timestamp = now.toISOString();
438
+ migrateLegacyPluginCapabilityGrants(controllerHome, now);
281
439
  const grant: PluginCapabilityAuthorizationGrant = {
282
440
  schemaVersion: 1,
283
441
  grantId: `plugin-grant-${randomUUID()}`,
@@ -298,16 +456,19 @@ export function recordPluginCapabilityAuthorization(
298
456
  { scope: 'global', resource: 'plugin-capability-authorization-grants' },
299
457
  `plugin-capability-grant:${ownerScope}`,
300
458
  () => {
301
- const store = loadStore(controllerHome);
302
- store.grants = store.grants.filter((entry) => !(entry.ownerScope === ownerScope
303
- && entry.repoId === repoId
304
- && entry.pluginId === pluginId
305
- && entry.capabilityId === capabilityId
306
- && entry.target.kind === target.kind
307
- && entry.target.id === target.id));
308
- store.grants.push(grant);
309
- saveStore(controllerHome, store);
310
- return grant;
459
+ const canonicalGrant = recordCanonicalGrant(controllerHome, {
460
+ grantId: grant.grantId,
461
+ principalId: grant.ownerScope,
462
+ ownerScope: grant.ownerScope,
463
+ capabilities: [`${grant.pluginId}:${grant.capabilityId}`, grant.capabilityId],
464
+ target: { kind: grant.target.kind, id: grant.target.id, repoId: grant.repoId, ...(grant.target.identityFingerprint ? { identityFingerprint: grant.target.identityFingerprint } : {}) },
465
+ scopes: grant.scopes,
466
+ riskCeiling: grant.riskCeiling,
467
+ constraints: { kind: 'plugin_capability_authorization', pluginId: grant.pluginId, capabilityId: grant.capabilityId },
468
+ expiresInMinutes,
469
+ now,
470
+ });
471
+ return pluginGrantFromCanonical(canonicalGrant, grant)!;
311
472
  },
312
473
  5_000,
313
474
  );
@@ -333,22 +494,56 @@ export function revokePluginCapabilityAuthorization(
333
494
  { scope: 'global', resource: 'plugin-capability-authorization-grants' },
334
495
  `plugin-capability-revoke:${ownerScope}`,
335
496
  () => {
336
- const store = loadStore(controllerHome);
337
- const index = store.grants.findIndex((grant) => grant.grantId === grantId);
338
- if (index < 0) throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_NOT_FOUND', `Grant ${grantId} was not found.`);
339
- const current = store.grants[index]!;
497
+ const current = findActivePluginCapabilityAuthorizationById(controllerHome, grantId, now);
498
+ if (!current) throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_NOT_FOUND', `Grant ${grantId} was not found.`);
340
499
  if (current.ownerScope !== ownerScope) {
341
500
  throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_OWNER_MISMATCH', `Grant ${grantId} belongs to another owner scope.`);
342
501
  }
343
- const revoked: PluginCapabilityAuthorizationGrant = {
344
- ...current,
345
- updatedAt: now.toISOString(),
346
- revokedAt: now.toISOString(),
347
- revokedReason: reason,
502
+ const canonical = revokeCanonicalGrant(controllerHome, { grantId, ownerScope, reason, now });
503
+ return pluginGrantFromCanonical(canonical, current)!;
504
+ },
505
+ 5_000,
506
+ );
507
+ } catch (error) {
508
+ if (error instanceof ControllerLockContentionError) {
509
+ throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_STORE_BUSY', error.message);
510
+ }
511
+ throw error;
512
+ }
513
+ }
514
+
515
+ export function reconcilePluginCapabilityAuthorizations(
516
+ controllerHome: string,
517
+ input: ReconcilePluginCapabilityAuthorizationsInput = {},
518
+ ): ReconcilePluginCapabilityAuthorizationsResult {
519
+ const retiredOwnerScopes = new Set(
520
+ (input.retiredOwnerScopes ?? [])
521
+ .map((ownerScope) => required(ownerScope, 'retiredOwnerScopes[]')),
522
+ );
523
+ const nowMs = (input.now ?? new Date()).getTime();
524
+ try {
525
+ return withControllerLock(
526
+ controllerHome,
527
+ { scope: 'global', resource: 'plugin-capability-authorization-grants' },
528
+ 'plugin-capability-reconcile',
529
+ () => {
530
+ void nowMs;
531
+ migrateLegacyPluginCapabilityGrants(controllerHome, input.now ?? new Date());
532
+ const canonical = reconcileCanonicalGrants(controllerHome, {
533
+ retiredOwnerScopes: [...retiredOwnerScopes],
534
+ now: input.now,
535
+ });
536
+ const grants = listCanonicalGrants(controllerHome)
537
+ .map((grant) => pluginGrantFromCanonical(grant))
538
+ .filter((grant): grant is PluginCapabilityAuthorizationGrant => Boolean(grant));
539
+ return {
540
+ removedRetiredOwner: canonical.removedRetiredOwner,
541
+ removedRevoked: canonical.removedRevoked,
542
+ removedExpired: canonical.removedExpired,
543
+ removedTotal: canonical.removedTotal,
544
+ remaining: grants.length,
545
+ changed: canonical.changed,
348
546
  };
349
- store.grants[index] = revoked;
350
- saveStore(controllerHome, store);
351
- return revoked;
352
547
  },
353
548
  5_000,
354
549
  );
@@ -364,8 +559,9 @@ export function listPluginCapabilityAuthorizations(
364
559
  controllerHome: string,
365
560
  ownerScope?: string,
366
561
  ): PluginCapabilityAuthorizationGrant[] {
562
+ migrateLegacyPluginCapabilityGrants(controllerHome);
367
563
  const normalizedOwner = ownerScope?.trim();
368
- return loadStore(controllerHome).grants
369
- .filter((grant) => !normalizedOwner || grant.ownerScope === normalizedOwner)
370
- .map((grant) => structuredClone(grant));
564
+ return listCanonicalGrants(controllerHome, normalizedOwner ? { ownerScope: normalizedOwner } : undefined)
565
+ .map((grant) => pluginGrantFromCanonical(grant))
566
+ .filter((grant): grant is PluginCapabilityAuthorizationGrant => Boolean(grant));
371
567
  }
@@ -0,0 +1,219 @@
1
+ import { createHash, randomUUID } from 'crypto';
2
+ import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'fs';
3
+ import { homedir } from 'os';
4
+ import { dirname, join } from 'path';
5
+ import { resolveControllerHome } from '../../cli/repositories/controller-home';
6
+ import { loadRuntimeReleaseManifest } from '../root/release-manifest';
7
+ import { readRuntimeReleaseAuthority } from '../root/release-store';
8
+ import { ensureBrowserStateInControllerHome } from './browser-session-store';
9
+ import { renderWorkflowSupervisorNativeManifest } from '../../../supervisor/native-messaging/manifest';
10
+ import { WORKFLOW_SUPERVISOR_NATIVE_HOST_NAME } from '../../../supervisor/native-messaging/host';
11
+
12
+ const EXTENSION_FILES = ['manifest.json', 'background.js', 'content.js', 'core.js'] as const;
13
+ const FORGE_NATIVE_MESSAGING_DECLARATION = 'forge-native-messaging-host.json';
14
+
15
+ export interface WorkflowSupervisorBrowserInstallation {
16
+ browser: 'chrome' | 'chrome-for-testing' | 'chromium' | 'vivaldi' | 'forge-managed';
17
+ nativeMessagingRoot: string;
18
+ }
19
+ interface ActiveBrowserAdapterRelease {
20
+ releaseId: string;
21
+ extensionSourcePath: string;
22
+ nativeHostSourcePath: string;
23
+ nativeHostArtifactIdentity: string;
24
+ }
25
+ export interface WorkflowSupervisorBrowserAdapterDependencies {
26
+ homeDir?: string;
27
+ browserInstallations?: readonly WorkflowSupervisorBrowserInstallation[];
28
+ activeRelease?: (controllerHome: string) => ActiveBrowserAdapterRelease;
29
+ repository?: { repoId: string; repoRoot: string };
30
+ }
31
+ export interface WorkflowSupervisorBrowserAdapterStatus {
32
+ state: 'ready' | 'not_installed' | 'projection_stale' | 'runtime_release_incomplete';
33
+ extensionPath: string;
34
+ extensionId?: string;
35
+ nativeHostPath: string;
36
+ activeReleaseId?: string;
37
+ nativeManifestPaths: string[];
38
+ projectionCurrent: boolean;
39
+ detail: string;
40
+ }
41
+
42
+ function sha256(path: string): string {
43
+ return createHash('sha256').update(readFileSync(path)).digest('hex');
44
+ }
45
+ export function workflowSupervisorExtensionIdFromManifestKey(value: string): string {
46
+ const key = value.trim();
47
+ if (!/^[A-Za-z0-9+/]+={0,2}$/.test(key)) throw new Error('WORKFLOW_SUPERVISOR_EXTENSION_KEY_INVALID');
48
+ const bytes = Buffer.from(key, 'base64');
49
+ if (bytes.length < 64 || bytes.toString('base64').replace(/=+$/,'') !== key.replace(/=+$/,'')) {
50
+ throw new Error('WORKFLOW_SUPERVISOR_EXTENSION_KEY_INVALID');
51
+ }
52
+ return createHash('sha256').update(bytes).digest('hex').slice(0, 32)
53
+ .replace(/[0-9a-f]/g, (nibble) => String.fromCharCode(97 + Number.parseInt(nibble, 16)));
54
+ }
55
+ function extensionId(path: string): string {
56
+ const manifest = JSON.parse(readFileSync(join(path, 'manifest.json'), 'utf8')) as { key?: unknown };
57
+ if (typeof manifest.key !== 'string') throw new Error('WORKFLOW_SUPERVISOR_EXTENSION_KEY_REQUIRED');
58
+ return workflowSupervisorExtensionIdFromManifestKey(manifest.key);
59
+ }
60
+ function controllerPaths(controllerHome: string) {
61
+ const root = join(resolveControllerHome(controllerHome), 'supervisor', 'browser-adapter');
62
+ return {
63
+ extensionPath: join(root, 'chrome-extension'),
64
+ nativeHostPath: join(root, 'forge-workflow-supervisor-native-host'),
65
+ };
66
+ }
67
+ function defaultBrowserInstallations(
68
+ controllerHome: string,
69
+ homeDir = process.env.HOME ?? homedir(),
70
+ repository?: { repoId: string; repoRoot: string },
71
+ ): WorkflowSupervisorBrowserInstallation[] {
72
+ const installations: WorkflowSupervisorBrowserInstallation[] = [
73
+ { browser: 'chrome', nativeMessagingRoot: join(homeDir, 'Library', 'Application Support', 'Google', 'Chrome', 'NativeMessagingHosts') },
74
+ { browser: 'chrome-for-testing', nativeMessagingRoot: join(homeDir, 'Library', 'Application Support', 'Google', 'ChromeForTesting', 'NativeMessagingHosts') },
75
+ { browser: 'chromium', nativeMessagingRoot: join(homeDir, 'Library', 'Application Support', 'Chromium', 'NativeMessagingHosts') },
76
+ { browser: 'vivaldi', nativeMessagingRoot: join(homeDir, 'Library', 'Application Support', 'Vivaldi', 'NativeMessagingHosts') },
77
+ ];
78
+ if (repository) {
79
+ const browserStateRoot = ensureBrowserStateInControllerHome(controllerHome, repository.repoId, repository.repoRoot);
80
+ installations.push({ browser: 'forge-managed', nativeMessagingRoot: join(browserStateRoot, 'profiles', 'default', 'NativeMessagingHosts') });
81
+ }
82
+ return installations;
83
+ }
84
+ function activeBrowserAdapterRelease(controllerHome: string): ActiveBrowserAdapterRelease {
85
+ const home = resolveControllerHome(controllerHome);
86
+ const authority = readRuntimeReleaseAuthority(home);
87
+ if (!authority) throw new Error('WORKFLOW_SUPERVISOR_BROWSER_RUNTIME_RELEASE_MISSING');
88
+ const manifest = loadRuntimeReleaseManifest(authority.active.manifestPath, home);
89
+ if (!manifest.packageRoot || !manifest.workflowSupervisorNativeHostEntrypoint || !manifest.workflowSupervisorNativeHostArtifactIdentity) {
90
+ throw new Error('WORKFLOW_SUPERVISOR_BROWSER_RUNTIME_RELEASE_INCOMPLETE');
91
+ }
92
+ const releaseRoot = dirname(authority.active.manifestPath);
93
+ return {
94
+ releaseId: authority.active.releaseId,
95
+ extensionSourcePath: join(releaseRoot, manifest.packageRoot, 'adapters', 'computer', 'chrome-extension'),
96
+ nativeHostSourcePath: join(releaseRoot, manifest.workflowSupervisorNativeHostEntrypoint),
97
+ nativeHostArtifactIdentity: manifest.workflowSupervisorNativeHostArtifactIdentity,
98
+ };
99
+ }
100
+ function assertRegularFile(path: string, code: string): void {
101
+ if (!existsSync(path)) throw new Error(code + ': ' + path);
102
+ const status = lstatSync(path);
103
+ if (status.isSymbolicLink() || !status.isFile()) throw new Error(code + ': ' + path);
104
+ }
105
+ function projectExtension(source: string, destination: string): void {
106
+ const staging = destination + '.staging-' + randomUUID().slice(0, 12);
107
+ rmSync(staging, { recursive: true, force: true });
108
+ mkdirSync(staging, { recursive: true, mode: 0o700 });
109
+ try {
110
+ for (const file of EXTENSION_FILES) {
111
+ const sourcePath = join(source, file);
112
+ assertRegularFile(sourcePath, 'WORKFLOW_SUPERVISOR_EXTENSION_SOURCE_INVALID');
113
+ writeFileSync(join(staging, file), readFileSync(sourcePath), { mode: 0o600 });
114
+ }
115
+ rmSync(destination, { recursive: true, force: true });
116
+ renameSync(staging, destination);
117
+ } catch (error) {
118
+ rmSync(staging, { recursive: true, force: true });
119
+ throw error;
120
+ }
121
+ }
122
+ function projectNativeHost(source: string, expectedIdentity: string, destination: string): void {
123
+ assertRegularFile(source, 'WORKFLOW_SUPERVISOR_NATIVE_HOST_SOURCE_INVALID');
124
+ if ('sha256:' + sha256(source) !== expectedIdentity) throw new Error('WORKFLOW_SUPERVISOR_NATIVE_HOST_IDENTITY_MISMATCH');
125
+ mkdirSync(dirname(destination), { recursive: true, mode: 0o700 });
126
+ const temporary = destination + '.' + randomUUID().slice(0, 12) + '.tmp';
127
+ writeFileSync(temporary, readFileSync(source), { mode: 0o700 });
128
+ chmodSync(temporary, 0o700);
129
+ renameSync(temporary, destination);
130
+ }
131
+ function extensionProjectionCurrent(source: string, destination: string): boolean {
132
+ return EXTENSION_FILES.every((file) => {
133
+ const sourcePath = join(source, file), destinationPath = join(destination, file);
134
+ return existsSync(sourcePath) && existsSync(destinationPath) && sha256(sourcePath) === sha256(destinationPath);
135
+ });
136
+ }
137
+ function nativeManifestPath(installation: WorkflowSupervisorBrowserInstallation): string {
138
+ return join(installation.nativeMessagingRoot, WORKFLOW_SUPERVISOR_NATIVE_HOST_NAME + '.json');
139
+ }
140
+ function nativeManifestReady(path: string, nativeHostPath: string, exactExtensionId: string): boolean {
141
+ if (!existsSync(path)) return false;
142
+ try {
143
+ const manifest = JSON.parse(readFileSync(path, 'utf8')) as { path?: unknown; allowed_origins?: unknown };
144
+ return manifest.path === nativeHostPath
145
+ && JSON.stringify(manifest.allowed_origins) === JSON.stringify(['chrome-extension://' + exactExtensionId + '/']);
146
+ } catch { return false; }
147
+ }
148
+ function inspectWithRelease(
149
+ controllerHome: string,
150
+ release: ActiveBrowserAdapterRelease,
151
+ dependencies: WorkflowSupervisorBrowserAdapterDependencies,
152
+ ): WorkflowSupervisorBrowserAdapterStatus {
153
+ const paths = controllerPaths(controllerHome);
154
+ const exactExtensionId = extensionId(release.extensionSourcePath);
155
+ const hostCurrent = existsSync(paths.nativeHostPath) && 'sha256:' + sha256(paths.nativeHostPath) === release.nativeHostArtifactIdentity;
156
+ const extensionCurrent = existsSync(paths.extensionPath)
157
+ && extensionProjectionCurrent(release.extensionSourcePath, paths.extensionPath)
158
+ && nativeManifestReady(join(paths.extensionPath, FORGE_NATIVE_MESSAGING_DECLARATION), paths.nativeHostPath, exactExtensionId);
159
+ const installations = dependencies.browserInstallations ?? defaultBrowserInstallations(controllerHome, dependencies.homeDir, dependencies.repository);
160
+ const nativeManifestPaths = installations.map(nativeManifestPath);
161
+ const manifestsCurrent = nativeManifestPaths.every((path) => nativeManifestReady(path, paths.nativeHostPath, exactExtensionId));
162
+ const projectionCurrent = hostCurrent && extensionCurrent && manifestsCurrent;
163
+ return {
164
+ state: projectionCurrent ? 'ready' : (existsSync(paths.extensionPath) || existsSync(paths.nativeHostPath) ? 'projection_stale' : 'not_installed'),
165
+ extensionPath: paths.extensionPath,
166
+ extensionId: exactExtensionId,
167
+ nativeHostPath: paths.nativeHostPath,
168
+ activeReleaseId: release.releaseId,
169
+ nativeManifestPaths,
170
+ projectionCurrent,
171
+ detail: projectionCurrent
172
+ ? 'Workflow Supervisor browser adapter is projected from the active Runtime release and Native Messaging is registered for the package-derived extension id. Live browser connectivity is proven separately by Supervisor browser_discovery.'
173
+ : 'Controller browser-adapter projection does not match the active Runtime release.',
174
+ };
175
+ }
176
+ export function inspectWorkflowSupervisorBrowserAdapter(
177
+ controllerHome: string,
178
+ dependencies: WorkflowSupervisorBrowserAdapterDependencies = {},
179
+ ): WorkflowSupervisorBrowserAdapterStatus {
180
+ const paths = controllerPaths(controllerHome);
181
+ try {
182
+ return inspectWithRelease(controllerHome, (dependencies.activeRelease ?? activeBrowserAdapterRelease)(controllerHome), dependencies);
183
+ } catch (error) {
184
+ return {
185
+ state: 'runtime_release_incomplete',
186
+ extensionPath: paths.extensionPath,
187
+ nativeHostPath: paths.nativeHostPath,
188
+ nativeManifestPaths: [],
189
+ projectionCurrent: false,
190
+ detail: error instanceof Error ? error.message : String(error),
191
+ };
192
+ }
193
+ }
194
+ export function installWorkflowSupervisorBrowserAdapter(
195
+ controllerHome: string,
196
+ dependencies: WorkflowSupervisorBrowserAdapterDependencies = {},
197
+ ): WorkflowSupervisorBrowserAdapterStatus {
198
+ const release = (dependencies.activeRelease ?? activeBrowserAdapterRelease)(controllerHome);
199
+ const paths = controllerPaths(controllerHome);
200
+ const exactExtensionId = extensionId(release.extensionSourcePath);
201
+ projectExtension(release.extensionSourcePath, paths.extensionPath);
202
+ projectNativeHost(release.nativeHostSourcePath, release.nativeHostArtifactIdentity, paths.nativeHostPath);
203
+ writeFileSync(
204
+ join(paths.extensionPath, FORGE_NATIVE_MESSAGING_DECLARATION),
205
+ renderWorkflowSupervisorNativeManifest({ executablePath: paths.nativeHostPath, extensionId: exactExtensionId }),
206
+ { mode: 0o600 },
207
+ );
208
+ for (const installation of dependencies.browserInstallations ?? defaultBrowserInstallations(controllerHome, dependencies.homeDir, dependencies.repository)) {
209
+ const path = nativeManifestPath(installation);
210
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
211
+ const temporary = path + '.' + randomUUID().slice(0, 12) + '.tmp';
212
+ writeFileSync(temporary, renderWorkflowSupervisorNativeManifest({
213
+ executablePath: paths.nativeHostPath,
214
+ extensionId: exactExtensionId,
215
+ }), { mode: 0o600 });
216
+ renameSync(temporary, path);
217
+ }
218
+ return inspectWithRelease(controllerHome, release, dependencies);
219
+ }