@moretea-labs/forge 1.8.1 → 1.8.2

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 (328) hide show
  1. package/AGENTS.md +5 -2
  2. package/CHANGELOG.md +20 -0
  3. package/README.en.md +24 -3
  4. package/README.md +10 -6
  5. package/README.zh-CN.md +5 -3
  6. package/THIRD_PARTY_NOTICES.md +1 -2
  7. package/adapters/chatgpt/browser-delivery-runtime.ts +5 -3
  8. package/adapters/chatgpt/provider-delivery.ts +1 -1
  9. package/adapters/chatgpt/work-conversation-binding-store.ts +20 -1
  10. package/adapters/computer/chatgpt-extension-target.ts +295 -50
  11. package/adapters/computer/chrome-extension/background.js +227 -71
  12. package/adapters/computer/chrome-extension/content.js +181 -157
  13. package/adapters/computer/chrome-extension/manifest.json +3 -1
  14. package/adapters/computer/desktop-operator-provider.ts +18 -6
  15. package/adapters/computer/interaction-target-authority.ts +39 -9
  16. package/adapters/mcp/access-mode.ts +18 -39
  17. package/adapters/mcp/auth.ts +2 -6
  18. package/adapters/mcp/instructions.ts +2 -2
  19. package/adapters/mcp/multi-repository.ts +7 -28
  20. package/adapters/mcp/runtime-gateway/context-adapter.ts +79 -40
  21. package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +7 -15
  22. package/adapters/mcp/runtime-gateway/execution-tools.ts +7 -13
  23. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +16 -3
  24. package/adapters/mcp/runtime-gateway/process-tools.ts +3 -21
  25. package/adapters/mcp/runtime-gateway/router.ts +3 -5
  26. package/adapters/mcp/runtime-gateway/routing-policy.ts +17 -10
  27. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1 -1
  28. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +14 -9
  29. package/adapters/mcp/runtime-gateway/runtime-tools.ts +1 -5
  30. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +36 -1
  31. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +58 -4
  32. package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +7 -3
  33. package/adapters/mcp/runtime-gateway/work-adapter.ts +16 -16
  34. package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +0 -17
  35. package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +69 -12
  36. package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +26 -2
  37. package/adapters/mcp/server.ts +24 -3
  38. package/adapters/mcp/setup.ts +4 -10
  39. package/adapters/mcp/tool-mapping/access-tools.ts +0 -6
  40. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +672 -2935
  41. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +1 -1
  42. package/adapters/mcp/tool-mapping/repository-tools.ts +52 -86
  43. package/adapters/mcp/toolset-names.ts +11 -42
  44. package/adapters/mcp/toolset.ts +16 -90
  45. package/{src/cli → adapters}/mcp/transports/AGENTS.md +5 -5
  46. package/{src/cli → adapters}/mcp/transports/CLAUDE.md +5 -5
  47. package/adapters/mcp/transports/http-observation.ts +18 -6
  48. package/adapters/mcp/transports/http-post-lifecycle.ts +19 -0
  49. package/adapters/mcp/transports/http.ts +54 -14
  50. package/adapters/mcp/types.ts +0 -6
  51. package/assets/hooks/lib/session-state.sh +0 -38
  52. package/assets/hooks/post-bash.sh +1 -1
  53. package/assets/hooks/post-tool-observer.sh +0 -5
  54. package/assets/hooks/prompt-guard.sh +0 -55
  55. package/assets/partials/04-project-structure.partial.md +1 -1
  56. package/assets/partials/07-footer.partial.md +1 -1
  57. package/assets/partials-agents/02-operating-mode.partial.md +1 -1
  58. package/assets/partials-agents/08-deep-docs.partial.md +1 -1
  59. package/assets/reference-configs/agentic-development-flow.md +1 -1
  60. package/assets/reference-configs/external-tooling.md +0 -76
  61. package/assets/reference-configs/release-deploy.md +1 -1
  62. package/assets/templates/helpers/architecture-queue.sh +1 -1
  63. package/assets/templates/helpers/check-agent-tooling.sh +1 -327
  64. package/assets/templates/helpers/check-task-workflow.sh +1 -1
  65. package/assets/templates/helpers/ensure-task-workflow.sh +0 -14
  66. package/assets/templates/helpers/migrate-project-template.sh +2 -2
  67. package/assets/workflow-contract.v1.json +0 -2
  68. package/docs/README.md +5 -2
  69. package/docs/architecture/CURRENT.md +114 -30
  70. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +42 -0
  71. package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +15 -2
  72. package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +4 -2
  73. package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +109 -4
  74. package/docs/architecture/decisions/20261009-exact-code-context-discovery.md +19 -0
  75. package/docs/architecture/decisions/20261009-generated-xcode-cache-retention.md +37 -0
  76. package/docs/architecture/decisions/20261011-autonomous-continuation-convergence.md +597 -0
  77. package/docs/architecture/decisions/20261017-edit-session-finalization-reconciliation.md +17 -0
  78. package/docs/architecture/decisions/20261019-retire-host-skills.md +16 -0
  79. package/docs/architecture/diagrams/agentic-dev-plugin-architecture.html +1 -1
  80. package/docs/architecture/domains/public-surface.md +3 -5
  81. package/docs/architecture/domains/verification.md +5 -5
  82. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +2 -2
  83. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +4 -4
  84. package/docs/architecture/reviews/20261015-code-search-runtime-acceptance.md +75 -0
  85. package/docs/forge-plugin-management.md +14 -2
  86. package/docs/operations/controller-browser-plugin.md +23 -0
  87. package/docs/operations/features.md +6 -7
  88. package/docs/operations/features.zh-CN.md +1 -1
  89. package/docs/operations/github-repository.md +4 -4
  90. package/docs/operations/github-semantic-release-projection.md +20 -0
  91. package/docs/operations/homebrew.md +2 -2
  92. package/docs/operations/mcp-tool-exposure.md +13 -54
  93. package/docs/operations/runtime-performance-diagnostics.md +8 -0
  94. package/docs/operations/standalone-disaster-recovery.md +1 -1
  95. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  96. package/docs/tutorials/03-first-repository-task.md +2 -0
  97. package/docs/wiki/Computer-and-Plugins.md +78 -0
  98. package/docs/wiki/Computer-and-Plugins.zh-CN.md +88 -0
  99. package/docs/wiki/Home.md +16 -2
  100. package/docs/wiki/Implementation.md +24 -12
  101. package/docs/wiki/Installation.md +28 -32
  102. package/docs/wiki/Integrations.md +4 -0
  103. package/docs/wiki/Quick-Start.md +35 -22
  104. package/docs/wiki/Releases-and-Upgrades.md +15 -25
  105. package/docs/wiki/_Sidebar.md +5 -1
  106. package/package.json +1 -12
  107. package/packages/kernel/controller/infrastructure/controller-session-store.ts +63 -38
  108. package/packages/kernel/identity/infrastructure/activity-projection-store.ts +10 -0
  109. package/packages/kernel/scheduler/application/eligibility.ts +4 -1
  110. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +13 -10
  111. package/packages/kernel/work/application/work-service.ts +1 -0
  112. package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
  113. package/packages/kernel/work/domain/state-machine.ts +9 -2
  114. package/packages/kernel/work/infrastructure/work-contract-store.ts +28 -1
  115. package/packages/plugin-runtime/browser/provider-registry.ts +12 -36
  116. package/packages/plugin-runtime/computer/conversation-target.ts +17 -7
  117. package/scripts/assemble-template.ts +11 -72
  118. package/scripts/check-mcp-compatibility.ts +55 -74
  119. package/scripts/check-package-identity.mjs +0 -5
  120. package/scripts/check-runtime-architecture.mjs +305 -28
  121. package/scripts/init-project.sh +0 -46
  122. package/scripts/install-ios-hid-toolchain.sh +60 -0
  123. package/scripts/install-standalone-recovery.ts +1 -1
  124. package/scripts/lib/project-init-lib.sh +3 -3
  125. package/scripts/migrate-windows-wsl-controller-home.ts +2 -0
  126. package/scripts/open-source-audit-allowlist.txt +0 -1
  127. package/scripts/run-governed-gate.ts +2 -0
  128. package/scripts/smoke-ios-physical-device.ts +38 -10
  129. package/scripts/smoke-mcp-http-runtime.ts +7 -8
  130. package/scripts/smoke-mcp-tool-surface.ts +27 -41
  131. package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +6 -3
  132. package/scripts/sync-generated-authority.mjs +0 -1
  133. package/src/cli/agent-jobs/executor-health.ts +1 -1
  134. package/src/cli/agent-jobs/integration.ts +2 -2
  135. package/src/cli/chatgpt-browser/file-policy.ts +2 -2
  136. package/src/cli/commands/doctor.ts +1 -143
  137. package/src/cli/commands/global-runtime.ts +3 -196
  138. package/src/cli/commands/init-hook.ts +6 -23
  139. package/src/cli/commands/init.ts +3 -252
  140. package/src/cli/commands/mcp.ts +0 -5
  141. package/src/cli/commands/prompt-guard-decision.ts +0 -2
  142. package/src/cli/commands/recovery.ts +2 -14
  143. package/src/cli/commands/repository.ts +17 -1
  144. package/src/cli/commands/setup-execution.ts +41 -0
  145. package/src/cli/controller/completion-orchestrator.ts +1 -1
  146. package/src/cli/controller/composite-operations.ts +1 -1
  147. package/src/cli/controller/context/instruction-context.ts +1 -1
  148. package/src/cli/controller/context/known-paths.ts +2 -2
  149. package/src/cli/controller/context/query-planning.ts +11 -4
  150. package/src/cli/controller/context/rule-resolution.ts +2 -2
  151. package/src/cli/controller/context/source-materializer.ts +2 -2
  152. package/src/cli/controller/context/structural-provider.ts +84 -0
  153. package/src/cli/controller/context/structural.ts +6 -6
  154. package/src/cli/controller/context/types.ts +8 -9
  155. package/src/cli/controller/context-pack.ts +97 -76
  156. package/src/cli/controller/operational-plan.ts +1 -1
  157. package/src/cli/controller/systemd-user.ts +4 -10
  158. package/src/cli/editing/edit-session.ts +17 -5
  159. package/src/cli/github/github.ts +4 -4
  160. package/src/cli/github/semantic-projection.ts +174 -0
  161. package/src/cli/hook/prompt-intents.ts +0 -9
  162. package/src/cli/index.ts +1 -43
  163. package/src/cli/local-bridge/connector-freshness.ts +1 -12
  164. package/src/cli/local-bridge/console-automations.ts +1 -1
  165. package/src/cli/local-bridge/console-view-models.ts +1 -1
  166. package/src/cli/local-bridge/facade-api.ts +0 -2
  167. package/src/cli/repositories/command-classifier.ts +0 -9
  168. package/src/cli/repositories/command-scope.ts +2 -2
  169. package/src/cli/repositories/locks.ts +20 -0
  170. package/src/cli/repositories/registry.ts +2 -1
  171. package/src/cli/repositories/safe-patch.ts +2 -12
  172. package/src/cli/repositories/structured-git.ts +39 -3
  173. package/src/cli/repository/inspector.ts +185 -343
  174. package/src/effects/process-runner.ts +17 -1
  175. package/src/runtime/context/generic-lsp-provider.ts +1 -1
  176. package/src/runtime/context/semantic-navigation-contract.ts +7 -0
  177. package/src/runtime/context/semantic-navigation.ts +1 -1
  178. package/src/runtime/control-plane/cleanup-artifact-retention.ts +4 -4
  179. package/src/runtime/control-plane/execution/work-delivery-service.ts +150 -31
  180. package/src/runtime/control-plane/execution/work-handle-authority.ts +12 -4
  181. package/src/runtime/control-plane/execution/work-handle-store.ts +53 -1
  182. package/src/runtime/control-plane/execution/work-operation-service.ts +20 -10
  183. package/src/runtime/control-plane/execution/work-preparation-service.ts +108 -4
  184. package/src/runtime/control-plane/execution/work-process-evidence.ts +2 -2
  185. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +28 -13
  186. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +5 -1
  187. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +1 -1
  188. package/src/runtime/control-plane/facade/capability-registry.ts +2 -0
  189. package/src/runtime/control-plane/facade/work-continuation.ts +1 -1
  190. package/src/runtime/control-plane/generated-cache-retention.ts +114 -63
  191. package/src/runtime/control-plane/global-scheduler/maintenance.ts +20 -8
  192. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -11
  193. package/src/runtime/control-plane/governance/external-effects.ts +1 -1
  194. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +1 -0
  195. package/src/runtime/control-plane/runtime-cleanup.ts +12 -93
  196. package/src/runtime/evidence/sensitive-output.ts +22 -2
  197. package/src/runtime/execution/jobs/child-reference.ts +11 -0
  198. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -11
  199. package/src/runtime/execution/process-runtime/command-facade.ts +3 -3
  200. package/src/runtime/execution/process-runtime/runtime.ts +20 -16
  201. package/src/runtime/execution/thin-harness/execution-router.ts +16 -16
  202. package/src/runtime/execution/thin-harness/fast-executor.ts +2 -2
  203. package/src/runtime/execution/thin-harness/search-worker.ts +2 -2
  204. package/src/runtime/execution/workers/executor.ts +1 -2
  205. package/src/runtime/maintenance/cleanup.ts +41 -5
  206. package/src/runtime/plugins/action-application.ts +12 -1
  207. package/src/runtime/plugins/app-store-connect-adapter.ts +252 -4
  208. package/src/runtime/plugins/browser-adapter.ts +381 -313
  209. package/src/runtime/plugins/browser-macos-bridge.ts +114 -112
  210. package/src/runtime/plugins/browser-manifest-surface.ts +2 -1
  211. package/src/runtime/plugins/browser-registration.ts +2 -0
  212. package/src/runtime/plugins/browser-runtime.ts +26 -35
  213. package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +11 -2
  214. package/src/runtime/plugins/computer-registration.ts +76 -9
  215. package/src/runtime/plugins/github-adapter.ts +30 -3
  216. package/src/runtime/plugins/gmail-adapter.ts +166 -1
  217. package/src/runtime/plugins/google-shared.ts +10 -5
  218. package/src/runtime/plugins/ios/remote-xpc-hid.ts +70 -30
  219. package/src/runtime/plugins/ios-adapter.ts +45 -40
  220. package/src/runtime/plugins/ios-agent-device.ts +54 -4
  221. package/src/runtime/plugins/ios-device-ownership.ts +103 -0
  222. package/src/runtime/plugins/ios-physical-device.ts +497 -79
  223. package/src/runtime/plugins/local-system-adapter.ts +1 -99
  224. package/src/runtime/plugins/store.ts +80 -7
  225. package/src/runtime/plugins/types.ts +4 -0
  226. package/src/runtime/projections/git-status-sampler.ts +2 -19
  227. package/src/runtime/recovery/actions.ts +1 -1
  228. package/src/runtime/recovery/lifecycle-inventory.ts +1 -11
  229. package/src/runtime/recovery/probe.ts +2 -2
  230. package/src/runtime/release/release-coordinator.ts +20 -4
  231. package/src/runtime/root/controller-progression-composition.ts +67 -5
  232. package/src/runtime/root/package-connector-service.ts +123 -11
  233. package/src/runtime/root/package-runtime-release.ts +6 -18
  234. package/src/runtime/root/package-runtime-service.ts +7 -13
  235. package/src/runtime/root/release-execution-canary.ts +18 -5
  236. package/src/runtime/root/release-manifest.ts +0 -34
  237. package/src/runtime/root/release-materialize.ts +0 -78
  238. package/src/runtime/root/runtime.ts +1 -2
  239. package/src/runtime/root/scheduled-controller-composition.ts +2 -1
  240. package/src/runtime/root/service.ts +90 -11
  241. package/src/runtime/root/types.ts +0 -6
  242. package/src/runtime/root/workflow-supervisor-composition.ts +207 -19
  243. package/src/runtime/root/workflow-supervisor-runtime.ts +365 -10
  244. package/src/runtime/safe-tooling/google-credential-store.ts +1 -11
  245. package/src/runtime/safe-tooling/ios-development.ts +22 -8
  246. package/src/runtime/safe-tooling/job-summary.ts +1 -1
  247. package/src/runtime/safe-tooling/workspace-auth.ts +4 -1
  248. package/src/runtime/shared/atomic-text-file.ts +21 -0
  249. package/src/runtime/shared/json-files.ts +8 -3
  250. package/src/runtime/standalone-recovery/core.ts +189 -174
  251. package/src/runtime/standalone-recovery/entry.ts +62 -30
  252. package/src/runtime/standalone-recovery/operation-lock.ts +11 -1
  253. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -0
  254. package/src/runtime/workflow/schedules/engine.ts +36 -14
  255. package/src/runtime/workflow/schedules/pending-activation.ts +11 -4
  256. package/src/runtime/workflow/schedules/work-continuation.ts +51 -6
  257. package/supervisor/client.ts +31 -14
  258. package/supervisor/control-plane.ts +210 -115
  259. package/supervisor/forge-validators.ts +88 -21
  260. package/supervisor/native-browser-adapter.ts +393 -28
  261. package/supervisor/native-messaging/host.ts +21 -2
  262. package/supervisor/protocol.ts +74 -26
  263. package/supervisor/server.ts +16 -8
  264. package/supervisor/store.ts +476 -57
  265. package/supervisor/types.ts +19 -13
  266. package/SKILL.md +0 -427
  267. package/adapters/computer/chatgpt-target-router.ts +0 -103
  268. package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +0 -87
  269. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +0 -1
  270. package/adapters/mcp/toolset-selection.ts +0 -41
  271. package/agents/openai.yaml +0 -4
  272. package/assets/skill-hooks.json +0 -41
  273. package/assets/skill-version.json +0 -189
  274. package/assets/skills/claude-review/SKILL.md +0 -231
  275. package/assets/skills/codex-review/SKILL.md +0 -103
  276. package/assets/templates/helpers/check-skill-version.ts +0 -193
  277. package/scripts/check-skill-version.ts +0 -5
  278. package/scripts/ensure-codegraph.sh +0 -16
  279. package/scripts/run-skill-hook.ts +0 -244
  280. package/scripts/sync-codex-installed-copies.sh +0 -182
  281. package/src/cli/commands/tools.ts +0 -167
  282. package/src/cli/mcp/access-mode.ts +0 -2
  283. package/src/cli/mcp/access-tools.ts +0 -2
  284. package/src/cli/mcp/audit.ts +0 -2
  285. package/src/cli/mcp/auth.ts +0 -2
  286. package/src/cli/mcp/instructions.ts +0 -2
  287. package/src/cli/mcp/legacy-context.ts +0 -2
  288. package/src/cli/mcp/legacy-tool-service.ts +0 -2
  289. package/src/cli/mcp/multi-repository.ts +0 -2
  290. package/src/cli/mcp/oauth.ts +0 -2
  291. package/src/cli/mcp/openai-secure-tunnel.ts +0 -2
  292. package/src/cli/mcp/paths.ts +0 -2
  293. package/src/cli/mcp/policy.ts +0 -2
  294. package/src/cli/mcp/post-finalize-work-attribution.ts +0 -2
  295. package/src/cli/mcp/proxy-env.ts +0 -2
  296. package/src/cli/mcp/readiness-projection.ts +0 -2
  297. package/src/cli/mcp/redaction.ts +0 -2
  298. package/src/cli/mcp/repo.ts +0 -2
  299. package/src/cli/mcp/repository-tools.ts +0 -2
  300. package/src/cli/mcp/server.ts +0 -2
  301. package/src/cli/mcp/setup.ts +0 -2
  302. package/src/cli/mcp/tool-capability-cache.ts +0 -2
  303. package/src/cli/mcp/tool-contract.ts +0 -2
  304. package/src/cli/mcp/tools.ts +0 -2
  305. package/src/cli/mcp/toolset-names.ts +0 -2
  306. package/src/cli/mcp/toolset-selection.ts +0 -2
  307. package/src/cli/mcp/toolset.ts +0 -2
  308. package/src/cli/mcp/transports/http.ts +0 -2
  309. package/src/cli/mcp/transports/session-registry.ts +0 -2
  310. package/src/cli/mcp/transports/stdio.ts +0 -2
  311. package/src/cli/mcp/types.ts +0 -2
  312. package/src/cli/mcp/umbrella-tools.ts +0 -2
  313. package/src/cli/tools/codegraph.ts +0 -695
  314. package/src/core/adoption/helper-wrapper-plan.ts +0 -159
  315. package/src/runtime/context/codegraph-cache-boundary.ts +0 -255
  316. package/src/runtime/context/codegraph-read-provider.ts +0 -611
  317. package/src/runtime/context/codegraph-sidecar.cjs +0 -248
  318. package/src/runtime/control-plane/codegraph-cache-retention.ts +0 -164
  319. package/src/runtime/gateway/mcp/execution-tools.ts +0 -2
  320. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +0 -2
  321. package/src/runtime/gateway/mcp/persisted-check-process.ts +0 -2
  322. package/src/runtime/gateway/mcp/process-tools.ts +0 -2
  323. package/src/runtime/gateway/mcp/router.ts +0 -2
  324. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +0 -2
  325. package/src/runtime/gateway/mcp/runtime-tools.ts +0 -2
  326. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +0 -2
  327. package/src/runtime/plugins/browser-provider-registry.ts +0 -2
  328. package/src/runtime/plugins/computer-chatgpt-macos-target.ts +0 -891
