@moretea-labs/forge 1.7.2 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (900) hide show
  1. package/AGENTS.md +121 -62
  2. package/CHANGELOG.md +40 -0
  3. package/CLAUDE.md +57 -62
  4. package/CONTRIBUTING.md +3 -2
  5. package/README.md +2 -2
  6. package/SKILL.md +0 -1
  7. package/THIRD_PARTY_NOTICES.md +5 -2
  8. package/adapters/browser/index.ts +2 -0
  9. package/adapters/chatgpt/browser-delivery-host.ts +77 -0
  10. package/adapters/chatgpt/browser-delivery-runtime.ts +1256 -0
  11. package/adapters/chatgpt/controller-binding-store.ts +134 -0
  12. package/adapters/chatgpt/controller-round-host.ts +68 -0
  13. package/adapters/chatgpt/controller-round-settlement-store.ts +99 -0
  14. package/adapters/chatgpt/provider-delivery.ts +340 -0
  15. package/adapters/chatgpt/work-conversation-binding-store.ts +218 -0
  16. package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
  17. package/adapters/computer/chatgpt-extension-target.ts +463 -0
  18. package/adapters/computer/chatgpt-target-router.ts +103 -0
  19. package/adapters/computer/chrome-extension/background.js +385 -0
  20. package/adapters/computer/chrome-extension/content.js +378 -0
  21. package/adapters/computer/chrome-extension/core.js +24 -0
  22. package/adapters/computer/chrome-extension/manifest.json +30 -0
  23. package/adapters/computer/desktop-operator-contract.ts +7 -0
  24. package/adapters/computer/desktop-operator-discovery.ts +181 -0
  25. package/adapters/computer/desktop-operator-negotiation.ts +174 -0
  26. package/adapters/computer/desktop-operator-provider.ts +312 -0
  27. package/adapters/computer/index.ts +1 -0
  28. package/adapters/computer/interaction-target-authority.ts +1068 -0
  29. package/adapters/controller-process/binding-store.ts +34 -0
  30. package/adapters/controller-process/controller-host.ts +26 -0
  31. package/adapters/mcp/access-mode.ts +132 -0
  32. package/adapters/mcp/audit.ts +32 -0
  33. package/adapters/mcp/auth.ts +406 -0
  34. package/adapters/mcp/instructions.ts +37 -0
  35. package/adapters/mcp/legacy-context.ts +18 -0
  36. package/adapters/mcp/multi-repository.ts +421 -0
  37. package/adapters/mcp/oauth.ts +464 -0
  38. package/adapters/mcp/paths.ts +143 -0
  39. package/adapters/mcp/policy.ts +232 -0
  40. package/adapters/mcp/proxy-env.ts +62 -0
  41. package/adapters/mcp/readiness-projection.ts +24 -0
  42. package/adapters/mcp/redaction.ts +21 -0
  43. package/adapters/mcp/repo.ts +24 -0
  44. package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
  45. package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +87 -0
  46. package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
  47. package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
  48. package/adapters/mcp/runtime-gateway/context-adapter.ts +1167 -0
  49. package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
  50. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +206 -0
  51. package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
  52. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +130 -0
  53. package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
  54. package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
  55. package/adapters/mcp/runtime-gateway/execution-tools.ts +173 -0
  56. package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
  57. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +412 -0
  58. package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
  59. package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
  60. package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
  61. package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
  62. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +380 -0
  63. package/adapters/mcp/runtime-gateway/process-tools.ts +457 -0
  64. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +88 -0
  65. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +285 -0
  66. package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
  67. package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
  68. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +79 -0
  69. package/adapters/mcp/runtime-gateway/result-adapter.ts +114 -0
  70. package/adapters/mcp/runtime-gateway/router.ts +242 -0
  71. package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
  72. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1200 -0
  73. package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
  74. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +574 -0
  75. package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
  76. package/adapters/mcp/runtime-gateway/runtime-tools.ts +78 -0
  77. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +187 -0
  78. package/adapters/mcp/runtime-gateway/shared-adapter.ts +45 -0
  79. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +962 -0
  80. package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
  81. package/adapters/mcp/runtime-gateway/work-adapter.ts +325 -0
  82. package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
  83. package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
  84. package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
  85. package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
  86. package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
  87. package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
  88. package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
  89. package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
  90. package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
  91. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
  92. package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
  93. package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
  94. package/adapters/mcp/server.ts +1092 -0
  95. package/adapters/mcp/setup.ts +789 -0
  96. package/adapters/mcp/tool-capability-cache.ts +87 -0
  97. package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
  98. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5139 -0
  99. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +145 -0
  100. package/adapters/mcp/tool-mapping/repository-tools.ts +1682 -0
  101. package/adapters/mcp/tool-mapping/tools.ts +9 -0
  102. package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
  103. package/adapters/mcp/toolset-names.ts +79 -0
  104. package/adapters/mcp/toolset-selection.ts +41 -0
  105. package/adapters/mcp/toolset.ts +337 -0
  106. package/adapters/mcp/transports/http-observation.ts +365 -0
  107. package/adapters/mcp/transports/http.ts +933 -0
  108. package/adapters/mcp/transports/oauth-http.ts +322 -0
  109. package/adapters/mcp/transports/session-registry.ts +519 -0
  110. package/adapters/mcp/transports/stdio.ts +8 -0
  111. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +223 -0
  112. package/adapters/mcp/types.ts +41 -0
  113. package/assets/AGENTS.md +2 -16
  114. package/assets/CLAUDE.md +2 -16
  115. package/assets/hooks/AGENTS.md +3 -14
  116. package/assets/hooks/CLAUDE.md +3 -14
  117. package/assets/hooks/hook-input.sh +9 -2
  118. package/assets/hooks/lib/workflow-state.sh +28 -10
  119. package/assets/hooks/prompt-guard.sh +63 -37
  120. package/assets/hooks/security-sentinel.sh +2 -1
  121. package/assets/hooks/session-start-context.sh +7 -5
  122. package/assets/hooks/stop-orchestrator.sh +13 -3
  123. package/assets/initializer-question-pack.v4.json +11 -7
  124. package/assets/partials/05-workflow.partial.md +18 -73
  125. package/assets/partials/07-footer.partial.md +1 -1
  126. package/assets/partials-agents/02-operating-mode.partial.md +1 -2
  127. package/assets/partials-agents/04-task-protocol.partial.md +14 -59
  128. package/assets/partials-agents/06-quality-safety.partial.md +11 -16
  129. package/assets/partials-agents/08-deep-docs.partial.md +1 -2
  130. package/assets/plan-map.json +11 -11
  131. package/assets/plugin-registry.v1.json +11 -3
  132. package/assets/plugin-trust-policy.v1.json +6 -0
  133. package/assets/recovery/ForgeRecovery.ps1 +100 -0
  134. package/assets/recovery/forge-wsl-rescue +271 -0
  135. package/assets/reference-configs/agentic-development-flow.md +25 -0
  136. package/assets/reference-configs/development-protocol.md +11 -15
  137. package/assets/reference-configs/document-generation.md +1 -1
  138. package/assets/reference-configs/global-working-rules.md +28 -1
  139. package/assets/reference-configs/harness-overview.md +22 -112
  140. package/assets/reference-configs/hook-operations.md +1 -1
  141. package/assets/reference-configs/sprint-contracts.md +2 -2
  142. package/assets/skill-version.json +2 -2
  143. package/assets/templates/helpers/architecture-event.ts +3 -34
  144. package/assets/templates/helpers/architecture-queue.sh +61 -10
  145. package/assets/templates/helpers/archive-workflow.sh +90 -180
  146. package/assets/templates/helpers/capability-config.ts +8 -11
  147. package/assets/templates/helpers/capability-resolver.ts +37 -19
  148. package/assets/templates/helpers/capture-plan.sh +2 -2
  149. package/assets/templates/helpers/check-architecture-sync.sh +1 -9
  150. package/assets/templates/helpers/check-task-sync.sh +82 -11
  151. package/assets/templates/helpers/check-task-workflow.sh +1 -3
  152. package/assets/templates/helpers/context-contract-sync.sh +1 -12
  153. package/assets/templates/helpers/contract-worktree.sh +26 -24
  154. package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
  155. package/assets/templates/helpers/inspect-project-state.ts +1 -1
  156. package/assets/templates/helpers/migrate-project-template.sh +1 -1
  157. package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
  158. package/assets/templates/helpers/new-plan.sh +2 -2
  159. package/assets/templates/helpers/plan-to-todo.sh +3 -31
  160. package/assets/templates/helpers/sprint-backlog.sh +1 -1
  161. package/assets/templates/helpers/switch-plan.sh +1 -1
  162. package/assets/templates/helpers/workflow-contract.ts +1 -0
  163. package/assets/workflow-contract.v1.json +63 -171
  164. package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
  165. package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
  166. package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
  167. package/docs/architecture/CURRENT.md +233 -28
  168. package/docs/architecture/EVOLUTION.md +13 -0
  169. package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
  170. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
  171. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
  172. package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
  173. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
  174. package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
  175. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +37 -0
  176. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +311 -0
  177. package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
  178. package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
  179. package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
  180. package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
  181. package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
  182. package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
  183. package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
  184. package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
  185. package/docs/architecture/domains/verification.md +8 -0
  186. package/docs/architecture/domains/workflow-engine.md +7 -1
  187. package/docs/architecture/index.md +3 -3
  188. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
  189. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
  190. package/docs/forge-chatgpt-mcp-setup.md +5 -3
  191. package/docs/forge-plugin-management.md +20 -4
  192. package/docs/forge-runtime-self-healing-loop.md +2 -0
  193. package/docs/operations/benchmark-cross-version-freeze.md +38 -0
  194. package/docs/operations/controller-browser-plugin.md +3 -1
  195. package/docs/operations/controller-performance-and-502.md +17 -0
  196. package/docs/operations/controller-reliability-runbook.md +2 -2
  197. package/docs/operations/features.md +1 -1
  198. package/docs/operations/features.zh-CN.md +1 -1
  199. package/docs/operations/homebrew.md +2 -2
  200. package/docs/operations/mcp-tool-exposure.md +2 -0
  201. package/docs/operations/platform-support.md +16 -8
  202. package/docs/operations/platform-support.zh-CN.md +2 -2
  203. package/docs/operations/releasing.md +2 -0
  204. package/docs/operations/standalone-disaster-recovery.md +38 -5
  205. package/docs/operations/uu-remote-rescue.md +41 -0
  206. package/docs/operations/windows-wsl-host-rescue.md +133 -0
  207. package/docs/public-usage-guide.md +1 -1
  208. package/docs/public-usage-guide.zh-CN.md +1 -1
  209. package/docs/tutorials/01-install-and-start.md +1 -1
  210. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  211. package/docs/tutorials/02-connect-chatgpt.md +4 -4
  212. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
  213. package/docs/wiki/Implementation.md +1 -1
  214. package/package.json +46 -26
  215. package/packages/kernel/cognition/api/index.ts +6 -0
  216. package/packages/kernel/cognition/application/activation-service.ts +451 -0
  217. package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
  218. package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
  219. package/packages/kernel/cognition/application/memory-service.ts +64 -0
  220. package/packages/kernel/cognition/domain/learning.ts +106 -0
  221. package/packages/kernel/cognition/domain/memory.ts +254 -0
  222. package/packages/kernel/controller/api/index.ts +3 -0
  223. package/packages/kernel/controller/application/continuation-service.ts +281 -0
  224. package/packages/kernel/controller/application/controller-round-service.ts +41 -0
  225. package/packages/kernel/controller/application/controller-service.ts +30 -0
  226. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +529 -0
  227. package/packages/kernel/controller/domain/controller-round.ts +110 -0
  228. package/packages/kernel/controller/domain/execution-quality.ts +135 -0
  229. package/packages/kernel/controller/domain/types.ts +56 -0
  230. package/packages/kernel/controller/index.ts +1 -0
  231. package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
  232. package/packages/kernel/controller/infrastructure/controller-round-store.ts +1970 -0
  233. package/packages/kernel/controller/infrastructure/controller-session-store.ts +804 -0
  234. package/packages/kernel/controller/ports/controller-host.ts +18 -0
  235. package/packages/kernel/goal/api/index.ts +1 -0
  236. package/packages/kernel/goal/domain/types.ts +40 -0
  237. package/packages/kernel/goal/index.ts +1 -0
  238. package/packages/kernel/identity/api/index.ts +15 -0
  239. package/packages/kernel/identity/application/identity-service.ts +67 -0
  240. package/packages/kernel/identity/domain/activity-projection.ts +43 -0
  241. package/packages/kernel/identity/domain/capability-broker.ts +60 -0
  242. package/packages/kernel/identity/domain/grant.ts +72 -0
  243. package/packages/kernel/identity/domain/owned-resource.ts +116 -0
  244. package/packages/kernel/identity/domain/scope.ts +63 -0
  245. package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
  246. package/packages/kernel/identity/domain/types.ts +44 -0
  247. package/packages/kernel/identity/domain/user-request.ts +93 -0
  248. package/packages/kernel/identity/domain/workspace.ts +56 -0
  249. package/packages/kernel/identity/index.ts +1 -0
  250. package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
  251. package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
  252. package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
  253. package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
  254. package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
  255. package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
  256. package/packages/kernel/memory/api/index.ts +5 -0
  257. package/packages/kernel/memory/application/experience-service.ts +118 -0
  258. package/packages/kernel/memory/application/outcome-service.ts +77 -0
  259. package/packages/kernel/memory/domain/experience.ts +103 -0
  260. package/packages/kernel/memory/domain/operational-prior.ts +92 -0
  261. package/packages/kernel/memory/index.ts +1 -0
  262. package/packages/kernel/scheduler/api/index.ts +4 -0
  263. package/packages/kernel/scheduler/application/eligibility.ts +252 -0
  264. package/packages/kernel/scheduler/application/schedule-service.ts +27 -0
  265. package/packages/kernel/scheduler/application/settlement.ts +182 -0
  266. package/packages/kernel/scheduler/domain/schedule.ts +236 -0
  267. package/packages/kernel/scheduler/index.ts +1 -0
  268. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +805 -0
  269. package/packages/kernel/work/api/index.ts +16 -0
  270. package/packages/kernel/work/application/work-service.ts +123 -0
  271. package/packages/kernel/work/domain/check-receipt.ts +43 -0
  272. package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
  273. package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
  274. package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
  275. package/packages/kernel/work/domain/engineering-design.ts +286 -0
  276. package/packages/kernel/work/domain/engineering-profile.ts +238 -0
  277. package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
  278. package/packages/kernel/work/domain/execution-concurrency.ts +247 -0
  279. package/packages/kernel/work/domain/execution-snapshot.ts +2 -0
  280. package/packages/kernel/work/domain/implementation-review.ts +193 -0
  281. package/packages/kernel/work/domain/objective-graph.ts +119 -0
  282. package/packages/kernel/work/domain/project-engineering-contract.ts +224 -0
  283. package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
  284. package/{src/runtime/control-plane/facade/work-state-machine.ts → packages/kernel/work/domain/state-machine.ts} +37 -66
  285. package/packages/kernel/work/domain/types.ts +534 -0
  286. package/packages/kernel/work/index.ts +1 -0
  287. package/packages/kernel/work/infrastructure/work-contract-store.ts +1967 -0
  288. package/packages/kernel/work/ports/work-contract-store.ts +21 -0
  289. package/packages/plugin-runtime/browser/index.ts +3 -0
  290. package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
  291. package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
  292. package/packages/plugin-runtime/browser/session-authority.ts +62 -0
  293. package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
  294. package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
  295. package/packages/plugin-runtime/computer/index.ts +8 -0
  296. package/packages/plugin-runtime/computer/provider-error.ts +26 -0
  297. package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
  298. package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
  299. package/packages/plugin-runtime/computer/provider.ts +19 -0
  300. package/packages/plugin-runtime/computer/target-authority.ts +215 -0
  301. package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
  302. package/packages/plugin-runtime/external/index.ts +1 -0
  303. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +581 -0
  304. package/packages/plugin-runtime/index.ts +3 -0
  305. package/packages/protocols/browser/index.ts +1 -0
  306. package/packages/protocols/browser/session.ts +98 -0
  307. package/packages/protocols/computer/contract.ts +225 -0
  308. package/packages/protocols/computer/index.ts +1 -0
  309. package/packages/protocols/failure/contract.ts +158 -0
  310. package/packages/protocols/failure/index.ts +1 -0
  311. package/packages/protocols/handoff/index.ts +1 -0
  312. package/packages/protocols/handoff/status.ts +22 -0
  313. package/packages/protocols/mcp/execution-context.ts +11 -0
  314. package/packages/protocols/mcp/index.ts +2 -0
  315. package/packages/protocols/mcp/tool-contract.ts +42 -0
  316. package/packages/workflow-runtime/api/index.ts +2 -0
  317. package/packages/workflow-runtime/application/interpreter.ts +342 -0
  318. package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
  319. package/packages/workflow-runtime/index.ts +1 -0
  320. package/scripts/activate-source-baseline.ts +7 -9
  321. package/scripts/assemble-template.ts +1 -1
  322. package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
  323. package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
  324. package/scripts/canary-controller-semantic-wait.ts +183 -0
  325. package/scripts/canary-global-hook.sh +2 -2
  326. package/scripts/check-mcp-compatibility.ts +85 -7
  327. package/scripts/check-platform-support.mjs +28 -2
  328. package/scripts/check-public-docs.sh +1 -1
  329. package/scripts/check-repository-hygiene.mjs +92 -0
  330. package/scripts/check-runtime-architecture.mjs +1643 -127
  331. package/scripts/check-source-duplication.mjs +9 -1
  332. package/scripts/controller-ngrok-rotation.sh +5 -4
  333. package/scripts/create-project-dirs.sh +10 -74
  334. package/scripts/forge-local-recovery-helper.mjs +282 -0
  335. package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
  336. package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
  337. package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
  338. package/scripts/hook-shim.sh +2 -2
  339. package/scripts/init-project.sh +12 -70
  340. package/scripts/install-desktop-operator-registration.ts +4 -2
  341. package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
  342. package/scripts/install-standalone-recovery.ts +122 -10
  343. package/scripts/install-uu-remote-rescue-registration.ts +74 -0
  344. package/scripts/install-windows-host-recovery-registration.ts +22 -0
  345. package/scripts/lib/controller-home-env.sh +6 -3
  346. package/scripts/lib/project-init-lib.sh +42 -582
  347. package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
  348. package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
  349. package/scripts/open-source-audit-allowlist.txt +11 -1
  350. package/scripts/portability-bindings.mjs +70 -0
  351. package/scripts/run-bounded-command.mjs +60 -0
  352. package/scripts/run-bun-test-file.ts +22 -6
  353. package/scripts/run-governed-gate.ts +4 -2
  354. package/scripts/run-mobile-monitor-bridge.ts +0 -1
  355. package/scripts/smoke-control-plane-failure-domain.ts +94 -0
  356. package/scripts/smoke-mcp-http-runtime.ts +6 -1
  357. package/scripts/smoke-runtime-recovery.ts +13 -11
  358. package/scripts/smoke-schedule-engine.ts +24 -16
  359. package/scripts/smoke-unattended-transport-recovery.ts +223 -0
  360. package/scripts/smoke-user-request-continuation-restart.ts +232 -0
  361. package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
  362. package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
  363. package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
  364. package/scripts/smoke-workflow-supervisor.ts +103 -0
  365. package/scripts/test-governance.ts +51 -2
  366. package/scripts/verify-forge-runtime.sh +2 -1
  367. package/src/cli/agent-jobs/executor-health.ts +11 -34
  368. package/src/cli/agent-jobs/integration.ts +18 -49
  369. package/src/cli/agent-jobs/job-manager.ts +17 -4
  370. package/src/cli/chatgpt-browser/bind-server.ts +2 -1
  371. package/src/cli/chatgpt-browser/binding.ts +20 -10
  372. package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
  373. package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
  374. package/src/cli/chatgpt-browser/engine.ts +10 -11
  375. package/src/cli/chatgpt-browser/native-provider.ts +24 -83
  376. package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
  377. package/src/cli/chatgpt-browser/types.ts +1 -0
  378. package/src/cli/commands/bootstrap-control.ts +334 -0
  379. package/src/cli/commands/brain-assistant.ts +52 -0
  380. package/src/cli/commands/brain.ts +191 -40
  381. package/src/cli/commands/chatgpt.ts +32 -67
  382. package/src/cli/commands/computer.ts +437 -0
  383. package/src/cli/commands/doctor.ts +18 -1
  384. package/src/cli/commands/init-hook.ts +71 -61
  385. package/src/cli/commands/init.ts +3 -1
  386. package/src/cli/commands/mcp.ts +16 -10
  387. package/src/cli/commands/plugin.ts +38 -12
  388. package/src/cli/commands/recovery.ts +507 -80
  389. package/src/cli/commands/runtime.ts +12 -7
  390. package/src/cli/commands/setup-profile.ts +97 -39
  391. package/src/cli/commands/status.ts +2 -1
  392. package/src/cli/controller/check-runner.ts +500 -100
  393. package/src/cli/controller/completion-backlog.ts +3 -3
  394. package/src/cli/controller/completion-orchestrator.ts +4 -43
  395. package/src/cli/controller/completion-target.ts +3 -72
  396. package/src/cli/controller/context/source-materializer.ts +81 -100
  397. package/src/cli/controller/context/types.ts +7 -1
  398. package/src/cli/controller/context-pack.ts +28 -6
  399. package/src/cli/controller/execution-completion.ts +4 -7
  400. package/src/cli/controller/execution-policy.ts +36 -153
  401. package/src/cli/controller/governance.ts +2 -3
  402. package/src/cli/controller/issue-store.ts +4 -99
  403. package/src/cli/controller/launch-agents.ts +59 -18
  404. package/src/cli/controller/operational-plan.ts +24 -51
  405. package/src/cli/controller/runtime-config.ts +5 -1
  406. package/src/cli/controller/stuck-state-migration.ts +2 -3
  407. package/src/cli/controller/systemd-user.ts +148 -0
  408. package/src/cli/controller/types.ts +10 -70
  409. package/src/cli/controller/worklog.ts +67 -13
  410. package/src/cli/editing/edit-operation-contract.ts +92 -0
  411. package/src/cli/editing/edit-session.ts +290 -20
  412. package/src/cli/github/plugin.ts +17 -12
  413. package/src/cli/hook/route-registry.ts +3 -5
  414. package/src/cli/hook/runtime.ts +49 -3
  415. package/src/cli/index.ts +3 -0
  416. package/src/cli/local-bridge/connector-freshness.ts +2 -2
  417. package/src/cli/local-bridge/console-automations.ts +20 -15
  418. package/src/cli/local-bridge/console-view-models.ts +3 -12
  419. package/src/cli/local-bridge/facade-api.ts +105 -205
  420. package/src/cli/local-bridge/job-store.ts +1 -1
  421. package/src/cli/local-bridge/server.ts +48 -174
  422. package/src/cli/local-bridge/types.ts +0 -1
  423. package/src/cli/mcp/access-mode.ts +2 -132
  424. package/src/cli/mcp/access-tools.ts +2 -387
  425. package/src/cli/mcp/audit.ts +2 -32
  426. package/src/cli/mcp/auth.ts +2 -404
  427. package/src/cli/mcp/instructions.ts +2 -34
  428. package/src/cli/mcp/legacy-context.ts +2 -16
  429. package/src/cli/mcp/legacy-tool-service.ts +2 -5308
  430. package/src/cli/mcp/multi-repository.ts +2 -436
  431. package/src/cli/mcp/oauth-client-retirement.ts +74 -0
  432. package/src/cli/mcp/oauth.ts +2 -375
  433. package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
  434. package/src/cli/mcp/paths.ts +2 -140
  435. package/src/cli/mcp/policy.ts +2 -232
  436. package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
  437. package/src/cli/mcp/proxy-env.ts +2 -62
  438. package/src/cli/mcp/readiness-projection.ts +2 -24
  439. package/src/cli/mcp/redaction.ts +2 -21
  440. package/src/cli/mcp/repo.ts +2 -24
  441. package/src/cli/mcp/repository-tools.ts +2 -1613
  442. package/src/cli/mcp/server.ts +2 -951
  443. package/src/cli/mcp/setup.ts +2 -737
  444. package/src/cli/mcp/tool-capability-cache.ts +2 -87
  445. package/src/cli/mcp/tool-contract.ts +2 -14
  446. package/src/cli/mcp/tools.ts +2 -9
  447. package/src/cli/mcp/toolset-names.ts +2 -70
  448. package/src/cli/mcp/toolset-selection.ts +2 -41
  449. package/src/cli/mcp/toolset.ts +2 -336
  450. package/src/cli/mcp/transports/AGENTS.md +1 -10
  451. package/src/cli/mcp/transports/CLAUDE.md +1 -10
  452. package/src/cli/mcp/transports/http.ts +2 -1414
  453. package/src/cli/mcp/transports/session-registry.ts +2 -490
  454. package/src/cli/mcp/transports/stdio.ts +2 -8
  455. package/src/cli/mcp/types.ts +2 -41
  456. package/src/cli/mcp/umbrella-tools.ts +2 -117
  457. package/src/cli/repositories/command-executor.ts +91 -39
  458. package/src/cli/repositories/command-scope.ts +139 -4
  459. package/src/cli/repositories/controller-home.ts +167 -1
  460. package/src/cli/repositories/ephemeral-workspace.ts +20 -30
  461. package/src/cli/repositories/git-command-executor.ts +2 -2
  462. package/src/cli/repositories/identity.ts +13 -0
  463. package/src/cli/repositories/registry.ts +145 -60
  464. package/src/cli/repositories/repository-snapshot.ts +53 -7
  465. package/src/cli/repositories/runtime-storage.ts +27 -2
  466. package/src/cli/repositories/safe-patch.ts +34 -38
  467. package/src/cli/repositories/selected-path-actions.ts +22 -2
  468. package/src/cli/repositories/structured-git.ts +167 -27
  469. package/src/cli/repository/session-cache.ts +92 -6
  470. package/src/cli/runtime/helper-runner.ts +7 -0
  471. package/src/cli/runtime-invocation.ts +8 -1
  472. package/src/cli/tools/codegraph.ts +44 -25
  473. package/src/core/adoption/manifest-templates.ts +1 -1
  474. package/src/core/adoption/plan.ts +3 -26
  475. package/src/core/adoption/workflow-contract-plan.ts +17 -9
  476. package/src/effects/process-runner.ts +8 -14
  477. package/src/runtime/context/assistant-context.ts +167 -0
  478. package/src/runtime/context/assistant-work-context.ts +209 -0
  479. package/src/runtime/context/automatic-learning.ts +565 -0
  480. package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
  481. package/src/runtime/context/codegraph-read-provider.ts +55 -22
  482. package/src/runtime/context/codegraph-sidecar.cjs +8 -1
  483. package/src/runtime/context/cognitive-attention.ts +173 -0
  484. package/src/runtime/context/cognitive-skill-canary.ts +158 -0
  485. package/src/runtime/context/context-closure.ts +305 -0
  486. package/src/runtime/context/context-pack-process.ts +103 -0
  487. package/src/runtime/context/context-plane.ts +203 -0
  488. package/src/runtime/context/direct-controller-learning.ts +298 -0
  489. package/src/runtime/context/project-engineering-contract.ts +60 -0
  490. package/src/runtime/context/semantic-navigation-contract.ts +9 -0
  491. package/src/runtime/context/semantic-navigation.ts +6 -34
  492. package/src/runtime/context/typescript-navigation-process.ts +145 -0
  493. package/src/runtime/context/typescript-navigation.ts +89 -3
  494. package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
  495. package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
  496. package/src/runtime/control-plane/bootstrap/index.ts +5 -0
  497. package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
  498. package/src/runtime/control-plane/bootstrap/store.ts +21 -0
  499. package/src/runtime/control-plane/bootstrap/types.ts +87 -0
  500. package/src/runtime/control-plane/cleanup-artifact-retention.ts +531 -0
  501. package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
  502. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +282 -0
  503. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +380 -0
  504. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +113 -16
  505. package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
  506. package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
  507. package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
  508. package/src/runtime/control-plane/execution/retained-work-resume.ts +439 -102
  509. package/src/runtime/control-plane/execution/session-store.ts +31 -1
  510. package/src/runtime/control-plane/execution/validation.ts +34 -11
  511. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +87 -0
  512. package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
  513. package/src/runtime/control-plane/execution/work-completion-authority.ts +31 -44
  514. package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
  515. package/src/runtime/control-plane/execution/work-evidence-policy.ts +46 -53
  516. package/src/runtime/control-plane/execution/work-execution-support.ts +85 -164
  517. package/src/runtime/control-plane/execution/work-handle-authority.ts +349 -73
  518. package/src/runtime/control-plane/execution/work-handle-store.ts +55 -2
  519. package/src/runtime/control-plane/execution/work-head-settlement.ts +108 -0
  520. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +173 -28
  521. package/src/runtime/control-plane/execution/work-operation-service.ts +92 -40
  522. package/src/runtime/control-plane/execution/work-preparation-service.ts +145 -269
  523. package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
  524. package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
  525. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +543 -225
  526. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +59 -45
  527. package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
  528. package/src/runtime/control-plane/execution/work-verification-service.ts +662 -52
  529. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +205 -16
  530. package/src/runtime/control-plane/facade/capability-registry.ts +380 -44
  531. package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
  532. package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
  533. package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
  534. package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
  535. package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -22
  536. package/src/runtime/control-plane/facade/goal-workloop.ts +565 -1202
  537. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +145 -0
  538. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +266 -52
  539. package/src/runtime/control-plane/facade/index.ts +4 -3
  540. package/src/runtime/control-plane/facade/operation-digest.ts +34 -73
  541. package/src/runtime/control-plane/facade/plan-contract-store.ts +671 -245
  542. package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
  543. package/src/runtime/control-plane/facade/repository-work-admission.ts +31 -23
  544. package/src/runtime/control-plane/facade/requirement-authority.ts +121 -0
  545. package/src/runtime/control-plane/facade/requirement-board.ts +9 -4
  546. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +85 -0
  547. package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
  548. package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
  549. package/src/runtime/control-plane/facade/suggested-actions.ts +15 -15
  550. package/src/runtime/control-plane/facade/types.ts +218 -620
  551. package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
  552. package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
  553. package/src/runtime/control-plane/generated-cache-retention.ts +336 -19
  554. package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
  555. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +13 -2
  556. package/src/runtime/control-plane/global-scheduler/maintenance.ts +274 -84
  557. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
  558. package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
  559. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -2
  560. package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
  561. package/src/runtime/control-plane/global-scheduler/scheduler.ts +227 -109
  562. package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
  563. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
  564. package/src/runtime/control-plane/governance/access-policy.ts +19 -2
  565. package/src/runtime/control-plane/governance/authorization.ts +20 -3
  566. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +160 -0
  567. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
  568. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +139 -670
  569. package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
  570. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +25 -6
  571. package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -194
  572. package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
  573. package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
  574. package/src/runtime/control-plane/persistence/experience-store.ts +302 -0
  575. package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
  576. package/src/runtime/control-plane/persistence/requirement-store.ts +230 -80
  577. package/src/runtime/control-plane/persistence/sqlite-store.ts +654 -78
  578. package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
  579. package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
  580. package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
  581. package/src/runtime/control-plane/release-retention.ts +154 -26
  582. package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
  583. package/src/runtime/control-plane/runtime-cleanup.ts +844 -38
  584. package/src/runtime/control-plane/runtime-generation.ts +6 -0
  585. package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
  586. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +479 -0
  587. package/src/runtime/control-plane/workspace/workspace-store.ts +249 -0
  588. package/src/runtime/diagnostics/incident-repair.ts +573 -0
  589. package/src/runtime/diagnostics/mcp-timing.ts +239 -6
  590. package/src/runtime/diagnostics/performance.ts +6 -14
  591. package/src/runtime/diagnostics/read-only-tool.ts +1 -1
  592. package/src/runtime/evidence/artifact-store.ts +216 -35
  593. package/src/runtime/evidence/evidence-store.ts +112 -8
  594. package/src/runtime/evidence/operational-shadow.ts +38 -0
  595. package/src/runtime/evidence/output-handle-index.ts +225 -0
  596. package/src/runtime/evidence/process-check-receipt.ts +5 -43
  597. package/src/runtime/evidence/result-store.ts +158 -44
  598. package/src/runtime/evidence/sensitive-output.ts +4 -4
  599. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
  600. package/src/runtime/execution/jobs/store.ts +124 -6
  601. package/src/runtime/execution/managed-workspace.ts +112 -5
  602. package/src/runtime/execution/process-runtime/canary.ts +19 -5
  603. package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
  604. package/src/runtime/execution/process-runtime/check-facade.ts +24 -10
  605. package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
  606. package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
  607. package/src/runtime/execution/process-runtime/check-result.ts +177 -2
  608. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +58 -9
  609. package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
  610. package/src/runtime/execution/process-runtime/command-facade.ts +117 -114
  611. package/src/runtime/execution/process-runtime/gc.ts +15 -2
  612. package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
  613. package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
  614. package/src/runtime/execution/process-runtime/index.ts +4 -0
  615. package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
  616. package/src/runtime/execution/process-runtime/lightweight-managed.ts +283 -73
  617. package/src/runtime/execution/process-runtime/persisted-check.ts +130 -23
  618. package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
  619. package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
  620. package/src/runtime/execution/process-runtime/resource-claims.ts +125 -64
  621. package/src/runtime/execution/process-runtime/runtime.ts +167 -17
  622. package/src/runtime/execution/process-runtime/store.ts +30 -2
  623. package/src/runtime/execution/process-runtime/types.ts +27 -0
  624. package/src/runtime/execution/workers/executor.ts +18 -19
  625. package/src/runtime/execution/workers/worker-entry.ts +1 -1
  626. package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
  627. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
  628. package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
  629. package/src/runtime/gateway/mcp/process-tools.ts +2 -289
  630. package/src/runtime/gateway/mcp/router.ts +2 -1125
  631. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
  632. package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6655
  633. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
  634. package/src/runtime/maintenance/cleanup.ts +38 -24
  635. package/src/runtime/platform/browser-product-discovery.ts +174 -0
  636. package/src/runtime/platform/computer-platform.ts +14 -0
  637. package/src/runtime/platform/executable-discovery.ts +54 -0
  638. package/src/runtime/platform/fetch-http-server.ts +90 -0
  639. package/src/runtime/platform/service-inventory.ts +130 -0
  640. package/src/runtime/platform/service-manager.ts +146 -0
  641. package/src/runtime/platform/windows-host-discovery.ts +118 -0
  642. package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
  643. package/src/runtime/plugins/action-application.ts +192 -0
  644. package/src/runtime/plugins/app-store-connect-adapter.ts +47 -35
  645. package/src/runtime/plugins/browser-adapter.ts +898 -135
  646. package/src/runtime/plugins/browser-automation-service.ts +33 -34
  647. package/src/runtime/plugins/browser-macos-bridge.ts +386 -46
  648. package/src/runtime/plugins/browser-manifest-surface.ts +75 -17
  649. package/src/runtime/plugins/browser-native-session-service.ts +64 -14
  650. package/src/runtime/plugins/browser-provider-registry.ts +2 -109
  651. package/src/runtime/plugins/browser-registration.ts +32 -0
  652. package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
  653. package/src/runtime/plugins/browser-runtime.ts +55 -10
  654. package/src/runtime/plugins/browser-session-authority.ts +101 -291
  655. package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
  656. package/src/runtime/plugins/browser-session-store.ts +243 -80
  657. package/src/runtime/plugins/browser-session-types.ts +2 -98
  658. package/src/runtime/plugins/capability-authorization-grants.ts +272 -59
  659. package/src/runtime/plugins/catalog-trust.ts +82 -0
  660. package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
  661. package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
  662. package/src/runtime/plugins/computer-registration.ts +1050 -0
  663. package/src/runtime/plugins/config-store.ts +119 -0
  664. package/src/runtime/plugins/desktop-operator-external-policy.ts +964 -0
  665. package/src/runtime/plugins/desktop-operator-registration.ts +123 -12
  666. package/src/runtime/plugins/errors.ts +8 -1
  667. package/src/runtime/plugins/external-adapter.ts +82 -323
  668. package/src/runtime/plugins/external-provider-policy.ts +48 -0
  669. package/src/runtime/plugins/external-registration.ts +30 -2
  670. package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
  671. package/src/runtime/plugins/external-unix-socket.ts +64 -193
  672. package/src/runtime/plugins/first-party-external-registration.ts +163 -0
  673. package/src/runtime/plugins/first-party-registry.ts +3 -1
  674. package/src/runtime/plugins/github-adapter.ts +1 -2
  675. package/src/runtime/plugins/gmail-adapter.ts +29 -8
  676. package/src/runtime/plugins/google-calendar-adapter.ts +8 -8
  677. package/src/runtime/plugins/google-shared.ts +45 -53
  678. package/src/runtime/plugins/google-tasks-adapter.ts +8 -8
  679. package/src/runtime/plugins/ios-adapter.ts +2 -3
  680. package/src/runtime/plugins/lightweight-action.ts +208 -2
  681. package/src/runtime/plugins/local-system-adapter.ts +192 -2
  682. package/src/runtime/plugins/macos-capability-broker.ts +15 -105
  683. package/src/runtime/plugins/managed-process-adapter.ts +12 -5
  684. package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
  685. package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
  686. package/src/runtime/plugins/resend-adapter.ts +15 -24
  687. package/src/runtime/plugins/store.ts +1003 -387
  688. package/src/runtime/plugins/types.ts +28 -1
  689. package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
  690. package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
  691. package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
  692. package/src/runtime/plugins/xiaohongshu-publish.ts +102 -430
  693. package/src/runtime/projections/controller-context.ts +12 -6
  694. package/src/runtime/projections/git-status-sampler.ts +73 -22
  695. package/src/runtime/projections/invalidation.ts +7 -3
  696. package/src/runtime/projections/materialized-view.ts +93 -43
  697. package/src/runtime/recovery/actions.ts +3 -3
  698. package/src/runtime/recovery/capability-action-application.ts +229 -0
  699. package/src/runtime/recovery/index.ts +3 -0
  700. package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
  701. package/src/runtime/recovery/local-jobs-repair.ts +14 -14
  702. package/src/runtime/recovery/maintenance-executor.ts +701 -168
  703. package/src/runtime/recovery/quarantine-retention.ts +256 -0
  704. package/src/runtime/recovery/store.ts +29 -11
  705. package/src/runtime/release/identity.ts +6 -0
  706. package/src/runtime/release/release-coordinator.ts +296 -0
  707. package/src/runtime/release/release-session.ts +716 -0
  708. package/src/runtime/release/release-state-migration.ts +25 -0
  709. package/src/runtime/resources/leases/store.ts +56 -25
  710. package/src/runtime/root/active-execution-power-assertion.ts +139 -0
  711. package/src/runtime/root/assistant-learning-loop.ts +45 -0
  712. package/src/runtime/root/browser-session-composition.ts +59 -0
  713. package/src/runtime/root/computer-composition.ts +376 -0
  714. package/src/runtime/root/computer-target-composition.ts +16 -0
  715. package/src/runtime/root/computer-target-persistence.ts +39 -0
  716. package/src/runtime/root/controller-progression-composition.ts +192 -0
  717. package/src/runtime/root/controller-round-composition.ts +126 -0
  718. package/src/runtime/root/controller-services.ts +2 -0
  719. package/src/runtime/root/deployment-topology.ts +106 -0
  720. package/src/runtime/root/entry.ts +8 -6
  721. package/src/runtime/root/gateway-adapter.ts +3 -3
  722. package/src/runtime/root/known-good-recovery.ts +184 -0
  723. package/src/runtime/root/local-bridge.ts +5 -3
  724. package/src/runtime/root/mcp-transport.ts +116 -23
  725. package/src/runtime/root/ownership.ts +261 -9
  726. package/src/runtime/root/package-connector-service.ts +212 -74
  727. package/src/runtime/root/package-runtime-release.ts +199 -33
  728. package/src/runtime/root/package-runtime-service.ts +77 -108
  729. package/src/runtime/root/release-execution-canary.ts +103 -0
  730. package/src/runtime/root/release-loader.ts +150 -0
  731. package/src/runtime/root/release-manifest.ts +185 -7
  732. package/src/runtime/root/release-materialize.ts +474 -31
  733. package/src/runtime/root/release-store.ts +425 -37
  734. package/src/runtime/root/runtime-lane.ts +191 -0
  735. package/src/runtime/root/runtime.ts +379 -32
  736. package/src/runtime/root/scheduled-controller-composition.ts +76 -0
  737. package/src/runtime/root/scheduler.ts +12 -4
  738. package/src/runtime/root/service-runner.ts +3 -3
  739. package/src/runtime/root/service.ts +73 -45
  740. package/src/runtime/root/status.ts +49 -0
  741. package/src/runtime/root/types.ts +55 -3
  742. package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
  743. package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
  744. package/src/runtime/root/write-fence.ts +50 -2
  745. package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
  746. package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
  747. package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
  748. package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
  749. package/src/runtime/shared/local-bridge-surface.ts +1 -1
  750. package/src/runtime/shared/process-environment.ts +36 -10
  751. package/src/runtime/shared/response-budget.ts +1 -3
  752. package/src/runtime/shared/storage-capacity.ts +68 -0
  753. package/src/runtime/standalone-recovery/controller-home-migration.ts +1108 -0
  754. package/src/runtime/standalone-recovery/core.ts +4609 -495
  755. package/src/runtime/standalone-recovery/entry.ts +642 -81
  756. package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
  757. package/src/runtime/standalone-recovery/installer.ts +399 -177
  758. package/src/runtime/standalone-recovery/mcp-server.ts +79 -0
  759. package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
  760. package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
  761. package/src/runtime/standalone-recovery/performance.ts +173 -0
  762. package/src/runtime/standalone-recovery/release.ts +58 -5
  763. package/src/runtime/standalone-recovery/service-labels.ts +5 -2
  764. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
  765. package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
  766. package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
  767. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +11 -4
  768. package/src/runtime/workflow/schedules/engine.ts +298 -484
  769. package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
  770. package/src/runtime/workflow/schedules/settlement.ts +2 -191
  771. package/src/runtime/workflow/schedules/store.ts +2 -607
  772. package/src/runtime/workflow/schedules/types.ts +2 -127
  773. package/src/runtime/workflow/schedules/work-continuation.ts +220 -16
  774. package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
  775. package/src/runtime/workflows/runtime.ts +482 -0
  776. package/src/testing/test-governance.ts +95 -30
  777. package/supervisor/chatgpt-conversation.ts +23 -0
  778. package/supervisor/client.ts +201 -0
  779. package/supervisor/control-plane.ts +868 -0
  780. package/supervisor/entry.ts +15 -0
  781. package/supervisor/forge-validators.ts +80 -0
  782. package/supervisor/index.ts +5 -0
  783. package/supervisor/native-browser-adapter.ts +786 -0
  784. package/supervisor/native-messaging/host.ts +39 -0
  785. package/supervisor/native-messaging/manifest.ts +26 -0
  786. package/supervisor/native-messaging/protocol.ts +27 -0
  787. package/supervisor/paths.ts +18 -0
  788. package/supervisor/protocol.ts +202 -0
  789. package/supervisor/server.ts +404 -0
  790. package/supervisor/store.ts +1451 -0
  791. package/supervisor/tsconfig.json +8 -0
  792. package/supervisor/types.ts +255 -0
  793. package/assets/templates/helpers/archive-architecture-request.sh +0 -244
  794. package/docs/architecture/history/README.md +0 -7
  795. package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
  796. package/docs/architecture/history/global-hook-runtime.md +0 -166
  797. package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
  798. package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  799. package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
  800. package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
  801. package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
  802. package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
  803. package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  804. package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  805. package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  806. package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
  807. package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
  808. package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  809. package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  810. package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
  811. package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  812. package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  813. package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  814. package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  815. package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
  816. package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
  817. package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
  818. package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
  819. package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
  820. package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  821. package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  822. package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
  823. package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
  824. package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
  825. package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
  826. package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
  827. package/docs/architecture/requests/archive/2026/root.md +0 -68
  828. package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
  829. package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
  830. package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
  831. package/scripts/archive-architecture-request.sh +0 -4
  832. package/scripts/benchmark-background-check-overlap.ts +0 -228
  833. package/scripts/benchmark-controller-hotpath.ts +0 -212
  834. package/scripts/benchmark-controller-round2.ts +0 -139
  835. package/scripts/benchmark-ios-agent-device.ts +0 -514
  836. package/scripts/benchmark-mcp-host.sh +0 -117
  837. package/scripts/benchmark-mcp-transport.ts +0 -170
  838. package/scripts/benchmark-quality-harness.ts +0 -146
  839. package/scripts/benchmark-route-session-concurrency.ts +0 -915
  840. package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
  841. package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
  842. package/scripts/benchmark-thin-harness.ts +0 -251
  843. package/scripts/benchmark-typescript-navigation.ts +0 -186
  844. package/scripts/benchmark-work-efficiency.ts +0 -334
  845. package/scripts/build-controller-ui.ts +0 -28
  846. package/scripts/check-npm-release.sh +0 -32
  847. package/scripts/check-public-release-surface.sh +0 -19
  848. package/scripts/check-release-published.sh +0 -57
  849. package/scripts/check-release-readiness.sh +0 -37
  850. package/scripts/check-release-version.mjs +0 -74
  851. package/scripts/check-tarball-install-smoke.sh +0 -67
  852. package/scripts/package-source-archive.sh +0 -147
  853. package/scripts/public-release-files.txt +0 -39
  854. package/scripts/public-release.ts +0 -174
  855. package/scripts/publish-release-tarball.sh +0 -27
  856. package/scripts/route-nl-vs-ts-eval.ts +0 -679
  857. package/scripts/run-skill-evals.ts +0 -1022
  858. package/scripts/stage-runtime-release.ts +0 -39
  859. package/src/cli/controller/progress.ts +0 -656
  860. package/src/cli/controller/work-mode.ts +0 -181
  861. package/src/cli/local-bridge/console-assets.ts +0 -17
  862. package/src/cli/local-bridge/dashboard.ts +0 -17
  863. package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
  864. package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
  865. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
  866. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
  867. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
  868. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
  869. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
  870. package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
  871. package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
  872. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
  873. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
  874. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
  875. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
  876. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
  877. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
  878. package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
  879. package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
  880. package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
  881. package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
  882. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
  883. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
  884. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
  885. package/src/cli/local-bridge/frontend/src/styles.css +0 -28
  886. package/src/cli/local-bridge/frontend/src/types.ts +0 -15
  887. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
  888. package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
  889. package/src/cli/local-bridge/ui-dist/app.css +0 -1
  890. package/src/cli/local-bridge/ui-dist/app.js +0 -11228
  891. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -385
  892. package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -1913
  893. package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
  894. package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -449
  895. package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -1118
  896. package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -93
  897. package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
  898. package/src/runtime/control-plane/routing/index.ts +0 -1
  899. package/src/runtime/control-plane/routing/route-policy.ts +0 -382
  900. package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
