@moretea-labs/forge 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (396) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +26 -0
  3. package/CLAUDE.md +2 -0
  4. package/README.md +2 -2
  5. package/README.zh-CN.md +1 -1
  6. package/SKILL.md +2 -2
  7. package/THIRD_PARTY_NOTICES.md +1 -1
  8. package/agents/openai.yaml +1 -1
  9. package/assets/hooks/hook-input.sh +11 -8
  10. package/assets/hooks/lib/session-state.sh +10 -7
  11. package/assets/hooks/lib/workflow-state.sh +13 -177
  12. package/assets/hooks/post-tool-observer.sh +8 -2
  13. package/assets/hooks/prompt-guard.sh +5 -81
  14. package/assets/hooks/session-start-context.sh +56 -1
  15. package/assets/partials/05-workflow.partial.md +1 -1
  16. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  17. package/assets/plugin-registry.v1.json +1 -1
  18. package/assets/reference-configs/document-generation.md +1 -1
  19. package/assets/reference-configs/harness-overview.md +1 -1
  20. package/assets/reference-configs/sprint-contracts.md +3 -3
  21. package/assets/skill-version.json +2 -2
  22. package/assets/templates/contract.template.md +2 -7
  23. package/assets/templates/helpers/capture-plan.sh +2 -2
  24. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  25. package/assets/templates/helpers/contract-worktree.sh +1 -21
  26. package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
  27. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  28. package/assets/templates/helpers/new-plan.sh +1 -1
  29. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  30. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  31. package/assets/templates/helpers/verify-contract.sh +1 -111
  32. package/assets/templates/helpers/verify-sprint.sh +3 -180
  33. package/assets/templates/plan.template.md +1 -1
  34. package/assets/templates/review.template.md +9 -34
  35. package/assets/workflow-contract.v1.json +10 -19
  36. package/docs/README.md +10 -10
  37. package/docs/architecture/CURRENT.md +170 -0
  38. package/docs/architecture/EVOLUTION.md +42 -0
  39. package/docs/architecture/current/README.md +4 -156
  40. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  41. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  42. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  43. package/docs/architecture/history/README.md +7 -0
  44. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  45. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  46. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  47. package/docs/architecture/history.md +3 -34
  48. package/docs/architecture/index.md +15 -98
  49. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  50. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  51. package/docs/architecture/versions/1.6.md +38 -0
  52. package/docs/architecture/versions/1.7.md +41 -0
  53. package/docs/forge-chatgpt-browser-engine.md +2 -2
  54. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  55. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  56. package/docs/forge-github-issue-launcher.md +1 -1
  57. package/docs/forge-runtime-self-healing-loop.md +1 -1
  58. package/docs/operations/controller-browser-plugin.md +26 -6
  59. package/docs/operations/controller-performance-and-502.md +124 -0
  60. package/docs/operations/controller-reliability-runbook.md +1 -1
  61. package/docs/operations/features.md +2 -2
  62. package/docs/operations/github-repository.md +1 -1
  63. package/docs/operations/homebrew.md +2 -2
  64. package/docs/operations/mcp-tool-exposure.md +5 -3
  65. package/docs/wiki/Architecture.md +6 -6
  66. package/docs/wiki/Implementation.md +2 -2
  67. package/docs/wiki/Integrations.md +2 -2
  68. package/docs/wiki/Work-Lifecycle.md +1 -1
  69. package/package.json +29 -10
  70. package/scripts/activate-source-baseline.ts +17 -13
  71. package/scripts/benchmark-background-check-overlap.ts +228 -0
  72. package/scripts/benchmark-mcp-host.sh +117 -0
  73. package/scripts/benchmark-mcp-transport.ts +170 -0
  74. package/scripts/benchmark-quality-harness.ts +146 -0
  75. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  76. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  77. package/scripts/benchmark-typescript-navigation.ts +186 -0
  78. package/scripts/benchmark-work-efficiency.ts +334 -0
  79. package/scripts/capture-plan.sh +3 -3
  80. package/scripts/check-agent-tooling.sh +8 -35
  81. package/scripts/check-architecture-sync.sh +17 -86
  82. package/scripts/check-mcp-compatibility.ts +14 -0
  83. package/scripts/check-runtime-architecture.mjs +107 -64
  84. package/scripts/check-stable-baseline.ts +92 -0
  85. package/scripts/check-task-workflow.sh +4 -4
  86. package/scripts/contract-worktree.sh +1 -21
  87. package/scripts/ensure-task-workflow.sh +22 -55
  88. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  89. package/scripts/install-standalone-recovery.ts +24 -0
  90. package/scripts/lib/project-init-lib.sh +15 -44
  91. package/scripts/migrate-project-template.sh +23 -1179
  92. package/scripts/new-plan.sh +1 -1
  93. package/scripts/plan-to-todo.sh +11 -36
  94. package/scripts/run-governed-gate.ts +2 -1
  95. package/scripts/ship-worktrees.sh +2 -29
  96. package/scripts/smoke-ios-physical-device.ts +4 -2
  97. package/scripts/smoke-runtime-recovery.ts +1 -35
  98. package/scripts/smoke-schedule-engine.ts +1 -20
  99. package/scripts/verify-contract.sh +1 -111
  100. package/scripts/verify-forge-runtime.sh +0 -1
  101. package/scripts/verify-sprint.sh +3 -180
  102. package/scripts/workflow-contract.ts +1 -5
  103. package/src/cli/commands/init-hook.ts +3 -3
  104. package/src/cli/commands/mcp.ts +9 -5
  105. package/src/cli/commands/plugin.ts +1 -1
  106. package/src/cli/commands/recovery.ts +25 -3
  107. package/src/cli/commands/runtime.ts +55 -3
  108. package/src/cli/commands/security.ts +12 -1
  109. package/src/cli/commands/setup-profile.ts +31 -21
  110. package/src/cli/controller/check-runner.ts +106 -3
  111. package/src/cli/controller/composite-result.ts +1 -1
  112. package/src/cli/controller/context/focus.ts +51 -0
  113. package/src/cli/controller/context/instruction-context.ts +114 -0
  114. package/src/cli/controller/context/known-paths.ts +76 -0
  115. package/src/cli/controller/context/query-planning.ts +110 -0
  116. package/src/cli/controller/context/rule-resolution.ts +532 -0
  117. package/src/cli/controller/context/source-materializer.ts +278 -0
  118. package/src/cli/controller/context/structural.ts +16 -0
  119. package/src/cli/controller/context/types.ts +286 -0
  120. package/src/cli/controller/context-pack.ts +785 -469
  121. package/src/cli/controller/launch-agents.ts +77 -2
  122. package/src/cli/controller/operational-plan.ts +2 -2
  123. package/src/cli/controller/work-mode.ts +6 -4
  124. package/src/cli/editing/edit-session.ts +60 -7
  125. package/src/cli/github/github.ts +133 -0
  126. package/src/cli/installer/managed-entries.ts +15 -1
  127. package/src/cli/local-bridge/console-automations.ts +54 -78
  128. package/src/cli/local-bridge/console-view-models.ts +1 -35
  129. package/src/cli/local-bridge/facade-api.ts +0 -217
  130. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  131. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
  132. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  133. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
  134. package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
  135. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  136. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
  137. package/src/cli/local-bridge/frontend/src/styles.css +1 -1
  138. package/src/cli/local-bridge/frontend/src/types.ts +5 -8
  139. package/src/cli/local-bridge/job-store.ts +1 -490
  140. package/src/cli/local-bridge/server.ts +0 -513
  141. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  142. package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
  143. package/src/cli/mcp/auth.ts +5 -5
  144. package/src/cli/mcp/instructions.ts +3 -2
  145. package/src/cli/mcp/legacy-tool-service.ts +0 -189
  146. package/src/cli/mcp/multi-repository.ts +16 -5
  147. package/src/cli/mcp/oauth.ts +175 -26
  148. package/src/cli/mcp/repository-tools.ts +247 -107
  149. package/src/cli/mcp/server.ts +537 -111
  150. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  151. package/src/cli/mcp/toolset-selection.ts +1 -1
  152. package/src/cli/mcp/toolset.ts +42 -5
  153. package/src/cli/mcp/transports/http.ts +101 -14
  154. package/src/cli/mcp/transports/session-registry.ts +43 -5
  155. package/src/cli/mcp/types.ts +3 -3
  156. package/src/cli/repositories/command-classifier.ts +101 -19
  157. package/src/cli/repositories/command-executor.ts +55 -521
  158. package/src/cli/repositories/command-process.ts +108 -0
  159. package/src/cli/repositories/command-scope.ts +99 -41
  160. package/src/cli/repositories/controller-home.ts +7 -8
  161. package/src/cli/repositories/git-command-executor.ts +2 -0
  162. package/src/cli/repositories/locks.ts +53 -16
  163. package/src/cli/repositories/repository-snapshot.ts +121 -0
  164. package/src/cli/repositories/selected-path-actions.ts +17 -9
  165. package/src/cli/repositories/structured-git.ts +61 -3
  166. package/src/cli/repository/inspector.ts +553 -63
  167. package/src/cli/repository/session-cache.ts +58 -17
  168. package/src/cli/v81-entry.ts +1 -4
  169. package/src/effects/fs-transaction.ts +85 -17
  170. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  171. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  172. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  173. package/src/runtime/context/lsp-client.ts +283 -0
  174. package/src/runtime/context/semantic-navigation.ts +275 -0
  175. package/src/runtime/context/swift-navigation.ts +565 -0
  176. package/src/runtime/context/typescript-navigation.ts +226 -0
  177. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  178. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +47 -8
  179. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  180. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  181. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  182. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  183. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  184. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  185. package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
  186. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +143 -8
  187. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
  188. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +85 -8
  189. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  190. package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
  191. package/src/runtime/control-plane/facade/controller-session-store.ts +132 -1
  192. package/src/runtime/control-plane/facade/goal-workloop-access.ts +2 -0
  193. package/src/runtime/control-plane/facade/goal-workloop.ts +686 -129
  194. package/src/runtime/control-plane/facade/index.ts +1 -0
  195. package/src/runtime/control-plane/facade/plan-contract-store.ts +185 -20
  196. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  197. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  198. package/src/runtime/control-plane/facade/types.ts +22 -5
  199. package/src/runtime/control-plane/facade/work-contract-store.ts +175 -73
  200. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  201. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  202. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  203. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  204. package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
  205. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  206. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  207. package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
  208. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  209. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  210. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  211. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  212. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  213. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  214. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  215. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  216. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  217. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  218. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
  219. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +355 -116
  220. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
  221. package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
  222. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  223. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  224. package/src/runtime/control-plane/release-retention.ts +62 -2
  225. package/src/runtime/control-plane/routing/route-policy.ts +7 -16
  226. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  227. package/src/runtime/diagnostics/mcp-timing.ts +13 -0
  228. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  229. package/src/runtime/diagnostics/performance.ts +24 -1
  230. package/src/runtime/evidence/event-ledger.ts +1 -1
  231. package/src/runtime/execution/jobs/store.ts +58 -4
  232. package/src/runtime/execution/jobs/types.ts +1 -1
  233. package/src/runtime/execution/managed-workspace.ts +203 -1
  234. package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
  235. package/src/runtime/execution/process-runtime/check-result.ts +53 -0
  236. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  237. package/src/runtime/execution/process-runtime/command-facade.ts +345 -40
  238. package/src/runtime/execution/process-runtime/gc.ts +38 -6
  239. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
  240. package/src/runtime/execution/process-runtime/persisted-check.ts +47 -4
  241. package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
  242. package/src/runtime/execution/process-runtime/runtime.ts +43 -3
  243. package/src/runtime/execution/process-runtime/store.ts +1 -0
  244. package/src/runtime/execution/process-runtime/types.ts +2 -0
  245. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  246. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  247. package/src/runtime/execution/workers/executor.ts +0 -48
  248. package/src/runtime/gateway/mcp/execution-tools.ts +1192 -114
  249. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  250. package/src/runtime/gateway/mcp/router.ts +184 -82
  251. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
  252. package/src/runtime/gateway/mcp/runtime-tools.ts +2224 -1589
  253. package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
  254. package/src/runtime/plugins/browser-adapter.ts +2901 -241
  255. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  256. package/src/runtime/plugins/browser-handoff.ts +92 -2
  257. package/src/runtime/plugins/browser-macos-bridge.ts +582 -49
  258. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  259. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  260. package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
  261. package/src/runtime/plugins/browser-runtime.ts +183 -0
  262. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  263. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  264. package/src/runtime/plugins/desktop-operator-registration.ts +8 -2
  265. package/src/runtime/plugins/external-adapter.ts +324 -3
  266. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  267. package/src/runtime/plugins/first-party-registry.ts +2 -0
  268. package/src/runtime/plugins/github-adapter.ts +125 -0
  269. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  270. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  271. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  272. package/src/runtime/plugins/ios-adapter.ts +106 -16
  273. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  274. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  275. package/src/runtime/plugins/lightweight-action.ts +153 -0
  276. package/src/runtime/plugins/local-system-adapter.ts +123 -6
  277. package/src/runtime/plugins/macos-capability-broker.ts +55 -9
  278. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  279. package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
  280. package/src/runtime/plugins/resend-adapter.ts +1 -1
  281. package/src/runtime/plugins/store.ts +269 -29
  282. package/src/runtime/plugins/types.ts +35 -2
  283. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  284. package/src/runtime/projections/git-status-sampler.ts +16 -16
  285. package/src/runtime/projections/materialized-view.ts +28 -10
  286. package/src/runtime/recovery/maintenance-executor.ts +393 -28
  287. package/src/runtime/root/mcp-transport.ts +180 -10
  288. package/src/runtime/root/package-connector-service.ts +186 -8
  289. package/src/runtime/root/package-runtime-release.ts +143 -17
  290. package/src/runtime/root/package-runtime-service.ts +430 -22
  291. package/src/runtime/root/release-manifest.ts +7 -2
  292. package/src/runtime/root/release-materialize.ts +19 -0
  293. package/src/runtime/root/release-store.ts +48 -11
  294. package/src/runtime/root/runtime.ts +23 -5
  295. package/src/runtime/root/service.ts +100 -29
  296. package/src/runtime/root/types.ts +2 -0
  297. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  298. package/src/runtime/shared/trusted-executable.ts +62 -0
  299. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  300. package/src/runtime/standalone-recovery/core.ts +700 -92
  301. package/src/runtime/standalone-recovery/entry.ts +99 -23
  302. package/src/runtime/standalone-recovery/installer.ts +19 -3
  303. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  304. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  305. package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
  306. package/src/runtime/workflow/schedules/engine.ts +287 -151
  307. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  308. package/src/runtime/workflow/schedules/settlement.ts +48 -4
  309. package/src/runtime/workflow/schedules/store.ts +131 -10
  310. package/src/runtime/workflow/schedules/types.ts +2 -4
  311. package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
  312. package/src/testing/test-governance.ts +8 -40
  313. package/assets/reference-configs/heartbeat-triage.md +0 -57
  314. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  315. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  316. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  317. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  318. package/docs/architecture/current/architecture-invariants.md +0 -331
  319. package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
  320. package/docs/architecture/current/codegraph-context-provider.md +0 -238
  321. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  322. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  323. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
  324. package/docs/architecture/current/entity-model.md +0 -612
  325. package/docs/architecture/current/external-plugin-governance.md +0 -396
  326. package/docs/architecture/current/failure-recovery.md +0 -447
  327. package/docs/architecture/current/governance.md +0 -213
  328. package/docs/architecture/current/human-interaction-plane.md +0 -87
  329. package/docs/architecture/current/implementation-status.md +0 -128
  330. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  331. package/docs/architecture/current/legacy-governance.md +0 -58
  332. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  333. package/docs/architecture/current/migration-roadmap.md +0 -105
  334. package/docs/architecture/current/multi-repository-execution.md +0 -371
  335. package/docs/architecture/current/operations-runbook.md +0 -45
  336. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  337. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  338. package/docs/architecture/current/repository-access-modes.md +0 -102
  339. package/docs/architecture/current/route-policy.md +0 -15
  340. package/docs/architecture/current/route-session-concurrency.md +0 -85
  341. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  342. package/docs/architecture/current/runtime-directory-map.md +0 -49
  343. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  344. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  345. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  346. package/docs/architecture/current/system-overview.md +0 -169
  347. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  348. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  349. package/docs/architecture/current/thin-harness-v1.md +0 -321
  350. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  351. package/docs/forge-autonomous-goal-loop.md +0 -9
  352. package/docs/forge-provider-configuration.md +0 -7
  353. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
  354. package/docs/operations/gmail-assistant-routines.md +0 -186
  355. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  356. package/docs/operations/provider-configuration.md +0 -31
  357. package/scripts/harness-trace-grade.sh +0 -150
  358. package/scripts/heartbeat-triage.sh +0 -361
  359. package/scripts/maintenance-triage.sh +0 -167
  360. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  361. package/src/cli/repositories/goal-registry.ts +0 -239
  362. package/src/runtime/assistant/action-proposals.ts +0 -279
  363. package/src/runtime/assistant/intent.ts +0 -395
  364. package/src/runtime/assistant/model-provider.ts +0 -460
  365. package/src/runtime/assistant/openapi.ts +0 -223
  366. package/src/runtime/assistant/policy.ts +0 -89
  367. package/src/runtime/assistant/readiness.ts +0 -258
  368. package/src/runtime/assistant/routine-runtime.ts +0 -477
  369. package/src/runtime/assistant/schedule-binding.ts +0 -171
  370. package/src/runtime/assistant/standing-grants.ts +0 -375
  371. package/src/runtime/assistant/store.ts +0 -243
  372. package/src/runtime/assistant/types.ts +0 -135
  373. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  374. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  375. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  376. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  377. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  378. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  379. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  380. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  381. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  382. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  383. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  384. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  385. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  386. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  387. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  388. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  389. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  390. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  391. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  392. package/src/runtime/workflow/findings/store.ts +0 -204
  393. package/src/runtime/workflow/findings/types.ts +0 -58
  394. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  395. package/src/runtime/workflow/portfolio/store.ts +0 -185
  396. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -9,6 +9,12 @@ import { readJsonFile, sanitizeFileComponent, writeJsonAtomic } from '../shared/