@@ -59,8 +59,8 @@ const SENSITIVE_PATTERNS: readonly SensitivePattern[] = [
59
59
  },
60
60
  {
61
61
  type: 'secret_assignment',
62
- pattern: /((?:^|[^\w]))(["']?)([A-Za-z0-9_.-]*(?:api[_-]?key|access[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|private[_-]?key|password|passwd|secret|credentials?|auth[_-]?token|token|cookie)[A-Za-z0-9_.-]*)\2(\s*(?:=>|:=|=|:)\s*)(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,}\r\n]+)/gim,
63
- replacement: '$1$2$3$2$4[REDACTED]',
62
+ pattern: /((?:^|[^\w]))(["']?)([A-Za-z0-9_.-]*(?:api[_-]?key|access[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|private[_-]?key|password|passwd|secret|credentials?|auth[_-]?token|token|cookie)[A-Za-z0-9_.-]*)\2(\s*(?:=>|:=|=|:)\s*)((?:[A-Z][A-Za-z0-9_]*(?:[?!])?\s*=\s*)?)(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,}\r\n]+)/gim,
63
+ replacement: '$1$2$3$2$4$5[REDACTED]',
64
64
  },
65
65
  {
66
66
  type: 'database_url_assignment',
@@ -136,6 +136,26 @@ export function redactSensitiveText(input: string): SensitiveTextRedactionResult
136
136
  let text = input;
137
137
  const counts = new Map<string, number>();
138
138
  for (const entry of SENSITIVE_PATTERNS) {
139
+ if (entry.type === 'secret_assignment') {
140
+ let redactions = 0;
141
+ text = text.replace(entry.pattern, (matched, boundary: string, quote: string, key: string, separator: string, typeDefault: string) => {
142
+ // Preserve non-credential data flow even when a source symbol ends in "Token".
143
+ // An unquoted floating-point counter or a reference to the local token
144
+ // variable is not a secret literal. Keep unknown opaque/quoted token values
145
+ // redacted, including compound token keys, rather than whitelisting names.
146
+ const prefix = `${boundary}${quote}${key}${quote}${separator}${typeDefault}`;
147
+ const rhs = matched.slice(prefix.length);
148
+ const genericToken = /token$/i.test(key) && !/^(?:token|(?:api|access|refresh|auth|session|bearer|csrf|id|jwt|oauth)[_-]?token)$/i.test(key);
149
+ const sourceCounter = /^[+-]?(?:\d+\.\d*|\.\d+)(?:e[+-]?\d+)?[;)]?$/i.test(rhs);
150
+ const sourceReference = /^token[;)]?$/.test(rhs);
151
+ const sourceType = separator.trim() === ':' && !typeDefault && /^(?:Double|Float|Int|Int8|Int16|Int32|Int64|Bool|String|Character|CGFloat|Date|UUID)[?!]?[),{;]?$/.test(rhs);
152
+ if (genericToken && (sourceCounter || sourceReference || sourceType)) return matched;
153
+ redactions += 1;
154
+ return `${prefix}[REDACTED]`;
155
+ });
156
+ if (redactions) counts.set(entry.type, (counts.get(entry.type) ?? 0) + redactions);
157
+ continue;
158
+ }
139
159
  const matches = text.match(entry.pattern);
140
160
  if (!matches?.length) continue;
141
161
  counts.set(entry.type, (counts.get(entry.type) ?? 0) + matches.length);
@@ -38,6 +38,17 @@ export function isAgentDelegationLocalAction(action: string | undefined): boolea
38
38
  return AGENT_DELEGATION_LOCAL_ACTIONS.has(action);
39
39
  }
40
40
 
41
+ /** Shared delegation classification for Scheduler replay and durable receipt recovery. */
42
+ export function isAgentDelegationJob(job: ExecutionJob): boolean {
43
+ return isAgentDelegationOperation(job.payload.operation)
44
+ || job.type === 'agent-run'
45
+ || job.type === 'dispatch-task'
46
+ || job.payload.arguments?.agentDelegation === true
47
+ || (job.payload.operation === 'legacy-local-job'
48
+ && typeof job.payload.arguments?.localAction === 'string'
49
+ && isAgentDelegationLocalAction(job.payload.arguments.localAction));
50
+ }
51
+
41
52
  function stringField(value: unknown): string | undefined {
42
53
  if (typeof value !== 'string') return undefined;
43
54
  const trimmed = value.trim();
@@ -4,7 +4,7 @@ import {
4
4
  childReferenceFromJob,
5
5
  childReferenceFromReceipt,
6
6
  hasDurableChildReference,
7
- isAgentDelegationOperation,
7
+ isAgentDelegationJob,
8
8
  mergeChildReferences,
9
9
  } from './child-reference';
10
10
  import { operationReceiptMatchesJobOwnership, readOperationReceipt } from './receipt-store';
@@ -18,16 +18,6 @@ export type RecoveredReceiptOutcome = {
18
18
  outcome: 'succeeded' | 'failed' | 'delegated';
19
19
  };
20
20
 
21
- function isAgentDelegationJob(job: ExecutionJob): boolean {
22
- return isAgentDelegationOperation(job.payload.operation)
23
- || job.type === 'agent-run'
24
- || job.type === 'dispatch-task'
25
- || job.payload.arguments?.agentDelegation === true
26
- || (job.payload.operation === 'legacy-local-job'
27
- && typeof job.payload.arguments?.localAction === 'string'
28
- && ['launch-task', 'quick-agent-session'].includes(String(job.payload.arguments.localAction)));
29
- }
30
-
31
21
  /**
32
22
  * Recover a running Job from a matching completed/delegated Worker receipt.
33
23
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * repository_command_execute → three explicit execution lanes.
2
+ * execute_command → three explicit execution lanes.
3
3
  *
4
4
  * - Ephemeral Direct: bounded ordinary reads and commands that finish inside
5
5
  * the interactive budget.
@@ -345,7 +345,7 @@ export async function executeRepositoryCommandViaProcessRuntime(
345
345
  durableSideEffects: emptyEffects,
346
346
  suggestedOperation: decision.reason === 'standalone_recovery_lifecycle_required'
347
347
  ? 'Use the standalone Forge Recovery connector for canonical Runtime/connector lifecycle operations.'
348
- : 'repository_command_execute via Durable Work / Local Job',
348
+ : 'execute_command via Durable Work / Local Job',
349
349
  externalEffect: decision.route === 'durable'
350
350
  ? {
351
351
  outcome: 'not_started',
@@ -550,7 +550,7 @@ export async function executeRepositoryCommandViaProcessRuntime(
550
550
  resourceClaims: toProcessClaims(claims),
551
551
  origin: {
552
552
  surface: 'command',
553
- toolName: 'repository_command_execute',
553
+ toolName: 'execute_command',
554
554
  requestId: input.requestId,
555
555
  correlationId: input.workId,
556
556
  },
@@ -1584,17 +1584,19 @@ export async function spawnManagedProcess(input: SpawnManagedProcessInput): Prom
1584
1584
  }
1585
1585
 
1586
1586
  const completed = await new Promise<ManagedProcessRecord | 'timeout'>((resolve) => {
1587
- const timer = setTimeout(() => resolve('timeout'), interactiveWaitMs);
1588
- timer.unref?.();
1589
- monitor.waiters.push((done) => {
1587
+ const finish = (done: ManagedProcessRecord | 'timeout') => {
1590
1588
  clearTimeout(timer);
1589
+ const index = monitor.waiters.indexOf(finish);
1590
+ if (index >= 0) monitor.waiters.splice(index, 1);
1591
1591
  resolve(done);
1592
- });
1592
+ };
1593
+ const timer = setTimeout(() => finish('timeout'), interactiveWaitMs);
1594
+ timer.unref?.();
1595
+ monitor.waiters.push(finish);
1593
1596
  if (monitor.settled) {
1594
1597
  const current = getProcessRecord(input.controllerHome, input.repoId, processId);
1595
1598
  if (current?.terminalWritten) {
1596
- clearTimeout(timer);
1597
- resolve(current);
1599
+ finish(current);
1598
1600
  }
1599
1601
  }
1600
1602
  });
@@ -1693,27 +1695,29 @@ export async function waitForProcess(
1693
1695
  if (monitor) {
1694
1696
  const waitMs = options.timeoutMs ?? existing.timeoutMs;
1695
1697
  const done = await new Promise<ManagedProcessRecord | 'timeout'>((resolve) => {
1696
- const timer = setTimeout(() => resolve('timeout'), Math.max(1, waitMs));
1698
+ const finish = (record: ManagedProcessRecord | 'timeout') => {
1699
+ clearTimeout(timer);
1700
+ options.signal?.removeEventListener('abort', onAbort);
1701
+ const index = monitor.waiters.indexOf(finish);
1702
+ if (index >= 0) monitor.waiters.splice(index, 1);
1703
+ resolve(record);
1704
+ };
1705
+ const timer = setTimeout(() => finish('timeout'), Math.max(1, waitMs));
1697
1706
  timer.unref?.();
1698
1707
  const onAbort = () => {
1699
1708
  clearTimeout(timer);
1700
1709
  void cancelProcess(controllerHome, repoId, processId).then(() => {
1701
1710
  const current = getProcessRecord(controllerHome, repoId, processId);
1702
- if (current) resolve(current);
1703
- else resolve('timeout');
1711
+ if (current) finish(current);
1712
+ else finish('timeout');
1704
1713
  });
1705
1714
  };
1706
1715
  options.signal?.addEventListener('abort', onAbort, { once: true });
1707
- monitor.waiters.push((record) => {
1708
- clearTimeout(timer);
1709
- options.signal?.removeEventListener('abort', onAbort);
1710
- resolve(record);
1711
- });
1716
+ monitor.waiters.push(finish);
1712
1717
  if (monitor.settled) {
1713
1718
  const current = getProcessRecord(controllerHome, repoId, processId);
1714
1719
  if (current?.terminalWritten) {
1715
- clearTimeout(timer);
1716
- resolve(current);
1720
+ finish(current);
1717
1721
  }
1718
1722
  }
1719
1723
  });
@@ -317,7 +317,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
317
317
  }
318
318
 
319
319
  if (commandLooksDestructive(input.command) || operation.includes('destructive')) {
320
- if (input.approvalContinuation === true && operation === 'repository_command_execute') {
320
+ if (input.approvalContinuation === true && operation === 'execute_command') {
321
321
  return decisionBase({
322
322
  mode: 'durable',
323
323
  reasons: ['destructive_approval_continuation'],
@@ -325,7 +325,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
325
325
  estimatedClass: 'unknown',
326
326
  requiresIsolation: true,
327
327
  requiresRecovery: false,
328
- suggestedOperation: 'repository_command_execute through Durable Work / Local Job',
328
+ suggestedOperation: 'execute_command through Durable Work / Local Job',
329
329
  effects: {
330
330
  readsWorkspace: true,
331
331
  mutatesWorkspace: true,
@@ -342,7 +342,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
342
342
  requiresIsolation: true,
343
343
  requiresRecovery: false,
344
344
  rejectCode: 'DESTRUCTIVE_REJECTED',
345
- suggestedOperation: 'repository_command_preview + explicit strong confirmation via Durable Work',
345
+ suggestedOperation: 'execute_command(dry_run=true) + explicit strong confirmation via Durable Work',
346
346
  effects: {
347
347
  readsWorkspace: true,
348
348
  mutatesWorkspace: true,
@@ -381,7 +381,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
381
381
 
382
382
  let commandEffectsValue: ExecutionEffects | undefined;
383
383
 
384
- if (operation === 'repository_command_execute' || operation === 'run_short_command' || operation === 'run_focused_check') {
384
+ if (operation === 'execute_command' || operation === 'run_short_command' || operation === 'run_focused_check') {
385
385
  if (input.command !== undefined) {
386
386
  const classification = classifyRepositoryCommand(input.command, input.defaultBranch);
387
387
  const canonical = normalizeRepositoryCommand(input.command);
@@ -401,7 +401,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
401
401
  requiresIsolation: true,
402
402
  requiresRecovery: false,
403
403
  rejectCode: 'UNSAFE_SHELL',
404
- suggestedOperation: 'repository_command_preview',
404
+ suggestedOperation: 'execute_command(dry_run=true)',
405
405
  effects: commandEffectsValue,
406
406
  }, operation);
407
407
  }
@@ -411,7 +411,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
411
411
  }
412
412
  if (classification.risk === 'remote_write') reasons.push('command_classified_remote_write');
413
413
  if (classification.risk === 'destructive') {
414
- if (input.approvalContinuation === true && operation === 'repository_command_execute') {
414
+ if (input.approvalContinuation === true && operation === 'execute_command') {
415
415
  return decisionBase({
416
416
  mode: 'durable',
417
417
  reasons: ['destructive_approval_continuation', ...classification.reasons],
@@ -419,7 +419,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
419
419
  estimatedClass: 'unknown',
420
420
  requiresIsolation: true,
421
421
  requiresRecovery: false,
422
- suggestedOperation: 'repository_command_execute through Durable Work / Local Job',
422
+ suggestedOperation: 'execute_command through Durable Work / Local Job',
423
423
  effects: commandEffectsValue,
424
424
  }, operation);
425
425
  }
@@ -431,7 +431,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
431
431
  requiresIsolation: true,
432
432
  requiresRecovery: false,
433
433
  rejectCode: 'DESTRUCTIVE_COMMAND',
434
- suggestedOperation: 'repository_command_preview',
434
+ suggestedOperation: 'execute_command(dry_run=true)',
435
435
  effects: commandEffectsValue,
436
436
  }, operation);
437
437
  }
@@ -440,14 +440,14 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
440
440
  reasons.push('not_a_strict_focused_check');
441
441
  }
442
442
  if (classification.risk === 'workspace_write' && !focused && !safeShellCombo) {
443
- if (operation === 'repository_command_execute' || operation === 'run_short_command' || operation === 'run_focused_check') {
443
+ if (operation === 'execute_command' || operation === 'run_short_command' || operation === 'run_focused_check') {
444
444
  reasons.push('mutating_or_unfocused_command');
445
445
  }
446
446
  }
447
447
  if (classification.risk !== 'readonly' && !focused && !safeShellCombo && !FAST_OPERATIONS.has(operation)) {
448
448
  reasons.push('untrusted_or_unclassified_command');
449
449
  }
450
- } else if (operation === 'repository_command_execute' || operation === 'run_focused_check') {
450
+ } else if (operation === 'execute_command' || operation === 'run_focused_check') {
451
451
  reasons.push('missing_command');
452
452
  }
453
453
  }
@@ -564,7 +564,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
564
564
  }, operation);
565
565
  }
566
566
 
567
- if (!FAST_OPERATIONS.has(operation) && operation !== 'repository_command_execute') {
567
+ if (!FAST_OPERATIONS.has(operation) && operation !== 'execute_command') {
568
568
  return decisionBase({
569
569
  mode: 'durable',
570
570
  reasons: ['operation_not_in_fast_allowlist'],
@@ -576,7 +576,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
576
576
  }, operation);
577
577
  }
578
578
 
579
- if (operation === 'repository_command_execute' || operation === 'run_short_command' || operation === 'run_focused_check') {
579
+ if (operation === 'execute_command' || operation === 'run_short_command' || operation === 'run_focused_check') {
580
580
  if (input.command === undefined) {
581
581
  return decisionBase({
582
582
  mode: 'durable',
@@ -585,7 +585,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
585
585
  estimatedClass: 'unknown',
586
586
  requiresIsolation: false,
587
587
  requiresRecovery: false,
588
- suggestedOperation: 'repository_command_execute via Durable Work',
588
+ suggestedOperation: 'execute_command via Durable Work',
589
589
  }, operation);
590
590
  }
591
591
  const classification = classifyRepositoryCommand(input.command, input.defaultBranch);
@@ -602,7 +602,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
602
602
  estimatedClass: 'long',
603
603
  requiresIsolation: false,
604
604
  requiresRecovery: false,
605
- suggestedOperation: 'repository_command_execute via Durable Work / Local Job',
605
+ suggestedOperation: 'execute_command via Durable Work / Local Job',
606
606
  effects,
607
607
  }, operation);
608
608
  }
@@ -632,7 +632,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
632
632
  estimatedClass: 'long',
633
633
  requiresIsolation: false,
634
634
  requiresRecovery: false,
635
- suggestedOperation: 'repository_command_execute via Durable Work / Local Job',
635
+ suggestedOperation: 'execute_command via Durable Work / Local Job',
636
636
  effects,
637
637
  }, operation);
638
638
  }
@@ -673,7 +673,7 @@ export function routeExecution(input: RouteExecutionInput): ExecutionDecision {
673
673
  function durableSuggestion(operation: string): string {
674
674
  if (operation === 'run_check' || operation === 'run_focused_check') return 'run_check through Durable Work';
675
675
  if (operation.includes('patch')) return 'repository_safe_patch_apply with apply_mode=async or Durable Work';
676
- if (operation.includes('command')) return 'repository_command_execute through Durable Work / Local Job';
676
+ if (operation.includes('command')) return 'execute_command through Durable Work / Local Job';
677
677
  if (operation.includes('agent') || operation === 'quick_agent_session') return 'quick_agent_session / dispatch_task';
678
678
  return 'Durable Work (ExecutionJob path)';
679
679
  }
@@ -6,7 +6,7 @@ import {
6
6
  previewRepositoryCommandExecutionAsync,
7
7
  type RepositoryCommandSnapshot,
8
8
  } from '../../../cli/repositories/command-executor';
9
- import { getMcpPolicy } from '../../../cli/mcp/policy';
9
+ import { getMcpPolicy } from '../../../../adapters/mcp/policy';
10
10
  import { readRepositoryRange } from '../../../cli/repository/inspector';
11
11
  import type { RepositoryRecord } from '../../../cli/repositories/types';
12
12
  import { getLeaseSideEffectMetrics } from '../../resources/leases/store';
@@ -878,7 +878,7 @@ export async function executeFast(
878
878
  } else if (
879
879
  operation === 'run_short_command'
880
880
  || operation === 'run_focused_check'
881
- || operation === 'repository_command_execute'
881
+ || operation === 'execute_command'
882
882
  ) {
883
883
  const command = request.input.command as string | string[];
884
884
  const preview = await trace.measure('policyMs', async () => previewRepositoryCommandExecutionAsync(ctx.repository, {
@@ -4,8 +4,8 @@
4
4
  */
5
5
  import { isMainThread, parentPort, workerData, Worker } from 'worker_threads';
6
6
  import { fileURLToPath } from 'url';
7
- import { getMcpPolicy } from '../../../cli/mcp/policy';
8
- import type { McpPolicy } from '../../../cli/mcp/types';
7
+ import { getMcpPolicy } from '../../../../adapters/mcp/policy';
8
+ import type { McpPolicy } from '../../../../adapters/mcp/types';
9
9
  import {
10
10
  searchRepository,
11
11
  searchRepositoryMany,
@@ -518,8 +518,7 @@ export async function executeExecutionJob(controllerHome: string, job: Execution
518
518
  ...context,
519
519
  controllerHome,
520
520
  explicitRepository: repository,
521
- toolset: job.payload.toolset === 'core' || job.payload.toolset === 'full' ? job.payload.toolset : 'advanced',
522
- toolsetLocked: true,
521
+ toolset: 'full',
523
522
  };
524
523
  // Durable Work mutations execute through control-plane application services.
525
524
  // The Worker must never import MCP transport to obtain Work semantics.
@@ -88,12 +88,42 @@ function isSafeForgeTemp(path: string): boolean {
88
88
  const name = basename(path);
89
89
  if (!name.startsWith('forge')) return false;
90
90
  const parent = dirname(path);
91
- const allowedParents = new Set([resolve(tmpdir()), '/private/tmp', '/tmp']);
92
- return allowedParents.has(resolve(parent));
91
+ const allowedParents = new Set([tmpdir(), '/private/tmp', '/tmp'].filter((root) => existsSync(root)).map((root) => realpathSync(root)));
92
+ try { return allowedParents.has(realpathSync(parent)); } catch { return false; }
93
+ }
94
+
95
+ // Temp cleanup cannot dispose of a Git worktree just because its directory
96
+ // begins with "forge". Bounded traversal fails closed on nested repositories,
97
+ // symlinks and uninspectable or high-cardinality source.
98
+ function protectsGitSource(root: string): boolean {
99
+ const pending: Array<{ path: string; depth: number }> = [{ path: root, depth: 0 }];
100
+ let visited = 0;
101
+ while (pending.length > 0) {
102
+ const current = pending.pop()!;
103
+ if (++visited > 256) return true;
104
+ try {
105
+ const stat = lstatSync(current.path);
106
+ if (stat.isSymbolicLink()) return true;
107
+ if (!stat.isDirectory()) continue;
108
+ if (existsSync(join(current.path, '.git'))
109
+ || (existsSync(join(current.path, 'HEAD')) && existsSync(join(current.path, 'objects')) && existsSync(join(current.path, 'refs')))) return true;
110
+ const entries = readdirSync(current.path, { withFileTypes: true });
111
+ if (entries.length + visited > 256) return true;
112
+ for (const entry of entries) {
113
+ if (entry.isSymbolicLink()) return true;
114
+ if (!entry.isDirectory()) continue;
115
+ if (current.depth >= 5) return true;
116
+ pending.push({ path: join(current.path, entry.name), depth: current.depth + 1 });
117
+ }
118
+ } catch {
119
+ return true;
120
+ }
121
+ }
122
+ return false;
93
123
  }
94
124
 
95
125
  function scanTempDirs(minAgeMinutes: number, limit: number): RuntimeCleanupCandidate[] {
96
- const roots = Array.from(new Set([tmpdir(), '/private/tmp', '/tmp'].filter((root) => existsSync(root)).map((root) => resolve(root))));
126
+ const roots = Array.from(new Set([tmpdir(), '/private/tmp', '/tmp'].filter((root) => existsSync(root)).map((root) => realpathSync(root))));
97
127
  const processes = processCommands();
98
128
  const at = Date.now();
99
129
  const candidates: RuntimeCleanupCandidate[] = [];
@@ -108,11 +138,14 @@ function scanTempDirs(minAgeMinutes: number, limit: number): RuntimeCleanupCandi
108
138
  if (!stat.isDirectory()) continue;
109
139
  const occupied = processes.find((process) => process.command.includes(path));
110
140
  const ageMinutes = Math.max(0, Math.round((at - stat.mtimeMs) / 60_000));
111
- const safe = !occupied && ageMinutes >= minAgeMinutes;
141
+ const gitSourceProtected = protectsGitSource(path);
142
+ const safe = !occupied && ageMinutes >= minAgeMinutes && !gitSourceProtected;
112
143
  candidates.push({
113
144
  kind: 'temp_dir',
114
145
  path,
115
- reason: safe
146
+ reason: gitSourceProtected
147
+ ? 'Git worktree, repository, symlink or uninspectable source is protected; use its Work/Git owner.'
148
+ : safe
116
149
  ? `Forge temp directory is older than ${minAgeMinutes} minutes and not referenced by a running process.`
117
150
  : 'Temp directory is too new or still referenced by a running process.',
118
151
  ageMinutes,
@@ -368,6 +401,9 @@ export function applyRuntimeCleanup(repoRoot: string, options: RuntimeCleanupOpt
368
401
  const applied: Array<RuntimeCleanupCandidate & { applied: boolean; error?: string }> = eligible.slice(0, normalized.maxRemovals).map((candidate) => {
369
402
  try {
370
403
  if (candidate.kind === 'temp_dir' && candidate.path && isSafeForgeTemp(candidate.path)) {
404
+ // A preview is not a mutation lease. Recheck source and liveness.
405
+ if (protectsGitSource(candidate.path)) throw new Error('RUNTIME_CLEANUP_GIT_SOURCE_PROTECTED');
406
+ if (processCommands().some((process) => process.command.includes(candidate.path!))) throw new Error('RUNTIME_CLEANUP_TEMP_PROCESS_OCCUPIED');
371
407
  rmSync(candidate.path, { recursive: true, force: true });
372
408
  return { ...candidate, applied: true };
373
409
  }
@@ -16,6 +16,8 @@ import {
16
16
  getControllerPluginManifest,
17
17
  isDirectNonPersistentPluginAction,
18
18
  isDirectPluginReadAction,
19
+ preflightPluginActionWorkBinding,
20
+ readPluginActionReceiptForRequest,
19
21
  submitAssistantPluginAction,
20
22
  submitControllerPluginAction,
21
23
  } from './store';
@@ -112,6 +114,12 @@ export async function executeAssistantPluginActionApplication(input: {
112
114
  };
113
115
  }
114
116
 
117
+ // Reject an invalid Work binding before allocating an immutable Process request ID.
118
+ // Reattach any existing authoritative provider receipt without fresh preflight.
119
+ if (action && input.request.workId && !readPluginActionReceiptForRequest(input.controllerHome, input.request.requestId)) {
120
+ preflightPluginActionWorkBinding(input.controllerHome, input.scope, action, input.request);
121
+ }
122
+
115
123
  if (action?.executionMode === 'runtime') {
116
124
  // Stateful providers may own in-memory resources whose identity is valid only
117
125
  // inside the canonical Runtime process. Keep the normal durable request/effect
@@ -169,7 +177,10 @@ export async function executeAssistantPluginActionApplication(input: {
169
177
  process: handle,
170
178
  };
171
179
  }
172
- if (!handle.ok) {
180
+ // Provider settlement remains authoritative across a sidecar failure.
181
+ // Re-enter the store below to validate exact args/scope/Work before reading
182
+ // any recorded outcome; process failure alone never authorizes redispatch.
183
+ if (!handle.ok && !readPluginActionReceiptForRequest(input.controllerHome, input.request.requestId)) {
173
184
  return {
174
185
  kind: 'managed_failed',
175
186
  manifest,