@@ -0,0 +1,1682 @@
1
+ import { bindRepositoryEntities } from '../../../src/cli/repositories/entity-migration';
2
+ import { bootstrapLocalProject, diagnoseLatestLocalProjectSource } from '../../../src/cli/repositories/local-project-onboarding';
3
+ import { resolveEphemeralWorkspaceTarget } from '../../../src/cli/repositories/ephemeral-workspace';
4
+ import { commandExecutionScopeKey, type RepositoryCommandScopeTarget } from '../../../src/cli/repositories/command-scope';
5
+ import { executionIdentityForWork, type ResolvedExecutionIdentity } from '../../../src/runtime/control-plane/execution/execution-identity';
6
+ import { assertNoBoundExecutionSessionMutation, resolveClaimedRepositoryWorkId, resolveExplicitClaimedRepositoryWork, type RepositoryWorkAttributionCaller } from '../../../src/runtime/control-plane/execution/repository-work-attribution';
7
+ import { getWorkContract, promoteWorkToRepositoryChange, semanticWorkState, type WorkContract } from '../../../packages/kernel/work/api';
8
+ import { assertCanonicalRepositoryMutationWorkHandleAvailable, ensureRepositoryMutationWorkHandle, markRepositoryMutationStarted } from '../../../src/runtime/control-plane/execution/work-handle-authority';
9
+ import { readWorkHandle } from '../../../src/runtime/control-plane/execution/work-handle-store';
10
+ import { ensureRunningRepositoryWorkCheckout } from '../../../src/runtime/control-plane/execution/retained-work-resume';
11
+ import { executeRepositoryCommand, previewRepositoryCommandExecution } from '../../../src/cli/repositories/command-executor';
12
+ import { withControllerLock } from '../../../src/cli/repositories/locks';
13
+ import {
14
+ disableRepository,
15
+ findIdenticalRepositoryRegistration,
16
+ getRepository,
17
+ listRepositories,
18
+ reconcileRepositoryCheckouts,
19
+ refreshRepository,
20
+ registerRepository,
21
+ removeRepository,
22
+ repositorySummary,
23
+ resolveRepositorySelection,
24
+ updateRepository,
25
+ validateRepository,
26
+ } from '../../../src/cli/repositories/registry';
27
+ import { buildControllerWorkbench } from '../../../src/cli/repositories/workbench';
28
+ import { applySafePatch, buildSafePatchPlan } from '../../../src/cli/repositories/safe-patch';
29
+ import { EDIT_OPERATION_INPUT_SCHEMA } from '../../../src/cli/editing/edit-operation-contract';
30
+ import { getEditSession, getEditSessionDiff, type EditSessionBinding } from '../../../src/cli/editing/edit-session';
31
+ import { buildSyncOperationDigest, classifyUserFacingError } from '../../../src/runtime/control-plane/facade/operation-digest';
32
+ import {
33
+ repositoryGitCommit,
34
+ repositoryGitCreateBranch,
35
+ repositoryGitDeleteBranch,
36
+ repositoryGitDiff,
37
+ repositoryGitFinishWorkflow,
38
+ repositoryGitMergeBranch,
39
+ repositoryGitStatus,
40
+ repositoryGitSwitchBranch,
41
+ } from '../../../src/cli/repositories/structured-git';
42
+ import {
43
+ readRepositoryGitStatusSample,
44
+ writeRepositoryGitStatusSample,
45
+ } from '../../../src/runtime/projections/git-status-sampler';
46
+ import {
47
+ executeLightweightLanes,
48
+ executeRepositoryBatch,
49
+ integratePatchProposals,
50
+ listFastReceipts,
51
+ readFastReceipt,
52
+ routeExecution,
53
+ } from '../../../src/runtime/execution/thin-harness';
54
+ import {
55
+ classifyRawGitCommitScope,
56
+ classifyRepositoryCommandRoute,
57
+ executeRepositoryCommandViaProcessRuntime,
58
+ } from '../../../src/runtime/execution/process-runtime/command-facade';
59
+ import { classifyRepositoryCommand } from '../../../src/cli/repositories/command-classifier';
60
+ import { normalizeRepositoryCommand } from '../../../src/cli/repositories/command-normalization';
61
+ import { readRepositoryRange } from '../../../src/cli/repository/inspector';
62
+ import { getMcpPolicy } from '../policy';
63
+ import { redactMcpText } from '../redaction';
64
+ import type { CallToolResult, McpToolDefinition } from '../../../packages/protocols/mcp/tool-contract';
65
+ import {
66
+ boundUtf8,
67
+ compactCommandOutput,
68
+ compactErrorMessage,
69
+ compactRoutingSummary,
70
+ RESPONSE_BUDGET,
71
+ } from '../../../src/runtime/shared/response-budget';
72
+
73
+ export type RepositoryToolResult = CallToolResult;
74
+
75
+ function definition(
76
+ name: string,
77
+ description: string,
78
+ properties: Record<string, unknown>,
79
+ required: string[] = [],
80
+ readOnlyHint = false,
81
+ destructiveHint = false,
82
+ ): McpToolDefinition {
83
+ return {
84
+ name,
85
+ description,
86
+ inputSchema: {
87
+ type: 'object',
88
+ properties,
89
+ ...(required.length > 0 ? { required } : {}),
90
+ additionalProperties: false,
91
+ },
92
+ annotations: { readOnlyHint, openWorldHint: false, destructiveHint },
93
+ };
94
+ }
95
+
96
+ const repoId = { type: 'string', description: 'Stable Repository Registry repoId.' };
97
+
98
+ export const repositoryToolDefinitions: McpToolDefinition[] = [
99
+ definition('repository_register', 'Register a Git repository with the Controller.', {
100
+ path: { type: 'string' },
101
+ display_name: { type: 'string' },
102
+ remote_url: { type: 'string' },
103
+ default_branch: { type: 'string' },
104
+ detail_level: { type: 'string', enum: ['summary', 'detail'], description: 'Defaults to summary; detail returns full checkout and migration evidence.' },
105
+ }, ['path']),
106
+ definition('repository_latest_source_diagnose', 'Read-only diagnosis that compares sibling project directories and recommends the latest usable source tree.', {
107
+ path: { type: 'string', description: 'Absolute local project path.' },
108
+ repo_id: repoId,
109
+ }, [], true),
110
+ definition('repository_bootstrap_local_project', 'Safely initialize and optionally register a trusted non-Git local project directory.', {
111
+ path: { type: 'string', description: 'Absolute local project path.' },
112
+ display_name: { type: 'string' },
113
+ default_branch: { type: 'string' },
114
+ mode: { type: 'string', enum: ['init_git_only', 'init_git_and_register', 'replace_registration'] },
115
+ replace_registered_repo_id: repoId,
116
+ confirm_authorization: { type: 'boolean', description: 'Must be true to authorize Git initialization and registration.' },
117
+ }, ['path', 'confirm_authorization'], false, true),
118
+ definition('repository_list', 'List registered repositories.', {
119
+ include_removed: { type: 'boolean' },
120
+ }, [], true),
121
+ definition('repository_get', 'Inspect one registered repository.', {
122
+ repo_id: repoId,
123
+ include_removed: { type: 'boolean' },
124
+ detail_level: { type: 'string', enum: ['summary', 'detail'], description: 'Defaults to summary; detail returns the complete checkout history.' },
125
+ }, ['repo_id'], true),
126
+ definition('read_repository_file', 'Read a line range from one policy-readable repository file.', {
127
+ repo_id: repoId,
128
+ checkout_id: { type: 'string' },
129
+ path: { type: 'string' },
130
+ start_line: { type: 'number' },
131
+ end_line: { type: 'number' },
132
+ }, ['path'], true),
133
+ definition('repository_validate', 'Validate repository identity and migrate legacy ownership.', {
134
+ repo_id: repoId,
135
+ detail_level: { type: 'string', enum: ['summary', 'detail'], description: 'Defaults to summary; detail returns full migration evidence.' },
136
+ }, ['repo_id']),
137
+ definition('repository_refresh', 'Refresh repository Git and checkout metadata.', {
138
+ repo_id: repoId,
139
+ detail_level: { type: 'string', enum: ['summary', 'detail'], description: 'Defaults to summary; detail returns full checkout and migration evidence.' },
140
+ }, ['repo_id']),
141
+ definition('repository_update', 'Update mutable repository metadata.', {
142
+ repo_id: repoId,
143
+ display_name: { type: 'string' },
144
+ default_branch: { type: 'string' },
145
+ enabled: { type: 'boolean' },
146
+ }, ['repo_id']),
147
+ definition('repository_disable', 'Disable new execution while retaining audit history.', {
148
+ repo_id: repoId,
149
+ }, ['repo_id']),
150
+ definition('repository_remove', 'Soft-remove a repository while retaining audit history.', {
151
+ repo_id: repoId,
152
+ }, ['repo_id']),
153
+ definition('repository_workbench', 'Return Workbench state or invoke one bounded Thin Harness operation without adding top-level MCP tools. Prefer batch_execute for multi-step status→search→read→diff or read→patch→check flows so ChatGPT pays one routing/receipt ownership cycle.', {
154
+ repo_id: repoId,
155
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repository-scoped operations.' },
156
+ include_removed: { type: 'boolean' },
157
+ operation: {
158
+ type: 'string',
159
+ enum: ['summary', 'batch_execute', 'lanes_execute', 'lanes_integrate', 'fast_receipt_get', 'fast_receipt_list', 'execution_route'],
160
+ description: 'Defaults to summary. Use batch_execute for multi-step repository capability execution and lanes_execute for limited parallel reads. Workflow/mode selection belongs to the model/user, not Workbench.',
161
+ },
162
+ payload: {
163
+ type: 'object',
164
+ description: 'Operation-specific bounded arguments. Batch write operations should include request_id. For batch_execute: { steps:[{id?, kind, input}], stop_on_error?, allowed_paths?, timeout_ms?, request_id?, purpose? }.' ,
165
+ additionalProperties: true,
166
+ },
167
+ }),
168
+ definition('repository_git_status', 'Return a structured Git status snapshot for the selected repository checkout.', {
169
+ repo_id: repoId,
170
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
171
+ refresh: { type: 'boolean', description: 'When true, explicitly refresh the Git status sample. The default public hot path only reads the latest daemon sample.' },
172
+ }, [], true),
173
+
174
+ definition('repository_git_diff', 'Return a bounded structured git diff for the selected repository, optionally staged and path-scoped.', {
175
+ repo_id: repoId,
176
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
177
+ staged: { type: 'boolean' },
178
+ paths: { type: 'array', items: { type: 'string' } },
179
+ max_bytes: { type: 'number' },
180
+ }, [], true),
181
+ definition('repository_git_create_branch', 'Create a safe local Git branch, optionally switching to it.', {
182
+ repo_id: repoId,
183
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
184
+ branch: { type: 'string' },
185
+ start_point: { type: 'string' },
186
+ switch_to: { type: 'boolean', description: 'Defaults to true. False creates the branch without switching.' },
187
+ }, ['branch']),
188
+ definition('repository_git_switch_branch', 'Switch to a safe local Git branch name.', {
189
+ repo_id: repoId,
190
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
191
+ branch: { type: 'string' },
192
+ }, ['branch']),
193
+ definition('repository_git_merge_branch', 'Merge a branch into the current branch using --ff-only by default.', {
194
+ repo_id: repoId,
195
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
196
+ branch: { type: 'string' },
197
+ no_ff: { type: 'boolean', description: 'Use --no-ff instead of the default --ff-only.' },
198
+ }, ['branch']),
199
+ definition('repository_git_delete_branch', 'Delete a safe local Git branch after it has been merged or intentionally discarded.', {
200
+ repo_id: repoId,
201
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
202
+ branch: { type: 'string' },
203
+ force: { type: 'boolean' },
204
+ }, ['branch']),
205
+
206
+ definition('repository_git_commit', 'Stage optional explicit paths and commit through a structured Git action.', {
207
+ repo_id: repoId,
208
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
209
+ message: { type: 'string' },
210
+ paths: { type: 'array', items: { type: 'string' } },
211
+ allow_empty: { type: 'boolean' },
212
+ }, ['message']),
213
+ definition('repository_git_finish_workflow', 'Finish the current feature workflow: require clean tree, switch to target, merge feature branch, and delete it by default.', {
214
+ repo_id: repoId,
215
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
216
+ feature_branch: { type: 'string', description: 'Defaults to the current branch.' },
217
+ target_branch: { type: 'string', description: 'Defaults to repository defaultBranch or main.' },
218
+ delete_branch: { type: 'boolean', description: 'Defaults to true.' },
219
+ no_ff: { type: 'boolean', description: 'Use --no-ff instead of the default --ff-only.' },
220
+ }, []),
221
+ definition('repository_safe_patch_plan', 'Plan a deterministic chunked repository patch with fresh file fingerprints before applying.', {
222
+ repo_id: repoId,
223
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
224
+ operations: { type: 'array', items: EDIT_OPERATION_INPUT_SCHEMA, description: 'Typed edit operations. Use type=create for a new path; write/replace/insert/prepend/append/delete require an existing target and preserve strict preconditions.' },
225
+ chunk_size: { type: 'number', description: 'Maximum operations per deterministic chunk. Capped at 100.' },
226
+ }, ['operations'], true),
227
+ definition('repository_safe_patch_apply', 'Apply one coherent deterministic edit batch and return bounded review evidence. Checks are opt-in: pass check_ids only when the batch is stable. Long checks return managed Process handles instead of blocking the MCP call; use validation_only with the returned session/request ids to join later without replaying the patch.', {
228
+ repo_id: repoId,
229
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
230
+ work_id: { type: 'string', description: 'Optional durable Work identity. Workflow controllers should pass the exact claimed Work id so attribution survives transient MCP transport sessions.' },
231
+ session_id: { type: 'string', description: 'Existing edit session id. Required for validation_only; otherwise omit to create one.' },
232
+ purpose: { type: 'string', description: 'Purpose for a newly created edit session.' },
233
+ operations: { type: 'array', items: EDIT_OPERATION_INPUT_SCHEMA, description: 'Typed edit operations. Use type=create for a new path; write/replace/insert/prepend/append/delete require an existing target and preserve strict preconditions. Required unless validation_only=true.' },
234
+ chunk_size: { type: 'number', description: 'Maximum operations per deterministic chunk. Capped at 100.' },
235
+ expected_revision: { type: 'number', description: 'Expected starting edit-session revision.' },
236
+ allowed_paths: { type: 'array', items: { type: 'string' }, description: 'Optional allowed path globs for a newly created session.' },
237
+ continue_on_error: { type: 'boolean', description: 'Continue applying later independent chunks after a failed chunk. Defaults to false.' },
238
+ refresh_fingerprints: { type: 'boolean', description: 'Refresh file fingerprints before every chunk. Defaults to true.' },
239
+ recover_stale_session: { type: 'boolean', description: 'For new sessions, recover stale edit-session fingerprints into a fresh session. Defaults to true.' },
240
+ check_ids: { type: 'array', items: { type: 'string' }, description: 'Optional focused validation for this stable coherent edit batch. Omit to edit without running tests.' },
241
+ check_timeout_ms: { type: 'number', description: 'Per-check execution budget. Long checks still return managed handles immediately.' },
242
+ lease_wait_ms: { type: 'number', description: 'Bounded resource-lease wait for conflicting validation lanes.' },
243
+ validation_request_id: { type: 'string', description: 'Stable validation identity returned by a prior edit+validation call; reuse with validation_only to join without replaying edits.' },
244
+ validation_only: { type: 'boolean', description: 'When true, do not edit. Reconcile/reuse the existing session validation Processes identified by session_id/check_ids/validation_request_id.' },
245
+ apply_mode: { type: 'string', enum: ['sync', 'async'], description: 'Defaults to sync for interactive development. Set async only for a separately durable operation.' },
246
+ }),
247
+ definition('repository_command_preview', 'Preview one repository-scoped local command with classification, approval token, and Git snapshots.', {
248
+ repo_id: repoId,
249
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
250
+ command: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' }, minItems: 1 }], description: 'Legacy shell string or typed argv array. Typed argv executes without a shell.' },
251
+ cwd: { type: 'string', description: 'Optional root-relative working directory.' },
252
+ workspace_root: { type: 'string', description: 'Absolute existing local directory used as an ephemeral execution root. Mutually exclusive with repo_id/checkout_id; does not register or initialize the directory.' },
253
+ }, ['command'], true),
254
+ definition('repository_command_execute', 'Compatibility facade for commands whose domain context is a repository/workspace. Repository targeting, Git/source mutation fences, and compatibility response fields live here; Process Runtime owns command execution and process identity. New broad host-local command callers should use process_exec. Use rh_context for routine code discovery/reading; shell exploration is fallback-only.', {
255
+ repo_id: repoId,
256
+ checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
257
+ work_id: { type: 'string', description: 'Optional durable Work identity. Workflow controllers should pass the exact claimed Work id so attribution survives transient MCP transport sessions.' },
258
+ command: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' }, minItems: 1 }], description: 'Legacy shell string or typed argv array. Typed argv executes without a shell.' },
259
+ cwd: { type: 'string', description: 'Optional root-relative working directory.' },
260
+ workspace_root: { type: 'string', description: 'Absolute existing local directory used as an ephemeral execution root. Mutually exclusive with repo_id/checkout_id; does not register or initialize the directory.' },
261
+ approval_token: { type: 'string', description: 'Exact approval token returned by repository_command_preview.' },
262
+ approval_request_id: { type: 'string', description: 'Resolved approvalRequestId returned by approval_resolve.' },
263
+ timeout_ms: { type: 'number', description: 'Optional execution timeout in milliseconds.' },
264
+ max_output_bytes: { type: 'number', description: 'Optional cap for captured stdout/stderr.' },
265
+ }, ['command']),
266
+
267
+ // Thin Harness V1 is exposed through repository_workbench operations to keep the stable tool surface bounded.
268
+ ];
269
+
270
+ export const repositoryToolNames = repositoryToolDefinitions.map((tool) => tool.name);
271
+
272
+ function result(value: Record<string, unknown>): RepositoryToolResult {
273
+ // Compact text channel by default; structuredContent remains the machine view.
274
+ return {
275
+ content: [{ type: 'text', text: JSON.stringify(value) }],
276
+ structuredContent: value,
277
+ };
278
+ }
279
+
280
+ const DEFAULT_MIGRATION_SAMPLE_LIMIT = 3;
281
+
282
+ function withResponseMeta(payload: Record<string, unknown>, startedAt: number): Record<string, unknown> {
283
+ const response = {
284
+ ...payload,
285
+ responseMeta: {
286
+ serverDurationMs: Math.round((performance.now() - startedAt) * 100) / 100,
287
+ structuredPayloadBytes: 0,
288
+ },
289
+ };
290
+ response.responseMeta.structuredPayloadBytes = Buffer.byteLength(JSON.stringify(response), 'utf8');
291
+ return response;
292
+ }
293
+
294
+ export type RepositoryToolCallerContext = RepositoryWorkAttributionCaller;
295
+
296
+ export function claimedSessionEditBinding(
297
+ controllerHome: string,
298
+ repository: ReturnType<typeof resolveRepositorySelection>,
299
+ caller?: RepositoryToolCallerContext,
300
+ explicitWorkId?: unknown,
301
+ ): EditSessionBinding | undefined {
302
+ const workId = resolveClaimedRepositoryWorkId(controllerHome, repository, caller, explicitWorkId);
303
+ if (!workId || !caller?.principalId?.trim()) return undefined;
304
+ const work = getWorkContract({ controllerHome, repoId: repository.repoId }, workId);
305
+ if (!work) return undefined;
306
+ return {
307
+ workId,
308
+ repoId: repository.repoId,
309
+ checkoutId: repository.activeCheckoutId,
310
+ principalId: caller.principalId.trim(),
311
+ controllerInstanceId: caller.controllerInstanceId,
312
+ };
313
+ }
314
+
315
+ function safePatchEditBinding(
316
+ controllerHome: string,
317
+ repository: ReturnType<typeof resolveRepositorySelection>,
318
+ caller: RepositoryToolCallerContext | undefined,
319
+ args: Record<string, unknown>,
320
+ ): EditSessionBinding | undefined {
321
+ const explicitWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
322
+ if (explicitWorkId) return claimedSessionEditBinding(controllerHome, repository, caller, explicitWorkId);
323
+ const sessionId = typeof args.session_id === 'string' ? args.session_id.trim() : '';
324
+ if (sessionId) {
325
+ const existingWorkId = getEditSession(repository.canonicalRoot, sessionId).workId?.trim();
326
+ return existingWorkId
327
+ ? claimedSessionEditBinding(controllerHome, repository, caller, existingWorkId)
328
+ : undefined;
329
+ }
330
+ assertNoBoundExecutionSessionMutation(controllerHome, repository, caller);
331
+ return undefined;
332
+ }
333
+
334
+ function resolveRepositorySelectionForClaimedWork(
335
+ controllerHome: string,
336
+ args: Record<string, unknown>,
337
+ repoIdValue: string,
338
+ caller?: RepositoryToolCallerContext,
339
+ ): ReturnType<typeof resolveRepositorySelection> {
340
+ const checkoutId = typeof args.checkout_id === 'string' ? args.checkout_id.trim() : '';
341
+ const explicitWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
342
+ let repository = resolveRepositorySelection({
343
+ repoId: repoIdValue || undefined,
344
+ checkoutId: checkoutId || undefined,
345
+ controllerHome,
346
+ allowSoleRepository: true,
347
+ });
348
+ if (!explicitWorkId) return repository;
349
+ const recordedWork = getWorkContract({ controllerHome, repoId: repository.repoId }, explicitWorkId);
350
+ const recordedHandle = readWorkHandle(controllerHome, repository.repoId, explicitWorkId);
351
+ const workCheckoutId = recordedHandle?.checkoutId?.trim() || recordedWork?.checkoutId?.trim();
352
+ if (!checkoutId && workCheckoutId && workCheckoutId !== repository.activeCheckoutId) {
353
+ repository = resolveRepositorySelection({
354
+ repoId: repository.repoId,
355
+ checkoutId: workCheckoutId,
356
+ controllerHome,
357
+ allowSoleRepository: true,
358
+ });
359
+ }
360
+ let work = resolveExplicitClaimedRepositoryWork(controllerHome, repository, caller, explicitWorkId);
361
+ if (!work) return repository;
362
+ if (work.workKind !== 'repository_change') {
363
+ work = promoteWorkToRepositoryChange({ controllerHome, repoId: repository.repoId }, work.workId);
364
+ }
365
+ const principalId = caller?.principalId?.trim();
366
+ const sessionId = caller?.sessionId?.trim();
367
+ const controllerInstanceId = caller?.controllerInstanceId?.trim();
368
+ if (principalId && sessionId && controllerInstanceId) {
369
+ ensureRunningRepositoryWorkCheckout({
370
+ controllerHome,
371
+ repository,
372
+ workId: work.workId,
373
+ identity: { principalId, sessionId, controllerInstanceId },
374
+ });
375
+ const refreshedWork = getWorkContract({ controllerHome, repoId: repository.repoId }, work.workId);
376
+ const refreshedHandle = readWorkHandle(controllerHome, repository.repoId, work.workId);
377
+ const refreshedCheckoutId = refreshedHandle?.checkoutId?.trim() || refreshedWork?.checkoutId?.trim();
378
+ if (refreshedCheckoutId && refreshedCheckoutId !== repository.activeCheckoutId) {
379
+ repository = resolveRepositorySelection({
380
+ repoId: repository.repoId,
381
+ checkoutId: refreshedCheckoutId,
382
+ controllerHome,
383
+ allowSoleRepository: true,
384
+ });
385
+ }
386
+ }
387
+ return repository;
388
+ }
389
+
390
+ function rawDefaultBranchMergeCommand(repository: ReturnType<typeof resolveRepositorySelection>, command: unknown): boolean {
391
+ if (!repository.defaultBranch) return false;
392
+ const checkout = repository.checkouts.find((entry) => entry.checkoutId === repository.activeCheckoutId);
393
+ if (checkout?.branch !== repository.defaultBranch) return false;
394
+ const normalized = normalizeRepositoryCommand(command);
395
+ if (normalized.kind === 'argv') {
396
+ const executable = normalized.executable?.split('/').pop();
397
+ return executable === 'git' && normalized.args?.[0] === 'merge';
398
+ }
399
+ return /(?:^|[;&|]\s*)git\s+merge\b/.test(normalized.shellCommand ?? '');
400
+ }
401
+
402
+ interface HistoricalReadOnlyWorkContext {
403
+ workId: string;
404
+ state: WorkContract['semanticState'];
405
+ mode: 'historical_read_only_observation';
406
+ attribution: 'not_active_work';
407
+ }
408
+
409
+ function historicalReadOnlyWorkContext(
410
+ controllerHome: string,
411
+ repository: ReturnType<typeof resolveRepositorySelection>,
412
+ args: Record<string, unknown>,
413
+ ): HistoricalReadOnlyWorkContext | undefined {
414
+ const requestedWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
415
+ if (!requestedWorkId) return undefined;
416
+ const work = getWorkContract({ controllerHome, repoId: repository.repoId }, requestedWorkId);
417
+ if (!work || semanticWorkState(work) === 'open') return undefined;
418
+ const classification = classifyRepositoryCommand(args.command as string | string[], repository.defaultBranch);
419
+ if (classification.risk !== 'readonly') return undefined;
420
+ return {
421
+ workId: work.workId,
422
+ state: work.semanticState,
423
+ mode: 'historical_read_only_observation',
424
+ attribution: 'not_active_work',
425
+ };
426
+ }
427
+
428
+ function resolveRepositoryCommandTarget(
429
+ controllerHome: string,
430
+ args: Record<string, unknown>,
431
+ repoIdValue: string,
432
+ caller?: RepositoryToolCallerContext,
433
+ ): {
434
+ /** Present only for a repository target; absent for an arbitrary workspace. */
435
+ repository?: ReturnType<typeof resolveRepositorySelection>;
436
+ /** Typed command target used for scope, cwd, policy, audit and approval storage. */
437
+ commandTarget: RepositoryCommandScopeTarget;
438
+ executionIdentity: ResolvedExecutionIdentity;
439
+ workspace?: { workspaceId: string; root: string; registered: false };
440
+ historicalWorkContext?: HistoricalReadOnlyWorkContext;
441
+ } {
442
+ const workspaceRoot = typeof args.workspace_root === 'string' ? args.workspace_root.trim() : '';
443
+ const checkoutId = typeof args.checkout_id === 'string' ? args.checkout_id.trim() : '';
444
+ if (workspaceRoot) {
445
+ if (repoIdValue || checkoutId) {
446
+ throw new Error('EPHEMERAL_WORKSPACE_TARGET_CONFLICT: workspace_root cannot be combined with repo_id or checkout_id');
447
+ }
448
+ const target = resolveEphemeralWorkspaceTarget(workspaceRoot, controllerHome);
449
+ return {
450
+ commandTarget: target.commandTarget,
451
+ executionIdentity: {
452
+ schemaVersion: 1,
453
+ authority: 'ephemeral_workspace',
454
+ repositoryId: target.workspaceId,
455
+ checkoutId: target.checkoutId,
456
+ canonicalRoot: target.canonicalRoot,
457
+ },
458
+ workspace: { workspaceId: target.workspaceId, root: target.canonicalRoot, registered: false },
459
+ };
460
+ }
461
+ // A terminal Work no longer owns mutation or checkout authority. An exact
462
+ // terminal work_id may still accompany a read-only observation immediately
463
+ // after finalize, so preserve it as explicit historical context while
464
+ // executing against the caller-selected/current repository checkout. Any
465
+ // non-read-only command continues through claimed Work resolution and fails
466
+ // closed for terminal attribution.
467
+ const selectedRepository = resolveRepositorySelection({
468
+ repoId: repoIdValue || undefined,
469
+ checkoutId: checkoutId || undefined,
470
+ controllerHome,
471
+ allowSoleRepository: true,
472
+ });
473
+ const historicalWorkContext = historicalReadOnlyWorkContext(controllerHome, selectedRepository, args);
474
+ if (historicalWorkContext) {
475
+ return {
476
+ repository: selectedRepository,
477
+ commandTarget: selectedRepository,
478
+ executionIdentity: {
479
+ schemaVersion: 1,
480
+ authority: 'repository',
481
+ repositoryId: selectedRepository.repoId,
482
+ checkoutId: selectedRepository.activeCheckoutId,
483
+ canonicalRoot: selectedRepository.canonicalRoot,
484
+ },
485
+ historicalWorkContext,
486
+ };
487
+ }
488
+
489
+ const repository = resolveRepositorySelectionForClaimedWork(controllerHome, args, repoIdValue, caller);
490
+ const explicitWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
491
+ const workId = explicitWorkId
492
+ ? resolveClaimedRepositoryWorkId(controllerHome, repository, caller, explicitWorkId)
493
+ : undefined;
494
+ return {
495
+ repository,
496
+ commandTarget: repository,
497
+ executionIdentity: {
498
+ schemaVersion: 1,
499
+ authority: 'repository',
500
+ repositoryId: repository.repoId,
501
+ checkoutId: repository.activeCheckoutId,
502
+ canonicalRoot: repository.canonicalRoot,
503
+ ...(workId ? { workId } : {}),
504
+ },
505
+ };
506
+ }
507
+
508
+ export function summarizeEntityMigrationReport(
509
+ report: ReturnType<typeof bindRepositoryEntities>,
510
+ detail = false,
511
+ ): ReturnType<typeof bindRepositoryEntities> | Record<string, unknown> {
512
+ if (detail) return report;
513
+ const files = report.files.slice(0, DEFAULT_MIGRATION_SAMPLE_LIMIT);
514
+ const errors = report.errors.slice(0, DEFAULT_MIGRATION_SAMPLE_LIMIT);
515
+ return {
516
+ repoId: report.repoId,
517
+ checkoutId: report.checkoutId,
518
+ scanned: report.scanned,
519
+ updated: report.updated,
520
+ unresolved: report.unresolved,
521
+ fileCount: report.files.length,
522
+ errorCount: report.errors.length,
523
+ files,
524
+ errors,
525
+ truncated: report.files.length > files.length || report.errors.length > errors.length,
526
+ omittedFileCount: Math.max(0, report.files.length - files.length),
527
+ omittedErrorCount: Math.max(0, report.errors.length - errors.length),
528
+ };
529
+ }
530
+
531
+ export function summarizeRepositoryRegistration(
532
+ repository: ReturnType<typeof registerRepository>,
533
+ migration: ReturnType<typeof bindRepositoryEntities>,
534
+ detail = false,
535
+ fastPath = false,
536
+ ): Record<string, unknown> {
537
+ if (detail) {
538
+ return { detailLevel: 'detail', repository, migration, fastPath };
539
+ }
540
+ const checkoutCounts = repository.checkouts.reduce<Record<string, number>>((counts, checkout) => {
541
+ const lifecycle = checkout.lifecycle ?? 'active';
542
+ counts[lifecycle] = (counts[lifecycle] ?? 0) + 1;
543
+ return counts;
544
+ }, {});
545
+ return {
546
+ detailLevel: 'summary',
547
+ repository: {
548
+ ...repositorySummary(repository),
549
+ activeCheckoutId: repository.activeCheckoutId,
550
+ checkoutCount: repository.checkouts.length,
551
+ checkoutCounts,
552
+ },
553
+ migration: summarizeEntityMigrationReport(migration),
554
+ fastPath,
555
+ next: 'Re-call with detail_level=detail only when full checkout or migration evidence is required.',
556
+ };
557
+ }
558
+
559
+ export function summarizeRepositoryInspection(
560
+ repository: ReturnType<typeof getRepository>,
561
+ detail = false,
562
+ ): Record<string, unknown> {
563
+ if (detail) return { detailLevel: 'detail', repository };
564
+ const checkoutCounts = repository.checkouts.reduce<Record<string, number>>((counts, checkout) => {
565
+ const lifecycle = checkout.lifecycle ?? 'active';
566
+ counts[lifecycle] = (counts[lifecycle] ?? 0) + 1;
567
+ return counts;
568
+ }, {});
569
+ const activeCheckout = repository.checkouts.find((checkout) => checkout.checkoutId === repository.activeCheckoutId);
570
+ return {
571
+ detailLevel: 'summary',
572
+ repository: {
573
+ ...repositorySummary(repository),
574
+ activeCheckoutId: repository.activeCheckoutId,
575
+ ...(activeCheckout ? { activeCheckout } : {}),
576
+ checkoutCount: repository.checkouts.length,
577
+ checkoutCounts,
578
+ },
579
+ omittedCheckoutCount: Math.max(0, repository.checkouts.length - (activeCheckout ? 1 : 0)),
580
+ next: 'Re-call with detail_level=detail only when complete checkout history is required.',
581
+ };
582
+ }
583
+
584
+ function failure(error: unknown): RepositoryToolResult {
585
+ const message = compactErrorMessage(error);
586
+ const code = message.includes(':') ? message.slice(0, message.indexOf(':')) : 'REPOSITORY_TOOL_FAILED';
587
+ const details = typeof error === 'object' && error !== null && 'details' in error ? (error as { details?: unknown }).details : undefined;
588
+ const compactDetails = details !== undefined && Buffer.byteLength(JSON.stringify(details) ?? '', 'utf8') > RESPONSE_BUDGET.previewBytes
589
+ ? { omitted: true, message: 'Error details exceeded budget; inspect job/artifact/result refs.' }
590
+ : details;
591
+ return { ...result({ error: { code, message, ...(compactDetails !== undefined ? { details: compactDetails } : {}) } }), isError: true };
592
+ }
593
+
594
+ interface RepositoryExplorationGuidance {
595
+ code: 'FRAGMENTED_REPOSITORY_EXPLORATION';
596
+ recommendedTool: 'rh_context';
597
+ recommendedOperation: 'search';
598
+ message: string;
599
+ }
600
+
601
+ function fragmentedRepositoryExplorationGuidance(command: unknown): RepositoryExplorationGuidance | undefined {
602
+ if (typeof command !== 'string') return undefined;
603
+ const segments = command.split(/(?:&&|\|\||[;|])/g);
604
+ const readPrograms = new Set<string>();
605
+ let readStepCount = 0;
606
+ for (const segment of segments) {
607
+ const match = segment.trim().match(/^(?:(?:command|env)\s+)?(?:[A-Za-z_][A-Za-z0-9_]*=[^\s]+\s+)*(rg|grep|sed|cat|head|tail)\b/i);
608
+ if (!match) continue;
609
+ readStepCount += 1;
610
+ readPrograms.add(match[1]!.toLowerCase());
611
+ }
612
+ if (readStepCount < 2) return undefined;
613
+ return {
614
+ code: 'FRAGMENTED_REPOSITORY_EXPLORATION',
615
+ recommendedTool: 'rh_context',
616
+ recommendedOperation: 'search',
617
+ message: `Detected ${readStepCount} chained repository read steps (${[...readPrograms].join(', ')}). Prefer a broad rh_context request first, then repeat rh_context with selected paths or relationships when more evidence can improve correctness; keep shell search as fallback when the Context Plane cannot supply the needed evidence.`,
618
+ };
619
+ }
620
+
621
+ function compactProcessCommandPayload(input: {
622
+ accepted?: boolean;
623
+ mode: string;
624
+ path: string;
625
+ route?: string;
626
+ reasons: string[];
627
+ decision?: unknown;
628
+ /** Repository id for a repository target; absent for an arbitrary workspace target. */
629
+ repoId?: string;
630
+ /** Workspace scope id when the target is an arbitrary workspace. */
631
+ scopeId?: string;
632
+ checkoutId?: string;
633
+ workspace?: { workspaceId: string; root: string; registered: false };
634
+ historicalWorkContext?: HistoricalReadOnlyWorkContext;
635
+ processId?: string;
636
+ process?: unknown;
637
+ completed?: boolean;
638
+ ok?: boolean;
639
+ exitCode?: number | null;
640
+ stdout?: string;
641
+ stderr?: string;
642
+ durableSideEffects?: Record<string, number>;
643
+ next: string;
644
+ /** summary (default) omits nested process / routing dumps; detail restores diagnostics. */
645
+ detailLevel?: 'summary' | 'detail';
646
+ includeFullProcess?: boolean;
647
+ guidance?: RepositoryExplorationGuidance;
648
+ }): Record<string, unknown> {
649
+ const output = compactCommandOutput(input.stdout, input.stderr, { ok: input.ok === true });
650
+ const detail = input.detailLevel === 'detail' || input.includeFullProcess === true;
651
+ const effects = input.durableSideEffects ?? {
652
+ executionJobCount: 0,
653
+ localJobCount: 0,
654
+ workerSpawnCount: 0,
655
+ projectionUpdateCount: 0,
656
+ };
657
+ const reason = input.reasons[0] ?? 'readonly_fast_path';
658
+
659
+ // Default success/failure: one authoritative stdout/stderr pair, no nested process dump.
660
+ if (!detail) {
661
+ const completed = input.completed !== false;
662
+ const ok = input.ok === true;
663
+ const payload: Record<string, unknown> = {
664
+ ...(completed ? { ok } : {}),
665
+ accepted: input.accepted ?? true,
666
+ mode: input.mode,
667
+ path: input.path,
668
+ route: input.route ?? input.path,
669
+ reason,
670
+ repoId: input.repoId,
671
+ ...(input.checkoutId ? { checkoutId: input.checkoutId } : {}),
672
+ ...(input.workspace ? { workspace: input.workspace } : {}),
673
+ ...(input.historicalWorkContext ? { historicalWorkContext: input.historicalWorkContext } : {}),
674
+ ...(input.processId ? { processId: input.processId } : {}),
675
+ ...(completed ? { exitCode: input.exitCode ?? (ok ? 0 : 1) } : {}),
676
+ // A running default response otherwise loses its only dependency-bound
677
+ // continuation advice when the detailed Process record is omitted.
678
+ ...(!completed ? { next: input.next } : {}),
679
+ stdout: output.stdout ?? '',
680
+ stderr: output.stderr ?? '',
681
+ ...(output.stdoutTruncated ? { stdoutTruncated: true, stdoutBytes: output.stdoutBytes } : {}),
682
+ ...(output.stderrTruncated ? { stderrTruncated: true, stderrBytes: output.stderrBytes } : {}),
683
+ ...(input.guidance ? { guidance: input.guidance, suggestedOperation: 'rh_context' } : {}),
684
+ };
685
+ if (completed && !ok) {
686
+ payload.error = {
687
+ code: 'PROCESS_COMMAND_FAILED',
688
+ message: (output.stderr || 'process_direct command failed').slice(0, 800),
689
+ retryable: false,
690
+ exitCode: input.exitCode ?? 1,
691
+ };
692
+ }
693
+ // Zero side-effects are implicit for process_direct summary; only surface non-zero.
694
+ const nonZeroEffects = Object.fromEntries(
695
+ Object.entries(effects).filter(([, value]) => typeof value === 'number' && value > 0),
696
+ );
697
+ if (Object.keys(nonZeroEffects).length > 0) payload.durableSideEffects = nonZeroEffects;
698
+ return payload;
699
+ }
700
+
701
+ return {
702
+ accepted: input.accepted ?? true,
703
+ mode: input.mode,
704
+ path: input.path,
705
+ route: input.route ?? input.path,
706
+ routing: {
707
+ ...compactRoutingSummary({ path: input.path, mode: input.mode, reasons: input.reasons }),
708
+ ...(input.decision ? { decision: input.decision } : {}),
709
+ },
710
+ reason,
711
+ repoId: input.repoId,
712
+ ...(input.checkoutId ? { checkoutId: input.checkoutId } : {}),
713
+ ...(input.workspace ? { workspace: input.workspace } : {}),
714
+ ...(input.historicalWorkContext ? { historicalWorkContext: input.historicalWorkContext } : {}),
715
+ ...(input.processId ? { processId: input.processId } : {}),
716
+ ok: input.ok,
717
+ exitCode: input.exitCode,
718
+ ...output,
719
+ // Nested process only in detail mode; never duplicate stdout/stderr there.
720
+ ...(input.process && typeof input.process === 'object'
721
+ ? {
722
+ process: (() => {
723
+ const record = { ...(input.process as Record<string, unknown>) };
724
+ delete record.stdout;
725
+ delete record.stderr;
726
+ delete record.durableSideEffects;
727
+ return record;
728
+ })(),
729
+ }
730
+ : {}),
731
+ durableSideEffects: effects,
732
+ ...(input.guidance ? { guidance: input.guidance, suggestedOperation: 'rh_context' } : {}),
733
+ next: input.next,
734
+ detailLevel: 'detail',
735
+ };
736
+ }
737
+
738
+ function emptyRepositoryMigration(repository: ReturnType<typeof registerRepository>) {
739
+ return {
740
+ repoId: repository.repoId,
741
+ checkoutId: repository.activeCheckoutId,
742
+ scanned: 0,
743
+ updated: 0,
744
+ unresolved: 0,
745
+ files: [] as string[],
746
+ errors: [] as Array<{ path: string; error: string }>,
747
+ };
748
+ }
749
+
750
+ function registrationOnlyMetadataRefresh(
751
+ match: ReturnType<typeof findIdenticalRepositoryRegistration>,
752
+ ): boolean {
753
+ if (!match || match.identical || match.reasons.length === 0) return false;
754
+ const metadataReasons = new Set([
755
+ 'remote_identity_changed',
756
+ 'default_branch_changed',
757
+ 'display_name_changed',
758
+ ]);
759
+ return match.reasons.every((reason) => metadataReasons.has(reason));
760
+ }
761
+
762
+ function registrationOnlyAttachedExistingWorktree(repository: ReturnType<typeof registerRepository>): boolean {
763
+ const selected = repository.checkouts.find((checkout) => checkout.checkoutId === repository.activeCheckoutId);
764
+ return selected?.worktree === true
765
+ && repository.checkouts.some((checkout) => checkout.checkoutId !== selected.checkoutId);
766
+ }
767
+
768
+ export async function callRepositoryTool(
769
+ controllerHome: string,
770
+ name: string,
771
+ args: Record<string, unknown>,
772
+ caller?: RepositoryToolCallerContext,
773
+ ): Promise<RepositoryToolResult | undefined> {
774
+ if (!name.startsWith('repository_') && name !== 'read_repository_file') return undefined;
775
+ try {
776
+ const repoIdValue = typeof args.repo_id === 'string' ? args.repo_id.trim() : '';
777
+ switch (name) {
778
+ case 'read_repository_file': {
779
+ const repository = resolveRepositorySelection({
780
+ repoId: typeof args.repo_id === 'string' ? args.repo_id : undefined,
781
+ checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined,
782
+ controllerHome,
783
+ allowSoleRepository: true,
784
+ });
785
+ const path = String(args.path ?? '').trim();
786
+ if (!path) throw new Error('REPOSITORY_PATH_REQUIRED: path is required');
787
+ const session = caller?.sessionId
788
+ ? { sessionId: caller.sessionId, repoId: repository.repoId, checkoutId: repository.activeCheckoutId }
789
+ : undefined;
790
+ const range = readRepositoryRange(
791
+ repository.canonicalRoot,
792
+ getMcpPolicy('controller', { repoRoot: repository.canonicalRoot }),
793
+ path,
794
+ typeof args.start_line === 'number' ? args.start_line : 1,
795
+ typeof args.end_line === 'number' ? args.end_line : 200,
796
+ session,
797
+ );
798
+ const redacted = redactMcpText(range.content);
799
+ return result({ ...range, content: redacted.text, redactions: redacted.redactions });
800
+ }
801
+ case 'repository_register': {
802
+ const startedAt = performance.now();
803
+ const registerInput = {
804
+ path: String(args.path ?? ''),
805
+ controllerHome,
806
+ displayName: typeof args.display_name === 'string' ? args.display_name : undefined,
807
+ remoteUrl: typeof args.remote_url === 'string' ? args.remote_url : undefined,
808
+ defaultBranch: typeof args.default_branch === 'string' ? args.default_branch : undefined,
809
+ };
810
+ const identical = findIdenticalRepositoryRegistration(registerInput);
811
+ if (identical?.identical) {
812
+ const repository = identical.repository;
813
+ // Repeat registration with identical identity returns immediately.
814
+ // No legacy migration / edit-session migration / historical issue
815
+ // scan / old issue rebinding runs on this hot path.
816
+ const migration = emptyRepositoryMigration(repository);
817
+ return result(withResponseMeta(
818
+ summarizeRepositoryRegistration(repository, migration, args.detail_level === 'detail', true),
819
+ startedAt,
820
+ ));
821
+ }
822
+ const metadataRefresh = registrationOnlyMetadataRefresh(identical);
823
+ const repository = registerRepository(registerInput);
824
+ // Adding a worktree checkout or changing only display/remote/default-branch
825
+ // metadata preserves repository + checkout authority. Existing entities
826
+ // are already bound to the stable repoId, so a historical migration scan
827
+ // would only add latency and stale unresolved diagnostics.
828
+ const migration = registrationOnlyAttachedExistingWorktree(repository) || metadataRefresh
829
+ ? emptyRepositoryMigration(repository)
830
+ : bindRepositoryEntities(repository);
831
+ return result(withResponseMeta(
832
+ summarizeRepositoryRegistration(repository, migration, args.detail_level === 'detail', metadataRefresh),
833
+ startedAt,
834
+ ));
835
+ }
836
+ case 'repository_latest_source_diagnose':
837
+ return result({
838
+ diagnosis: diagnoseLatestLocalProjectSource({
839
+ path: typeof args.path === 'string' ? args.path : undefined,
840
+ repoId: typeof args.repo_id === 'string' ? args.repo_id : undefined,
841
+ controllerHome,
842
+ }),
843
+ });
844
+ case 'repository_bootstrap_local_project':
845
+ return result({
846
+ bootstrap: bootstrapLocalProject({
847
+ path: String(args.path ?? ''),
848
+ controllerHome,
849
+ displayName: typeof args.display_name === 'string' ? args.display_name : undefined,
850
+ defaultBranch: typeof args.default_branch === 'string' ? args.default_branch : undefined,
851
+ mode: typeof args.mode === 'string' ? args.mode as 'init_git_only' | 'init_git_and_register' | 'replace_registration' : undefined,
852
+ replaceRegisteredRepoId: typeof args.replace_registered_repo_id === 'string' ? args.replace_registered_repo_id : undefined,
853
+ confirmAuthorization: args.confirm_authorization === true,
854
+ }),
855
+ });
856
+ case 'repository_list':
857
+ return result({ repositories: listRepositories(controllerHome, { includeRemoved: args.include_removed === true }).map(repositorySummary) });
858
+ case 'repository_get': {
859
+ // Repository inspection is also the bounded reconciliation point for
860
+ // checkout lifecycle drift caused by worktrees being removed outside
861
+ // Forge. Reuse the existing registry reconciler so stale worktrees are
862
+ // not exposed as lifecycle=active to automation callers.
863
+ reconcileRepositoryCheckouts(repoIdValue, controllerHome);
864
+ const repository = getRepository(repoIdValue, controllerHome, { includeRemoved: args.include_removed === true });
865
+ return result(summarizeRepositoryInspection(repository, args.detail_level === 'detail'));
866
+ }
867
+ case 'repository_validate': {
868
+ const startedAt = performance.now();
869
+ const repository = getRepository(repoIdValue, controllerHome, { includeRemoved: true });
870
+ const payload = {
871
+ detailLevel: args.detail_level === 'detail' ? 'detail' : 'summary',
872
+ validation: validateRepository(repoIdValue, controllerHome),
873
+ migration: summarizeEntityMigrationReport(bindRepositoryEntities(repository), args.detail_level === 'detail'),
874
+ };
875
+ return result(withResponseMeta(payload, startedAt));
876
+ }
877
+ case 'repository_refresh': {
878
+ const startedAt = performance.now();
879
+ const repository = refreshRepository(repoIdValue, controllerHome);
880
+ const migration = bindRepositoryEntities(repository);
881
+ return result(withResponseMeta(
882
+ summarizeRepositoryRegistration(repository, migration, args.detail_level === 'detail'),
883
+ startedAt,
884
+ ));
885
+ }
886
+ case 'repository_update':
887
+ return result({ repository: updateRepository(repoIdValue, {
888
+ displayName: typeof args.display_name === 'string' ? args.display_name : undefined,
889
+ defaultBranch: typeof args.default_branch === 'string' ? args.default_branch : undefined,
890
+ enabled: typeof args.enabled === 'boolean' ? args.enabled : undefined,
891
+ }, controllerHome) });
892
+ case 'repository_disable':
893
+ return result({ repository: disableRepository(repoIdValue, controllerHome) });
894
+ case 'repository_remove':
895
+ return result({ repository: removeRepository(repoIdValue, controllerHome) });
896
+ case 'repository_workbench': {
897
+ const operation = typeof args.operation === 'string' ? args.operation : 'summary';
898
+ if (operation === 'summary') {
899
+ return result({ workbench: buildControllerWorkbench(controllerHome, {
900
+ repoId: repoIdValue || undefined,
901
+ includeRemoved: args.include_removed === true,
902
+ }) });
903
+ }
904
+ const payload = typeof args.payload === 'object' && args.payload !== null
905
+ ? args.payload as Record<string, unknown>
906
+ : {};
907
+ const internalTool = {
908
+ batch_execute: 'repository_batch_execute',
909
+ lanes_execute: 'repository_lanes_execute',
910
+ lanes_integrate: 'repository_lanes_integrate',
911
+ fast_receipt_get: 'repository_fast_receipt_get',
912
+ fast_receipt_list: 'repository_fast_receipt_list',
913
+ execution_route: 'repository_execution_route',
914
+ }[operation];
915
+ if (!internalTool) {
916
+ return failure(new Error(`REPOSITORY_WORKBENCH_OPERATION_INVALID: ${operation}`));
917
+ }
918
+ return callRepositoryTool(controllerHome, internalTool, {
919
+ ...payload,
920
+ repo_id: repoIdValue || payload.repo_id,
921
+ checkout_id: typeof args.checkout_id === 'string' ? args.checkout_id : payload.checkout_id,
922
+ });
923
+ }
924
+ case 'repository_git_status': {
925
+ const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
926
+ const status = args.refresh === true
927
+ ? writeRepositoryGitStatusSample(controllerHome, repository)
928
+ : readRepositoryGitStatusSample(controllerHome, repository.repoId, repository.activeCheckoutId);
929
+ return result({
930
+ status: status ?? {
931
+ repoId: repository.repoId,
932
+ checkoutId: repository.activeCheckoutId,
933
+ sampleSource: 'daemon-sample',
934
+ sampled: false,
935
+ observedAt: null,
936
+ staleAgeMs: null,
937
+ message: 'Git status has not been sampled by the Forge Runtime yet. Retry after scheduler heartbeat or call with refresh=true for an explicit live refresh.',
938
+ },
939
+ });
940
+ }
941
+
942
+ case 'repository_git_diff': {
943
+ const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
944
+ return result({ diff: repositoryGitDiff(repository, { staged: args.staged, paths: args.paths, maxBytes: args.max_bytes }) });
945
+ }
946
+ case 'repository_git_create_branch': {
947
+ const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
948
+ return result({ repoId: repository.repoId, checkoutId: repository.activeCheckoutId, ...repositoryGitCreateBranch(controllerHome, repository, { branch: args.branch, startPoint: args.start_point, switchTo: args.switch_to }) as unknown as Record<string, unknown> });
949
+ }
950
+ case 'repository_git_switch_branch': {
951
+ const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
952
+ return result({ repoId: repository.repoId, checkoutId: repository.activeCheckoutId, ...repositoryGitSwitchBranch(controllerHome, repository, { branch: args.branch }) as unknown as Record<string, unknown> });
953
+ }
954
+ case 'repository_git_merge_branch': {
955
+ const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
956
+ return result({ repoId: repository.repoId, checkoutId: repository.activeCheckoutId, ...repositoryGitMergeBranch(controllerHome, repository, { branch: args.branch, noFf: args.no_ff }) as unknown as Record<string, unknown> });
957
+ }
958
+ case 'repository_git_delete_branch': {
959
+ const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
960
+ return result({ repoId: repository.repoId, checkoutId: repository.activeCheckoutId, ...repositoryGitDeleteBranch(controllerHome, repository, { branch: args.branch, force: args.force }) as unknown as Record<string, unknown> });
961
+ }
962
+
963
+ case 'repository_git_commit': {
964
+ const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
965
+ return result({ commit: repositoryGitCommit(controllerHome, repository, { message: args.message, paths: args.paths, allowEmpty: args.allow_empty }) });
966
+ }
967
+ case 'repository_git_finish_workflow': {
968
+ const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
969
+ return result({ finish: repositoryGitFinishWorkflow(controllerHome, repository, { featureBranch: args.feature_branch, targetBranch: args.target_branch, deleteBranch: args.delete_branch, noFf: args.no_ff }) });
970
+ }
971
+ case 'repository_safe_patch_plan': {
972
+ const repository = resolveRepositorySelection({
973
+ repoId: repoIdValue || undefined,
974
+ checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined,
975
+ controllerHome,
976
+ allowSoleRepository: true,
977
+ });
978
+ return result({
979
+ repoId: repository.repoId,
980
+ checkoutId: repository.activeCheckoutId,
981
+ plan: buildSafePatchPlan(repository, { operations: args.operations, chunkSize: args.chunk_size }),
982
+ });
983
+ }
984
+ case 'repository_safe_patch_apply': {
985
+ const repository = resolveRepositorySelectionForClaimedWork(controllerHome, args, repoIdValue, caller);
986
+ const binding = safePatchEditBinding(controllerHome, repository, caller, args);
987
+ const applied = withControllerLock(
988
+ controllerHome,
989
+ { scope: 'repository', repoId: repository.repoId },
990
+ 'mcp:repository_safe_patch_apply',
991
+ () => {
992
+ if (args.validation_only !== true) {
993
+ assertCanonicalRepositoryMutationWorkHandleAvailable({
994
+ controllerHome,
995
+ repositoryId: repository.repoId,
996
+ checkoutId: repository.activeCheckoutId,
997
+ workId: binding?.workId,
998
+ });
999
+ }
1000
+ const mutationAuthority = binding?.workId
1001
+ ? ensureRepositoryMutationWorkHandle({
1002
+ controllerHome,
1003
+ repository,
1004
+ workId: binding.workId,
1005
+ principalId: binding.principalId ?? caller?.principalId ?? '',
1006
+ sessionId: caller?.sessionId,
1007
+ })
1008
+ : undefined;
1009
+ const patchResult = applySafePatch(repository, {
1010
+ sessionId: args.session_id,
1011
+ purpose: args.purpose,
1012
+ operations: args.operations,
1013
+ chunkSize: args.chunk_size,
1014
+ expectedRevision: args.expected_revision,
1015
+ allowedPaths: args.allowed_paths,
1016
+ continueOnError: args.continue_on_error,
1017
+ refreshFingerprints: args.refresh_fingerprints,
1018
+ recoverStaleSession: args.recover_stale_session,
1019
+ binding,
1020
+ });
1021
+ if (binding?.workId && mutationAuthority && patchResult.appliedChunks.length > 0) {
1022
+ markRepositoryMutationStarted({
1023
+ controllerHome,
1024
+ repository,
1025
+ workId: binding.workId,
1026
+ expectedDeliveryBase: mutationAuthority.handle.deliveryBaseCommit ?? mutationAuthority.handle.baseCommit,
1027
+ });
1028
+ }
1029
+ return patchResult;
1030
+ },
1031
+ 60_000,
1032
+ );
1033
+ const changedFiles = [
1034
+ ...new Set(
1035
+ (applied.appliedChunks ?? []).flatMap((chunk) => chunk.paths ?? []),
1036
+ ),
1037
+ ];
1038
+ const ok = applied.status === 'applied';
1039
+ const firstFailure = applied.failures?.[0];
1040
+ const editDiff = applied.appliedChunks.length > 0
1041
+ ? getEditSessionDiff(repository.canonicalRoot, applied.session.sessionId)
1042
+ : undefined;
1043
+ const boundedEditDiff = editDiff
1044
+ ? boundUtf8(editDiff.patch, RESPONSE_BUDGET.inlineOutputBytes)
1045
+ : undefined;
1046
+ const reviewEvidence = editDiff && boundedEditDiff
1047
+ ? {
1048
+ source: 'edit_session' as const,
1049
+ sessionId: editDiff.sessionId,
1050
+ revision: editDiff.revision,
1051
+ sha256: editDiff.sha256,
1052
+ patchPreview: boundedEditDiff.text,
1053
+ truncated: boundedEditDiff.truncated,
1054
+ byteLength: boundedEditDiff.byteLength,
1055
+ semanticReviewAuthority: 'chatgpt' as const,
1056
+ next: boundedEditDiff.truncated
1057
+ ? 'The edit diff exceeds the inline budget. Expand only the affected ranges or diff needed for semantic review.'
1058
+ : 'Review this exact edit-session diff before deciding whether more edits or focused validation are needed.',
1059
+ }
1060
+ : undefined;
1061
+ const digest = buildSyncOperationDigest({
1062
+ ok,
1063
+ operation: 'repository_safe_patch_apply',
1064
+ summary: ok
1065
+ ? `补丁已同步应用,涉及 ${changedFiles.length} 个文件。`
1066
+ : applied.status === 'partial'
1067
+ ? `补丁部分应用:${changedFiles.length} 个文件成功,存在失败 chunk。`
1068
+ : `补丁应用失败:${firstFailure?.message || '请检查 failures 摘要'}`,
1069
+ changedFiles,
1070
+ errorClass: ok ? undefined : classifyUserFacingError({
1071
+ code: firstFailure?.code,
1072
+ message: firstFailure?.message,
1073
+ infrastructure: firstFailure?.code === 'APPLY_FAILED',
1074
+ }),
1075
+ errorMessage: firstFailure?.message,
1076
+ });
1077
+ const payload = {
1078
+ repoId: repository.repoId,
1079
+ checkoutId: repository.activeCheckoutId,
1080
+ ...applied as unknown as Record<string, unknown>,
1081
+ phase: digest.phase,
1082
+ statusLabel: digest.statusLabel,
1083
+ summary: digest.summary,
1084
+ terminal: true,
1085
+ applyMode: 'sync',
1086
+ digest,
1087
+ ...(reviewEvidence ? { reviewEvidence } : {}),
1088
+ suggestedNextActions: digest.suggestedNextActions,
1089
+ };
1090
+ return ok ? result(payload) : { ...result(payload), isError: true };
1091
+ }
1092
+ case 'repository_command_preview': {
1093
+ const target = resolveRepositoryCommandTarget(controllerHome, args, repoIdValue, caller);
1094
+ const { commandTarget } = target;
1095
+ const execution = withControllerLock(
1096
+ controllerHome,
1097
+ { scope: 'repository', repoId: commandExecutionScopeKey(commandTarget) },
1098
+ 'mcp:repository_command_preview',
1099
+ () => executeRepositoryCommand(controllerHome, commandTarget, {
1100
+ command: args.command as string | string[],
1101
+ cwd: typeof args.cwd === 'string' ? args.cwd : undefined,
1102
+ dryRun: true,
1103
+ allowNonGitWorkspace: target.workspace !== undefined,
1104
+ }),
1105
+ 60_000,
1106
+ );
1107
+ // Repository targets keep repoId/checkoutId. An arbitrary workspace target
1108
+ // reports its workspace scope id and never a repository id.
1109
+ const { repoId: _previewRepoId, checkoutId: _previewCheckoutId, ...workspacePreviewFields } = execution as unknown as Record<string, unknown>;
1110
+ return result({
1111
+ ...(commandTarget.repoId ? execution as unknown as Record<string, unknown> : workspacePreviewFields),
1112
+ ...(commandTarget.repoId ? {} : { scopeId: commandExecutionScopeKey(commandTarget) }),
1113
+ ...(target.workspace ? { workspace: target.workspace } : {}),
1114
+ });
1115
+ }
1116
+ case 'repository_command_execute': {
1117
+ const explorationGuidance = fragmentedRepositoryExplorationGuidance(args.command);
1118
+ let target = resolveRepositoryCommandTarget(controllerHome, args, repoIdValue, caller);
1119
+ let { repository, commandTarget, executionIdentity, historicalWorkContext } = target;
1120
+ // Repository targets keep their established identity fields. An arbitrary
1121
+ // workspace target reports a workspace scope id and never a repository id.
1122
+ const commandScopeKey = commandExecutionScopeKey(commandTarget);
1123
+ const scopeFields: Record<string, unknown> = repository
1124
+ ? { repoId: commandScopeKey, checkoutId: commandTarget.activeCheckoutId }
1125
+ : { scopeId: commandScopeKey };
1126
+ const explicitWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
1127
+ // Repository-semantic checks apply only when a repository target exists;
1128
+ // an arbitrary workspace has no bound session, default branch, or Work.
1129
+ if (repository && !explicitWorkId) assertNoBoundExecutionSessionMutation(controllerHome, repository, caller);
1130
+ // Git integration is a capability with its own repository, scope and
1131
+ // concurrency checks. A Work's semantic completion and any optional
1132
+ // verification/review evidence never admit or deny this operation.
1133
+ const timeoutMs = typeof args.timeout_ms === 'number'
1134
+ ? args.timeout_ms
1135
+ : typeof args.timeout_ms === 'string'
1136
+ ? Number(args.timeout_ms)
1137
+ : undefined;
1138
+ const maxOutputBytes = typeof args.max_output_bytes === 'number'
1139
+ ? args.max_output_bytes
1140
+ : typeof args.max_output_bytes === 'string'
1141
+ ? Number(args.max_output_bytes)
1142
+ : undefined;
1143
+ // Worker-owned durable executions must not re-enter Fast Path or create a
1144
+ // nested ExecutionJob. Local Job remains the worker settlement surface.
1145
+ const fromDurableWorker = args.__from_durable_worker === true
1146
+ || typeof args.__execution_job_id === 'string';
1147
+ // Process Runtime owns local async execution. apply_mode=async means
1148
+ // return its Process handle immediately; it must not promote a local
1149
+ // command into the retired ExecutionJob path. Explicit mode=durable
1150
+ // remains a separate external-Controller boundary.
1151
+ const returnHandleImmediately = args.apply_mode === 'async'
1152
+ || args.mode === 'async'
1153
+ || args.async === true
1154
+ || args.background === true;
1155
+ const forceDurable = fromDurableWorker
1156
+ || args.mode === 'durable'
1157
+ || args.force_durable === true;
1158
+ const rawCommitScope = classifyRawGitCommitScope(args.command as string | string[]);
1159
+ const routeClass = classifyRepositoryCommandRoute(args.command as string | string[], {
1160
+ forceDurable,
1161
+ workId: executionIdentity.workId,
1162
+ defaultBranch: commandTarget.defaultBranch,
1163
+ timeoutMs,
1164
+ });
1165
+ const processRequestId = typeof args.request_id === 'string' ? args.request_id.trim() : '';
1166
+ if (executionIdentity.workId
1167
+ && (routeClass.route === 'process_direct' || routeClass.route === 'process_managed' || routeClass.route === 'durable')
1168
+ && !processRequestId) {
1169
+ throw new Error(
1170
+ `WORK_PROCESS_REQUEST_ID_REQUIRED: ${executionIdentity.workId}; `
1171
+ + 'Work-attributed Process Runtime commands require a stable request_id so reconnect/retry can resume the original Process without duplicate execution.',
1172
+ );
1173
+ }
1174
+ let mutationAuthority: ReturnType<typeof ensureRepositoryMutationWorkHandle> | undefined;
1175
+ const mutationClassification = classifyRepositoryCommand(args.command as string | string[], commandTarget.defaultBranch);
1176
+ if (
1177
+ (mutationClassification.risk === 'workspace_write' || mutationClassification.risk === 'destructive')
1178
+ && (routeClass.route === 'process_direct' || routeClass.route === 'process_managed')
1179
+ ) {
1180
+ if (executionIdentity.workId) {
1181
+ mutationAuthority = withControllerLock(
1182
+ controllerHome,
1183
+ { scope: 'repository', repoId: commandExecutionScopeKey(commandTarget) },
1184
+ 'mcp:repository_command_execute:mutation-authority',
1185
+ () => ensureRepositoryMutationWorkHandle({
1186
+ controllerHome,
1187
+ repository: repository!,
1188
+ workId: executionIdentity.workId!,
1189
+ principalId: caller?.principalId ?? '',
1190
+ sessionId: caller?.sessionId,
1191
+ deferEffectPromotion: true,
1192
+ }),
1193
+ 60_000,
1194
+ );
1195
+ // Pre-mutation reconciliation may advance expectedHead. Preserve the
1196
+ // complete WorkHandle identity after that CAS; rebuilding a generic
1197
+ // repository identity here drops branch/expectedHead and prevents the
1198
+ // successful command from settling its legitimate successor HEAD.
1199
+ executionIdentity = executionIdentityForWork(repository!, mutationAuthority.handle);
1200
+ } else {
1201
+ withControllerLock(
1202
+ controllerHome,
1203
+ { scope: 'repository', repoId: commandExecutionScopeKey(commandTarget) },
1204
+ 'mcp:repository_command_execute:unattributed-mutation-authority',
1205
+ () => assertCanonicalRepositoryMutationWorkHandleAvailable({
1206
+ controllerHome,
1207
+ repositoryId: commandExecutionScopeKey(commandTarget),
1208
+ checkoutId: commandTarget.activeCheckoutId ?? '',
1209
+ }),
1210
+ 60_000,
1211
+ );
1212
+ }
1213
+ }
1214
+ // Compatibility boundary only: this facade owns repository-domain
1215
+ // context (target resolution, Git/source mutation fencing and legacy
1216
+ // response translation), while Unified Process Runtime owns execution,
1217
+ // handles, cancellation and persistence. Do not create another generic
1218
+ // command executor here. Thin Harness is consulted only for an explicit
1219
+ // external durable boundary, not as a second command classifier.
1220
+ const routingDecision = forceDurable
1221
+ ? routeExecution({
1222
+ operation: 'repository_command_execute',
1223
+ mode: 'durable',
1224
+ timeoutMs,
1225
+ background: args.background === true || args.async === true,
1226
+ })
1227
+ : {
1228
+ mode: 'fast' as const,
1229
+ reasons: ['repository_command_process_runtime_authoritative'],
1230
+ risk: 'unknown' as const,
1231
+ estimatedClass: 'unknown' as const,
1232
+ requiresIsolation: false,
1233
+ requiresRecovery: false,
1234
+ suggestedOperation: undefined,
1235
+ effects: {
1236
+ readsWorkspace: true,
1237
+ mutatesWorkspace: true,
1238
+ mutatesGitRefs: false,
1239
+ remoteWrite: false,
1240
+ },
1241
+ };
1242
+ // Unified Process Runtime for local commands (Direct/Managed) when not forced durable.
1243
+ // Ephemeral workspaces cannot persist a recoverable async Process handle,
1244
+ // so their async requests remain promotion-required below.
1245
+ if (!forceDurable && !fromDurableWorker && !(target.workspace && returnHandleImmediately)) {
1246
+ try {
1247
+ if (routeClass.route === 'reject') {
1248
+ return result({
1249
+ accepted: false,
1250
+ mode: 'reject',
1251
+ path: routeClass.reason,
1252
+ route: 'reject',
1253
+ status: 'rejected',
1254
+ ...scopeFields,
1255
+ workspace: target.workspace,
1256
+ message: routeClass.reason === 'standalone_recovery_lifecycle_required'
1257
+ ? 'Canonical Runtime and connector lifecycle operations must be submitted to the standalone Forge Recovery owner, not executed as a child of repository Process Runtime.'
1258
+ : 'Repository command execution was rejected by Process Runtime routing policy.',
1259
+ suggestedOperation: routeClass.reason === 'standalone_recovery_lifecycle_required'
1260
+ ? 'Use the standalone Forge Recovery connector.'
1261
+ : routingDecision.suggestedOperation,
1262
+ durableSideEffects: {
1263
+ executionJobCount: 0,
1264
+ localJobCount: 0,
1265
+ workerSpawnCount: 0,
1266
+ projectionUpdateCount: 0,
1267
+ },
1268
+ });
1269
+ }
1270
+ if (routeClass.route === 'durable' && target.workspace) {
1271
+ return result({
1272
+ accepted: false,
1273
+ mode: 'durable',
1274
+ path: 'ephemeral_workspace_promotion_required',
1275
+ ...scopeFields,
1276
+ workspace: target.workspace,
1277
+ message: 'Ephemeral workspaces must be registered before durable remote effects so Process identity and reconciliation have a stable repository authority.',
1278
+ suggestedOperation: 'repository_register',
1279
+ });
1280
+ }
1281
+ if (routeClass.route === 'process_direct' || routeClass.route === 'process_managed' || routeClass.route === 'durable') {
1282
+ const processResult = await executeRepositoryCommandViaProcessRuntime({
1283
+ controllerHome,
1284
+ repository: commandTarget,
1285
+ command: args.command as string | string[],
1286
+ cwd: typeof args.cwd === 'string' ? args.cwd : undefined,
1287
+ timeoutMs,
1288
+ interactiveWaitMs: returnHandleImmediately
1289
+ ? 0
1290
+ : typeof args.interactive_wait_ms === 'number' ? args.interactive_wait_ms : undefined,
1291
+ maxOutputBytes,
1292
+ returnHandleImmediately,
1293
+ requestId: processRequestId || undefined,
1294
+ workId: executionIdentity.workId,
1295
+ executionIdentity,
1296
+ allowNonGitWorkspace: target.workspace !== undefined,
1297
+ principalId: caller?.principalId ?? undefined,
1298
+ });
1299
+ if (repository && mutationAuthority && (processResult.route === 'process_direct' || processResult.route === 'process_managed')) {
1300
+ const processHandle = processResult.process;
1301
+ const postStatus = repositoryGitStatus(repository);
1302
+ const expectedHead = mutationAuthority.handle.expectedHead;
1303
+ const mutationObserved = !postStatus.clean
1304
+ || (Boolean(postStatus.head) && postStatus.head !== expectedHead);
1305
+ if (mutationObserved) {
1306
+ withControllerLock(
1307
+ controllerHome,
1308
+ { scope: 'repository', repoId: commandExecutionScopeKey(commandTarget) },
1309
+ 'mcp:repository_command_execute:mutation-started',
1310
+ () => markRepositoryMutationStarted({
1311
+ controllerHome,
1312
+ repository,
1313
+ workId: executionIdentity.workId!,
1314
+ expectedDeliveryBase: mutationAuthority!.handle.deliveryBaseCommit ?? mutationAuthority!.handle.baseCommit,
1315
+ }),
1316
+ 60_000,
1317
+ );
1318
+ }
1319
+ }
1320
+ if (processResult.route === 'process_direct' || processResult.route === 'process_managed') {
1321
+ const handle = processResult.process;
1322
+ const detailLevel = args.detail_level === 'detail' || args.detail === true
1323
+ ? 'detail'
1324
+ : 'summary';
1325
+ const directWithoutHandle = processResult.route === 'process_direct' && !handle;
1326
+ const directNotExecuted = directWithoutHandle
1327
+ && processResult.executionStatus !== undefined
1328
+ && processResult.executionStatus !== 'executed';
1329
+ if (directNotExecuted) {
1330
+ const authorization = processResult.authorizationDecision ?? {
1331
+ decision: 'deny' as const,
1332
+ reason: 'Command was not executed because the required authorization was not satisfied.',
1333
+ };
1334
+ return result({
1335
+ accepted: false,
1336
+ mode: processResult.route,
1337
+ path: processResult.reason ?? routeClass.reason,
1338
+ route: processResult.route,
1339
+ ...scopeFields,
1340
+ workspace: target.workspace,
1341
+ status: processResult.executionStatus,
1342
+ policyDecision: processResult.policyDecision ?? 'approval_required',
1343
+ authorization,
1344
+ ...(processResult.approvalRequestId ? { approvalRequestId: processResult.approvalRequestId } : {}),
1345
+ message: authorization.decision === 'user_confirmation_required'
1346
+ ? authorization.humanSummary
1347
+ : authorization.reason,
1348
+ suggestedOperation: 'repository_command_preview',
1349
+ });
1350
+ }
1351
+ const completed = directWithoutHandle || handle?.completed === true;
1352
+ const status = directWithoutHandle
1353
+ ? (processResult.ok === true ? 'succeeded' : 'failed')
1354
+ : !handle
1355
+ ? 'rejected'
1356
+ : completed
1357
+ ? (handle.cancelled ? 'cancelled' : handle.timedOut ? 'timed_out' : processResult.ok === true ? 'succeeded' : 'failed')
1358
+ : 'running';
1359
+ const payload = compactProcessCommandPayload({
1360
+ accepted: true,
1361
+ mode: processResult.route,
1362
+ path: processResult.route,
1363
+ route: processResult.route,
1364
+ reasons: [processResult.reason ?? routeClass.reason, ...routingDecision.reasons],
1365
+ decision: detailLevel === 'detail' ? routingDecision : undefined,
1366
+ ...scopeFields,
1367
+ workspace: target.workspace,
1368
+ historicalWorkContext,
1369
+ processId: handle?.processId,
1370
+ process: detailLevel === 'detail' ? handle : undefined,
1371
+ completed,
1372
+ ok: completed ? processResult.ok : undefined,
1373
+ exitCode: completed ? processResult.exitCode : undefined,
1374
+ stdout: processResult.stdout,
1375
+ stderr: processResult.stderr,
1376
+ durableSideEffects: processResult.durableSideEffects,
1377
+ guidance: explorationGuidance,
1378
+ next: directWithoutHandle
1379
+ ? processResult.reason === 'readonly_fast_path'
1380
+ ? 'Bounded readonly execution completed without Process record / Lease / Local Job / ExecutionJob / Worker.'
1381
+ : 'Bounded ephemeral workspace execution completed without Process record / Lease / Local Job / ExecutionJob / Worker.'
1382
+ : processResult.route === 'process_direct' || completed
1383
+ ? 'Process Runtime completed without Local Job / ExecutionJob / Worker.'
1384
+ : `Process Runtime is managing ${handle?.processId}. Continue independent work; use process_get only if an observation can change the next decision, then join with process_wait when this result becomes a dependency. Do not re-run or periodically poll.`,
1385
+ detailLevel,
1386
+ });
1387
+ payload.status = status;
1388
+ payload.processId = handle?.processId;
1389
+ payload.authorization = {
1390
+ decision: 'allow',
1391
+ source: directWithoutHandle ? 'bounded_read_direct' : 'process_runtime',
1392
+ reason: directWithoutHandle
1393
+ ? 'Readonly repository command executed through the bounded non-persistent direct path.'
1394
+ : 'Repository command executed through Unified Process Runtime.',
1395
+ };
1396
+ if (directWithoutHandle && processResult.reason !== 'readonly_fast_path') {
1397
+ payload.authorization = processResult.authorizationDecision?.decision === 'allow'
1398
+ ? processResult.authorizationDecision
1399
+ : {
1400
+ decision: 'allow',
1401
+ source: 'ephemeral_workspace_direct',
1402
+ reason: 'Bounded ephemeral workspace command executed through the non-persistent direct path.',
1403
+ };
1404
+ }
1405
+ if (handle?.processId) {
1406
+ payload.resultRef = { kind: 'process_logs', processId: handle.processId };
1407
+ }
1408
+ // Keep accepted true once the command was admitted to Process Runtime.
1409
+ payload.accepted = true;
1410
+ return processResult.ok === true || !completed
1411
+ ? result(payload)
1412
+ : { ...result(payload), isError: true };
1413
+ }
1414
+ }
1415
+ } catch (error) {
1416
+ // Process Runtime is authoritative. Falling through could spawn the
1417
+ // same command again through the legacy Fast/Local Job path.
1418
+ if (process.env.FORGE_DEBUG_PROCESS_RUNTIME === '1') {
1419
+ console.error('[repository_command_execute] process runtime error', error);
1420
+ }
1421
+ return failure(error);
1422
+ }
1423
+ }
1424
+ if (target.workspace && (forceDurable || returnHandleImmediately)) {
1425
+ return result({
1426
+ accepted: false,
1427
+ mode: 'durable',
1428
+ path: 'ephemeral_workspace_promotion_required',
1429
+ ...scopeFields,
1430
+ workspace: target.workspace,
1431
+ message: 'Ephemeral workspaces support bounded local Direct/Managed execution only. Register the directory before durable, remote, release, or resumable Work.',
1432
+ suggestedOperation: 'repository_register',
1433
+ });
1434
+ }
1435
+ // Durable Worker calls skip Process/Fast above, then use the Local Bridge
1436
+ // compatibility projection below for writes/long commands so the worker
1437
+ // can settle the legacy child without creating a nested ExecutionJob.
1438
+ // Short readonly commands keep the zero-Local-Job direct path.
1439
+ if (fromDurableWorker) {
1440
+ try {
1441
+ const routeClass = classifyRepositoryCommandRoute(args.command as string | string[], {
1442
+ forceDurable: false,
1443
+ defaultBranch: commandTarget.defaultBranch,
1444
+ timeoutMs,
1445
+ });
1446
+ if (routeClass.route === 'process_direct') {
1447
+ const processResult = await executeRepositoryCommandViaProcessRuntime({
1448
+ controllerHome,
1449
+ repository: commandTarget,
1450
+ command: args.command as string | string[],
1451
+ cwd: typeof args.cwd === 'string' ? args.cwd : undefined,
1452
+ timeoutMs,
1453
+ maxOutputBytes,
1454
+ requestId: typeof args.request_id === 'string' ? args.request_id : undefined,
1455
+ workId: executionIdentity.workId,
1456
+ executionIdentity,
1457
+ principalId: caller?.principalId ?? undefined,
1458
+ });
1459
+ if (processResult.route === 'process_direct') {
1460
+ const execRecord = processResult.process as unknown as Record<string, unknown> | undefined;
1461
+ const ok = processResult.ok === true;
1462
+ const inlinePayload = {
1463
+ accepted: ok,
1464
+ mode: 'durable',
1465
+ path: 'durable_worker_inline',
1466
+ routing: compactRoutingSummary({
1467
+ path: 'durable',
1468
+ mode: 'durable',
1469
+ reasons: ['durable_worker_inline_process_direct', routeClass.reason, ...routingDecision.reasons],
1470
+ }),
1471
+ ...scopeFields,
1472
+ ok,
1473
+ processId: typeof execRecord?.processId === 'string' ? execRecord.processId : undefined,
1474
+ status: typeof execRecord?.status === 'string' ? execRecord.status : undefined,
1475
+ exitCode: typeof processResult.exitCode === 'number' ? processResult.exitCode : undefined,
1476
+ ...compactCommandOutput(
1477
+ typeof processResult.stdout === 'string' ? processResult.stdout : undefined,
1478
+ typeof processResult.stderr === 'string' ? processResult.stderr : undefined,
1479
+ { ok },
1480
+ ),
1481
+ durableSideEffects: processResult.durableSideEffects,
1482
+ next: 'Durable Worker executed a short readonly repository command inline without a Local Job.',
1483
+ };
1484
+ return ok ? result(inlinePayload) : { ...result(inlinePayload), isError: true };
1485
+ }
1486
+ }
1487
+ } catch (error) {
1488
+ if (process.env.FORGE_DEBUG_PROCESS_RUNTIME === '1') {
1489
+ console.error('[repository_command_execute] durable worker inline process runtime error', error);
1490
+ }
1491
+ }
1492
+ }
1493
+ return result({
1494
+ accepted: false,
1495
+ mode: 'durable',
1496
+ path: 'external_controller_required',
1497
+ routing: compactRoutingSummary({ path: 'durable', mode: 'durable', reasons: [routingDecision.reasons.join(','), 'local_bridge_execution_retired'] }),
1498
+ message: 'This command requires explicit external Controller handling; Local Bridge Jobs are retired for repository commands.',
1499
+ suggestedOperation: 'Create or claim WorkContract, then use rh_work.launcher_start or a Process Runtime-compatible command.',
1500
+ externalEffect: {
1501
+ outcome: 'not_started',
1502
+ ambiguousFailureOutcome: 'outcome_unknown',
1503
+ replayPolicy: 'never_auto_retry',
1504
+ reconciliation: 'After an ambiguous dispatch failure, inspect the remote ref/registry/release state before deciding whether to retry.',
1505
+ },
1506
+ });
1507
+ }
1508
+ case 'repository_batch_execute': {
1509
+ const repository = resolveRepositorySelection({
1510
+ repoId: repoIdValue || undefined,
1511
+ checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined,
1512
+ controllerHome,
1513
+ allowSoleRepository: true,
1514
+ });
1515
+ const steps = Array.isArray(args.steps)
1516
+ ? args.steps
1517
+ .filter((entry): entry is Record<string, unknown> => typeof entry === 'object' && entry !== null)
1518
+ .map((entry) => ({
1519
+ id: typeof entry.id === 'string' ? entry.id : undefined,
1520
+ kind: String(entry.kind ?? '') as
1521
+ | 'read_file'
1522
+ | 'search'
1523
+ | 'git_status'
1524
+ | 'git_diff'
1525
+ | 'apply_patch'
1526
+ | 'run_short_command'
1527
+ | 'run_focused_check'
1528
+ | 'stage_paths'
1529
+ | 'commit_paths',
1530
+ input: (typeof entry.input === 'object' && entry.input !== null
1531
+ ? entry.input
1532
+ : {}) as Record<string, unknown>,
1533
+ }))
1534
+ : [];
1535
+ const batch = await executeRepositoryBatch(
1536
+ { controllerHome, repository },
1537
+ {
1538
+ repoId: repository.repoId,
1539
+ checkoutId: repository.activeCheckoutId,
1540
+ mode: args.mode === 'fast' || args.mode === 'durable' || args.mode === 'auto' ? args.mode : 'auto',
1541
+ steps,
1542
+ stopOnError: args.stop_on_error !== false,
1543
+ allowedPaths: Array.isArray(args.allowed_paths) ? args.allowed_paths.map(String) : undefined,
1544
+ timeoutMs: typeof args.timeout_ms === 'number' ? args.timeout_ms : undefined,
1545
+ includeLatencyBreakdown: args.include_latency_breakdown === true,
1546
+ purpose: typeof args.purpose === 'string' ? args.purpose : undefined,
1547
+ requestId: typeof args.request_id === 'string' ? args.request_id.trim() || undefined : undefined,
1548
+ },
1549
+ );
1550
+ const payload = batch as unknown as Record<string, unknown>;
1551
+ return batch.ok ? result(payload) : { ...result(payload), isError: true };
1552
+ }
1553
+ case 'repository_lanes_execute': {
1554
+ const repository = resolveRepositorySelection({
1555
+ repoId: repoIdValue || undefined,
1556
+ checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined,
1557
+ controllerHome,
1558
+ allowSoleRepository: true,
1559
+ });
1560
+ const lanes = await executeLightweightLanes(
1561
+ { controllerHome, repository },
1562
+ {
1563
+ repoId: repository.repoId,
1564
+ checkoutId: repository.activeCheckoutId,
1565
+ readLanes: Array.isArray(args.read_lanes)
1566
+ ? args.read_lanes
1567
+ .filter((entry): entry is Record<string, unknown> => typeof entry === 'object' && entry !== null)
1568
+ .map((entry) => ({
1569
+ id: typeof entry.id === 'string' ? entry.id : undefined,
1570
+ kind: String(entry.kind ?? 'search') as 'search' | 'read_file' | 'git_status' | 'git_diff' | 'run_short_command',
1571
+ input: (typeof entry.input === 'object' && entry.input !== null
1572
+ ? entry.input
1573
+ : {}) as Record<string, unknown>,
1574
+ }))
1575
+ : undefined,
1576
+ patchProposalLanes: Array.isArray(args.patch_proposal_lanes)
1577
+ ? args.patch_proposal_lanes
1578
+ .filter((entry): entry is Record<string, unknown> => typeof entry === 'object' && entry !== null)
1579
+ .map((entry) => ({
1580
+ id: typeof entry.id === 'string' ? entry.id : undefined,
1581
+ readPaths: Array.isArray(entry.read_paths) ? entry.read_paths.map(String) : [],
1582
+ writePaths: Array.isArray(entry.write_paths) ? entry.write_paths.map(String) : [],
1583
+ proposedOperations: Array.isArray(entry.proposed_operations) ? entry.proposed_operations : [],
1584
+ assumptions: Array.isArray(entry.assumptions) ? entry.assumptions.map(String) : undefined,
1585
+ riskNotes: Array.isArray(entry.risk_notes) ? entry.risk_notes.map(String) : undefined,
1586
+ suggestedFocusedCheck: entry.suggested_focused_check as string | string[] | undefined,
1587
+ }))
1588
+ : undefined,
1589
+ failFast: args.fail_fast === true,
1590
+ maxConcurrency: typeof args.max_concurrency === 'number' ? args.max_concurrency : undefined,
1591
+ includeLatencyBreakdown: args.include_latency_breakdown === true,
1592
+ },
1593
+ );
1594
+ const payload = lanes as unknown as Record<string, unknown>;
1595
+ return lanes.ok ? result(payload) : { ...result(payload), isError: true };
1596
+ }
1597
+ case 'repository_lanes_integrate': {
1598
+ const repository = resolveRepositorySelection({
1599
+ repoId: repoIdValue || undefined,
1600
+ checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined,
1601
+ controllerHome,
1602
+ allowSoleRepository: true,
1603
+ });
1604
+ const proposals = Array.isArray(args.proposals)
1605
+ ? args.proposals
1606
+ .filter((entry): entry is Record<string, unknown> => typeof entry === 'object' && entry !== null)
1607
+ .map((entry) => ({
1608
+ id: String(entry.id ?? 'proposal'),
1609
+ ok: entry.ok !== false,
1610
+ durationMs: 0,
1611
+ readPaths: Array.isArray(entry.read_paths) ? entry.read_paths.map(String) : [],
1612
+ writePaths: Array.isArray(entry.write_paths) ? entry.write_paths.map(String) : [],
1613
+ proposedOperations: Array.isArray(entry.proposed_operations) ? entry.proposed_operations : [],
1614
+ analysisOnly: entry.analysis_only === true,
1615
+ proposalId: typeof entry.proposal_id === 'string'
1616
+ ? entry.proposal_id
1617
+ : typeof entry.proposalId === 'string'
1618
+ ? entry.proposalId
1619
+ : undefined,
1620
+ }))
1621
+ : [];
1622
+ const integrated = await integratePatchProposals(
1623
+ { controllerHome, repository },
1624
+ proposals,
1625
+ {
1626
+ sessionId: typeof args.session_id === 'string' ? args.session_id : undefined,
1627
+ allowedPaths: Array.isArray(args.allowed_paths) ? args.allowed_paths.map(String) : undefined,
1628
+ purpose: typeof args.purpose === 'string' ? args.purpose : undefined,
1629
+ requestId: typeof args.request_id === 'string' ? args.request_id.trim() || undefined : undefined,
1630
+ continueOnError: args.continue_on_error === true,
1631
+ },
1632
+ );
1633
+ const payload = integrated as unknown as Record<string, unknown>;
1634
+ return integrated.ok ? result(payload) : { ...result(payload), isError: true };
1635
+ }
1636
+ case 'repository_fast_receipt_get': {
1637
+ const repository = resolveRepositorySelection({
1638
+ repoId: repoIdValue || undefined,
1639
+ checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined,
1640
+ controllerHome,
1641
+ allowSoleRepository: true,
1642
+ });
1643
+ const receipt = readFastReceipt(controllerHome, repository.repoId, String(args.execution_id ?? ''));
1644
+ if (!receipt) return failure(new Error(`FAST_RECEIPT_NOT_FOUND: ${String(args.execution_id ?? '')}`));
1645
+ return result({ receipt });
1646
+ }
1647
+ case 'repository_fast_receipt_list': {
1648
+ const repository = resolveRepositorySelection({
1649
+ repoId: repoIdValue || undefined,
1650
+ checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined,
1651
+ controllerHome,
1652
+ allowSoleRepository: true,
1653
+ });
1654
+ const limit = typeof args.limit === 'number' ? args.limit : undefined;
1655
+ return result({ receipts: listFastReceipts(controllerHome, repository.repoId, limit) });
1656
+ }
1657
+ case 'repository_execution_route': {
1658
+ const repository = resolveRepositorySelection({
1659
+ repoId: repoIdValue || undefined,
1660
+ checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined,
1661
+ controllerHome,
1662
+ allowSoleRepository: true,
1663
+ });
1664
+ const decision = routeExecution({
1665
+ operation: String(args.operation ?? ''),
1666
+ mode: args.mode === 'fast' || args.mode === 'durable' || args.mode === 'auto' ? args.mode : 'auto',
1667
+ command: args.command as string | string[] | undefined,
1668
+ timeoutMs: typeof args.timeout_ms === 'number' ? args.timeout_ms : undefined,
1669
+ background: args.background === true,
1670
+ paths: Array.isArray(args.paths) ? args.paths.map(String) : undefined,
1671
+ allowedPaths: Array.isArray(args.allowed_paths) ? args.allowed_paths.map(String) : undefined,
1672
+ defaultBranch: repository.defaultBranch,
1673
+ });
1674
+ return result({ decision, repoId: repository.repoId, checkoutId: repository.activeCheckoutId });
1675
+ }
1676
+ default:
1677
+ return failure(new Error(`UNKNOWN_REPOSITORY_TOOL: ${name}`));
1678
+ }
1679
+ } catch (error) {
1680
+ return failure(error);
1681
+ }
1682
+ }