9
9
  import { createFirstPartyPluginAdapterMap } from './first-party-registry';
10
10
  import { getExternalPluginAdapter, listExternalPluginAdapters } from './external-adapter';
11
11
  import { AssistantPluginError, toAssistantPluginError } from './errors';
12
+ import {
13
+ findActivePluginCapabilityAuthorization,
14
+ pluginCapabilityAuthorizationOwnerScope,
15
+ recordPluginCapabilityAuthorization,
16
+ type PluginCapabilityAuthorizationGrant,
17
+ } from './capability-authorization-grants';
12
18
  import { markControllerContextProjectionDirty } from '../projections/controller-context';
13
19
  import { classifyRepositoryCommand } from '../../cli/repositories/command-classifier';
14
20
  import {
@@ -24,6 +30,7 @@ import type {
24
30
  AssistantPluginActionDescriptor,
25
31
  AssistantPluginActionExecutionInput,
26
32
  AssistantPluginActionRequest,
33
+ AssistantPluginAuthorizationContext,
27
34
  AssistantPluginManifest,
28
35
  AssistantPluginRegistryIndex,
29
36
  AssistantPluginRegistryIndexEntry,
@@ -44,7 +51,8 @@ export function assistantPluginScope(pluginId: string, controllerHome?: string):
44
51
  return PLUGIN_ADAPTERS.get(pluginId)?.scope ?? (controllerHome ? getExternalPluginAdapter(controllerHome, pluginId)?.scope : undefined);
45
52
  }
46
53
 
47
- const PLUGIN_MANIFEST_CACHE_TTL_MS = 5_000;
54
+ const PLUGIN_MANIFEST_LIVE_CACHE_TTL_MS = 5_000;
55
+ const PLUGIN_MANIFEST_STORED_CACHE_TTL_MS = Number.POSITIVE_INFINITY;
48
56
 
49
57
 
50
58
  interface PluginManifestCacheEntry<T> {
@@ -83,6 +91,7 @@ export function controllerPluginRepository(controllerHome: string): RepositoryRe
83
91
 
84
92
  function adapterMatchesRepository(adapter: AssistantPluginAdapter, repository: RepositoryRecord): boolean {
85
93
  const scope = adapter.scope ?? 'repository';
94
+ if (scope === 'controller_with_repository_overlay') return true;
86
95
  return repository.repoId === CONTROLLER_SCOPE_REPO_ID ? scope === 'controller' : scope === 'repository';
87
96
  }
88
97
 
@@ -101,10 +110,11 @@ function itemCacheKey(controllerHome: string, repoId: string, pluginId: string,
101
110
  function readPluginManifestCache<T>(
102
111
  cache: Map<string, PluginManifestCacheEntry<T>>,
103
112
  key: string,
113
+ maxAgeMs = PLUGIN_MANIFEST_LIVE_CACHE_TTL_MS,
104
114
  ): T | undefined {
105
115
  const cached = cache.get(key);
106
116
  if (!cached) return undefined;
107
- if (Date.now() - cached.createdAt > PLUGIN_MANIFEST_CACHE_TTL_MS) {
117
+ if (Number.isFinite(maxAgeMs) && Date.now() - cached.createdAt > maxAgeMs) {
108
118
  cache.delete(key);
109
119
  return undefined;
110
120
  }
@@ -231,8 +241,11 @@ function cachedManifestForRepository(
231
241
  repoId: string,
232
242
  pluginId: string,
233
243
  ): AssistantPluginManifest | undefined {
234
- return readPluginManifestCache(pluginManifestItemCache, itemCacheKey(controllerHome, repoId, pluginId, true))
235
- ?? readPluginManifestCache(pluginManifestItemCache, itemCacheKey(controllerHome, repoId, pluginId, false));
244
+ return readPluginManifestCache(
245
+ pluginManifestItemCache,
246
+ itemCacheKey(controllerHome, repoId, pluginId, true),
247
+ PLUGIN_MANIFEST_STORED_CACHE_TTL_MS,
248
+ ) ?? readPluginManifestCache(pluginManifestItemCache, itemCacheKey(controllerHome, repoId, pluginId, false));
236
249
  }
237
250
 
238
251
  function computeManifest(
@@ -244,7 +257,12 @@ function computeManifest(
244
257
  const adapter = resolvedAdapter ?? resolvePluginAdapter(controllerHome, pluginId);
245
258
  if (!adapter || !adapterMatchesRepository(adapter, repository)) throw new Error(`PLUGIN_NOT_FOUND: ${pluginId}`);
246
259
  const previous = readStoredManifest(controllerHome, repository.repoId, pluginId);
247
- const built = adapter.buildManifest(previous?.revision ?? 0, previous?.updatedAt, repository.canonicalRoot);
260
+ const built = adapter.buildManifest(previous?.revision ?? 0, previous?.updatedAt, repository.canonicalRoot, {
261
+ controllerHome,
262
+ repoId: repository.repoId,
263
+ repoRoot: repository.canonicalRoot,
264
+ controllerScoped: repository.repoId === CONTROLLER_SCOPE_REPO_ID,
265
+ });
248
266
  const changed = !previous || fingerprintManifest(previous) !== fingerprintManifest(built);
249
267
  return {
250
268
  ...built,
@@ -356,27 +374,63 @@ function enforceConfirmation(action: AssistantPluginActionDescriptor, request: A
356
374
  }
357
375
  }
358
376
 
377
+ export interface PluginActionAuthorizationEvidence {
378
+ source: 'none' | 'host_permission_model' | 'capability_grant' | 'strong_confirmation';
379
+ reusable: boolean;
380
+ capabilityId?: string;
381
+ target?: { kind: string; id: string };
382
+ grantId?: string;
383
+ established?: boolean;
384
+ lookupError?: string;
385
+ persistenceError?: string;
386
+ }
387
+
359
388
  function actionForManifest(manifest: AssistantPluginManifest, actionId: string): AssistantPluginActionDescriptor {
360
389
  const action = manifest.actions.find((entry) => entry.actionId === actionId);
361
390
  if (!action) throw new Error(`PLUGIN_ACTION_NOT_FOUND: ${manifest.pluginId}/${actionId}`);
362
391
  return action;
363
392
  }
364
393
 
365
- const STANDING_GRANT_SAFE_ACTIONS = new Set([
366
- 'gmail/create_draft',
367
- 'gmail/archive_message',
368
- 'gmail/mark_message_read',
369
- 'gmail/mark_message_unread',
370
- 'google_tasks/create_task',
371
- ]);
394
+ function reusableCapabilityId(manifest: AssistantPluginManifest, action: AssistantPluginActionDescriptor): string {
395
+ const matches = manifest.capabilities
396
+ .filter((capability) => capability.actions.includes(action.actionId))
397
+ .map((capability) => capability.capabilityId)
398
+ .filter(Boolean)
399
+ .sort();
400
+ // Overlapping capability groups are ambiguous privilege boundaries. Fall back
401
+ // to action-local reuse instead of silently choosing a broader capability.
402
+ return matches.length === 1 ? matches[0]! : `action:${action.actionId}`;
403
+ }
372
404
 
373
- function denyAutomatedWrite(manifest: AssistantPluginManifest, action: AssistantPluginActionDescriptor, origin: AssistantPluginActionExecutionInput['origin']): void {
374
- if (origin.surface === 'standing-grant') {
375
- const key = `${manifest.pluginId}/${action.actionId}`;
376
- if (STANDING_GRANT_SAFE_ACTIONS.has(key) && action.confirmation !== 'strong_confirmation') return;
377
- throw new Error(`STANDING_GRANT_ACTION_NOT_ALLOWED: ${key}`);
405
+ const INTERACTIVE_PLUGIN_AUTHORIZATION_SURFACES = new Set(['mcp', 'local-ui', 'chatgpt-action']);
406
+
407
+ function originMayEstablishCapabilityAuthorization(origin: AssistantPluginActionExecutionInput['origin']): boolean {
408
+ return INTERACTIVE_PLUGIN_AUTHORIZATION_SURFACES.has(origin.surface);
409
+ }
410
+
411
+ function authorizationTargetSummary(context: AssistantPluginAuthorizationContext): { kind: string; id: string } {
412
+ return { kind: context.target.kind, id: context.target.id };
413
+ }
414
+
415
+ function authorizationError(error: unknown): string {
416
+ const message = error instanceof Error ? error.message : String(error);
417
+ return message.slice(0, 500);
418
+ }
419
+
420
+ function initialAuthorizationEvidence(
421
+ manifest: AssistantPluginManifest,
422
+ action: AssistantPluginActionDescriptor,
423
+ ): PluginActionAuthorizationEvidence {
424
+ if (action.confirmation === 'none') return { source: 'none', reusable: false };
425
+ const capabilityId = reusableCapabilityId(manifest, action);
426
+ if (action.confirmation === 'strong_confirmation') {
427
+ return { source: 'strong_confirmation', reusable: false, capabilityId };
378
428
  }
379
- if (!['schedule', 'reconciliation', 'system', 'assistant-routine'].includes(origin.surface)) return;
429
+ return { source: 'host_permission_model', reusable: false, capabilityId };
430
+ }
431
+
432
+ function denyAutomatedWrite(manifest: AssistantPluginManifest, action: AssistantPluginActionDescriptor, origin: AssistantPluginActionExecutionInput['origin']): void {
433
+ if (!['schedule', 'reconciliation', 'system'].includes(origin.surface)) return;
380
434
  if (action.readOnly) return;
381
435
  throw new Error(`EXTERNAL_EFFECT_AUTHORIZATION_REQUIRED: ${manifest.pluginId}/${action.actionId} cannot run from ${origin.surface}`);
382
436
  }
@@ -406,7 +460,14 @@ export function listAssistantPluginManifests(
406
460
  const preferStored = options.preferStored === true && options.forceRefresh !== true;
407
461
  const cacheKey = listCacheKey(controllerHome, repository.repoId, preferStored);
408
462
  if (options.forceRefresh !== true) {
409
- const cached = readPluginManifestCache(pluginManifestListCache, cacheKey);
463
+ // Stored manifests are persisted snapshots. Their freshness is governed by
464
+ // explicit registry/config mutation invalidation, not a wall-clock trust
465
+ // window. Live execution manifests retain the short identity-validation TTL.
466
+ const cached = readPluginManifestCache(
467
+ pluginManifestListCache,
468
+ cacheKey,
469
+ preferStored ? PLUGIN_MANIFEST_STORED_CACHE_TTL_MS : PLUGIN_MANIFEST_LIVE_CACHE_TTL_MS,
470
+ );
410
471
  if (cached) return cached;
411
472
  }
412
473
  const manifests = listAssistantPluginIds(controllerHome, repository)
@@ -544,6 +605,7 @@ export interface PluginActionReceipt {
544
605
  createdAt: string;
545
606
  workId?: string;
546
607
  origin?: { surface: string; actor?: string; correlationId?: string };
608
+ authorization?: PluginActionAuthorizationEvidence;
547
609
  result?: Record<string, unknown>;
548
610
  error?: { code: string; message: string };
549
611
  }
@@ -692,12 +754,73 @@ function remoteEffectWorkForPluginAction(
692
754
  throw new Error(`WORK_PLUGIN_RECEIPT_BINDING_CHECKS_PRESENT: ${workId} declares repository checks and cannot be completed by a plugin receipt alone`);
693
755
  }
694
756
  if (isTerminalWorkContractStatus(work.status)) {
695
- if (work.status === 'completed' && receiptId && work.completionReceipt?.receiptId === receiptId) return work;
757
+ const receiptAlreadyBound = Boolean(receiptId) && (
758
+ work.completionReceipt?.receiptId === receiptId
759
+ || work.evidenceRefs.some((evidence) => evidence.evidenceId === receiptId)
760
+ );
761
+ if (work.status === 'completed' && receiptAlreadyBound) return work;
696
762
  throw new Error(`WORK_PLUGIN_RECEIPT_BINDING_TERMINAL: ${workId} is ${work.status}`);
697
763
  }
698
764
  return work;
699
765
  }
700
766
 
767
+ function attributedWorkForPluginAction(
768
+ controllerHome: string,
769
+ repository: RepositoryRecord,
770
+ action: AssistantPluginActionDescriptor,
771
+ request: AssistantPluginActionRequest,
772
+ ): WorkContract | undefined {
773
+ const workId = request.workId?.trim();
774
+ if (!workId || action.risk === 'remote_write') return undefined;
775
+ const work = getWorkContract({ controllerHome, repoId: repository.repoId }, workId);
776
+ if (!work) throw new Error(`WORK_PLUGIN_ATTRIBUTION_NOT_FOUND: ${workId}`);
777
+ if (isTerminalWorkContractStatus(work.status)) {
778
+ throw new Error(`WORK_PLUGIN_ATTRIBUTION_TERMINAL: ${workId} is ${work.status}`);
779
+ }
780
+ return work;
781
+ }
782
+
783
+ export function finalizeRemoteEffectWorkFromActionReceipt(
784
+ controllerHome: string,
785
+ repoId: string,
786
+ workId: string,
787
+ ): WorkContract {
788
+ const work = getWorkContract({ controllerHome, repoId }, workId);
789
+ if (!work) throw new Error(`WORK_PLUGIN_RECEIPT_BINDING_NOT_FOUND: ${workId}`);
790
+ if (work.workKind !== 'remote_effect') {
791
+ throw new Error(`WORK_REMOTE_EFFECT_FINALIZE_KIND_MISMATCH: ${workId} is ${work.workKind}, expected remote_effect`);
792
+ }
793
+ if (work.status === 'completed' && work.completionReceipt?.source === 'remote_effect') return work;
794
+ if (isTerminalWorkContractStatus(work.status)) {
795
+ throw new Error(`WORK_REMOTE_EFFECT_FINALIZE_TERMINAL: ${workId} is ${work.status}`);
796
+ }
797
+ const receipt = work.evidenceRefs
798
+ .map((evidence) => evidence.evidenceId ? readPluginActionReceipt(controllerHome, repoId, evidence.evidenceId) : undefined)
799
+ .find((candidate) => candidate?.workId === workId && candidate.status === 'succeeded');
800
+ if (!receipt) {
801
+ throw new Error(`WORK_REMOTE_EFFECT_FINALIZE_RECEIPT_REQUIRED: ${workId} has no successful durable plugin action receipt`);
802
+ }
803
+ const remoteReceipt: RemoteEffectCompletionReceipt = {
804
+ schemaVersion: 1,
805
+ receiptId: receipt.receiptId,
806
+ source: 'remote_effect',
807
+ workId,
808
+ pluginId: receipt.pluginId,
809
+ actionId: receipt.actionId,
810
+ requestId: receipt.requestId,
811
+ semanticKey: receipt.semanticKey,
812
+ resultDigest: createHash('sha256').update(JSON.stringify(receipt.result ?? null)).digest('hex'),
813
+ recordedAt: new Date().toISOString(),
814
+ };
815
+ return recordWorkCompletionReceipt(
816
+ { controllerHome, repoId },
817
+ workId,
818
+ remoteReceipt,
819
+ 'completed_remote',
820
+ 'remote_effect',
821
+ );
822
+ }
823
+
701
824
  function bindRemoteEffectReceiptToWork(
702
825
  controllerHome: string,
703
826
  repository: RepositoryRecord,
@@ -709,15 +832,17 @@ function bindRemoteEffectReceiptToWork(
709
832
  if (receipt.status !== 'succeeded') return remoteEffectWorkForPluginAction(controllerHome, repository, action, request);
710
833
  const work = remoteEffectWorkForPluginAction(controllerHome, repository, action, request, receipt.receiptId);
711
834
  if (!work) return undefined;
712
- if (work.status === 'completed' && work.completionReceipt?.receiptId === receipt.receiptId) return work;
835
+ if (work.status === 'completed') return work;
836
+ let updated = work;
713
837
  if (!work.evidenceRefs.some((evidence) => evidence.evidenceId === receipt.receiptId)) {
714
- appendWorkEvidence({ controllerHome, repoId: repository.repoId }, work.workId, {
838
+ updated = appendWorkEvidence({ controllerHome, repoId: repository.repoId }, work.workId, {
715
839
  evidenceId: receipt.receiptId,
716
840
  title: 'typed remote plugin effect completed',
717
841
  summary: `${receipt.pluginId}/${receipt.actionId} completed with durable receipt ${receipt.receiptId}.`,
718
842
  detailLevel: 'summary',
719
843
  });
720
844
  }
845
+ if (action.remoteEffectWorkCompletion !== 'terminal') return updated;
721
846
  const remoteReceipt: RemoteEffectCompletionReceipt = {
722
847
  schemaVersion: 1,
723
848
  receiptId: receipt.receiptId,
@@ -785,8 +910,11 @@ export async function submitAssistantPluginAction(
785
910
  deduplicated: boolean;
786
911
  result?: Record<string, unknown>;
787
912
  receipt: PluginActionReceipt;
913
+ authorization?: PluginActionAuthorizationEvidence;
788
914
  workId?: string;
789
915
  }> {
916
+ const adapter = resolvePluginAdapter(controllerHome, request.pluginId);
917
+ if (!adapter) throw new Error(`PLUGIN_NOT_FOUND: ${request.pluginId}`);
790
918
  const manifest = getAssistantPluginManifest(controllerHome, repository, request.pluginId);
791
919
  const action = actionForManifest(manifest, request.actionId);
792
920
  if (!manifest.enabled && action.actionId !== 'configure') {
@@ -812,7 +940,7 @@ export async function submitAssistantPluginAction(
812
940
  if (request.workId && existing.workId !== request.workId) {
813
941
  throw new Error(`WORK_PLUGIN_RECEIPT_BINDING_CONFLICT: ${request.requestId} already belongs to Work ${existing.workId ?? 'none'}`);
814
942
  }
815
- if (request.workId) bindRemoteEffectReceiptToWork(controllerHome, repository, action, request, existing);
943
+ if (request.workId && action.risk === 'remote_write') bindRemoteEffectReceiptToWork(controllerHome, repository, action, request, existing);
816
944
  return {
817
945
  manifest,
818
946
  action,
@@ -820,19 +948,29 @@ export async function submitAssistantPluginAction(
820
948
  deduplicated: true,
821
949
  result: existing.result,
822
950
  receipt: existing,
951
+ authorization: existing.authorization,
823
952
  workId: existing.workId,
824
953
  };
825
954
  }
826
955
 
827
956
  const createdAt = new Date().toISOString();
828
957
  const receiptId = `PLG-${Date.now()}-${createHash('sha256').update(request.requestId).digest('hex').slice(0, 8)}`;
829
- const boundRemoteWork = remoteEffectWorkForPluginAction(controllerHome, repository, action, request);
958
+ const capabilityId = reusableCapabilityId(manifest, action);
959
+ let authorization = initialAuthorizationEvidence(manifest, action);
960
+ let authorizationContext: AssistantPluginAuthorizationContext | undefined;
961
+ let activeAuthorizationGrant: PluginCapabilityAuthorizationGrant | undefined;
830
962
  const requiresLocalEffectWork = localSystemActionRequiresWork(
831
963
  repository,
832
964
  request.pluginId,
833
965
  request.actionId,
834
966
  normalizedArgs,
835
967
  );
968
+ const boundRemoteWork = action.risk === 'remote_write'
969
+ ? remoteEffectWorkForPluginAction(controllerHome, repository, action, request)
970
+ : undefined;
971
+ const attributedWork = !requiresLocalEffectWork
972
+ ? attributedWorkForPluginAction(controllerHome, repository, action, request)
973
+ : undefined;
836
974
  const acceptedWork = requiresLocalEffectWork
837
975
  ? acceptSubmittedWorkContract(controllerHome, {
838
976
  requestId: request.requestId,
@@ -885,10 +1023,63 @@ export async function submitAssistantPluginAction(
885
1023
  receiptId,
886
1024
  risk: action.risk,
887
1025
  confirmation: action.confirmation,
1026
+ capabilityId,
1027
+ authorizationReuseSupported: action.confirmation === 'authorization' && Boolean(adapter.resolveAuthorizationContext),
888
1028
  },
889
1029
  });
890
1030
 
891
1031
  try {
1032
+ if (action.confirmation === 'authorization'
1033
+ && adapter.resolveAuthorizationContext
1034
+ && originMayEstablishCapabilityAuthorization(request.origin)) {
1035
+ authorizationContext = await adapter.resolveAuthorizationContext({
1036
+ controllerHome,
1037
+ repoId: repository.repoId,
1038
+ repoRoot: repository.canonicalRoot,
1039
+ pluginId: request.pluginId,
1040
+ actionId: request.actionId,
1041
+ requestId: request.requestId,
1042
+ args: normalizedArgs,
1043
+ origin: request.origin,
1044
+ jobId: receiptId,
1045
+ timeoutMs: request.timeoutMs,
1046
+ signal: request.signal,
1047
+ });
1048
+ if (authorizationContext) {
1049
+ authorization = {
1050
+ source: 'host_permission_model',
1051
+ reusable: false,
1052
+ capabilityId,
1053
+ target: authorizationTargetSummary(authorizationContext),
1054
+ };
1055
+ try {
1056
+ activeAuthorizationGrant = findActivePluginCapabilityAuthorization(controllerHome, {
1057
+ ownerScope: pluginCapabilityAuthorizationOwnerScope(request.origin),
1058
+ repoId: repository.repoId,
1059
+ pluginId: request.pluginId,
1060
+ capabilityId,
1061
+ target: authorizationContext.target,
1062
+ scopes: action.scopes,
1063
+ risk: action.risk,
1064
+ });
1065
+ if (activeAuthorizationGrant) {
1066
+ authorization = {
1067
+ source: 'capability_grant',
1068
+ reusable: true,
1069
+ capabilityId,
1070
+ target: authorizationTargetSummary(authorizationContext),
1071
+ grantId: activeAuthorizationGrant.grantId,
1072
+ };
1073
+ }
1074
+ } catch (error) {
1075
+ authorization = {
1076
+ ...authorization,
1077
+ lookupError: authorizationError(error),
1078
+ };
1079
+ }
1080
+ }
1081
+ }
1082
+
892
1083
  const result = await executeAssistantPluginAction({
893
1084
  controllerHome,
894
1085
  repoId: repository.repoId,
@@ -905,15 +1096,49 @@ export async function submitAssistantPluginAction(
905
1096
  ? Date.now() + Math.max(1, Math.trunc(request.timeoutMs))
906
1097
  : undefined,
907
1098
  });
1099
+ if (action.confirmation === 'authorization'
1100
+ && authorizationContext
1101
+ && !activeAuthorizationGrant
1102
+ && originMayEstablishCapabilityAuthorization(request.origin)) {
1103
+ try {
1104
+ const grant = recordPluginCapabilityAuthorization(controllerHome, {
1105
+ ownerScope: pluginCapabilityAuthorizationOwnerScope(request.origin),
1106
+ repoId: repository.repoId,
1107
+ pluginId: request.pluginId,
1108
+ capabilityId,
1109
+ target: authorizationContext.target,
1110
+ scopes: action.scopes,
1111
+ riskCeiling: action.risk,
1112
+ expiresInMinutes: authorizationContext.expiresInMinutes,
1113
+ });
1114
+ authorization = {
1115
+ source: 'host_permission_model',
1116
+ reusable: true,
1117
+ capabilityId,
1118
+ target: authorizationTargetSummary(authorizationContext),
1119
+ grantId: grant.grantId,
1120
+ established: true,
1121
+ ...(authorization.lookupError ? { lookupError: authorization.lookupError } : {}),
1122
+ };
1123
+ } catch (error) {
1124
+ authorization = {
1125
+ ...authorization,
1126
+ reusable: false,
1127
+ persistenceError: authorizationError(error),
1128
+ };
1129
+ }
1130
+ }
1131
+
908
1132
  let resultWithLineage = result;
909
1133
  if (boundRemoteWork) {
1134
+ const terminalRemoteEffect = action.remoteEffectWorkCompletion === 'terminal';
910
1135
  resultWithLineage = {
911
1136
  ...result,
912
1137
  work: {
913
1138
  workId: boundRemoteWork.workId,
914
1139
  workKind: 'remote_effect',
915
- completionOutcome: 'completed_remote',
916
- status: 'completed',
1140
+ ...(terminalRemoteEffect ? { completionOutcome: 'completed_remote' as const } : {}),
1141
+ status: terminalRemoteEffect ? 'completed' : 'running',
917
1142
  },
918
1143
  };
919
1144
  }
@@ -961,8 +1186,15 @@ export async function submitAssistantPluginAction(
961
1186
  semanticKey: key,
962
1187
  status: 'succeeded',
963
1188
  createdAt,
964
- ...(acceptedWork ? { workId: acceptedWork.workId } : boundRemoteWork ? { workId: boundRemoteWork.workId } : {}),
1189
+ ...(acceptedWork
1190
+ ? { workId: acceptedWork.workId }
1191
+ : boundRemoteWork
1192
+ ? { workId: boundRemoteWork.workId }
1193
+ : attributedWork
1194
+ ? { workId: attributedWork.workId }
1195
+ : {}),
965
1196
  origin: request.origin,
1197
+ authorization,
966
1198
  result: resultWithLineage,
967
1199
  };
968
1200
  writeJsonAtomic(pluginActionReceiptPath(controllerHome, repository.repoId, receiptId), receipt);
@@ -982,7 +1214,8 @@ export async function submitAssistantPluginAction(
982
1214
  deduplicated: false,
983
1215
  result: resultWithLineage,
984
1216
  receipt,
985
- workId: acceptedWork?.workId ?? boundRemoteWork?.workId,
1217
+ authorization,
1218
+ workId: acceptedWork?.workId ?? boundRemoteWork?.workId ?? attributedWork?.workId,
986
1219
  };
987
1220
  } catch (error) {
988
1221
  const message = error instanceof Error ? error.message : String(error);
@@ -1018,8 +1251,15 @@ export async function submitAssistantPluginAction(
1018
1251
  semanticKey: key,
1019
1252
  status: 'failed',
1020
1253
  createdAt,
1021
- ...(acceptedWork ? { workId: acceptedWork.workId } : boundRemoteWork ? { workId: boundRemoteWork.workId } : {}),
1254
+ ...(acceptedWork
1255
+ ? { workId: acceptedWork.workId }
1256
+ : boundRemoteWork
1257
+ ? { workId: boundRemoteWork.workId }
1258
+ : attributedWork
1259
+ ? { workId: attributedWork.workId }
1260
+ : {}),
1022
1261
  origin: request.origin,
1262
+ authorization,
1023
1263
  error: { code, message },
1024
1264
  };
1025
1265
  writeJsonAtomic(pluginActionReceiptPath(controllerHome, repository.repoId, receiptId), receipt);
@@ -4,8 +4,24 @@ export type AssistantPluginLifecycleState = 'enabled' | 'disabled' | 'degraded'
4
4
  export type AssistantPluginHealthState = 'ready' | 'disabled' | 'degraded' | 'error';
5
5
  export type AssistantPluginActionRisk = 'readonly' | 'workspace_write' | 'remote_write' | 'destructive';
6
6
  export type AssistantPluginActionConfirmation = 'none' | 'authorization' | 'strong_confirmation';
7
+ export type AssistantPluginForegroundEffect = 'none' | 'possible' | 'required';
7
8
  export type AssistantPluginResource = 'repo-state' | 'workspace' | 'remote' | 'git-refs' | 'provider-state';
8
9
 
10
+ export interface AssistantPluginAuthorizationTarget {
11
+ /** Stable provider resource class, for example ios-physical-device. */
12
+ kind: string;
13
+ /** Stable exact provider identity. Ephemeral request/session ids must never be used here. */
14
+ id: string;
15
+ /** Optional provider identity fence. A changed fingerprint invalidates reuse. */
16
+ identityFingerprint?: string;
17
+ }
18
+
19
+ export interface AssistantPluginAuthorizationContext {
20
+ target: AssistantPluginAuthorizationTarget;
21
+ /** Optional grant lifetime. The grant authority applies its own upper bound. */
22
+ expiresInMinutes?: number;
23
+ }
24
+
9
25
  export interface AssistantPluginPermissionScope {
10
26
  scope: string;
11
27
  mode: 'read' | 'write';
@@ -38,6 +54,14 @@ export interface AssistantPluginActionDescriptor {
38
54
  defaultTimeoutMs: number;
39
55
  cancellable: boolean;
40
56
  idempotent: boolean;
57
+ /**
58
+ * Successful remote-write actions normally append a durable Work receipt only.
59
+ * Set terminal only when this action itself is authoritative semantic completion
60
+ * for a bound remote_effect Work.
61
+ */
62
+ remoteEffectWorkCompletion?: 'intermediate' | 'terminal';
63
+ executionMode?: 'runtime' | 'lightweight_process';
64
+ foregroundEffect?: AssistantPluginForegroundEffect;
41
65
  scopes: string[];
42
66
  resourceClaims: AssistantPluginActionResourceClaim[];
43
67
  argumentsSchema: Record<string, unknown>;
@@ -128,12 +152,21 @@ export interface AssistantPluginActionExecutionInput {
128
152
  providerIdentityPrevalidated?: boolean;
129
153
  }
130
154
 
131
- export type AssistantPluginScope = 'repository' | 'controller';
155
+ export type AssistantPluginScope = 'repository' | 'controller' | 'controller_with_repository_overlay';
156
+
157
+ export interface AssistantPluginBuildContext {
158
+ controllerHome: string;
159
+ repoId: string;
160
+ repoRoot: string;
161
+ controllerScoped: boolean;
162
+ }
132
163
 
133
164
  export interface AssistantPluginAdapter {
134
165
  pluginId: string;
135
166
  scope?: AssistantPluginScope;
136
- buildManifest(previousRevision?: number, previousUpdatedAt?: string, repoRoot?: string): AssistantPluginManifest;
167
+ buildManifest(previousRevision?: number, previousUpdatedAt?: string, repoRoot?: string, context?: AssistantPluginBuildContext): AssistantPluginManifest;
168
+ /** Resolve an exact stable resource identity for reusable authorization-class actions. */
169
+ resolveAuthorizationContext?(input: AssistantPluginActionExecutionInput): Promise<AssistantPluginAuthorizationContext | undefined>;
137
170
  executeAction(input: AssistantPluginActionExecutionInput): Promise<Record<string, unknown>>;
138
171
  /** Defaults to true. External providers can defer routine health refresh until the normal manifest cache expires. */
139
172
  shouldRefreshManifestAfterAction?(actionId: string): boolean;