@moretea-labs/forge 1.8.0 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (695) hide show
  1. package/AGENTS.md +73 -7
  2. package/CHANGELOG.md +34 -0
  3. package/CLAUDE.md +17 -2
  4. package/CONTRIBUTING.md +3 -2
  5. package/README.en.md +24 -3
  6. package/README.md +10 -6
  7. package/README.zh-CN.md +5 -3
  8. package/THIRD_PARTY_NOTICES.md +1 -2
  9. package/adapters/chatgpt/browser-delivery-host.ts +7 -1
  10. package/adapters/chatgpt/browser-delivery-runtime.ts +531 -56
  11. package/adapters/chatgpt/controller-binding-store.ts +101 -22
  12. package/adapters/chatgpt/controller-round-host.ts +15 -25
  13. package/adapters/chatgpt/controller-round-settlement-store.ts +58 -4
  14. package/adapters/chatgpt/provider-delivery.ts +229 -2
  15. package/adapters/chatgpt/work-conversation-binding-store.ts +99 -54
  16. package/adapters/computer/chatgpt-extension-target.ts +708 -0
  17. package/adapters/computer/chrome-extension/background.js +541 -0
  18. package/adapters/computer/chrome-extension/content.js +402 -0
  19. package/adapters/computer/chrome-extension/core.js +24 -0
  20. package/adapters/computer/chrome-extension/manifest.json +32 -0
  21. package/adapters/computer/desktop-operator-discovery.ts +11 -31
  22. package/adapters/computer/desktop-operator-provider.ts +18 -6
  23. package/adapters/computer/interaction-target-authority.ts +178 -24
  24. package/adapters/mcp/access-mode.ts +18 -39
  25. package/adapters/mcp/auth.ts +2 -7
  26. package/adapters/mcp/instructions.ts +6 -5
  27. package/adapters/mcp/multi-repository.ts +10 -47
  28. package/adapters/mcp/oauth.ts +73 -0
  29. package/adapters/mcp/paths.ts +4 -1
  30. package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
  31. package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
  32. package/adapters/mcp/runtime-gateway/context-adapter.ts +490 -113
  33. package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +83 -0
  34. package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +20 -5
  35. package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
  36. package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +10 -27
  37. package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
  38. package/adapters/mcp/runtime-gateway/execution-tools.ts +14 -54
  39. package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +2 -1
  40. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +132 -47
  41. package/adapters/mcp/runtime-gateway/process-tools.ts +173 -23
  42. package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +16 -4
  43. package/adapters/mcp/runtime-gateway/recovery-adapter.ts +69 -52
  44. package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +4 -35
  45. package/adapters/mcp/runtime-gateway/result-adapter.ts +10 -4
  46. package/adapters/mcp/runtime-gateway/router.ts +19 -507
  47. package/adapters/mcp/runtime-gateway/routing-policy.ts +506 -0
  48. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +15 -66
  49. package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
  50. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +123 -178
  51. package/adapters/mcp/runtime-gateway/runtime-tools.ts +11 -5
  52. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +37 -1
  53. package/adapters/mcp/runtime-gateway/shared-adapter.ts +20 -0
  54. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +416 -270
  55. package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +140 -0
  56. package/adapters/mcp/runtime-gateway/work-adapter.ts +262 -3123
  57. package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
  58. package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
  59. package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +229 -0
  60. package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
  61. package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
  62. package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
  63. package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
  64. package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
  65. package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +320 -0
  66. package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
  67. package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +180 -0
  68. package/adapters/mcp/server.ts +91 -26
  69. package/adapters/mcp/setup.ts +5 -13
  70. package/adapters/mcp/tool-mapping/access-tools.ts +0 -6
  71. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +703 -3092
  72. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +5 -10
  73. package/adapters/mcp/tool-mapping/repository-tools.ts +170 -213
  74. package/adapters/mcp/toolset-names.ts +11 -37
  75. package/adapters/mcp/toolset.ts +19 -92
  76. package/{src/cli → adapters}/mcp/transports/AGENTS.md +5 -5
  77. package/{src/cli → adapters}/mcp/transports/CLAUDE.md +5 -5
  78. package/adapters/mcp/transports/http-observation.ts +377 -0
  79. package/adapters/mcp/transports/http-post-lifecycle.ts +19 -0
  80. package/adapters/mcp/transports/http.ts +200 -683
  81. package/adapters/mcp/transports/oauth-http.ts +322 -0
  82. package/adapters/mcp/transports/session-registry.ts +6 -0
  83. package/adapters/mcp/tunnels/openai-secure-tunnel.ts +89 -5
  84. package/adapters/mcp/types.ts +0 -6
  85. package/assets/hooks/lib/session-state.sh +0 -38
  86. package/assets/hooks/post-bash.sh +1 -1
  87. package/assets/hooks/post-tool-observer.sh +0 -5
  88. package/assets/hooks/prompt-guard.sh +0 -55
  89. package/assets/partials/04-project-structure.partial.md +1 -1
  90. package/assets/partials/07-footer.partial.md +1 -1
  91. package/assets/partials-agents/02-operating-mode.partial.md +1 -1
  92. package/assets/partials-agents/08-deep-docs.partial.md +1 -1
  93. package/assets/plugin-registry.v1.json +6 -4
  94. package/assets/reference-configs/agentic-development-flow.md +26 -1
  95. package/assets/reference-configs/external-tooling.md +0 -76
  96. package/assets/reference-configs/global-working-rules.md +19 -0
  97. package/assets/reference-configs/release-deploy.md +1 -1
  98. package/assets/templates/helpers/architecture-queue.sh +1 -1
  99. package/assets/templates/helpers/check-agent-tooling.sh +1 -327
  100. package/assets/templates/helpers/check-task-workflow.sh +1 -1
  101. package/assets/templates/helpers/ensure-task-workflow.sh +0 -14
  102. package/assets/templates/helpers/migrate-project-template.sh +2 -2
  103. package/assets/workflow-contract.v1.json +0 -2
  104. package/docs/README.md +5 -2
  105. package/docs/architecture/CURRENT.md +205 -51
  106. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
  107. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
  108. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +42 -0
  109. package/docs/architecture/decisions/20260907-assistant-learning-loop.md +2 -0
  110. package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +19 -4
  111. package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
  112. package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
  113. package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +234 -0
  114. package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
  115. package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +74 -0
  116. package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +342 -0
  117. package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
  118. package/docs/architecture/decisions/20261009-exact-code-context-discovery.md +19 -0
  119. package/docs/architecture/decisions/20261009-generated-xcode-cache-retention.md +37 -0
  120. package/docs/architecture/decisions/20261011-autonomous-continuation-convergence.md +597 -0
  121. package/docs/architecture/decisions/20261017-edit-session-finalization-reconciliation.md +17 -0
  122. package/docs/architecture/decisions/20261019-retire-host-skills.md +16 -0
  123. package/docs/architecture/diagrams/agentic-dev-plugin-architecture.html +1 -1
  124. package/docs/architecture/domains/public-surface.md +3 -5
  125. package/docs/architecture/domains/verification.md +6 -5
  126. package/docs/architecture/domains/workflow-engine.md +6 -0
  127. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +3 -3
  128. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +4 -4
  129. package/docs/architecture/reviews/20261015-code-search-runtime-acceptance.md +75 -0
  130. package/docs/forge-plugin-management.md +16 -4
  131. package/docs/operations/controller-browser-plugin.md +23 -0
  132. package/docs/operations/controller-performance-and-502.md +17 -0
  133. package/docs/operations/controller-reliability-runbook.md +2 -2
  134. package/docs/operations/features.md +6 -7
  135. package/docs/operations/features.zh-CN.md +1 -1
  136. package/docs/operations/github-repository.md +4 -4
  137. package/docs/operations/github-semantic-release-projection.md +20 -0
  138. package/docs/operations/homebrew.md +2 -2
  139. package/docs/operations/mcp-tool-exposure.md +13 -52
  140. package/docs/operations/releasing.md +2 -0
  141. package/docs/operations/runtime-performance-diagnostics.md +8 -0
  142. package/docs/operations/standalone-disaster-recovery.md +15 -7
  143. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  144. package/docs/tutorials/03-first-repository-task.md +2 -0
  145. package/docs/wiki/Computer-and-Plugins.md +78 -0
  146. package/docs/wiki/Computer-and-Plugins.zh-CN.md +88 -0
  147. package/docs/wiki/Home.md +16 -2
  148. package/docs/wiki/Implementation.md +25 -13
  149. package/docs/wiki/Installation.md +28 -32
  150. package/docs/wiki/Integrations.md +4 -0
  151. package/docs/wiki/Quick-Start.md +35 -22
  152. package/docs/wiki/Releases-and-Upgrades.md +15 -25
  153. package/docs/wiki/_Sidebar.md +5 -1
  154. package/package.json +25 -25
  155. package/packages/kernel/cognition/api/index.ts +6 -0
  156. package/packages/kernel/cognition/application/activation-service.ts +451 -0
  157. package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
  158. package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
  159. package/packages/kernel/cognition/application/memory-service.ts +64 -0
  160. package/packages/kernel/cognition/domain/learning.ts +106 -0
  161. package/packages/kernel/cognition/domain/memory.ts +254 -0
  162. package/packages/kernel/controller/api/index.ts +1 -0
  163. package/packages/kernel/controller/application/continuation-service.ts +281 -0
  164. package/packages/kernel/controller/application/controller-round-service.ts +12 -0
  165. package/packages/kernel/controller/domain/controller-round-transition-policy.ts +242 -21
  166. package/packages/kernel/controller/domain/controller-round.ts +4 -1
  167. package/packages/kernel/controller/domain/execution-quality.ts +3 -1
  168. package/packages/kernel/controller/infrastructure/controller-round-store.ts +689 -103
  169. package/packages/kernel/controller/infrastructure/controller-session-store.ts +348 -97
  170. package/packages/kernel/controller/ports/controller-host.ts +2 -0
  171. package/packages/kernel/identity/api/index.ts +10 -0
  172. package/packages/kernel/identity/domain/activity-projection.ts +43 -0
  173. package/packages/kernel/identity/domain/capability-broker.ts +60 -0
  174. package/packages/kernel/identity/domain/grant.ts +72 -0
  175. package/packages/kernel/identity/domain/owned-resource.ts +116 -0
  176. package/packages/kernel/identity/domain/user-request.ts +93 -0
  177. package/packages/kernel/identity/infrastructure/activity-projection-store.ts +87 -0
  178. package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
  179. package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
  180. package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
  181. package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
  182. package/packages/kernel/scheduler/api/index.ts +0 -2
  183. package/packages/kernel/scheduler/application/eligibility.ts +4 -1
  184. package/packages/kernel/scheduler/application/schedule-service.ts +3 -0
  185. package/packages/kernel/scheduler/application/settlement.ts +0 -15
  186. package/packages/kernel/scheduler/domain/schedule.ts +87 -0
  187. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +166 -143
  188. package/packages/kernel/work/api/index.ts +3 -2
  189. package/packages/kernel/work/application/work-service.ts +18 -42
  190. package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
  191. package/packages/kernel/work/domain/engineering-design.ts +10 -5
  192. package/packages/kernel/work/domain/engineering-profile.ts +8 -12
  193. package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
  194. package/packages/kernel/work/domain/execution-concurrency.ts +19 -26
  195. package/packages/kernel/work/domain/execution-snapshot.ts +0 -31
  196. package/packages/kernel/work/domain/implementation-review.ts +14 -331
  197. package/packages/kernel/work/domain/objective-graph.ts +119 -0
  198. package/packages/kernel/work/domain/project-engineering-contract.ts +23 -1
  199. package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
  200. package/packages/kernel/work/domain/state-machine.ts +34 -65
  201. package/packages/kernel/work/domain/types.ts +57 -51
  202. package/packages/kernel/work/infrastructure/work-contract-store.ts +847 -729
  203. package/packages/kernel/work/ports/work-contract-store.ts +5 -2
  204. package/packages/plugin-runtime/browser/provider-registry.ts +12 -36
  205. package/packages/plugin-runtime/browser/session-authority.ts +2 -1
  206. package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
  207. package/packages/plugin-runtime/computer/conversation-target.ts +153 -0
  208. package/packages/plugin-runtime/computer/index.ts +2 -0
  209. package/packages/plugin-runtime/computer/target-authority.ts +26 -1
  210. package/packages/plugin-runtime/external/unix-jsonl-transport.ts +142 -18
  211. package/packages/protocols/computer/contract.ts +27 -1
  212. package/scripts/activate-source-baseline.ts +7 -9
  213. package/scripts/assemble-template.ts +11 -72
  214. package/scripts/canary-controller-semantic-wait.ts +12 -24
  215. package/scripts/check-mcp-compatibility.ts +94 -201
  216. package/scripts/check-package-identity.mjs +0 -5
  217. package/scripts/check-runtime-architecture.mjs +921 -239
  218. package/scripts/forge-local-recovery-helper.mjs +93 -15
  219. package/scripts/init-project.sh +0 -46
  220. package/scripts/install-ios-hid-toolchain.sh +60 -0
  221. package/scripts/install-standalone-recovery.ts +1 -1
  222. package/scripts/lib/project-init-lib.sh +6 -3
  223. package/scripts/migrate-windows-wsl-controller-home.ts +2 -0
  224. package/scripts/open-source-audit-allowlist.txt +1 -3
  225. package/scripts/run-bounded-command.mjs +5 -0
  226. package/scripts/run-governed-gate.ts +2 -1
  227. package/scripts/run-mobile-monitor-bridge.ts +0 -1
  228. package/scripts/smoke-control-plane-failure-domain.ts +94 -0
  229. package/scripts/smoke-ios-physical-device.ts +38 -10
  230. package/scripts/smoke-mcp-http-runtime.ts +12 -8
  231. package/scripts/smoke-mcp-tool-surface.ts +27 -41
  232. package/scripts/smoke-runtime-recovery.ts +12 -43
  233. package/scripts/smoke-schedule-engine.ts +23 -15
  234. package/scripts/smoke-unattended-transport-recovery.ts +223 -0
  235. package/scripts/smoke-user-request-continuation-restart.ts +232 -0
  236. package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +95 -0
  237. package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
  238. package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
  239. package/scripts/smoke-workflow-supervisor.ts +103 -0
  240. package/scripts/sync-generated-authority.mjs +0 -1
  241. package/scripts/verify-forge-runtime.sh +2 -1
  242. package/src/cli/agent-jobs/executor-health.ts +12 -35
  243. package/src/cli/agent-jobs/integration.ts +20 -51
  244. package/src/cli/agent-jobs/job-manager.ts +7 -3
  245. package/src/cli/chatgpt-browser/file-policy.ts +2 -2
  246. package/src/cli/commands/brain-assistant.ts +27 -13
  247. package/src/cli/commands/brain.ts +13 -15
  248. package/src/cli/commands/chatgpt.ts +1 -163
  249. package/src/cli/commands/doctor.ts +1 -143
  250. package/src/cli/commands/global-runtime.ts +3 -196
  251. package/src/cli/commands/init-hook.ts +6 -23
  252. package/src/cli/commands/init.ts +3 -252
  253. package/src/cli/commands/mcp.ts +0 -5
  254. package/src/cli/commands/prompt-guard-decision.ts +0 -2
  255. package/src/cli/commands/recovery.ts +137 -135
  256. package/src/cli/commands/repository.ts +17 -1
  257. package/src/cli/commands/runtime.ts +3 -3
  258. package/src/cli/commands/setup-execution.ts +41 -0
  259. package/src/cli/commands/setup-profile.ts +8 -0
  260. package/src/cli/controller/check-runner.ts +13 -2
  261. package/src/cli/controller/completion-backlog.ts +3 -3
  262. package/src/cli/controller/completion-orchestrator.ts +5 -44
  263. package/src/cli/controller/completion-target.ts +3 -72
  264. package/src/cli/controller/composite-operations.ts +1 -1
  265. package/src/cli/controller/context/instruction-context.ts +1 -1
  266. package/src/cli/controller/context/known-paths.ts +2 -2
  267. package/src/cli/controller/context/query-planning.ts +11 -4
  268. package/src/cli/controller/context/rule-resolution.ts +2 -2
  269. package/src/cli/controller/context/source-materializer.ts +83 -102
  270. package/src/cli/controller/context/structural-provider.ts +84 -0
  271. package/src/cli/controller/context/structural.ts +6 -6
  272. package/src/cli/controller/context/types.ts +8 -9
  273. package/src/cli/controller/context-pack.ts +107 -84
  274. package/src/cli/controller/execution-completion.ts +4 -7
  275. package/src/cli/controller/execution-policy.ts +36 -153
  276. package/src/cli/controller/governance.ts +2 -3
  277. package/src/cli/controller/issue-store.ts +4 -99
  278. package/src/cli/controller/launch-agents.ts +59 -18
  279. package/src/cli/controller/operational-plan.ts +25 -52
  280. package/src/cli/controller/runtime-config.ts +5 -1
  281. package/src/cli/controller/stuck-state-migration.ts +2 -3
  282. package/src/cli/controller/systemd-user.ts +42 -10
  283. package/src/cli/editing/edit-session.ts +18 -12
  284. package/src/cli/github/github.ts +4 -4
  285. package/src/cli/github/semantic-projection.ts +174 -0
  286. package/src/cli/hook/prompt-intents.ts +0 -9
  287. package/src/cli/index.ts +1 -43
  288. package/src/cli/local-bridge/connector-freshness.ts +1 -12
  289. package/src/cli/local-bridge/console-automations.ts +19 -14
  290. package/src/cli/local-bridge/console-view-models.ts +4 -13
  291. package/src/cli/local-bridge/facade-api.ts +64 -193
  292. package/src/cli/local-bridge/server.ts +24 -147
  293. package/src/cli/local-bridge/types.ts +0 -1
  294. package/src/cli/mcp/oauth-client-retirement.ts +74 -0
  295. package/src/cli/repositories/command-classifier.ts +0 -9
  296. package/src/cli/repositories/command-executor.ts +29 -23
  297. package/src/cli/repositories/command-scope.ts +37 -18
  298. package/src/cli/repositories/controller-home.ts +48 -0
  299. package/src/cli/repositories/ephemeral-workspace.ts +20 -30
  300. package/src/cli/repositories/git-command-executor.ts +2 -2
  301. package/src/cli/repositories/locks.ts +20 -0
  302. package/src/cli/repositories/registry.ts +79 -12
  303. package/src/cli/repositories/safe-patch.ts +2 -12
  304. package/src/cli/repositories/structured-git.ts +101 -5
  305. package/src/cli/repository/inspector.ts +185 -343
  306. package/src/cli/repository/session-cache.ts +92 -6
  307. package/src/effects/process-runner.ts +17 -1
  308. package/src/runtime/context/assistant-context.ts +20 -7
  309. package/src/runtime/context/assistant-work-context.ts +89 -27
  310. package/src/runtime/context/automatic-learning.ts +565 -0
  311. package/src/runtime/context/cognitive-attention.ts +173 -0
  312. package/src/runtime/context/cognitive-skill-canary.ts +158 -0
  313. package/src/runtime/context/context-closure.ts +46 -9
  314. package/src/runtime/context/context-pack-process.ts +103 -0
  315. package/src/runtime/context/context-plane.ts +1 -9
  316. package/src/runtime/context/direct-controller-learning.ts +298 -0
  317. package/src/runtime/context/generic-lsp-provider.ts +1 -1
  318. package/src/runtime/context/semantic-navigation-contract.ts +16 -0
  319. package/src/runtime/context/semantic-navigation.ts +7 -35
  320. package/src/runtime/context/typescript-navigation-process.ts +145 -0
  321. package/src/runtime/context/typescript-navigation.ts +89 -3
  322. package/src/runtime/control-plane/cleanup-artifact-retention.ts +215 -12
  323. package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +9 -47
  324. package/src/runtime/control-plane/execution/controller-authority-recovery.ts +34 -4
  325. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +29 -20
  326. package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
  327. package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
  328. package/src/runtime/control-plane/execution/retained-work-resume.ts +206 -199
  329. package/src/runtime/control-plane/execution/session-store.ts +31 -1
  330. package/src/runtime/control-plane/execution/validation.ts +6 -8
  331. package/src/runtime/control-plane/execution/work-authority-reconciler.ts +13 -42
  332. package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
  333. package/src/runtime/control-plane/execution/work-completion-authority.ts +30 -85
  334. package/src/runtime/control-plane/execution/work-delivery-service.ts +616 -0
  335. package/src/runtime/control-plane/execution/work-evidence-policy.ts +12 -50
  336. package/src/runtime/control-plane/execution/work-execution-support.ts +50 -149
  337. package/src/runtime/control-plane/execution/work-handle-authority.ts +197 -110
  338. package/src/runtime/control-plane/execution/work-handle-store.ts +72 -44
  339. package/src/runtime/control-plane/execution/work-head-settlement.ts +53 -20
  340. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +52 -29
  341. package/src/runtime/control-plane/execution/work-operation-service.ts +60 -34
  342. package/src/runtime/control-plane/execution/work-preparation-service.ts +231 -267
  343. package/src/runtime/control-plane/execution/work-process-evidence.ts +2 -2
  344. package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
  345. package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
  346. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +201 -199
  347. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +33 -85
  348. package/src/runtime/control-plane/execution/work-verification-service.ts +406 -50
  349. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +6 -2
  350. package/src/runtime/control-plane/facade/capability-registry.ts +364 -37
  351. package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
  352. package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
  353. package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
  354. package/src/runtime/control-plane/facade/goal-workloop-access.ts +1 -20
  355. package/src/runtime/control-plane/facade/goal-workloop.ts +281 -1753
  356. package/src/runtime/control-plane/facade/handoff-inbox-application.ts +12 -25
  357. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +256 -52
  358. package/src/runtime/control-plane/facade/index.ts +0 -1
  359. package/src/runtime/control-plane/facade/operation-digest.ts +12 -15
  360. package/src/runtime/control-plane/facade/plan-contract-store.ts +451 -991
  361. package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
  362. package/src/runtime/control-plane/facade/repository-work-admission.ts +12 -22
  363. package/src/runtime/control-plane/facade/requirement-authority.ts +102 -199
  364. package/src/runtime/control-plane/facade/requirement-board.ts +3 -3
  365. package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +51 -14
  366. package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
  367. package/src/runtime/control-plane/facade/suggested-actions.ts +15 -18
  368. package/src/runtime/control-plane/facade/types.ts +49 -108
  369. package/src/runtime/control-plane/facade/work-continuation.ts +9 -9
  370. package/src/runtime/control-plane/generated-cache-retention.ts +206 -59
  371. package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
  372. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +1 -1
  373. package/src/runtime/control-plane/global-scheduler/maintenance.ts +256 -117
  374. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -12
  375. package/src/runtime/control-plane/global-scheduler/scheduler.ts +66 -40
  376. package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
  377. package/src/runtime/control-plane/governance/access-policy.ts +19 -2
  378. package/src/runtime/control-plane/governance/authorization.ts +20 -3
  379. package/src/runtime/control-plane/governance/external-effects.ts +1 -1
  380. package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +63 -300
  381. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +29 -300
  382. package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
  383. package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +5 -2
  384. package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -240
  385. package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
  386. package/src/runtime/control-plane/persistence/experience-store.ts +162 -29
  387. package/src/runtime/control-plane/persistence/requirement-store.ts +216 -74
  388. package/src/runtime/control-plane/persistence/sqlite-store.ts +113 -6
  389. package/src/runtime/control-plane/release-retention.ts +135 -47
  390. package/src/runtime/control-plane/repository-namespace-retention.ts +2 -2
  391. package/src/runtime/control-plane/runtime-cleanup.ts +295 -107
  392. package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
  393. package/src/runtime/control-plane/workspace/semantic-sync-service.ts +19 -5
  394. package/src/runtime/control-plane/workspace/workspace-store.ts +169 -0
  395. package/src/runtime/diagnostics/incident-repair.ts +313 -146
  396. package/src/runtime/diagnostics/mcp-timing.ts +114 -0
  397. package/src/runtime/evidence/artifact-store.ts +158 -40
  398. package/src/runtime/evidence/evidence-store.ts +112 -8
  399. package/src/runtime/evidence/output-handle-index.ts +225 -0
  400. package/src/runtime/evidence/result-store.ts +158 -44
  401. package/src/runtime/evidence/sensitive-output.ts +22 -2
  402. package/src/runtime/execution/jobs/child-reference.ts +11 -0
  403. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -11
  404. package/src/runtime/execution/jobs/store.ts +3 -4
  405. package/src/runtime/execution/managed-workspace.ts +57 -0
  406. package/src/runtime/execution/process-runtime/check-facade.ts +1 -1
  407. package/src/runtime/execution/process-runtime/check-result-retention.ts +2 -2
  408. package/src/runtime/execution/process-runtime/check-result.ts +105 -1
  409. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +18 -8
  410. package/src/runtime/execution/process-runtime/command-facade.ts +82 -30
  411. package/src/runtime/execution/process-runtime/gc.ts +15 -2
  412. package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
  413. package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
  414. package/src/runtime/execution/process-runtime/index.ts +3 -0
  415. package/src/runtime/execution/process-runtime/lightweight-managed.ts +251 -72
  416. package/src/runtime/execution/process-runtime/persisted-check.ts +3 -3
  417. package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
  418. package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
  419. package/src/runtime/execution/process-runtime/resource-claims.ts +65 -61
  420. package/src/runtime/execution/process-runtime/runtime.ts +55 -18
  421. package/src/runtime/execution/process-runtime/store.ts +30 -2
  422. package/src/runtime/execution/process-runtime/types.ts +6 -1
  423. package/src/runtime/execution/thin-harness/execution-router.ts +16 -16
  424. package/src/runtime/execution/thin-harness/fast-executor.ts +2 -2
  425. package/src/runtime/execution/thin-harness/search-worker.ts +2 -2
  426. package/src/runtime/execution/workers/executor.ts +3 -8
  427. package/src/runtime/maintenance/cleanup.ts +41 -5
  428. package/src/runtime/platform/service-inventory.ts +130 -0
  429. package/src/runtime/platform/service-manager.ts +8 -0
  430. package/src/runtime/plugins/action-application.ts +97 -29
  431. package/src/runtime/plugins/app-store-connect-adapter.ts +290 -18
  432. package/src/runtime/plugins/browser-adapter.ts +1124 -333
  433. package/src/runtime/plugins/browser-automation-service.ts +23 -3
  434. package/src/runtime/plugins/browser-macos-bridge.ts +431 -137
  435. package/src/runtime/plugins/browser-manifest-surface.ts +47 -12
  436. package/src/runtime/plugins/browser-native-session-service.ts +63 -13
  437. package/src/runtime/plugins/browser-registration.ts +18 -0
  438. package/src/runtime/plugins/browser-runtime.ts +40 -36
  439. package/src/runtime/plugins/browser-session-authority.ts +2 -2
  440. package/src/runtime/plugins/browser-session-store.ts +46 -76
  441. package/src/runtime/plugins/capability-authorization-grants.ts +258 -62
  442. package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +228 -0
  443. package/src/runtime/plugins/computer-registration.ts +298 -20
  444. package/src/runtime/plugins/desktop-operator-external-policy.ts +36 -4
  445. package/src/runtime/plugins/desktop-operator-registration.ts +12 -4
  446. package/src/runtime/plugins/external-adapter.ts +42 -6
  447. package/src/runtime/plugins/external-registration.ts +21 -1
  448. package/src/runtime/plugins/github-adapter.ts +30 -3
  449. package/src/runtime/plugins/gmail-adapter.ts +189 -3
  450. package/src/runtime/plugins/google-calendar-adapter.ts +2 -2
  451. package/src/runtime/plugins/google-shared.ts +24 -12
  452. package/src/runtime/plugins/google-tasks-adapter.ts +2 -2
  453. package/src/runtime/plugins/ios/remote-xpc-hid.ts +70 -30
  454. package/src/runtime/plugins/ios-adapter.ts +46 -41
  455. package/src/runtime/plugins/ios-agent-device.ts +54 -4
  456. package/src/runtime/plugins/ios-device-ownership.ts +103 -0
  457. package/src/runtime/plugins/ios-physical-device.ts +497 -79
  458. package/src/runtime/plugins/lightweight-action.ts +208 -2
  459. package/src/runtime/plugins/local-system-adapter.ts +12 -100
  460. package/src/runtime/plugins/managed-process-adapter.ts +12 -5
  461. package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
  462. package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
  463. package/src/runtime/plugins/store.ts +682 -435
  464. package/src/runtime/plugins/types.ts +24 -0
  465. package/src/runtime/plugins/xiaohongshu-publish.ts +16 -0
  466. package/src/runtime/projections/git-status-sampler.ts +2 -19
  467. package/src/runtime/projections/invalidation.ts +7 -3
  468. package/src/runtime/projections/materialized-view.ts +80 -36
  469. package/src/runtime/recovery/actions.ts +4 -4
  470. package/src/runtime/recovery/capability-action-application.ts +46 -45
  471. package/src/runtime/recovery/lifecycle-inventory.ts +30 -40
  472. package/src/runtime/recovery/maintenance-executor.ts +374 -123
  473. package/src/runtime/recovery/probe.ts +2 -2
  474. package/src/runtime/recovery/store.ts +29 -11
  475. package/src/runtime/release/release-coordinator.ts +312 -0
  476. package/src/runtime/release/release-session.ts +716 -0
  477. package/src/runtime/release/release-state-migration.ts +25 -0
  478. package/src/runtime/resources/leases/store.ts +56 -25
  479. package/src/runtime/root/computer-composition.ts +191 -6
  480. package/src/runtime/root/controller-progression-composition.ts +254 -0
  481. package/src/runtime/root/controller-round-composition.ts +20 -1
  482. package/src/runtime/root/deployment-topology.ts +106 -0
  483. package/src/runtime/root/entry.ts +4 -3
  484. package/src/runtime/root/known-good-recovery.ts +184 -0
  485. package/src/runtime/root/local-bridge.ts +0 -1
  486. package/src/runtime/root/mcp-transport.ts +111 -18
  487. package/src/runtime/root/ownership.ts +261 -9
  488. package/src/runtime/root/package-connector-service.ts +227 -29
  489. package/src/runtime/root/package-runtime-release.ts +117 -30
  490. package/src/runtime/root/package-runtime-service.ts +24 -24
  491. package/src/runtime/root/release-execution-canary.ts +57 -12
  492. package/src/runtime/root/release-loader.ts +90 -8
  493. package/src/runtime/root/release-manifest.ts +80 -40
  494. package/src/runtime/root/release-materialize.ts +372 -106
  495. package/src/runtime/root/release-store.ts +395 -36
  496. package/src/runtime/root/runtime-lane.ts +191 -0
  497. package/src/runtime/root/runtime.ts +160 -29
  498. package/src/runtime/root/scheduled-controller-composition.ts +29 -3
  499. package/src/runtime/root/service-runner.ts +2 -2
  500. package/src/runtime/root/service.ts +126 -44
  501. package/src/runtime/root/status.ts +48 -0
  502. package/src/runtime/root/types.ts +32 -9
  503. package/src/runtime/root/workflow-supervisor-composition.ts +681 -0
  504. package/src/runtime/root/workflow-supervisor-runtime.ts +564 -0
  505. package/src/runtime/root/write-fence.ts +50 -2
  506. package/src/runtime/safe-tooling/google-credential-store.ts +67 -22
  507. package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
  508. package/src/runtime/safe-tooling/ios-development.ts +22 -8
  509. package/src/runtime/safe-tooling/job-summary.ts +1 -1
  510. package/src/runtime/safe-tooling/workspace-auth.ts +5 -2
  511. package/src/runtime/shared/atomic-text-file.ts +21 -0
  512. package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
  513. package/src/runtime/shared/json-files.ts +8 -3
  514. package/src/runtime/shared/process-environment.ts +20 -9
  515. package/src/runtime/shared/response-budget.ts +1 -3
  516. package/src/runtime/standalone-recovery/controller-home-migration.ts +16 -24
  517. package/src/runtime/standalone-recovery/core.ts +3792 -417
  518. package/src/runtime/standalone-recovery/entry.ts +507 -45
  519. package/src/runtime/standalone-recovery/installer.ts +262 -148
  520. package/src/runtime/standalone-recovery/mcp-server.ts +29 -193
  521. package/src/runtime/standalone-recovery/operation-lock.ts +12 -2
  522. package/src/runtime/standalone-recovery/performance.ts +54 -16
  523. package/src/runtime/standalone-recovery/release.ts +53 -5
  524. package/src/runtime/standalone-recovery/service-labels.ts +5 -2
  525. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
  526. package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
  527. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +11 -3
  528. package/src/runtime/workflow/schedules/engine.ts +166 -178
  529. package/src/runtime/workflow/schedules/pending-activation.ts +11 -4
  530. package/src/runtime/workflow/schedules/work-continuation.ts +95 -11
  531. package/src/runtime/workflows/runtime.ts +15 -19
  532. package/src/testing/test-governance.ts +5 -1
  533. package/supervisor/chatgpt-conversation.ts +23 -0
  534. package/supervisor/client.ts +218 -0
  535. package/supervisor/control-plane.ts +963 -0
  536. package/supervisor/entry.ts +15 -0
  537. package/supervisor/forge-validators.ts +147 -0
  538. package/supervisor/index.ts +5 -0
  539. package/supervisor/native-browser-adapter.ts +1151 -0
  540. package/supervisor/native-messaging/host.ts +58 -0
  541. package/supervisor/native-messaging/manifest.ts +26 -0
  542. package/supervisor/native-messaging/protocol.ts +27 -0
  543. package/supervisor/paths.ts +18 -0
  544. package/supervisor/protocol.ts +250 -0
  545. package/supervisor/server.ts +412 -0
  546. package/supervisor/store.ts +1870 -0
  547. package/supervisor/tsconfig.json +8 -0
  548. package/supervisor/types.ts +261 -0
  549. package/SKILL.md +0 -427
  550. package/adapters/chatgpt/controller-host.ts +0 -119
  551. package/adapters/mcp/controller-round-compatibility.ts +0 -184
  552. package/adapters/mcp/frozen-client-semantic-compatibility.ts +0 -292
  553. package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +0 -502
  554. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +0 -1
  555. package/adapters/mcp/toolset-selection.ts +0 -41
  556. package/agents/openai.yaml +0 -4
  557. package/assets/skill-hooks.json +0 -41
  558. package/assets/skill-version.json +0 -189
  559. package/assets/skills/claude-review/SKILL.md +0 -231
  560. package/assets/skills/codex-review/SKILL.md +0 -103
  561. package/assets/templates/helpers/check-skill-version.ts +0 -193
  562. package/packages/kernel/progression/api/index.ts +0 -2
  563. package/packages/kernel/progression/application/projector.ts +0 -211
  564. package/packages/kernel/progression/domain/types.ts +0 -116
  565. package/packages/kernel/progression/index.ts +0 -1
  566. package/packages/kernel/scheduler/application/continuation-service.ts +0 -212
  567. package/packages/kernel/scheduler/domain/continuation.ts +0 -38
  568. package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +0 -38
  569. package/packages/kernel/work/domain/admission-policy.ts +0 -34
  570. package/scripts/benchmark-background-check-overlap.ts +0 -228
  571. package/scripts/benchmark-computer-hotpath.ts +0 -255
  572. package/scripts/benchmark-controller-hotpath.ts +0 -212
  573. package/scripts/benchmark-controller-round2.ts +0 -191
  574. package/scripts/benchmark-ios-agent-device.ts +0 -511
  575. package/scripts/benchmark-mcp-host.sh +0 -117
  576. package/scripts/benchmark-mcp-transport.ts +0 -169
  577. package/scripts/benchmark-operational-memory-activation.ts +0 -235
  578. package/scripts/benchmark-operational-shadow.ts +0 -28
  579. package/scripts/benchmark-quality-harness.ts +0 -146
  580. package/scripts/benchmark-route-session-concurrency.ts +0 -915
  581. package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
  582. package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
  583. package/scripts/benchmark-thin-harness.ts +0 -251
  584. package/scripts/benchmark-typescript-navigation.ts +0 -186
  585. package/scripts/benchmark-work-efficiency.ts +0 -334
  586. package/scripts/build-controller-ui.ts +0 -28
  587. package/scripts/check-npm-release.sh +0 -32
  588. package/scripts/check-public-release-surface.sh +0 -19
  589. package/scripts/check-release-published.sh +0 -57
  590. package/scripts/check-release-readiness.sh +0 -37
  591. package/scripts/check-release-version.mjs +0 -74
  592. package/scripts/check-skill-version.ts +0 -5
  593. package/scripts/check-tarball-install-smoke.sh +0 -150
  594. package/scripts/ensure-codegraph.sh +0 -16
  595. package/scripts/package-source-archive.sh +0 -147
  596. package/scripts/public-release-files.txt +0 -39
  597. package/scripts/public-release.ts +0 -174
  598. package/scripts/publish-release-tarball.sh +0 -27
  599. package/scripts/route-nl-vs-ts-eval.ts +0 -679
  600. package/scripts/run-skill-hook.ts +0 -244
  601. package/scripts/stage-runtime-release.ts +0 -44
  602. package/scripts/sync-codex-installed-copies.sh +0 -182
  603. package/src/cli/commands/tools.ts +0 -167
  604. package/src/cli/controller/progress.ts +0 -656
  605. package/src/cli/controller/work-mode.ts +0 -181
  606. package/src/cli/local-bridge/console-assets.ts +0 -17
  607. package/src/cli/local-bridge/dashboard.ts +0 -17
  608. package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
  609. package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
  610. package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
  611. package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
  612. package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
  613. package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
  614. package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
  615. package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
  616. package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
  617. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
  618. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
  619. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
  620. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
  621. package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
  622. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
  623. package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
  624. package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
  625. package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
  626. package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
  627. package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
  628. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
  629. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
  630. package/src/cli/local-bridge/frontend/src/styles.css +0 -28
  631. package/src/cli/local-bridge/frontend/src/types.ts +0 -15
  632. package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
  633. package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
  634. package/src/cli/local-bridge/ui-dist/app.css +0 -1
  635. package/src/cli/local-bridge/ui-dist/app.js +0 -11228
  636. package/src/cli/mcp/access-mode.ts +0 -2
  637. package/src/cli/mcp/access-tools.ts +0 -2
  638. package/src/cli/mcp/audit.ts +0 -2
  639. package/src/cli/mcp/auth.ts +0 -2
  640. package/src/cli/mcp/instructions.ts +0 -2
  641. package/src/cli/mcp/legacy-context.ts +0 -2
  642. package/src/cli/mcp/legacy-tool-service.ts +0 -2
  643. package/src/cli/mcp/multi-repository.ts +0 -2
  644. package/src/cli/mcp/oauth.ts +0 -2
  645. package/src/cli/mcp/openai-secure-tunnel.ts +0 -2
  646. package/src/cli/mcp/paths.ts +0 -2
  647. package/src/cli/mcp/policy.ts +0 -2
  648. package/src/cli/mcp/post-finalize-work-attribution.ts +0 -2
  649. package/src/cli/mcp/proxy-env.ts +0 -2
  650. package/src/cli/mcp/readiness-projection.ts +0 -2
  651. package/src/cli/mcp/redaction.ts +0 -2
  652. package/src/cli/mcp/repo.ts +0 -2
  653. package/src/cli/mcp/repository-tools.ts +0 -2
  654. package/src/cli/mcp/server.ts +0 -2
  655. package/src/cli/mcp/setup.ts +0 -2
  656. package/src/cli/mcp/tool-capability-cache.ts +0 -2
  657. package/src/cli/mcp/tool-contract.ts +0 -2
  658. package/src/cli/mcp/tools.ts +0 -2
  659. package/src/cli/mcp/toolset-names.ts +0 -2
  660. package/src/cli/mcp/toolset-selection.ts +0 -2
  661. package/src/cli/mcp/toolset.ts +0 -2
  662. package/src/cli/mcp/transports/http.ts +0 -2
  663. package/src/cli/mcp/transports/session-registry.ts +0 -2
  664. package/src/cli/mcp/transports/stdio.ts +0 -2
  665. package/src/cli/mcp/types.ts +0 -2
  666. package/src/cli/mcp/umbrella-tools.ts +0 -2
  667. package/src/cli/tools/codegraph.ts +0 -695
  668. package/src/core/adoption/helper-wrapper-plan.ts +0 -159
  669. package/src/runtime/context/codegraph-cache-boundary.ts +0 -255
  670. package/src/runtime/context/codegraph-read-provider.ts +0 -611
  671. package/src/runtime/context/codegraph-sidecar.cjs +0 -248
  672. package/src/runtime/control-plane/codegraph-cache-retention.ts +0 -164
  673. package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +0 -169
  674. package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +0 -85
  675. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -655
  676. package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -2850
  677. package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
  678. package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -468
  679. package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -3
  680. package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -142
  681. package/src/runtime/control-plane/facade/work-implementation-review.ts +0 -2
  682. package/src/runtime/control-plane/facade/work-state-machine.ts +0 -2
  683. package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
  684. package/src/runtime/control-plane/routing/index.ts +0 -1
  685. package/src/runtime/control-plane/routing/route-policy.ts +0 -401
  686. package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
  687. package/src/runtime/gateway/mcp/execution-tools.ts +0 -2
  688. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +0 -2
  689. package/src/runtime/gateway/mcp/persisted-check-process.ts +0 -2
  690. package/src/runtime/gateway/mcp/process-tools.ts +0 -2
  691. package/src/runtime/gateway/mcp/router.ts +0 -2
  692. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +0 -2
  693. package/src/runtime/gateway/mcp/runtime-tools.ts +0 -2
  694. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +0 -2
  695. package/src/runtime/plugins/browser-provider-registry.ts +0 -2
@@ -1,169 +1,35 @@
1
1
  import type { CallToolResult, McpToolDefinition } from '../../../packages/protocols/mcp/tool-contract';
2
2
  export type { CallToolResult, McpToolDefinition } from '../../../packages/protocols/mcp/tool-contract';
3
3
  import { createHash } from "crypto";
4
- import {
5
- existsSync,
6
- mkdirSync,
7
- readdirSync,
8
- readFileSync,
9
- statSync,
10
- writeFileSync,
11
- appendFileSync,
12
- } from "fs";
4
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync, appendFileSync } from "fs";
13
5
  import { basename, dirname, join } from "path";
14
- import { runProcess } from "../../../src/effects/process-runner";
15
- import {
16
- beginEditSession,
17
- applyEditOperations,
18
- createEditSavepoint,
19
- EditSessionPatchError,
20
- finalizeEditSession,
21
- getEditSession,
22
- getEditSessionDiff,
23
- listEditSessions,
24
- PREFERRED_EDIT_PATCH_BATCH_OPERATIONS,
25
- rollbackEditSession,
26
- verifyEditSession,
27
- } from "../../../src/cli/editing/edit-session";
28
- import {
29
- EDIT_OPERATION_INPUT_SCHEMA,
30
- normalizeEditOperations,
31
- } from "../../../src/cli/editing/edit-operation-contract";
32
- import {
33
- cancelAgentJob,
34
- getAgentJob,
35
- getAgentJobEvents,
36
- getAgentJobLog,
37
- listAgentJobs,
38
- } from "../../../src/cli/agent-jobs/job-manager";
39
- import {
40
- classifyGitHubCopilotPreflight,
41
- classifyLocalExecutorHealth,
42
- executorHealthCode,
43
- isExecutorHealthError,
44
- type ExecutorHealth,
45
- } from "../../../src/cli/agent-jobs/executor-health";
46
- import { integrateAgentJob, taskRunDiff } from "../../../src/cli/agent-jobs/integration";
47
- import {
48
- appendTask,
49
- archiveIssue,
50
- createIssue,
51
- getIssue,
52
- getIssueEffectiveView,
53
- getIssueReadView,
54
- inspectIssueReadiness,
55
- inspectTaskReadiness,
56
- acceptVerifiedTask,
57
- listIssueEffectiveViews,
58
- listIssues,
59
- projectIssueEffectiveView,
60
- planIssue,
61
- projectBoard,
62
- recordTaskVerification,
63
- restoreIssue,
64
- setTaskDependencies,
65
- splitTask,
66
- supersedeTask,
67
- updateIssue,
68
- updateTask,
69
- } from "../../../src/cli/controller/issue-store";
70
- import { applyMigratedIssueDecision } from '../../../src/cli/controller/migrated-issue-decision';
71
- import { legacyIssueAuthorityRetired } from '../../../src/cli/controller/legacy-issue-cutover';
72
- import {
73
- listControllerChecks,
74
- } from "../../../src/cli/controller/check-runner";
75
- import { normalizeCheckIds } from '../../../src/runtime/control-plane/facade/check-normalization';
6
+ import { beginEditSession, applyEditOperations, createEditSavepoint, EditSessionPatchError, finalizeEditSession, getEditSession, getEditSessionDiff, listEditSessions, PREFERRED_EDIT_PATCH_BATCH_OPERATIONS, rollbackEditSession } from "../../../src/cli/editing/edit-session";
7
+ import { EDIT_OPERATION_INPUT_SCHEMA, normalizeEditOperations } from "../../../src/cli/editing/edit-operation-contract";
8
+ import { listAgentJobs } from "../../../src/cli/agent-jobs/job-manager";
9
+ import { listControllerChecks } from "../../../src/cli/controller/check-runner";
76
10
  import { buildExecutionDiagnostics, buildRequirementBoard } from '../../../src/runtime/control-plane/facade/requirement-board';
77
- import { acceptVerifiedTaskFromControllerWork } from '../../../src/runtime/control-plane/execution/work-task-receipt';
78
11
  import { getWorkContract } from '../../../packages/kernel/work/api';
79
12
  import { admitDirectEditWorkContract } from '../../../src/runtime/control-plane/facade/repository-work-admission';
80
- import { decideRoute } from '../../../src/runtime/control-plane/routing/route-policy';
81
13
  import { listCapabilityDescriptors, summarizeCapabilityGroups } from '../../../src/runtime/control-plane/facade/capability-registry';
82
- import {
83
- getControllerTimeline,
84
- getProjectProgress,
85
- getTaskProgressDetail,
86
- getTaskProgressReadView,
87
- } from "../../../src/cli/controller/progress";
88
- import { exportControllerWorklog, parseWorklogCategory } from "../../../src/cli/controller/worklog";
89
- import { inspectProjectGovernance, reconcileProjectGovernance } from "../../../src/cli/controller/governance";
90
- import { assessWorkMode, parseExplicitTaskMode } from "../../../src/cli/controller/work-mode";
91
- import { taskExecutionPolicy } from "../../../src/cli/controller/execution-policy";
92
- import { finishEditSession, finishTaskRun } from "../../../src/cli/controller/completion-orchestrator";
93
- import { buildControllerTaskLedgerProjection } from "../../../src/cli/controller/task-ledger";
94
- import { buildControllerContextPack } from "../../../src/cli/controller/context-pack";
95
- import { loadControllerProjectState, saveControllerProjectState } from "../../../src/cli/controller/project-state";
96
- import {
97
- FORGE_MCP_SCHEMA_VERSION,
98
- FORGE_TOOL_SURFACE,
99
- FORGE_VERSION,
100
- forgeToolSurfaceFingerprint,
101
- MIN_AGENT_TIMEOUT_MS,
102
- formatDurationMs,
103
- normalizeAgentTimeoutMs,
104
- } from "../../../src/cli/controller/runtime-config";
105
- import { DEFAULT_CONTROLLER_TOOL_NAMES, PREFERRED_FACADE_TOOL_NAMES } from "../toolset-names";
14
+ import { exportControllerWorklog, listControllerWorklogEvents, parseWorklogCategory } from "../../../src/cli/controller/worklog";
15
+ import { FORGE_MCP_SCHEMA_VERSION, FORGE_TOOL_SURFACE, FORGE_VERSION, forgeToolSurfaceFingerprint, MIN_AGENT_TIMEOUT_MS, formatDurationMs, normalizeAgentTimeoutMs } from "../../../src/cli/controller/runtime-config";
16
+ import { PREFERRED_FACADE_TOOL_NAMES } from "../toolset-names";
106
17
  import { buildLocalConnectorStatusForRepo } from "../../../src/cli/local-bridge/connector-freshness";
107
- import type {
108
- ControllerAgent,
109
- ControllerTask,
110
- IssueKind,
111
- IssueStatus,
112
- TaskDraft,
113
- TaskRisk,
114
- TaskCommandEvidence,
115
- TaskStatus,
116
- TaskVerification,
117
- } from "../../../src/cli/controller/types";
118
- import {
119
- gitDiff,
120
- gitSnapshot,
121
- searchRepository,
122
- } from "../../../src/cli/repository/inspector";
123
- import {
124
- closeGitHubIssue,
125
- getGitHubStatus,
126
- publishIssueToGitHub,
127
- refreshGitHubIssue,
128
- } from "../../../src/cli/github/github";
129
- import {
130
- getGitHubPluginStatus,
131
- saveGitHubPluginConfig,
132
- } from "../../../src/cli/github/plugin";
133
- import {
134
- getLocalBridgeJobEvents,
135
- getLocalBridgeJobSnapshot,
136
- listLocalBridgeJobs,
137
- readLocalBridgeJobOutput,
138
- reconcileLocalBridgeJobs,
139
- } from "../../../src/cli/local-bridge/job-store";
18
+ import { gitDiff, gitSnapshot, searchRepository } from "../../../src/cli/repository/inspector";
19
+ import { getGitHubStatus } from "../../../src/cli/github/github";
20
+ import { getGitHubPluginStatus, saveGitHubPluginConfig } from "../../../src/cli/github/plugin";
21
+ import { getLocalBridgeJobEvents, getLocalBridgeJobSnapshot, listLocalBridgeJobs, readLocalBridgeJobOutput, reconcileLocalBridgeJobs } from "../../../src/cli/local-bridge/job-store";
140
22
  import { runHelper } from "../../../src/cli/runtime/helper-runner";
141
- import {
142
- listSessions,
143
- openSession,
144
- readSession,
145
- runBrowserConsult,
146
- runBrowserFollowup,
147
- } from "../../../src/cli/chatgpt-browser/engine";
148
- import type {
149
- BrowserProviderName,
150
- NativeBrowserChannel,
151
- ThinkingLevel,
152
- } from "../../../src/cli/chatgpt-browser/types";
23
+ import { listSessions, openSession, readSession, runBrowserConsult, runBrowserFollowup } from "../../../src/cli/chatgpt-browser/engine";
24
+ import type { BrowserProviderName, NativeBrowserChannel, ThinkingLevel } from "../../../src/cli/chatgpt-browser/types";
153
25
  import { hashMcpInput, tryWriteMcpAuditEntry } from "../audit";
154
- import { loadMcpRuntimeState } from "../auth";
155
26
  import { resolveLocalBridgeSurface, summarizeRecentJobs } from "../../../src/runtime/shared/local-bridge-surface";
156
27
  import { resolveRepoPreferredControllerHome } from "../../../src/cli/repositories/controller-home";
157
28
  import { resolveRepositoryCheckStorage } from '../../../src/runtime/execution/process-runtime/check-storage';
158
29
  import { resolveRepositorySelection } from '../../../src/cli/repositories/registry';
159
30
  import { ensureRepositoryRuntimeStorageBinding } from '../../../src/cli/repositories/runtime-storage';
160
- import { executionIdentityForRepository } from '../../../src/runtime/control-plane/execution/execution-identity';
161
- import { runPersistedCheckViaProcessRuntime } from '../runtime-gateway/persisted-check-process';
162
- import { getProcessRecord } from '../../../src/runtime/execution/process-runtime/store';
163
- import { processCheckCompletionReceipt } from '../../../src/runtime/execution/process-runtime/check-receipt';
164
- import { normalizeMcpRelativePath, resolveMcpPath } from "../paths";
31
+ import { resolveMcpPath } from "../paths";
165
32
  import { currentGitBranch, isForgeAdopted } from "../repo";
166
- import { repositoryToolNames } from "./repository-tools";
167
33
  import { redactMcpText } from "../redaction";
168
34
  import type { McpPolicy } from "../types";
169
35
 
@@ -179,10 +45,14 @@ export interface McpToolContext {
179
45
  principalId?: string;
180
46
  /** Authenticated transport controller/provider type. */
181
47
  controllerType?: 'chatgpt' | 'codex' | 'claude' | 'grok' | 'human';
48
+ /** Host-provided ChatGPT session correlation. Never authorization and never a canonical conversation URL. */
49
+ hostConversationSessionId?: string;
182
50
  controllerInstanceId?: string;
183
51
  /** Bound repository identity for session cache keys. */
184
52
  repoId?: string;
185
53
  checkoutId?: string;
54
+ /** Canonical tools/list projection supplied by the MCP registry; never authored by the client. */
55
+ publishedToolNames?: readonly string[];
186
56
  /** Request-scoped MCP cancellation; never persisted or reused across calls. */
187
57
  signal?: AbortSignal;
188
58
  }
@@ -219,30 +89,6 @@ function errorResult(
219
89
  return result;
220
90
  }
221
91
 
222
- function dispatchErrorResult(
223
- code: string,
224
- message: string,
225
- options: {
226
- requestId?: string;
227
- retryable?: boolean;
228
- details?: unknown;
229
- } = {},
230
- ): CallToolResult {
231
- const payload = {
232
- accepted: false,
233
- code,
234
- message: redactMcpText(message).text,
235
- retryable: options.retryable === true,
236
- requestId: options.requestId,
237
- details: options.details,
238
- };
239
- const result = textResult({
240
- error: payload,
241
- ...payload,
242
- });
243
- result.isError = true;
244
- return result;
245
- }
246
92
 
247
93
  function audit(
248
94
  ctx: McpToolContext,
@@ -324,433 +170,25 @@ function knownRunPathReplacements(
324
170
  .map(([from, to]) => ({ from, to }));
325
171
  }
326
172
 
327
- function redactRunText(
328
- text: string,
329
- repoRoot: string,
330
- run: Partial<{
331
- executionRoot: string;
332
- worktree: string;
333
- worktreePath: string;
334
- }>,
335
- maxChars: number,
336
- ): { text: string; truncated: boolean; redactions: Array<{ type: string; count: number }> } {
337
- let scrubbed = text;
338
- for (const entry of knownRunPathReplacements(repoRoot, run)) {
339
- scrubbed = scrubbed.split(entry.from).join(entry.to);
340
- }
341
- scrubbed = scrubbed
342
- .replace(/\/(?:private\/)?tmp\/[^\s"']+/g, "<abs-path>")
343
- .replace(/\/(?:private\/)?var\/folders\/[^\s"']+/g, "<abs-path>")
344
- .replace(/\/Users\/[^\s"']+/g, "<abs-path>")
345
- .replace(/[A-Za-z]:\\[^\s"']+/g, "<abs-path>");
346
- const redacted = redactMcpText(scrubbed);
347
- if (redacted.text.length <= maxChars) {
348
- return { text: redacted.text, truncated: false, redactions: redacted.redactions };
349
- }
350
- return {
351
- text: redacted.text.slice(-maxChars),
352
- truncated: true,
353
- redactions: redacted.redactions,
354
- };
355
- }
356
173
 
357
- function maybeTail(
358
- value: string | undefined,
359
- repoRoot: string,
360
- run: Partial<{
361
- executionRoot: string;
362
- worktree: string;
363
- worktreePath: string;
364
- }>,
365
- maxChars: number,
366
- ): { text: string; truncated: boolean; redactions: Array<{ type: string; count: number }> } | undefined {
367
- if (!value) return undefined;
368
- return redactRunText(value, repoRoot, run, maxChars);
369
- }
370
174
 
371
- function summarizeAgentRun(
372
- repoRoot: string,
373
- run: Record<string, unknown>,
374
- options: {
375
- includePaths?: boolean;
376
- includeOutput?: boolean;
377
- tailChars?: number;
378
- } = {},
379
- ): Record<string, unknown> {
380
- const tailChars = Math.min(Math.max(options.tailChars ?? 4096, 256), 16 * 1024);
381
- const base: Record<string, unknown> = {
382
- runId: run.runId,
383
- issueId: run.issueId,
384
- taskId: run.taskId,
385
- agent: run.agent,
386
- provider: run.provider,
387
- executionMode: run.executionMode,
388
- executionClass: run.executionClass,
389
- allowedPaths: run.allowedPaths,
390
- status: run.status,
391
- closureState: run.closureState,
392
- closureUpdatedAt: run.closureUpdatedAt,
393
- preservationReason: run.preservationReason,
394
- preservationDetails: typeof run.preservationDetails === "string"
395
- ? redactRunText(run.preservationDetails, repoRoot, {
396
- executionRoot: typeof run.executionRoot === "string" ? run.executionRoot : undefined,
397
- worktree: typeof run.worktree === "string" ? run.worktree : undefined,
398
- worktreePath: typeof run.worktreePath === "string" ? run.worktreePath : undefined,
399
- }, 1000).text
400
- : run.preservationDetails,
401
- branch: run.branch,
402
- baseRevision: run.baseRevision,
403
- changedFiles: Array.isArray(run.changedFiles) ? run.changedFiles.slice(0, 100) : run.changedFiles,
404
- exitCode: run.exitCode,
405
- error: typeof run.error === "string"
406
- ? redactRunText(run.error, repoRoot, {
407
- executionRoot: typeof run.executionRoot === "string" ? run.executionRoot : undefined,
408
- worktree: typeof run.worktree === "string" ? run.worktree : undefined,
409
- worktreePath: typeof run.worktreePath === "string" ? run.worktreePath : undefined,
410
- }, 1000).text
411
- : run.error,
412
- executorHealth: run.executorHealth,
413
- timeoutMs: run.timeoutMs,
414
- deadlineAt: run.deadlineAt,
415
- startupDeadlineAt: run.startupDeadlineAt,
416
- lastHeartbeatAt: run.lastHeartbeatAt,
417
- progress: run.progress,
418
- autoIntegrate: run.autoIntegrate,
419
- autoIntegrationError: typeof run.autoIntegrationError === "string"
420
- ? redactRunText(run.autoIntegrationError, repoRoot, {
421
- executionRoot: typeof run.executionRoot === "string" ? run.executionRoot : undefined,
422
- worktree: typeof run.worktree === "string" ? run.worktree : undefined,
423
- worktreePath: typeof run.worktreePath === "string" ? run.worktreePath : undefined,
424
- }, 1000).text
425
- : run.autoIntegrationError,
426
- worktreeCleanedAt: run.worktreeCleanedAt,
427
- cleanupBranchDeletedAt: run.cleanupBranchDeletedAt,
428
- diffArtifactPath: run.diffArtifactPath,
429
- terminationReason: run.terminationReason,
430
- createdAt: run.createdAt,
431
- startedAt: run.startedAt,
432
- finishedAt: run.finishedAt,
433
- integratedSessionId: run.integratedSessionId,
434
- integratedAt: run.integratedAt,
435
- timing: run.timing,
436
- github: run.github && typeof run.github === "object"
437
- ? {
438
- owner: (run.github as Record<string, unknown>).owner,
439
- repo: (run.github as Record<string, unknown>).repo,
440
- taskId: (run.github as Record<string, unknown>).taskId,
441
- state: (run.github as Record<string, unknown>).state,
442
- url: (run.github as Record<string, unknown>).url,
443
- pullRequestUrl: (run.github as Record<string, unknown>).pullRequestUrl,
444
- baseRef: (run.github as Record<string, unknown>).baseRef,
445
- model: (run.github as Record<string, unknown>).model,
446
- createPullRequest: (run.github as Record<string, unknown>).createPullRequest,
447
- }
448
- : run.github,
449
- detail: {
450
- pathsIncluded: options.includePaths === true,
451
- outputIncluded: options.includeOutput !== false,
452
- next: {
453
- log: "Call get_task_run_log for a larger bounded tail.",
454
- diff: "Call get_task_diff to inspect Git changes from an isolated worktree.",
455
- },
456
- },
457
- };
458
- if (options.includeOutput !== false) {
459
- const stdout = maybeTail(
460
- typeof run.stdoutTail === "string" ? run.stdoutTail : undefined,
461
- repoRoot,
462
- {
463
- executionRoot: typeof run.executionRoot === "string" ? run.executionRoot : undefined,
464
- worktree: typeof run.worktree === "string" ? run.worktree : undefined,
465
- worktreePath: typeof run.worktreePath === "string" ? run.worktreePath : undefined,
466
- },
467
- tailChars,
468
- );
469
- const stderr = maybeTail(
470
- typeof run.stderrTail === "string" ? run.stderrTail : undefined,
471
- repoRoot,
472
- {
473
- executionRoot: typeof run.executionRoot === "string" ? run.executionRoot : undefined,
474
- worktree: typeof run.worktree === "string" ? run.worktree : undefined,
475
- worktreePath: typeof run.worktreePath === "string" ? run.worktreePath : undefined,
476
- },
477
- tailChars,
478
- );
479
- if (stdout) {
480
- base.stdoutTail = stdout.text;
481
- base.stdoutTailTruncated = stdout.truncated;
482
- }
483
- if (stderr) {
484
- base.stderrTail = stderr.text;
485
- base.stderrTailTruncated = stderr.truncated;
486
- }
487
- const redactions = [...(stdout?.redactions ?? []), ...(stderr?.redactions ?? [])];
488
- if (redactions.length > 0) base.redactions = redactions;
489
- }
490
- if (options.includePaths === true) {
491
- Object.assign(base, {
492
- repoRoot: run.repoRoot,
493
- executionRoot: run.executionRoot,
494
- worktree: run.worktree,
495
- worktreePath: run.worktreePath,
496
- promptPath: run.promptPath,
497
- stdoutPath: run.stdoutPath,
498
- stderrPath: run.stderrPath,
499
- resultPath: run.resultPath,
500
- eventsPath: run.eventsPath,
501
- controllerPid: run.controllerPid,
502
- controllerEpoch: run.controllerEpoch,
503
- controllerEpochPath: run.controllerEpochPath,
504
- launchPid: run.launchPid,
505
- workerPid: run.workerPid,
506
- agentPid: run.agentPid,
507
- });
508
- }
509
- return base;
510
- }
511
175
 
512
- function summarizeRunList(
513
- repoRoot: string,
514
- runs: Array<Record<string, unknown>>,
515
- includePaths = false,
516
- ): Array<Record<string, unknown>> {
517
- return runs.map((run) => summarizeAgentRun(repoRoot, run, { includePaths, includeOutput: false }));
518
- }
519
176
 
520
- function compactDispatchRun(run: Record<string, unknown>): Record<string, unknown> {
521
- const runId = typeof run.runId === "string" ? run.runId : String(run.runId ?? "");
522
- const detail: Record<string, unknown> = runId
523
- ? {
524
- get_task_run: { run_id: runId },
525
- get_task_run_log: { run_id: runId },
526
- get_task_run_events: { run_id: runId },
527
- get_task_diff: { run_id: runId },
528
- }
529
- : {};
530
- return {
531
- runId: run.runId,
532
- issueId: run.issueId,
533
- taskId: run.taskId,
534
- status: run.status,
535
- agent: run.agent,
536
- provider: run.provider,
537
- executionMode: run.executionMode,
538
- executionClass: run.executionClass,
539
- timeoutMs: run.timeoutMs,
540
- startedAt: run.startedAt,
541
- deadlineAt: run.deadlineAt,
542
- finishedAt: run.finishedAt,
543
- integratedSessionId: run.integratedSessionId,
544
- autoIntegrationError: run.autoIntegrationError,
545
- worktreeCleanedAt: run.worktreeCleanedAt,
546
- accepted: run.accepted,
547
- reused: run.reused,
548
- github: run.github,
549
- error: run.error,
550
- detail,
551
- };
552
- }
553
177
 
554
- function compactTaskView(task: Record<string, unknown>): Record<string, unknown> {
555
- return {
556
- id: task.id,
557
- title: task.title,
558
- status: task.status,
559
- effectiveStatus: task.effectiveStatus,
560
- declaredStatus: task.declaredStatus,
561
- latestRunId: task.latestRunId,
562
- latestRunStatus: task.latestRunStatus,
563
- activeRunId: task.activeRunId,
564
- activeRunStatus: task.activeRunStatus,
565
- verificationStatus: task.verificationStatus,
566
- dispatchable: task.dispatchable,
567
- retryable: task.retryable,
568
- requiresExplicitRetry: task.requiresExplicitRetry,
569
- };
570
- }
571
178
 
572
- function compactIssueView(issue: Record<string, unknown>): Record<string, unknown> {
573
- const id = typeof issue.id === "string" ? issue.id : String(issue.id ?? "");
574
- const tasks = Array.isArray(issue.tasks) ? issue.tasks as Array<Record<string, unknown>> : [];
575
- return {
576
- id: issue.id,
577
- title: issue.title,
578
- status: issue.status,
579
- lifecycleStatus: issue.lifecycleStatus,
580
- github: issue.github,
581
- taskCount: tasks.length,
582
- tasks: tasks.map((task) => compactTaskView(task)),
583
- detail: id ? { get_issue: { issue_id: id } } : {},
584
- };
585
- }
586
179
 
587
180
  const PROJECT_BOARD_RECENT_ISSUE_LIMIT = 5;
588
181
  const PROJECT_BOARD_TASK_LIMIT = 8;
589
182
 
590
- function compactProjectBoardText(value: unknown, maxChars = 96): unknown {
591
- if (typeof value !== "string" || value.length <= maxChars) return value;
592
- return `${value.slice(0, Math.max(0, maxChars - 1))}…`;
593
- }
594
183
 
595
- function compactProjectBoardTask(
596
- task: Record<string, unknown>,
597
- options: { includeLifecycle?: boolean } = {},
598
- ): Record<string, unknown> {
599
- const compact: Record<string, unknown> = {
600
- issueId: task.issueId,
601
- taskId: task.taskId,
602
- id: task.id,
603
- title: compactProjectBoardText(task.title),
604
- agent: task.agent,
605
- effectiveStatus: task.effectiveStatus,
606
- executionClass: task.executionClass,
607
- approval: task.approval,
608
- };
609
- if (options.includeLifecycle) {
610
- Object.assign(compact, {
611
- status: task.status,
612
- declaredStatus: task.declaredStatus,
613
- statusReason: task.statusReason,
614
- verificationStatus: task.verificationStatus,
615
- dispatchable: task.dispatchable,
616
- queueable: task.queueable,
617
- retryable: task.retryable,
618
- requiresExplicitRetry: task.requiresExplicitRetry,
619
- });
620
- }
621
- return compact;
622
- }
623
184
 
624
- function compactProjectBoardIssue(
625
- issue: Record<string, unknown>,
626
- options: { includeTasks?: boolean } = {},
627
- ): Record<string, unknown> {
628
- const tasks = Array.isArray(issue.tasks) ? issue.tasks as Array<Record<string, unknown>> : [];
629
- const taskCounts = tasks.reduce<Record<string, number>>((counts, task) => {
630
- const status = typeof task.effectiveStatus === "string" ? task.effectiveStatus : "unknown";
631
- counts[status] = (counts[status] ?? 0) + 1;
632
- return counts;
633
- }, {});
634
- const compact: Record<string, unknown> = {
635
- id: issue.id,
636
- title: compactProjectBoardText(issue.title),
637
- kind: issue.kind,
638
- status: issue.status,
639
- lifecycleStatus: issue.lifecycleStatus,
640
- updatedAt: issue.updatedAt,
641
- isCurrent: issue.isCurrent === true,
642
- taskCount: tasks.length,
643
- taskCounts,
644
- readyTaskCount: tasks.filter((task) => task.dispatchable === true).length,
645
- queueableTaskCount: tasks.filter((task) => task.queueable === true).length,
646
- };
647
- if (options.includeTasks) {
648
- compact.tasks = tasks
649
- .slice(0, PROJECT_BOARD_TASK_LIMIT)
650
- .map((task) => compactProjectBoardTask(task, { includeLifecycle: true }));
651
- compact.taskTruncatedCount = Math.max(0, tasks.length - PROJECT_BOARD_TASK_LIMIT);
652
- }
653
- return compact;
654
- }
655
185
 
656
- function summarizeProjectBoard(board: Record<string, unknown>): Record<string, unknown> {
657
- const issues = Array.isArray(board.issues) ? board.issues as Array<Record<string, unknown>> : [];
658
- const readyTasks = Array.isArray(board.readyTasks) ? board.readyTasks as Array<Record<string, unknown>> : [];
659
- const queueableTasks = Array.isArray(board.queueableTasks) ? board.queueableTasks as Array<Record<string, unknown>> : [];
660
- const currentIssue = issues.find((issue) => issue.isCurrent === true);
661
- const recentIssueCandidates = currentIssue
662
- ? issues.filter((issue) => issue !== currentIssue)
663
- : issues;
664
- const recentIssues = recentIssueCandidates.slice(0, PROJECT_BOARD_RECENT_ISSUE_LIMIT);
665
- return {
666
- detailLevel: "summary",
667
- issueCount: issues.length,
668
- archivedIssueCount: board.archivedIssueCount,
669
- counts: board.counts,
670
- declaredCounts: board.declaredCounts,
671
- archivedCounts: board.archivedCounts,
672
- currentIssueId: board.currentIssueId,
673
- currentIssue: currentIssue ? compactProjectBoardIssue(currentIssue, { includeTasks: true }) : undefined,
674
- recentIssues: recentIssues.map((issue) => compactProjectBoardIssue(issue)),
675
- recentIssueTruncatedCount: Math.max(0, recentIssueCandidates.length - recentIssues.length),
676
- readyTaskCount: readyTasks.length,
677
- readyTasks: readyTasks.slice(0, PROJECT_BOARD_TASK_LIMIT).map((task) => compactProjectBoardTask(task)),
678
- readyTaskTruncatedCount: Math.max(0, readyTasks.length - PROJECT_BOARD_TASK_LIMIT),
679
- queueableTaskCount: queueableTasks.length,
680
- queueableTasks: queueableTasks.slice(0, PROJECT_BOARD_TASK_LIMIT).map((task) => compactProjectBoardTask(task)),
681
- queueableTaskTruncatedCount: Math.max(0, queueableTasks.length - PROJECT_BOARD_TASK_LIMIT),
682
- detailPointer: {
683
- tool: "get_project_board",
684
- arguments: { detail_level: "detail" },
685
- },
686
- };
687
- }
688
186
 
689
187
  const UPDATE_TASK_SUMMARY_TASK_LIMIT = 12;
690
188
 
691
- function compactUpdatedTaskView(task: Record<string, unknown>): Record<string, unknown> {
692
- return {
693
- ...compactTaskView(task),
694
- title: compactProjectBoardText(task.title),
695
- };
696
- }
697
189
 
698
- function summarizeUpdatedIssue(
699
- issue: Record<string, unknown>,
700
- taskId: string,
701
- ): Record<string, unknown> {
702
- const tasks = Array.isArray(issue.tasks) ? issue.tasks as Array<Record<string, unknown>> : [];
703
- const updatedTask = tasks.find((task) => task.id === taskId);
704
- return {
705
- detailLevel: "summary",
706
- id: issue.id,
707
- title: compactProjectBoardText(issue.title),
708
- status: issue.status,
709
- lifecycleStatus: issue.lifecycleStatus,
710
- github: issue.github,
711
- taskCount: tasks.length,
712
- tasks: tasks.slice(0, UPDATE_TASK_SUMMARY_TASK_LIMIT).map(compactUpdatedTaskView),
713
- taskTruncatedCount: Math.max(0, tasks.length - UPDATE_TASK_SUMMARY_TASK_LIMIT),
714
- updatedTask: updatedTask ? compactUpdatedTaskView(updatedTask) : undefined,
715
- detailPointer: {
716
- tool: "get_issue",
717
- arguments: { issue_id: issue.id, detail_level: "full" },
718
- },
719
- };
720
- }
721
190
 
722
- function dispatchExecutorHealthResult(
723
- health: ExecutorHealth,
724
- options: { requestId?: string; retryable?: boolean } = {},
725
- ): CallToolResult {
726
- return dispatchErrorResult(executorHealthCode(health), health.message, {
727
- requestId: options.requestId,
728
- retryable: options.retryable,
729
- details: { executorHealth: health },
730
- });
731
- }
732
191
 
733
- function taskExecutorHealth(
734
- ctx: McpToolContext,
735
- task: ControllerTask,
736
- agent: ControllerAgent,
737
- githubRepo?: string,
738
- ): ExecutorHealth | null {
739
- if (agent === "github-copilot") {
740
- return classifyGitHubCopilotPreflight(
741
- getGitHubStatus(ctx.repoRoot, githubRepo),
742
- task,
743
- );
744
- }
745
- return classifyLocalExecutorHealth(
746
- agent,
747
- {
748
- agentRunner: ctx.policy.execution.agentRunner,
749
- allowedAgents: ctx.policy.execution.allowedAgents,
750
- },
751
- task,
752
- );
753
- }
754
192
 
755
193
  function listFilesUnder(
756
194
  repoRoot: string,
@@ -829,28 +267,7 @@ function timestampPrefix(date = new Date()): string {
829
267
  return `${yyyy}${mm}${dd}-${hh}${min}`;
830
268
  }
831
269
 
832
- function parseControllerAgent(value: unknown): ControllerAgent | null {
833
- const normalized = String(value ?? "")
834
- .trim()
835
- .toLowerCase();
836
- return normalized === "codex" ||
837
- normalized === "claude" ||
838
- normalized === "github-copilot"
839
- ? normalized
840
- : null;
841
- }
842
270
 
843
- function resolveDispatchAgent(
844
- ctx: McpToolContext,
845
- task: ControllerTask,
846
- requested: unknown,
847
- ): ControllerAgent | null {
848
- const hasExplicitRequest = requested !== undefined && requested !== null && String(requested).trim() !== "";
849
- if (hasExplicitRequest) return parseControllerAgent(requested);
850
- if (task.recommendedAgent) return task.recommendedAgent;
851
- const firstLocal = ctx.policy.execution.allowedAgents[0];
852
- return firstLocal ?? task.recommendedAgent ?? null;
853
- }
854
271
 
855
272
  function runnerTimeoutMs(ctx: McpToolContext, value: unknown): number {
856
273
  return normalizeAgentTimeoutMs(value, {
@@ -1000,50 +417,8 @@ interface TaskVerificationLeaseConflict {
1000
417
  blockingProcessId?: string;
1001
418
  }
1002
419
 
1003
- function taskVerificationLeaseConflict(record: {
1004
- error?: { message?: unknown };
1005
- }): TaskVerificationLeaseConflict | undefined {
1006
- const message = typeof record.error?.message === "string" ? record.error.message.trim() : "";
1007
- if (!message.startsWith("PROCESS_LEASE_CONFLICT:")) return undefined;
1008
- const match = /^PROCESS_LEASE_CONFLICT:\s*(.+?)@process:([A-Za-z0-9._-]+)$/.exec(message);
1009
- return {
1010
- message,
1011
- resourceKey: match?.[1],
1012
- blockingProcessId: match?.[2],
1013
- };
1014
- }
1015
420
 
1016
- function taskVerificationRetryRequestId(
1017
- requestBase: string,
1018
- checkId: string,
1019
- conflictedProcessId: string,
1020
- ): string {
1021
- const stableBase = requestBase.replace(/:retry:[a-f0-9]{16}$/i, "");
1022
- const retryToken = createHash("sha256")
1023
- .update(`${stableBase}\0${checkId}\0${conflictedProcessId}`)
1024
- .digest("hex")
1025
- .slice(0, 16);
1026
- return `${stableBase}:retry:${retryToken}`;
1027
- }
1028
421
 
1029
- function controllerTaskDrafts(value: unknown): TaskDraft[] {
1030
- return taskObjects(value).map((task) => ({
1031
- title: String(task.title ?? "").trim(),
1032
- objective: String(task.objective ?? "").trim(),
1033
- dependsOn: stringList(task.depends_on ?? task.dependsOn),
1034
- allowedPaths: stringList(task.allowed_paths ?? task.allowedPaths),
1035
- forbiddenPaths: stringList(task.forbidden_paths ?? task.forbiddenPaths),
1036
- checks: stringList(task.checks),
1037
- acceptanceCriteria: stringList(
1038
- task.acceptance_criteria ?? task.acceptanceCriteria,
1039
- ),
1040
- risk: (["readonly", "low", "medium", "high", "destructive"].includes(String(task.risk))
1041
- ? String(task.risk)
1042
- : "medium") as TaskRisk,
1043
- recommendedAgent:
1044
- parseControllerAgent(task.agent ?? task.recommendedAgent) ?? undefined,
1045
- }));
1046
- }
1047
422
 
1048
423
  function renderPrdFromIdeaBody(args: Record<string, unknown>): string {
1049
424
  const title = String(args.title ?? "Untitled PRD").trim() || "Untitled PRD";
@@ -1575,18 +950,12 @@ export function buildMcpToolDefinitions(
1575
950
  {
1576
951
  name: "controller_context",
1577
952
  description:
1578
- "Return one compact controller start-work context: Git status, current Issue focus, active Runs and Jobs, recommended execution mode, and available checks.",
953
+ "Return one compact controller start-work context: Git status, current Issue focus, active Runs and Jobs, and available checks.",
1579
954
  inputSchema: {
1580
955
  type: "object",
1581
956
  properties: {
1582
957
  description: { type: "string" },
1583
- mode: { type: "string", enum: ["direct", "plan", "debug", "review", "release", "scale", "-direct", "-plan", "-debug", "-review", "-release", "-scale"] },
1584
958
  known_paths: { type: "array", items: { type: "string" } },
1585
- expected_files: { type: "number" },
1586
- expected_changed_lines: { type: "number" },
1587
- requires_investigation: { type: "boolean" },
1588
- requires_parallelism: { type: "boolean" },
1589
- requires_long_running_checks: { type: "boolean" },
1590
959
  needs_dependencies: { type: "boolean" },
1591
960
  risk: { type: "string", enum: ["readonly", "low", "medium", "high", "destructive"] },
1592
961
  },
@@ -1765,101 +1134,6 @@ export function buildMcpToolDefinitions(
1765
1134
  inputSchema: EMPTY_SCHEMA,
1766
1135
  annotations: readOnly,
1767
1136
  },
1768
- {
1769
- name: "list_issues",
1770
- description: "List controller-managed issues and task summaries.",
1771
- inputSchema: EMPTY_SCHEMA,
1772
- annotations: readOnly,
1773
- },
1774
- {
1775
- name: "get_project_board",
1776
- description: "Return the bounded Requirement Board by default. Explicit detail/full mode returns bounded Execution Diagnostics without exposing the full legacy Task/Run/Process ledger.",
1777
- inputSchema: {
1778
- type: "object",
1779
- properties: {
1780
- detail_level: { type: "string", enum: ["summary", "detail", "full"] },
1781
- requirement_id: { type: "string" },
1782
- },
1783
- additionalProperties: false,
1784
- },
1785
- annotations: readOnly,
1786
- },
1787
- {
1788
- name: "get_project_progress",
1789
- description:
1790
- "Return evidence-gate completion, effective status, blockers, throughput, current-focus progress, and attention items without lifecycle-percentage estimates.",
1791
- inputSchema: EMPTY_SCHEMA,
1792
- annotations: readOnly,
1793
- },
1794
- {
1795
- name: "get_project_governance",
1796
- description:
1797
- "Return the current execution focus, evidence-driven execution queue, dead dependencies, pending review/acceptance, duplicate active Issues, and closeout anomalies.",
1798
- inputSchema: EMPTY_SCHEMA,
1799
- annotations: readOnly,
1800
- },
1801
- {
1802
- name: "reconcile_project_governance",
1803
- description:
1804
- "Apply only safe governance repairs: replace superseded dependencies, return failed attempts to a retryable Task state, close fully completed Issues, and select the sole active Issue as focus.",
1805
- inputSchema: EMPTY_SCHEMA,
1806
- annotations: write,
1807
- },
1808
- {
1809
- name: "get_project_state",
1810
- description: "Return the current execution focus and Issue creation policy.",
1811
- inputSchema: EMPTY_SCHEMA,
1812
- annotations: readOnly,
1813
- },
1814
- {
1815
- name: "set_current_issue",
1816
- description: "Select the one active Issue that is allowed to drive the execution queue.",
1817
- inputSchema: {
1818
- type: "object",
1819
- properties: { issue_id: { type: "string" } },
1820
- required: ["issue_id"],
1821
- additionalProperties: false,
1822
- },
1823
- annotations: write,
1824
- },
1825
- {
1826
- name: "archive_issue",
1827
- description: "Move a done or cancelled Issue out of the current workspace while retaining its full evidence history.",
1828
- inputSchema: {
1829
- type: "object",
1830
- properties: { issue_id: { type: "string" } },
1831
- required: ["issue_id"],
1832
- additionalProperties: false,
1833
- },
1834
- annotations: write,
1835
- },
1836
- {
1837
- name: "restore_issue",
1838
- description: "Restore an archived Issue to the normal controller views.",
1839
- inputSchema: {
1840
- type: "object",
1841
- properties: { issue_id: { type: "string" } },
1842
- required: ["issue_id"],
1843
- additionalProperties: false,
1844
- },
1845
- annotations: write,
1846
- },
1847
- {
1848
- name: "get_task_progress_detail",
1849
- description:
1850
- "Return one Task with effective progress, bounded Run and timeline summaries by default, and full detail on demand.",
1851
- inputSchema: {
1852
- type: "object",
1853
- properties: {
1854
- issue_id: { type: "string" },
1855
- task_id: { type: "string" },
1856
- detail_level: { type: "string", enum: ["summary", "full"] },
1857
- },
1858
- required: ["issue_id", "task_id"],
1859
- additionalProperties: false,
1860
- },
1861
- annotations: readOnly,
1862
- },
1863
1137
  {
1864
1138
  name: "get_worklog_timeline",
1865
1139
  description:
@@ -1926,345 +1200,135 @@ export function buildMcpToolDefinitions(
1926
1200
  annotations: write,
1927
1201
  },
1928
1202
  {
1929
- name: "get_issue",
1930
- description: "Read one controller issue by ID with bounded task summaries by default and full detail on demand.",
1203
+ name: "github_status",
1204
+ description:
1205
+ "Check GitHub CLI authentication, repository mapping, Projects readiness, and cloud-agent session support.",
1931
1206
  inputSchema: {
1932
1207
  type: "object",
1933
- properties: {
1934
- issue_id: { type: "string" },
1935
- detail_level: { type: "string", enum: ["summary", "full"] },
1936
- },
1937
- required: ["issue_id"],
1208
+ properties: { repo: { type: "string" } },
1938
1209
  additionalProperties: false,
1939
1210
  },
1940
1211
  annotations: readOnly,
1941
1212
  },
1942
1213
  {
1943
- name: "github_status",
1214
+ name: "begin_edit_session",
1944
1215
  description:
1945
- "Check GitHub CLI authentication, repository mapping, Projects readiness, and cloud-agent session support.",
1216
+ "Open a bounded direct-edit session tied to a purpose and optional Issue/Task.",
1946
1217
  inputSchema: {
1947
1218
  type: "object",
1948
- properties: { repo: { type: "string" } },
1219
+ properties: {
1220
+ purpose: { type: "string" },
1221
+ work_id: { type: "string", description: "Optional existing WorkContract. Omit to create lightweight direct-control Work lineage." },
1222
+ issue_id: { type: "string" },
1223
+ task_id: { type: "string" },
1224
+ allowed_paths: { type: "array", items: { type: "string" } },
1225
+ max_files: { type: "number" },
1226
+ max_changed_lines: { type: "number" },
1227
+ checks: { type: "array", items: { type: "string" }, description: "Named checks to run before finalization." },
1228
+ },
1229
+ required: ["purpose"],
1949
1230
  additionalProperties: false,
1950
1231
  },
1951
- annotations: readOnly,
1232
+ annotations: write,
1952
1233
  },
1953
1234
  {
1954
- name: "publish_issue_to_github",
1235
+ name: "apply_patch",
1955
1236
  description:
1956
- "Create or update the controller Issue in GitHub, optionally mirroring Tasks as sub-issues and adding all items to a GitHub Project.",
1237
+ "Append one bounded patch batch to an active edit session. Prefer batches of 100 operations or fewer and pass expected_revision to avoid stale revisions.",
1957
1238
  inputSchema: {
1958
1239
  type: "object",
1959
1240
  properties: {
1960
- issue_id: { type: "string" },
1961
- repo: { type: "string" },
1962
- labels: { type: "array", items: { type: "string" } },
1963
- include_tasks: { type: "boolean" },
1964
- project_owner: { type: "string" },
1965
- project_number: { type: "number" },
1241
+ session_id: { type: "string" },
1242
+ expected_revision: { type: "number", description: "Optional current revision precondition for the edit session." },
1243
+ operations: {
1244
+ type: "array",
1245
+ items: EDIT_OPERATION_INPUT_SCHEMA,
1246
+ },
1966
1247
  },
1967
- required: ["issue_id"],
1248
+ required: ["session_id", "operations"],
1968
1249
  additionalProperties: false,
1969
1250
  },
1970
1251
  annotations: {
1971
1252
  readOnlyHint: false,
1972
- openWorldHint: true,
1253
+ openWorldHint: false,
1973
1254
  destructiveHint: false,
1974
1255
  },
1975
1256
  },
1976
1257
  {
1977
- name: "refresh_github_issue",
1978
- description:
1979
- "Refresh the linked GitHub Issue metadata and remote state.",
1258
+ name: "get_edit_session",
1259
+ description: "Read an edit session, revisions, savepoints, and operation manifest.",
1980
1260
  inputSchema: {
1981
1261
  type: "object",
1982
- properties: { issue_id: { type: "string" } },
1983
- required: ["issue_id"],
1262
+ properties: { session_id: { type: "string" } },
1263
+ required: ["session_id"],
1984
1264
  additionalProperties: false,
1985
1265
  },
1986
1266
  annotations: readOnly,
1987
1267
  },
1988
1268
  {
1989
- name: "close_github_issue",
1990
- description:
1991
- "Close the linked GitHub Issue after local controller acceptance.",
1269
+ name: "list_edit_sessions",
1270
+ description: "List recent direct-edit sessions with revision, changed-file, and check summaries.",
1992
1271
  inputSchema: {
1993
1272
  type: "object",
1994
- properties: { issue_id: { type: "string" } },
1995
- required: ["issue_id"],
1273
+ properties: { limit: { type: "number" } },
1996
1274
  additionalProperties: false,
1997
1275
  },
1998
- annotations: {
1999
- readOnlyHint: false,
2000
- openWorldHint: true,
2001
- destructiveHint: true,
2002
- },
1276
+ annotations: readOnly,
2003
1277
  },
2004
1278
  {
2005
- name: "inspect_issue_readiness",
2006
- description:
2007
- "Score launch readiness, list blockers and warnings, and summarize executable Tasks. Agent selection happens at dispatch time.",
1279
+ name: "get_edit_session_diff",
1280
+ description: "Read the current aggregate localized diff for one direct-edit session.",
2008
1281
  inputSchema: {
2009
1282
  type: "object",
2010
- properties: { issue_id: { type: "string" } },
2011
- required: ["issue_id"],
1283
+ properties: { session_id: { type: "string" } },
1284
+ required: ["session_id"],
2012
1285
  additionalProperties: false,
2013
1286
  },
2014
1287
  annotations: readOnly,
2015
1288
  },
2016
1289
  {
2017
- name: "inspect_task_readiness",
2018
- description:
2019
- "Evaluate launch readiness for one Task only. Other Issues, focus selection, unrelated Tasks, and missing named checks do not block it.",
1290
+ name: "create_edit_savepoint",
1291
+ description: "Name the current edit revision so later patch batches can be rolled back to it.",
2020
1292
  inputSchema: {
2021
1293
  type: "object",
2022
1294
  properties: {
2023
- issue_id: { type: "string" },
2024
- task_id: { type: "string" },
2025
- retry_from_run_id: { type: "string" },
2026
- approve_destructive: { type: "boolean" },
1295
+ session_id: { type: "string" },
1296
+ name: { type: "string" },
2027
1297
  },
2028
- required: ["issue_id", "task_id"],
1298
+ required: ["session_id", "name"],
2029
1299
  additionalProperties: false,
2030
1300
  },
2031
- annotations: readOnly,
1301
+ annotations: write,
2032
1302
  },
2033
1303
  {
2034
- name: "prepare_issue_launch",
2035
- description:
2036
- "Return an Issue Launcher preview without starting any agent session.",
1304
+ name: "verify_edit_session",
1305
+ description: "Verify the current edit revision through persisted Process Runtime checks. Retries with the same request_id reuse the original process and terminal receipts are attached to the exact revision.",
2037
1306
  inputSchema: {
2038
1307
  type: "object",
2039
1308
  properties: {
2040
- issue_id: { type: "string" },
2041
- max_parallel: { type: "number" },
1309
+ session_id: { type: "string" },
1310
+ check_ids: { type: "array", items: { type: "string" } },
1311
+ request_id: { type: "string" },
1312
+ reviewer: { type: "string" },
1313
+ note: { type: "string" },
2042
1314
  },
2043
- required: ["issue_id"],
1315
+ required: ["session_id"],
2044
1316
  additionalProperties: false,
2045
1317
  },
2046
- annotations: readOnly,
1318
+ annotations: write,
2047
1319
  },
2048
1320
  {
2049
- name: "assess_work_request",
1321
+ name: "rollback_edit_session",
2050
1322
  description:
2051
- "Choose direct_edit, quick_agent, or issue_task before creating work. Prefer repository search plus bounded direct edits for low/medium-risk changes, even when exact files need discovery; use an Agent only when direct patches are not safe or practical.",
1323
+ "Rollback an active edit session completely, to a prior revision, or to a named savepoint.",
2052
1324
  inputSchema: {
2053
1325
  type: "object",
2054
1326
  properties: {
2055
- description: { type: "string" },
2056
- mode: { type: "string", enum: ["direct", "plan", "debug", "review", "release", "scale", "-direct", "-plan", "-debug", "-review", "-release", "-scale"] },
2057
- known_paths: { type: "array", items: { type: "string" } },
2058
- expected_files: { type: "number" },
2059
- expected_changed_lines: { type: "number" },
2060
- requires_investigation: { type: "boolean" },
2061
- requires_parallelism: { type: "boolean" },
2062
- requires_long_running_checks: { type: "boolean" },
2063
- needs_dependencies: { type: "boolean" },
2064
- risk: { type: "string", enum: ["readonly", "low", "medium", "high", "destructive"] },
1327
+ session_id: { type: "string" },
1328
+ to_revision: { type: "number", description: "Keep revisions up to this value; omit for full rollback." },
1329
+ savepoint: { type: "string", description: "Rollback to a named savepoint." },
2065
1330
  },
2066
- required: ["description"],
2067
- additionalProperties: false,
2068
- },
2069
- annotations: readOnly,
2070
- },
2071
- {
2072
- name: "create_issue",
2073
- description:
2074
- "Create a durable Issue only for broad, long-running, dependency-aware, parallel, protected-path, or high-risk work. File discovery by itself is not a reason to create an Issue or start an Agent; search first and use bounded direct edits when practical.",
2075
- inputSchema: {
2076
- type: "object",
2077
- properties: {
2078
- title: { type: "string" },
2079
- kind: {
2080
- type: "string",
2081
- enum: ["bug", "feature", "governance", "investigation"],
2082
- },
2083
- summary: { type: "string" },
2084
- goals: { type: "array", items: { type: "string" } },
2085
- non_goals: { type: "array", items: { type: "string" } },
2086
- acceptance_criteria: { type: "array", items: { type: "string" } },
2087
- related_artifacts: { type: "array", items: { type: "string" } },
2088
- tasks: { type: "array", items: taskItemSchema },
2089
- allow_while_focused: { type: "boolean", description: "Explicitly allow a separate Issue while another Issue is the current execution focus." },
2090
- allow_duplicate: { type: "boolean", description: "Explicitly allow an active Issue with the same normalized title." },
2091
- allow_when_paused: { type: "boolean", description: "Explicitly override a paused Issue-creation policy." },
2092
- },
2093
- required: ["title"],
2094
- additionalProperties: false,
2095
- },
2096
- annotations: write,
2097
- },
2098
- {
2099
- name: "update_issue",
2100
- description: "Update issue intent, artifacts, or lifecycle status.",
2101
- inputSchema: {
2102
- type: "object",
2103
- properties: {
2104
- issue_id: { type: "string" },
2105
- title: { type: "string" },
2106
- summary: { type: "string" },
2107
- status: {
2108
- type: "string",
2109
- enum: [
2110
- "backlog",
2111
- "analysis",
2112
- "planned",
2113
- "launch_blocked",
2114
- "in_progress",
2115
- "review",
2116
- "done",
2117
- "cancelled",
2118
- ],
2119
- },
2120
- goals: { type: "array", items: { type: "string" } },
2121
- non_goals: { type: "array", items: { type: "string" } },
2122
- acceptance_criteria: { type: "array", items: { type: "string" } },
2123
- related_artifacts: { type: "array", items: { type: "string" } },
2124
- },
2125
- required: ["issue_id"],
2126
- additionalProperties: false,
2127
- },
2128
- annotations: write,
2129
- },
2130
- {
2131
- name: "plan_issue",
2132
- description:
2133
- "Replace the pre-execution task plan for an Issue with ordered dependency-aware tasks.",
2134
- inputSchema: {
2135
- type: "object",
2136
- properties: {
2137
- issue_id: { type: "string" },
2138
- tasks: { type: "array", items: taskItemSchema },
2139
- },
2140
- required: ["issue_id", "tasks"],
2141
- additionalProperties: false,
2142
- },
2143
- annotations: write,
2144
- },
2145
- {
2146
- name: "append_task",
2147
- description:
2148
- "Append a stable Task to an Issue after execution has already started.",
2149
- inputSchema: {
2150
- type: "object",
2151
- properties: { issue_id: { type: "string" }, task: taskItemSchema },
2152
- required: ["issue_id", "task"],
2153
- additionalProperties: false,
2154
- },
2155
- annotations: write,
2156
- },
2157
- {
2158
- name: "split_task",
2159
- description:
2160
- "Supersede one not-running Task with two or more smaller replacement Tasks while preserving downstream dependencies.",
2161
- inputSchema: {
2162
- type: "object",
2163
- properties: {
2164
- issue_id: { type: "string" },
2165
- task_id: { type: "string" },
2166
- tasks: { type: "array", minItems: 2, items: taskItemSchema },
2167
- },
2168
- required: ["issue_id", "task_id", "tasks"],
2169
- additionalProperties: false,
2170
- },
2171
- annotations: write,
2172
- },
2173
- {
2174
- name: "supersede_task",
2175
- description:
2176
- "Mark a Task as superseded and optionally link replacement Task IDs.",
2177
- inputSchema: {
2178
- type: "object",
2179
- properties: {
2180
- issue_id: { type: "string" },
2181
- task_id: { type: "string" },
2182
- replacement_task_ids: { type: "array", items: { type: "string" } },
2183
- },
2184
- required: ["issue_id", "task_id"],
2185
- additionalProperties: false,
2186
- },
2187
- annotations: write,
2188
- },
2189
- {
2190
- name: "set_task_dependencies",
2191
- description:
2192
- "Replace one Task dependency list and revalidate the Issue DAG.",
2193
- inputSchema: {
2194
- type: "object",
2195
- properties: {
2196
- issue_id: { type: "string" },
2197
- task_id: { type: "string" },
2198
- depends_on: { type: "array", items: { type: "string" } },
2199
- },
2200
- required: ["issue_id", "task_id", "depends_on"],
2201
- additionalProperties: false,
2202
- },
2203
- annotations: write,
2204
- },
2205
- {
2206
- name: "update_task",
2207
- description: "Update one task status or append a controller note.",
2208
- inputSchema: {
2209
- type: "object",
2210
- properties: {
2211
- issue_id: { type: "string" },
2212
- task_id: { type: "string" },
2213
- status: {
2214
- type: "string",
2215
- enum: [
2216
- "backlog",
2217
- "analysis",
2218
- "planned",
2219
- "ready",
2220
- "launch_blocked",
2221
- "running",
2222
- "blocked",
2223
- "review",
2224
- "integrated",
2225
- "verifying",
2226
- "changes_requested",
2227
- "verified",
2228
- "done",
2229
- "cancelled",
2230
- "superseded",
2231
- ],
2232
- },
2233
- note: { type: "string" },
2234
- detail_level: { type: "string", enum: ["summary", "detail", "full"] },
2235
- },
2236
- required: ["issue_id", "task_id"],
2237
- additionalProperties: false,
2238
- },
2239
- annotations: write,
2240
- },
2241
- {
2242
- name: "dispatch_task",
2243
- description:
2244
- "Execute one Task with an agent selected at dispatch time. Task definitions remain executor-neutral; Codex, Claude, and GitHub Copilot are optional implementation tools.",
2245
- inputSchema: {
2246
- type: "object",
2247
- properties: {
2248
- issue_id: { type: "string" },
2249
- task_id: { type: "string" },
2250
- request_id: { type: "string" },
2251
- agent: {
2252
- type: "string",
2253
- enum: ["codex", "claude", "github-copilot"],
2254
- },
2255
- timeout_ms: agentTimeoutSchema,
2256
- isolate: {
2257
- type: "boolean",
2258
- description:
2259
- "Optional. Omit for automatic placement: use the current workspace when no local Run is active and a worktree only for concurrency. false forces the current workspace and is rejected during concurrency; true forces a worktree.",
2260
- },
2261
- github_repo: { type: "string" },
2262
- base_ref: { type: "string" },
2263
- model: { type: "string" },
2264
- create_pull_request: { type: "boolean" },
2265
- approve_destructive: { type: "boolean", description: "Explicit same-request authorization for destructive or irreversible execution." },
2266
- },
2267
- required: ["issue_id", "task_id"],
1331
+ required: ["session_id"],
2268
1332
  additionalProperties: false,
2269
1333
  },
2270
1334
  annotations: {
@@ -2274,62 +1338,40 @@ export function buildMcpToolDefinitions(
2274
1338
  },
2275
1339
  },
2276
1340
  {
2277
- name: "launch_issue",
1341
+ name: "finalize_edit_session",
2278
1342
  description:
2279
- "Execute independent Tasks from one Issue with an optional runtime agent override. Task definitions do not permanently bind an agent.",
1343
+ "Close an active edit session. Configured checks must pass; sessions without configured checks finalize directly.",
2280
1344
  inputSchema: {
2281
1345
  type: "object",
2282
1346
  properties: {
2283
- issue_id: { type: "string" },
2284
- max_parallel: { type: "number" },
2285
- request_id: { type: "string" },
2286
- agent: { type: "string", enum: ["codex", "claude", "github-copilot"], description: "Optional runtime executor override for selected Tasks." },
2287
- timeout_ms: agentTimeoutSchema,
2288
- isolate: {
2289
- type: "boolean",
2290
- description:
2291
- "Optional. Omit for automatic placement: use the current workspace when no local Run is active and a worktree only for concurrency. false forces the current workspace and is rejected during concurrency; true forces a worktree.",
2292
- },
2293
- github_repo: { type: "string" },
2294
- base_ref: { type: "string" },
2295
- model: { type: "string" },
2296
- create_pull_request: { type: "boolean" },
2297
- approve_destructive: { type: "boolean", description: "Explicit same-request authorization for destructive or irreversible execution." },
1347
+ session_id: { type: "string" },
1348
+ reviewer: { type: "string" },
1349
+ note: { type: "string" },
1350
+ confirm_authorization: { type: "boolean", description: "Required when post-diff assurance detects protected paths or high semantic risk." },
2298
1351
  },
2299
- required: ["issue_id"],
1352
+ required: ["session_id"],
2300
1353
  additionalProperties: false,
2301
1354
  },
2302
- annotations: {
2303
- readOnlyHint: false,
2304
- openWorldHint: true,
2305
- destructiveHint: false,
2306
- },
1355
+ annotations: write,
2307
1356
  },
1357
+ );
1358
+ }
1359
+
1360
+ if (policy.execution.fixedWorkflowCheck) {
1361
+ tools.push({
1362
+ name: "run_workflow_check",
1363
+ description: "Run the fixed forge strict workflow check.",
1364
+ inputSchema: EMPTY_SCHEMA,
1365
+ annotations: write,
1366
+ });
1367
+ }
1368
+ if (opts.enableChatgptBrowser === true) {
1369
+ tools.push(
2308
1370
  {
2309
- name: "dispatch_ready_tasks",
1371
+ name: "run_chatgpt_browser_consult",
2310
1372
  description:
2311
- "Execute up to max_parallel independent Tasks with an optional runtime agent override.",
2312
- inputSchema: {
2313
- type: "object",
2314
- properties: {
2315
- issue_id: { type: "string" },
2316
- max_parallel: { type: "number" },
2317
- request_id: { type: "string" },
2318
- agent: { type: "string", enum: ["codex", "claude", "github-copilot"], description: "Optional runtime executor override for selected Tasks." },
2319
- timeout_ms: agentTimeoutSchema,
2320
- isolate: {
2321
- type: "boolean",
2322
- description:
2323
- "Optional. Omit for automatic placement: use the current workspace when no local Run is active and a worktree only for concurrency. false forces the current workspace and is rejected during concurrency; true forces a worktree.",
2324
- },
2325
- github_repo: { type: "string" },
2326
- base_ref: { type: "string" },
2327
- model: { type: "string" },
2328
- create_pull_request: { type: "boolean" },
2329
- approve_destructive: { type: "boolean", description: "Explicit same-request authorization for destructive or irreversible execution." },
2330
- },
2331
- additionalProperties: false,
2332
- },
1373
+ "Run a local ChatGPT Web browser consult through forge. This may create a real ChatGPT Web conversation unless dryRun is true.",
1374
+ inputSchema: browserRunSchema,
2333
1375
  annotations: {
2334
1376
  readOnlyHint: false,
2335
1377
  openWorldHint: true,
@@ -2337,522 +1379,97 @@ export function buildMcpToolDefinitions(
2337
1379
  },
2338
1380
  },
2339
1381
  {
2340
- name: "get_task_run",
2341
- description:
2342
- "Read one persistent agent run with current status, bounded output tails, and opt-in path details.",
2343
- inputSchema: {
2344
- type: "object",
2345
- properties: {
2346
- run_id: { type: "string" },
2347
- include_paths: { type: "boolean", description: "Opt in to full local filesystem paths and PIDs." },
2348
- include_output: { type: "boolean", description: "Return bounded stdout/stderr tails. Defaults to true." },
2349
- tail_chars: { type: "number", description: "Per-stream character budget for the bounded tails; capped at 16384." },
2350
- },
2351
- required: ["run_id"],
2352
- additionalProperties: false,
2353
- },
2354
- annotations: readOnly,
2355
- },
2356
- {
2357
- name: "get_task_run_events",
1382
+ name: "read_chatgpt_browser_session",
2358
1383
  description:
2359
- "Read structured lifecycle events for a local or GitHub cloud Task Run.",
2360
- inputSchema: {
2361
- type: "object",
2362
- properties: {
2363
- run_id: { type: "string" },
2364
- limit: { type: "number" },
2365
- since_event_index: { type: "number" },
2366
- include_heartbeats: { type: "boolean" },
2367
- },
2368
- required: ["run_id"],
2369
- additionalProperties: false,
2370
- },
1384
+ "Read a saved forge ChatGPT browser consult session.",
1385
+ inputSchema: browserSessionSchema,
2371
1386
  annotations: readOnly,
2372
1387
  },
2373
1388
  {
2374
- name: "get_task_run_log",
1389
+ name: "list_chatgpt_browser_sessions",
2375
1390
  description:
2376
- "Read a bounded, redacted tail of current local output or GitHub cloud-agent session logs. GitHub logs can be watched directly in the linked Agents UI.",
1391
+ "List saved forge ChatGPT browser consult sessions.",
2377
1392
  inputSchema: {
2378
1393
  type: "object",
2379
- properties: {
2380
- run_id: { type: "string" },
2381
- max_bytes: { type: "number", description: "Bounded log tail size; capped at 1 MiB." },
2382
- },
2383
- required: ["run_id"],
1394
+ properties: { limit: { type: "number" } },
2384
1395
  additionalProperties: false,
2385
1396
  },
2386
1397
  annotations: readOnly,
2387
1398
  },
2388
1399
  {
2389
- name: "get_task_diff",
1400
+ name: "open_chatgpt_browser_session",
2390
1401
  description:
2391
- "Inspect the Git status and diff from one isolated Task Run worktree.",
2392
- inputSchema: {
2393
- type: "object",
2394
- properties: {
2395
- run_id: { type: "string" },
2396
- max_bytes: { type: "number" },
2397
- },
2398
- required: ["run_id"],
2399
- additionalProperties: false,
2400
- },
1402
+ "Return the ChatGPT conversation URL for a saved browser session. The MCP tool does not launch the local browser.",
1403
+ inputSchema: browserSessionSchema,
2401
1404
  annotations: readOnly,
2402
1405
  },
2403
1406
  {
2404
- name: "integrate_task_run",
2405
- description:
2406
- "Integrate one reviewed isolated Task Run into the main working tree through a rollback-capable edit session.",
2407
- inputSchema: {
2408
- type: "object",
2409
- properties: { run_id: { type: "string" } },
2410
- required: ["run_id"],
2411
- additionalProperties: false,
2412
- },
2413
- annotations: {
2414
- readOnlyHint: false,
2415
- openWorldHint: false,
2416
- destructiveHint: true,
2417
- },
2418
- },
2419
- {
2420
- name: "finish_task_run",
1407
+ name: "continue_chatgpt_browser_session",
2421
1408
  description:
2422
- "Finish one reviewed Run in a single policy-gated step: integrate if needed, verify, accept when allowed, and clean the isolated worktree. Use decision=auto for low/medium trusted work; use approve_and_finish only for explicit human approval.",
1409
+ "Create a follow-up ChatGPT browser consult record linked to an existing session.",
2423
1410
  inputSchema: {
2424
1411
  type: "object",
2425
1412
  properties: {
2426
- run_id: { type: "string" },
2427
- decision: {
2428
- type: "string",
2429
- enum: ["auto", "approve_and_finish", "request_changes", "discard"],
2430
- },
2431
- reviewer: { type: "string" },
2432
- note: { type: "string" },
2433
- cleanup: { type: "boolean" },
2434
- commit: { type: "boolean", description: "Create a selected-path Git commit after successful finish. Refuses to mix with pre-staged changes." },
1413
+ sessionId: { type: "string" },
1414
+ prompt: { type: "string" },
1415
+ dryRun: { type: "boolean" },
2435
1416
  },
2436
- required: ["run_id"],
1417
+ required: ["sessionId", "prompt"],
2437
1418
  additionalProperties: false,
2438
1419
  },
2439
1420
  annotations: {
2440
1421
  readOnlyHint: false,
2441
- openWorldHint: false,
1422
+ openWorldHint: true,
2442
1423
  destructiveHint: false,
2443
1424
  },
2444
1425
  },
2445
- {
2446
- name: "list_task_runs",
2447
- description: "List recent persistent agent runs.",
2448
- inputSchema: {
2449
- type: "object",
2450
- properties: {
2451
- limit: { type: "number" },
2452
- include_paths: { type: "boolean", description: "Opt in to full local filesystem paths and PIDs." },
2453
- },
2454
- additionalProperties: false,
2455
- },
2456
- annotations: readOnly,
2457
- },
2458
- {
2459
- name: "cancel_task_run",
2460
- description: "Cancel a queued or running local agent task.",
2461
- inputSchema: {
2462
- type: "object",
2463
- properties: { run_id: { type: "string" } },
2464
- required: ["run_id"],
2465
- additionalProperties: false,
2466
- },
2467
- annotations: {
2468
- readOnlyHint: false,
2469
- openWorldHint: false,
2470
- destructiveHint: true,
2471
- },
2472
- },
2473
- {
2474
- name: "retry_task_run",
2475
- description:
2476
- "Create a new run for a failed, blocked, cancelled, or change-requested task.",
2477
- inputSchema: {
2478
- type: "object",
2479
- properties: {
2480
- run_id: { type: "string" },
2481
- timeout_ms: agentTimeoutSchema,
2482
- isolate: {
2483
- type: "boolean",
2484
- description:
2485
- "Optional. Omit for automatic placement: use the current workspace when no local Run is active and a worktree only for concurrency. false forces the current workspace and is rejected during concurrency; true forces a worktree.",
2486
- },
2487
- },
2488
- required: ["run_id"],
2489
- additionalProperties: false,
2490
- },
2491
- annotations: write,
2492
- },
2493
- {
2494
- name: "verify_task",
2495
- description:
2496
- "Record task-local completion evidence. Declared checks execute through persisted Process Runtime receipts; retries reuse the same processes and reported command evidence can satisfy risk-adaptive verification.",
2497
- inputSchema: {
2498
- type: "object",
2499
- properties: {
2500
- issue_id: { type: "string" },
2501
- task_id: { type: "string" },
2502
- run_id: { type: "string" },
2503
- integrated_revision: { type: "string" },
2504
- reviewed_diff_hash: { type: "string" },
2505
- reviewer: { type: "string" },
2506
- request_id: { type: "string", description: "Stable idempotency key for Process Runtime checks; retries reuse the original processes." },
2507
- check_results: {
2508
- type: "array",
2509
- items: {
2510
- type: "object",
2511
- properties: {
2512
- check_id: { type: "string" },
2513
- ok: { type: "boolean", description: "Deprecated compatibility field. The Controller executes the named check and uses the actual outcome." },
2514
- summary: { type: "string" },
2515
- },
2516
- required: ["check_id"],
2517
- additionalProperties: false,
2518
- },
2519
- },
2520
- reported_commands: {
2521
- type: "array",
2522
- items: {
2523
- type: "object",
2524
- properties: {
2525
- command: { type: "array", items: { type: "string" }, minItems: 1 },
2526
- cwd: { type: "string" },
2527
- ok: { type: "boolean" },
2528
- exit_code: { type: "number" },
2529
- stdout: { type: "string" },
2530
- stderr: { type: "string" },
2531
- artifact_path: { type: "string" },
2532
- executed_at: { type: "string" },
2533
- },
2534
- required: ["command", "ok"],
2535
- additionalProperties: false,
2536
- },
2537
- },
2538
- acceptance_results: {
2539
- type: "array",
2540
- items: {
2541
- type: "object",
2542
- properties: {
2543
- criterion: { type: "string" },
2544
- ok: { type: "boolean" },
2545
- evidence: { type: "string" },
2546
- },
2547
- required: ["criterion", "ok"],
2548
- additionalProperties: false,
2549
- },
2550
- },
2551
- },
2552
- required: ["issue_id", "task_id", "reviewer"],
2553
- additionalProperties: false,
2554
- },
2555
- annotations: write,
2556
- },
2557
- {
2558
- name: "accept_task",
2559
- description:
2560
- "Accept a verified task, mark it done, and unlock dependent tasks.",
2561
- inputSchema: {
2562
- type: "object",
2563
- properties: {
2564
- issue_id: { type: "string" },
2565
- task_id: { type: "string" },
2566
- work_id: { type: "string" },
2567
- note: { type: "string" },
2568
- },
2569
- required: ["issue_id", "task_id"],
2570
- additionalProperties: false,
2571
- },
2572
- annotations: write,
2573
- },
2574
- {
2575
- name: "request_task_changes",
2576
- description:
2577
- "Return a reviewed task for another focused execution run.",
2578
- inputSchema: {
2579
- type: "object",
2580
- properties: {
2581
- issue_id: { type: "string" },
2582
- task_id: { type: "string" },
2583
- note: { type: "string" },
2584
- },
2585
- required: ["issue_id", "task_id", "note"],
2586
- additionalProperties: false,
2587
- },
2588
- annotations: write,
2589
- },
2590
- {
2591
- name: "begin_edit_session",
2592
- description:
2593
- "Open a bounded direct-edit session tied to a purpose and optional Issue/Task.",
2594
- inputSchema: {
2595
- type: "object",
2596
- properties: {
2597
- purpose: { type: "string" },
2598
- work_id: { type: "string", description: "Optional existing WorkContract. Omit to create lightweight direct-control Work lineage." },
2599
- issue_id: { type: "string" },
2600
- task_id: { type: "string" },
2601
- allowed_paths: { type: "array", items: { type: "string" } },
2602
- max_files: { type: "number" },
2603
- max_changed_lines: { type: "number" },
2604
- checks: { type: "array", items: { type: "string" }, description: "Named checks to run before finalization." },
2605
- },
2606
- required: ["purpose"],
2607
- additionalProperties: false,
2608
- },
2609
- annotations: write,
2610
- },
2611
- {
2612
- name: "apply_patch",
2613
- description:
2614
- "Append one bounded patch batch to an active edit session. Prefer batches of 100 operations or fewer and pass expected_revision to avoid stale revisions.",
2615
- inputSchema: {
2616
- type: "object",
2617
- properties: {
2618
- session_id: { type: "string" },
2619
- expected_revision: { type: "number", description: "Optional current revision precondition for the edit session." },
2620
- operations: {
2621
- type: "array",
2622
- items: EDIT_OPERATION_INPUT_SCHEMA,
2623
- },
2624
- },
2625
- required: ["session_id", "operations"],
2626
- additionalProperties: false,
2627
- },
2628
- annotations: {
2629
- readOnlyHint: false,
2630
- openWorldHint: false,
2631
- destructiveHint: false,
2632
- },
2633
- },
2634
- {
2635
- name: "get_edit_session",
2636
- description: "Read an edit session, revisions, savepoints, and operation manifest.",
2637
- inputSchema: {
2638
- type: "object",
2639
- properties: { session_id: { type: "string" } },
2640
- required: ["session_id"],
2641
- additionalProperties: false,
2642
- },
2643
- annotations: readOnly,
2644
- },
2645
- {
2646
- name: "list_edit_sessions",
2647
- description: "List recent direct-edit sessions with revision, changed-file, and check summaries.",
2648
- inputSchema: {
2649
- type: "object",
2650
- properties: { limit: { type: "number" } },
2651
- additionalProperties: false,
2652
- },
2653
- annotations: readOnly,
2654
- },
2655
- {
2656
- name: "get_edit_session_diff",
2657
- description: "Read the current aggregate localized diff for one direct-edit session.",
2658
- inputSchema: {
2659
- type: "object",
2660
- properties: { session_id: { type: "string" } },
2661
- required: ["session_id"],
2662
- additionalProperties: false,
2663
- },
2664
- annotations: readOnly,
2665
- },
2666
- {
2667
- name: "create_edit_savepoint",
2668
- description: "Name the current edit revision so later patch batches can be rolled back to it.",
2669
- inputSchema: {
2670
- type: "object",
2671
- properties: {
2672
- session_id: { type: "string" },
2673
- name: { type: "string" },
2674
- },
2675
- required: ["session_id", "name"],
2676
- additionalProperties: false,
2677
- },
2678
- annotations: write,
2679
- },
2680
- {
2681
- name: "verify_edit_session",
2682
- description: "Verify the current edit revision through persisted Process Runtime checks. Retries with the same request_id reuse the original process and terminal receipts are attached to the exact revision.",
2683
- inputSchema: {
2684
- type: "object",
2685
- properties: {
2686
- session_id: { type: "string" },
2687
- check_ids: { type: "array", items: { type: "string" } },
2688
- request_id: { type: "string" },
2689
- reviewer: { type: "string" },
2690
- note: { type: "string" },
2691
- },
2692
- required: ["session_id"],
2693
- additionalProperties: false,
2694
- },
2695
- annotations: write,
2696
- },
2697
- {
2698
- name: "rollback_edit_session",
2699
- description:
2700
- "Rollback an active edit session completely, to a prior revision, or to a named savepoint.",
2701
- inputSchema: {
2702
- type: "object",
2703
- properties: {
2704
- session_id: { type: "string" },
2705
- to_revision: { type: "number", description: "Keep revisions up to this value; omit for full rollback." },
2706
- savepoint: { type: "string", description: "Rollback to a named savepoint." },
2707
- },
2708
- required: ["session_id"],
2709
- additionalProperties: false,
2710
- },
2711
- annotations: {
2712
- readOnlyHint: false,
2713
- openWorldHint: false,
2714
- destructiveHint: false,
2715
- },
2716
- },
2717
- {
2718
- name: "finalize_edit_session",
2719
- description:
2720
- "Close an active edit session. Configured checks must pass; sessions without configured checks finalize directly.",
2721
- inputSchema: {
2722
- type: "object",
2723
- properties: {
2724
- session_id: { type: "string" },
2725
- reviewer: { type: "string" },
2726
- note: { type: "string" },
2727
- confirm_authorization: { type: "boolean", description: "Required when post-diff assurance detects protected paths or high semantic risk." },
2728
- },
2729
- required: ["session_id"],
2730
- additionalProperties: false,
2731
- },
2732
- annotations: write,
2733
- },
2734
- {
2735
- name: "finish_edit_session",
2736
- description:
2737
- "Verify, finalize, selected-path commit, receipt, and complete the Task bound to one Direct Edit session.",
2738
- inputSchema: {
2739
- type: "object",
2740
- properties: {
2741
- session_id: { type: "string" },
2742
- decision: { type: "string", enum: ["auto", "approve_and_finish", "request_changes", "discard"] },
2743
- reviewer: { type: "string" },
2744
- note: { type: "string" },
2745
- check_ids: { type: "array", items: { type: "string" } },
2746
- commit: { type: "boolean" },
2747
- },
2748
- required: ["session_id"],
2749
- additionalProperties: false,
2750
- },
2751
- annotations: write,
2752
- },
2753
1426
  );
2754
1427
  }
1428
+ return tools.filter((tool) => !RETIRED_LEGACY_PUBLIC_TOOLS.has(tool.name));
1429
+ }
2755
1430
 
2756
- if (policy.execution.fixedWorkflowCheck) {
2757
- tools.push({
2758
- name: "run_workflow_check",
2759
- description: "Run the fixed forge strict workflow check.",
2760
- inputSchema: EMPTY_SCHEMA,
2761
- annotations: write,
2762
- });
2763
- }
2764
- if (opts.enableChatgptBrowser === true) {
2765
- tools.push(
2766
- {
2767
- name: "run_chatgpt_browser_consult",
2768
- description:
2769
- "Run a local ChatGPT Web browser consult through forge. This may create a real ChatGPT Web conversation unless dryRun is true.",
2770
- inputSchema: browserRunSchema,
2771
- annotations: {
2772
- readOnlyHint: false,
2773
- openWorldHint: true,
2774
- destructiveHint: false,
2775
- },
2776
- },
2777
- {
2778
- name: "read_chatgpt_browser_session",
2779
- description:
2780
- "Read a saved forge ChatGPT browser consult session.",
2781
- inputSchema: browserSessionSchema,
2782
- annotations: readOnly,
2783
- },
2784
- {
2785
- name: "list_chatgpt_browser_sessions",
2786
- description:
2787
- "List saved forge ChatGPT browser consult sessions.",
2788
- inputSchema: {
2789
- type: "object",
2790
- properties: { limit: { type: "number" } },
2791
- additionalProperties: false,
2792
- },
2793
- annotations: readOnly,
2794
- },
2795
- {
2796
- name: "open_chatgpt_browser_session",
2797
- description:
2798
- "Return the ChatGPT conversation URL for a saved browser session. The MCP tool does not launch the local browser.",
2799
- inputSchema: browserSessionSchema,
2800
- annotations: readOnly,
2801
- },
2802
- {
2803
- name: "continue_chatgpt_browser_session",
2804
- description:
2805
- "Create a follow-up ChatGPT browser consult record linked to an existing session.",
2806
- inputSchema: {
2807
- type: "object",
2808
- properties: {
2809
- sessionId: { type: "string" },
2810
- prompt: { type: "string" },
2811
- dryRun: { type: "boolean" },
2812
- },
2813
- required: ["sessionId", "prompt"],
2814
- additionalProperties: false,
2815
- },
2816
- annotations: {
2817
- readOnlyHint: false,
2818
- openWorldHint: true,
2819
- destructiveHint: false,
2820
- },
2821
- },
2822
- );
2823
- }
2824
- return tools;
2825
- }
2826
-
2827
- export function controllerExpectedToolNames(
2828
- policy: McpPolicy,
2829
- opts: { enableChatgptBrowser?: boolean } = {},
2830
- ): string[] {
2831
- const names = buildMcpToolDefinitions(policy, opts).map((tool) => tool.name);
2832
- if (policy.profile !== "controller") return names;
2833
- // The Connector readiness expectation must match the bounded default schema
2834
- // served by toolset.ts. Full compatibility tools are intentionally omitted.
2835
- return [...DEFAULT_CONTROLLER_TOOL_NAMES];
2836
- }
2837
-
2838
- const RETIRED_LEGACY_MUTATION_TOOLS = new Set([
1431
+ const RETIRED_LEGACY_PUBLIC_TOOLS = new Set([
1432
+ "list_issues",
1433
+ "get_project_board",
1434
+ "get_project_governance",
2839
1435
  "reconcile_project_governance",
1436
+ "get_project_state",
2840
1437
  "set_current_issue",
2841
1438
  "archive_issue",
2842
1439
  "restore_issue",
1440
+ "get_issue",
2843
1441
  "publish_issue_to_github",
2844
1442
  "refresh_github_issue",
2845
1443
  "close_github_issue",
1444
+ "inspect_issue_readiness",
1445
+ "inspect_task_readiness",
2846
1446
  "prepare_issue_launch",
2847
1447
  "create_issue",
1448
+ "update_issue",
2848
1449
  "plan_issue",
2849
1450
  "append_task",
2850
1451
  "split_task",
2851
1452
  "supersede_task",
2852
1453
  "set_task_dependencies",
2853
1454
  "update_task",
1455
+ "dispatch_task",
1456
+ "launch_issue",
1457
+ "dispatch_ready_tasks",
1458
+ "get_task_run",
1459
+ "get_task_run_events",
1460
+ "get_task_run_log",
1461
+ "get_task_diff",
1462
+ "integrate_task_run",
1463
+ "finish_task_run",
1464
+ "list_task_runs",
1465
+ "cancel_task_run",
1466
+ "retry_task_run",
1467
+ "verify_task",
1468
+ "accept_task",
1469
+ "request_task_changes",
2854
1470
  "record_task_verification",
2855
1471
  "accept_verified_task",
1472
+ "finish_edit_session",
2856
1473
  ]);
2857
1474
 
2858
1475
  export async function callMcpTool(
@@ -2861,1617 +1478,651 @@ export async function callMcpTool(
2861
1478
  args: Record<string, unknown> = {},
2862
1479
  ): Promise<CallToolResult> {
2863
1480
  try {
2864
- if (RETIRED_LEGACY_MUTATION_TOOLS.has(name) && legacyIssueAuthorityRetired(ctx.repoRoot)) {
1481
+ if (RETIRED_LEGACY_PUBLIC_TOOLS.has(name)) {
2865
1482
  audit(ctx, name, "blocked", args);
2866
- return errorResult(
2867
- "LEGACY_CONTROL_PLANE_MUTATION_RETIRED",
2868
- `${name} cannot mutate frozen Issue/Task/currentIssue/project-board compatibility files after SQLite cutover. Use Requirement, Plan and Work APIs.`,
2869
- );
1483
+ return errorResult("UNKNOWN_TOOL", `unknown tool: ${name}`);
2870
1484
  }
2871
1485
  switch (name) {
2872
1486
  case "harness_status": {
2873
1487
  const roots = [
2874
1488
  "docs/spec.md",
2875
- "plans",
2876
- "tasks/current.md",
2877
- ".ai/harness/session",
2878
- ".ai/harness/controller/packets",
2879
- ".ai/harness/projections",
2880
- ".ai/harness/checks",
2881
- ];
2882
- audit(ctx, name, "ok", args);
2883
- return textResult({
2884
- repoRoot: ctx.repoRoot,
2885
- adopted: isForgeAdopted(ctx.repoRoot),
2886
- profile: ctx.policy.profile,
2887
- branch: currentGitBranch(ctx.repoRoot),
2888
- workflowRoots: roots.map((path) => ({
2889
- path,
2890
- exists: existsSync(join(ctx.repoRoot, path)),
2891
- })),
2892
- });
2893
- }
2894
- case "harness_doctor": {
2895
- const localConfig = existsSync(
2896
- join(ctx.repoRoot, ".forge", "mcp.local.json"),
2897
- );
2898
- const codexConfig = existsSync(
2899
- join(ctx.repoRoot, ".codex", "config.toml"),
2900
- );
2901
- audit(ctx, name, "ok", args);
2902
- return textResult({
2903
- status: isForgeAdopted(ctx.repoRoot)
2904
- ? "ready_local"
2905
- : "not_adopted",
2906
- repo: ctx.repoRoot,
2907
- profile: ctx.policy.profile,
2908
- mcp: {
2909
- localConfig,
2910
- policy: "builtin",
2911
- profile: ctx.policy.profile,
2912
- toolSurface:
2913
- ctx.policy.profile === "controller"
2914
- ? FORGE_TOOL_SURFACE
2915
- : `${ctx.policy.profile}-legacy-v1`,
2916
- toolCount: buildMcpToolDefinitions(ctx.policy, {
2917
- enableChatgptBrowser: ctx.enableChatgptBrowser === true,
2918
- }).length,
2919
- repositoryRead:
2920
- ctx.policy.profile === "controller"
2921
- ? "ordinary repository source and document files are readable; immutable secret and runtime paths remain denied"
2922
- : "workflow-scoped",
2923
- deniedPaths: ctx.policy.denyGlobs.length,
2924
- runner: {
2925
- enabled: ctx.policy.execution.agentRunner,
2926
- defaultTimeoutMs: ctx.policy.execution.runnerTimeoutMs,
2927
- maxTimeoutMs: ctx.policy.execution.runnerMaxTimeoutMs,
2928
- },
2929
- },
2930
- codex: {
2931
- configured: codexConfig,
2932
- fix: codexConfig
2933
- ? null
2934
- : "forge mcp setup codex --repo . --scope project",
2935
- },
2936
- chatgpt: {
2937
- localEndpoint: "http://127.0.0.1:8765/mcp",
2938
- manualStepsRequired: true,
2939
- guide: "docs/forge-chatgpt-mcp-setup.md",
2940
- },
2941
- });
2942
- }
2943
- case "controller_capabilities": {
2944
- if (ctx.policy.profile !== "controller")
2945
- return errorResult(
2946
- "TOOL_DISABLED",
2947
- "controller_capabilities requires the controller profile",
2948
- );
2949
- const expectedTools = controllerExpectedToolNames(ctx.policy, {
2950
- enableChatgptBrowser: ctx.enableChatgptBrowser === true,
2951
- });
2952
- const preferredTools: string[] = [...PREFERRED_FACADE_TOOL_NAMES];
2953
- const capabilityDescriptors = listCapabilityDescriptors();
2954
- const connectorSnapshot = Array.isArray(args.connector_tool_names)
2955
- ? args.connector_tool_names.map(String)
2956
- : Array.isArray(args.connectorToolNames)
2957
- ? args.connectorToolNames.map(String)
2958
- : undefined;
2959
- const connectorFreshness = await buildLocalConnectorStatusForRepo({
2960
- repoRoot: ctx.repoRoot,
2961
- expectedTools,
2962
- connectorToolNames: connectorSnapshot,
2963
- });
2964
- // staleConnector only when ChatGPT snapshot is supplied and missing rh_*.
2965
- const missingFacadeTools = connectorFreshness.missingConnectorTools;
2966
- const staleConnector = connectorFreshness.status === "chatgpt_snapshot_missing_facade";
2967
- const payload = {
2968
- schemaVersion: FORGE_MCP_SCHEMA_VERSION,
2969
- toolSurface: FORGE_TOOL_SURFACE,
2970
- version: FORGE_VERSION,
2971
- toolSurfaceFingerprint: forgeToolSurfaceFingerprint(expectedTools),
2972
- executionModel: "chatgpt-controller-execution-bridge",
2973
- profile: ctx.policy.profile,
2974
- capabilities: {
2975
- repositoryInspection: true,
2976
- issueLauncher: true,
2977
- githubIssuesAndProjects: true,
2978
- localAgentRuns: ctx.policy.execution.agentRunner,
2979
- githubCopilotSessions: true,
2980
- dynamicTaskGraph: true,
2981
- runtimeAgentSelection: true,
2982
- taskAgentBinding: false,
2983
- structuredRunEvents: true,
2984
- boundedDirectEdits: true,
2985
- multiRevisionDirectEdits: true,
2986
- editSavepoints: true,
2987
- partialEditRollback: true,
2988
- directEditFirstRouting: true,
2989
- persistedEditDiffs: true,
2990
- verifiedEditFinalization: true,
2991
- verificationGate: true,
2992
- localRunWatch: true,
2993
- localVisualController: true,
2994
- hierarchicalControllerUI: true,
2995
- localApprovalQueue: false,
2996
- localRiskApprovalGate: false,
2997
- quickAgentSessions: true,
2998
- persistentTimeouts: true,
2999
- timeoutProgress: true,
3000
- connectorSchemaDiagnostics: true,
3001
- projectProgressAggregation: true,
3002
- effectiveTaskStatus: true,
3003
- unifiedWorklogTimeline: true,
3004
- exportableWorklogReports: true,
3005
- githubPluginConfiguration: true,
3006
- serverSentEventDashboard: true,
3007
- singleExecutionFocus: false,
3008
- taskLocalReadiness: true,
3009
- riskAdaptiveVerification: true,
3010
- ephemeralQuickAgentSessions: true,
3011
- automaticRunContinuation: true,
3012
- reportedCommandEvidence: true,
3013
- governanceDiagnostics: true,
3014
- safeGovernanceReconciliation: true,
3015
- evidenceGateProgress: true,
3016
- directTaskActions: true,
3017
- issueArchiving: true,
3018
- retryableRunFailures: true,
3019
- issueCreationGuardrails: true,
3020
- persistedCheckEvidence: true,
3021
- persistedCheckReuse: true,
3022
- directVerification: true,
3023
- controllerContextAggregation: true,
3024
- taskLedgerProjection: true,
3025
- assistantPluginRuntime: true,
3026
- chatgptFacadeTools: true,
3027
- goalWorkloop: true,
3028
- handoffInbox: true,
3029
- codexGrokDelegation: true,
3030
- selfHealingRepair: true,
3031
- },
3032
- runner: {
3033
- enabled: ctx.policy.execution.agentRunner,
3034
- allowedAgents: ctx.policy.execution.allowedAgents,
3035
- defaultTimeoutMs: ctx.policy.execution.runnerTimeoutMs,
3036
- defaultTimeout: formatDurationMs(
3037
- ctx.policy.execution.runnerTimeoutMs,
3038
- ),
3039
- maxTimeoutMs: ctx.policy.execution.runnerMaxTimeoutMs,
3040
- maxTimeout: formatDurationMs(
3041
- ctx.policy.execution.runnerMaxTimeoutMs,
3042
- ),
3043
- note: "Requested timeout values are validated explicitly and are never silently reduced to the default.",
3044
- },
3045
- preferredTools,
3046
- capabilityGroups: summarizeCapabilityGroups(),
3047
- toolArchitecture: {
3048
- facadeTools: preferredTools,
3049
- atomicTypedToolsRetained: true,
3050
- internalHandlersRetained: true,
3051
- domainSchemaLoading: 'static_stable_surface',
3052
- dynamicDomainSchemaLoadingSupported: false,
3053
- capabilityDescriptorCount: capabilityDescriptors.length,
3054
- },
3055
- toolExposure: {
3056
- preferred: preferredTools,
3057
- note: "Prefer the five rh_* workflow facades for orchestration. Typed repository, Git, Issue/Task, plugin, browser, iOS, artifact, and recovery tools remain available because the current MCP transport cannot load domain schemas per conversation.",
3058
- advanced: expectedTools.filter((tool) => !preferredTools.includes(tool)).slice(0, 40),
3059
- compatibilityRetained: true,
3060
- },
3061
- expectedTools,
3062
- facadeToolsPresentInExpectedTools: preferredTools.every((tool: string) => expectedTools.includes(tool)),
3063
- staleConnector,
3064
- missingFacadeTools,
3065
- connectorFreshness: {
3066
- status: connectorFreshness.status,
3067
- severity: connectorFreshness.severity,
3068
- summary: connectorFreshness.summary,
3069
- missingLocalTools: connectorFreshness.missingLocalTools,
3070
- missingConnectorTools: connectorFreshness.missingConnectorTools,
3071
- expectedFacadeTools: connectorFreshness.expectedFacadeTools,
3072
- restartRecommended: connectorFreshness.restartRecommended,
3073
- reconnectRecommended: connectorFreshness.reconnectRecommended,
3074
- suggestedActions: connectorFreshness.suggestedActions,
3075
- },
3076
- docs: [
3077
- "docs/architecture/current/README.md",
3078
- "docs/architecture/history.md",
3079
- "docs/operations/controller-reliability-runbook.md",
3080
- "docs/forge-github-issue-launcher.md",
3081
- "docs/forge-chatgpt-mcp-setup.md",
3082
- ],
3083
- staleConnectorHint: staleConnector
3084
- ? `Stale ChatGPT connector tool snapshot is missing facade tools: ${missingFacadeTools.join(", ")}. Refresh/reconnect MCP so tools/list reloads rh_status, rh_inbox, rh_context, and rh_work.`
3085
- : connectorFreshness.status === "unable_to_verify_chatgpt_snapshot"
3086
- ? `Local MCP tool surface includes preferred facade tools. ChatGPT connector snapshot was not supplied, so freshness cannot be verified from this call. If ChatGPT tools/list is missing rh_status/rh_inbox/rh_context/rh_work, reconnect MCP. Pass connector_tool_names for an exact check.`
3087
- : connectorFreshness.status === "stale_fingerprint"
3088
- ? `Local MCP process fingerprint/version may be stale. Restart controller/MCP, then reconnect ChatGPT if rh_* are still missing.`
3089
- : connectorFreshness.status === "local_mcp_missing_facade"
3090
- ? `Local MCP tool surface is missing facade tools: ${connectorFreshness.missingLocalTools.join(", ")}. Restart controller/MCP.`
3091
- : `Connector clients must match ${FORGE_TOOL_SURFACE} version ${FORGE_VERSION} / schema ${FORGE_MCP_SCHEMA_VERSION}. Prefer rh_status/rh_inbox/rh_context/rh_work. Pass connector_tool_names to detect a stale ChatGPT tool snapshot precisely.`,
3092
- };
3093
- audit(ctx, name, "ok", args);
3094
- return textResult(payload);
3095
- }
3096
- case "local_bridge_status": {
3097
- if (ctx.policy.profile !== "controller")
3098
- return errorResult(
3099
- "TOOL_DISABLED",
3100
- "local_bridge_status requires the controller profile",
3101
- );
3102
- const detailLevel = args.detail_level === "detail" || args.detail === true
3103
- ? "detail"
3104
- : "summary";
3105
- const controllerHome = resolveRepoPreferredControllerHome(ctx.repoRoot);
3106
- const surface = resolveLocalBridgeSurface({
3107
- controllerHome,
3108
- repoRoot: ctx.repoRoot,
3109
- allowProcessScan: detailLevel === "detail",
3110
- });
3111
- const jobs = listLocalBridgeJobs(ctx.repoRoot, detailLevel === "detail" ? 12 : 20);
3112
- const { activeJobCount, recentJobSummary } = summarizeRecentJobs(jobs);
3113
- // Prefer authoritative surface; never invent a legacy 8766 endpoint.
3114
- const endpoint = surface.endpoint ?? null;
3115
- const running = surface.enabled
3116
- && (surface.processRunning === true
3117
- || (surface.endpointConfigured && surface.mode === "embedded"));
3118
- if (detailLevel === "summary") {
3119
- const payload = {
3120
- detailLevel: "summary",
3121
- repoId: ctx.repoId,
3122
- running,
3123
- ready: surface.enabled ? (surface.processRunning !== false) : true,
3124
- health: surface.enabled
3125
- ? (surface.processRunning === true || surface.mode === "embedded" ? "ready" : "warning")
3126
- : "disabled",
3127
- mode: surface.mode,
3128
- endpoint,
3129
- endpointConfigured: surface.endpointConfigured,
3130
- endpointReachable: null as boolean | null,
3131
- processRunning: surface.processRunning ?? null,
3132
- expectedSurface: surface.expectedSurface,
3133
- requiredForReadiness: surface.requiredForReadiness,
3134
- warnings: surface.error && surface.requiredForReadiness
3135
- ? [{ code: "LOCAL_BRIDGE_ERROR", message: surface.error.slice(0, 300) }]
3136
- : [],
3137
- activeJobCount,
3138
- recentJobSummary,
3139
- statusSource: surface.source,
3140
- nonBlocking: !surface.requiredForReadiness,
3141
- approvalQueue: false,
3142
- };
3143
- audit(ctx, name, "ok", args);
3144
- return textResult(payload);
3145
- }
3146
- const reconciliation = reconcileLocalBridgeJobs(ctx.repoRoot);
3147
- const payload = {
3148
- detailLevel: "detail",
3149
- endpoint,
3150
- endpointConfigured: surface.endpointConfigured,
3151
- running,
3152
- error: surface.error,
3153
- mode: surface.mode,
3154
- requiredForReadiness: surface.requiredForReadiness,
3155
- statusSource: surface.source,
3156
- counts: recentJobSummary,
3157
- activeJobCount,
3158
- recentJobSummary,
3159
- approvalQueue: false,
3160
- reconciliation,
3161
- recentJobs: jobs.map((job) => ({
3162
- jobId: job.jobId,
3163
- action: job.action,
3164
- status: job.status,
3165
- checkId: job.action === "run-check" ? (job.payload as { checkId?: string }).checkId : undefined,
3166
- runId: job.runId,
3167
- issueId: job.issueId,
3168
- taskId: job.taskId,
3169
- createdAt: job.createdAt,
3170
- updatedAt: job.updatedAt,
3171
- finishedAt: job.finishedAt,
3172
- revision: job.revision,
3173
- deadlineAt: job.deadlineAt,
3174
- error: job.error?.slice(0, 300),
3175
- })),
3176
- nonBlocking: !surface.requiredForReadiness,
3177
- fallback:
3178
- "Open the localhost Local Controller to launch work or inspect execution when a ChatGPT write action is unavailable.",
3179
- };
3180
- audit(ctx, name, "ok", args);
3181
- return textResult(payload);
3182
- }
3183
- case "controller_context": {
3184
- if (ctx.policy.profile !== "controller")
3185
- return errorResult(
3186
- "TOOL_DISABLED",
3187
- "controller_context requires the controller profile",
3188
- );
3189
- const controllerHome = resolveRepoPreferredControllerHome(ctx.repoRoot);
3190
- const requirementBoard = buildRequirementBoard({ controllerHome, repoId: ctx.repoId });
3191
- const executionDiagnostics = buildExecutionDiagnostics({
3192
- controllerHome,
3193
- repoId: ctx.repoId,
3194
- detailLevel: "detail",
3195
- });
3196
- const runs = listAgentJobs(ctx.repoRoot, 8);
3197
- const reconciliation = reconcileLocalBridgeJobs(ctx.repoRoot);
3198
- const jobs = listLocalBridgeJobs(ctx.repoRoot, 8);
3199
- const checks = listControllerChecks(ctx.repoRoot).map((check) => ({
3200
- id: check.id,
3201
- description: check.description,
3202
- timeoutMs: check.timeoutMs,
3203
- source: check.source,
3204
- }));
3205
- const assessment = assessWorkMode({
3206
- description: typeof args.description === "string" && args.description.trim()
3207
- ? String(args.description)
3208
- : "Inspect the selected repository context.",
3209
- knownPaths: stringList(args.known_paths),
3210
- expectedFiles: typeof args.expected_files === "number" ? args.expected_files : undefined,
3211
- expectedChangedLines: typeof args.expected_changed_lines === "number" ? args.expected_changed_lines : undefined,
3212
- requiresInvestigation: args.requires_investigation === true,
3213
- requiresParallelism: args.requires_parallelism === true,
3214
- requiresLongRunningChecks: args.requires_long_running_checks === true,
3215
- needsDependencies: args.needs_dependencies === true,
3216
- risk: typeof args.risk === "string" ? args.risk as TaskRisk : undefined,
3217
- explicitMode: parseExplicitTaskMode(args.mode)
3218
- ?? (typeof args.description === "string" && args.description.trim() ? undefined : "direct"),
3219
- });
3220
- const modeContextPack = assessment.modeBehavior.structuralContext === "required"
3221
- ? buildControllerContextPack(ctx.repoRoot, ctx.policy, {
3222
- description: typeof args.description === "string" ? args.description : undefined,
3223
- knownPaths: stringList(args.known_paths),
3224
- structuralContext: "required",
3225
- maxFiles: 8,
3226
- maxSnippets: 20,
3227
- })
3228
- : undefined;
3229
- const payload = {
3230
- git: gitSnapshot(ctx.repoRoot),
3231
- requirementBoard,
3232
- executionDiagnostics,
3233
- legacyCompatibility: {
3234
- currentIssue: "deprecated_frozen",
3235
- issueTaskBoard: "deprecated_frozen",
3236
- authority: "controller-home-sqlite",
3237
- },
3238
- activeRuns: runs.map((run) => ({
3239
- runId: run.runId,
3240
- issueId: run.issueId,
3241
- taskId: run.taskId,
3242
- status: run.status,
3243
- agent: run.agent,
3244
- provider: run.provider,
3245
- executionMode: run.executionMode,
3246
- progress: run.progress,
3247
- lastHeartbeatAt: run.lastHeartbeatAt,
3248
- error: run.error?.slice(0, 300),
3249
- })),
3250
- localBridge: {
3251
- reconciliation,
3252
- recentJobs: jobs.map((job) => ({
3253
- jobId: job.jobId,
3254
- action: job.action,
3255
- status: job.status,
3256
- checkId: job.action === "run-check" ? (job.payload as { checkId?: string }).checkId : undefined,
3257
- issueId: job.issueId,
3258
- taskId: job.taskId,
3259
- updatedAt: job.updatedAt,
3260
- })),
3261
- },
3262
- checks,
3263
- recommendedExecution: assessment,
3264
- ...(modeContextPack ? { modeContextPack } : {}),
3265
- };
3266
- audit(ctx, name, "ok", args);
3267
- return textResult(payload);
3268
- }
3269
- case "quick_agent_session":
3270
- case "submit_local_job": {
3271
- if (ctx.policy.profile !== "controller") {
3272
- return errorResult("TOOL_DISABLED", `${name} requires the controller profile`);
3273
- }
3274
- audit(ctx, name, "blocked", args);
3275
- return errorResult(
3276
- name === "quick_agent_session" ? "AGENT_RUN_DEPRECATED" : "LOCAL_BRIDGE_JOB_RETIRED",
3277
- "Kernel-managed Agent and Local Bridge Jobs are retired. Create or claim WorkContract and use Process Runtime or the Thin Launcher.",
3278
- );
3279
- }
3280
- case "list_local_jobs": {
3281
- if (ctx.policy.profile !== "controller")
3282
- return errorResult(
3283
- "TOOL_DISABLED",
3284
- "list_local_jobs requires the controller profile",
3285
- );
3286
- const limit = typeof args.limit === "number" ? args.limit : 50;
3287
- const jobs = listLocalBridgeJobs(ctx.repoRoot, limit);
3288
- audit(ctx, name, "ok", args);
3289
- return textResult({ jobs });
3290
- }
3291
- case "get_local_job": {
3292
- if (ctx.policy.profile !== "controller")
3293
- return errorResult(
3294
- "TOOL_DISABLED",
3295
- "get_local_job requires the controller profile",
3296
- );
3297
- const jobId = String(args.job_id ?? "").trim();
3298
- const includeEvents = args.include_events === true;
3299
- const includeOutput = args.include_output === true;
3300
- const snapshot = getLocalBridgeJobSnapshot(ctx.repoRoot, jobId);
3301
- const payload = {
3302
- job: snapshot.job,
3303
- ...(snapshot.status !== "ok" ? { lookup: snapshot } : {}),
3304
- ...(includeEvents && snapshot.status === "ok" ? { events: getLocalBridgeJobEvents(ctx.repoRoot, jobId) } : {}),
3305
- ...(includeOutput
3306
- ? {
3307
- output: readLocalBridgeJobOutput(ctx.repoRoot, jobId, {
3308
- stream: args.stream === "stderr" ? "stderr" : "stdout",
3309
- maxBytes: typeof args.max_bytes === "number" ? args.max_bytes : undefined,
3310
- }),
3311
- }
3312
- : {}),
3313
- };
3314
- audit(ctx, name, "ok", args);
3315
- return textResult(payload);
3316
- }
3317
- case "get_local_job_output": {
3318
- if (ctx.policy.profile !== "controller")
3319
- return errorResult(
3320
- "TOOL_DISABLED",
3321
- "get_local_job_output requires the controller profile",
3322
- );
3323
- const jobId = String(args.job_id ?? "").trim();
3324
- const payload = readLocalBridgeJobOutput(ctx.repoRoot, jobId, {
3325
- stream: args.stream === "stderr" ? "stderr" : "stdout",
3326
- maxBytes: typeof args.max_bytes === "number" ? args.max_bytes : undefined,
3327
- });
3328
- audit(ctx, name, "ok", args);
3329
- return textResult(payload);
3330
- }
3331
- case "project_snapshot": {
3332
- if (ctx.policy.profile !== "controller") return errorResult("TOOL_DISABLED", "project_snapshot requires the controller profile");
3333
- const markerPaths = [
3334
- ".ai/harness/active-plan",
3335
- ".ai/harness/active-sprint",
3336
- ".ai/harness/active-worktree",
3337
- "tasks/current.md",
3338
- ];
3339
- const markers = markerPaths.map((path) => {
3340
- const absolute = join(ctx.repoRoot, path);
3341
- if (!existsSync(absolute)) return { path, exists: false };
3342
- const raw = readFileSync(absolute, "utf-8");
3343
- return {
3344
- path,
3345
- exists: true,
3346
- bytes: Buffer.byteLength(raw),
3347
- lines: raw.split(/\r?\n/).length,
3348
- sha256: createHash("sha256").update(raw).digest("hex").slice(0, 16),
3349
- preview: redactMcpText(raw).text.slice(0, 400),
3350
- };
3351
- });
3352
- const controllerHome = resolveRepoPreferredControllerHome(ctx.repoRoot);
3353
- const requirementBoard = buildRequirementBoard({ controllerHome, repoId: ctx.repoId });
3354
- const executionDiagnostics = buildExecutionDiagnostics({
3355
- controllerHome,
3356
- repoId: ctx.repoId,
3357
- detailLevel: "detail",
3358
- });
3359
- const snapshotRuns = listAgentJobs(ctx.repoRoot, 11);
3360
- const runs = snapshotRuns.slice(0, 10).map((run) => ({
3361
- runId: run.runId,
3362
- issueId: run.issueId,
3363
- taskId: run.taskId,
3364
- agent: run.agent,
3365
- provider: run.provider,
3366
- executionMode: run.executionMode,
3367
- status: run.status,
3368
- progress: run.progress,
3369
- createdAt: run.createdAt,
3370
- finishedAt: run.finishedAt,
3371
- error: run.error?.slice(0, 300),
3372
- }));
3373
- const payload = {
3374
- git: gitSnapshot(ctx.repoRoot),
3375
- requirementBoard,
3376
- executionDiagnostics,
3377
- legacyCompatibility: {
3378
- currentIssue: "deprecated_frozen",
3379
- issueTaskBoard: "deprecated_frozen",
3380
- authority: "controller-home-sqlite",
3381
- },
3382
- runs,
3383
- markers,
3384
- truncated: {
3385
- runs: snapshotRuns.length > 10,
3386
- },
3387
- };
3388
- audit(ctx, name, "ok", args);
3389
- return textResult(payload);
3390
- }
3391
- case "search_repository": {
3392
- if (ctx.policy.profile !== "controller")
3393
- return errorResult(
3394
- "TOOL_DISABLED",
3395
- "search_repository requires the controller profile",
3396
- );
3397
- const session = ctx.sessionId && ctx.repoId
3398
- ? {
3399
- sessionId: ctx.sessionId,
3400
- repoId: ctx.repoId,
3401
- checkoutId: ctx.checkoutId ?? ctx.repoId,
3402
- }
3403
- : undefined;
3404
- const result = searchRepository(ctx.repoRoot, ctx.policy, {
3405
- query: String(args.query ?? ""),
3406
- includeGlobs: stringList(args.include_globs),
3407
- excludeGlobs: stringList(args.exclude_globs),
3408
- maxResults:
3409
- typeof args.max_results === "number" ? args.max_results : undefined,
3410
- maxFiles:
3411
- typeof args.max_files === "number" ? args.max_files : undefined,
3412
- caseSensitive: args.case_sensitive === true,
3413
- session,
3414
- });
3415
- audit(ctx, name, "ok", args);
3416
- return textResult(result);
3417
- }
3418
- case "get_git_diff": {
3419
- if (ctx.policy.profile !== "controller")
3420
- return errorResult(
3421
- "TOOL_DISABLED",
3422
- "get_git_diff requires the controller profile",
3423
- );
3424
- const path =
3425
- typeof args.path === "string" && args.path.trim()
3426
- ? args.path.trim()
3427
- : undefined;
3428
- if (path) {
3429
- const decision = resolveMcpPath(
3430
- ctx.repoRoot,
3431
- path,
3432
- ctx.policy,
3433
- "read",
3434
- );
3435
- if (!decision.ok)
3436
- return errorResult(
3437
- "POLICY_DENIED",
3438
- decision.reason ?? "path denied",
3439
- { path },
3440
- );
3441
- }
3442
- const maxBytes =
3443
- typeof args.max_bytes === "number"
3444
- ? Math.min(Math.max(Math.trunc(args.max_bytes), 1024), 512 * 1024)
3445
- : 128 * 1024;
3446
- const result = gitDiff(ctx.repoRoot, path, maxBytes);
3447
- const redacted = redactMcpText(result.diff);
3448
- audit(ctx, name, "ok", args, path);
3449
- return textResult({
3450
- ...result,
3451
- diff: redacted.text,
3452
- redactions: redacted.redactions,
3453
- });
3454
- }
3455
- case "list_checks": {
3456
- if (ctx.policy.profile !== "controller")
3457
- return errorResult(
3458
- "TOOL_DISABLED",
3459
- "list_checks requires the controller profile",
3460
- );
3461
- const checks = listControllerChecks(ctx.repoRoot);
3462
- audit(ctx, name, "ok", args);
3463
- return textResult({ checks });
3464
- }
3465
- case "list_issues": {
3466
- if (ctx.policy.profile !== "controller")
3467
- return errorResult(
3468
- "TOOL_DISABLED",
3469
- "list_issues requires the controller profile",
3470
- );
3471
- const projectState = loadControllerProjectState(ctx.repoRoot);
3472
- const issues = listIssueEffectiveViews(ctx.repoRoot).map((issue) => {
3473
- const readiness = inspectIssueReadiness(ctx.repoRoot, issue.id);
3474
- return {
3475
- id: issue.id,
3476
- title: issue.title,
3477
- kind: issue.kind,
3478
- status: issue.status,
3479
- lifecycleStatus: issue.lifecycleStatus,
3480
- archivedAt: issue.archivedAt,
3481
- isCurrent: projectState.currentIssueId === issue.id,
3482
- updatedAt: issue.updatedAt,
3483
- taskCount: issue.tasks.length,
3484
- taskCounts: issue.tasks.reduce<Record<string, number>>((counts, task) => {
3485
- counts[task.effectiveStatus] = (counts[task.effectiveStatus] ?? 0) + 1;
3486
- return counts;
3487
- }, {}),
3488
- declaredTaskCounts: issue.tasks.reduce<Record<string, number>>((counts, task) => {
3489
- counts[task.declaredStatus] = (counts[task.declaredStatus] ?? 0) + 1;
3490
- return counts;
3491
- }, {}),
3492
- readyTaskIds: readiness.readyTaskIds,
3493
- queueableTaskIds: readiness.queueableTaskIds,
3494
- approvalPendingTaskIds: readiness.approvalPendingTaskIds,
3495
- };
3496
- });
3497
- audit(ctx, name, "ok", args);
3498
- return textResult({ issues });
3499
- }
3500
- case "get_project_board": {
3501
- if (ctx.policy.profile !== "controller")
3502
- return errorResult(
3503
- "TOOL_DISABLED",
3504
- "get_project_board requires the controller profile",
3505
- );
3506
- const startedAt = performance.now();
3507
- const controllerHome = resolveRepoPreferredControllerHome(ctx.repoRoot);
3508
- const detailLevel = args.detail_level === "full" ? "full" : args.detail_level === "detail" ? "detail" : "summary";
3509
- const payload = detailLevel === "summary"
3510
- ? buildRequirementBoard({ controllerHome, repoId: ctx.repoId })
3511
- : buildExecutionDiagnostics({
3512
- controllerHome,
3513
- repoId: ctx.repoId,
3514
- detailLevel,
3515
- requirementId: typeof args.requirement_id === "string" ? args.requirement_id : undefined,
3516
- });
3517
- audit(ctx, name, "ok", args);
3518
- const response = {
3519
- ...payload,
3520
- responseMeta: {
3521
- serverDurationMs: Number((performance.now() - startedAt).toFixed(2)),
3522
- structuredPayloadBytes: 0,
3523
- },
3524
- };
3525
- response.responseMeta.structuredPayloadBytes = Buffer.byteLength(JSON.stringify(response), "utf8");
3526
- return textResult(response);
3527
- }
3528
- case "get_project_progress": {
3529
- if (ctx.policy.profile !== "controller")
3530
- return errorResult("TOOL_DISABLED", "get_project_progress requires the controller profile");
3531
- const progress = getProjectProgress(ctx.repoRoot);
3532
- audit(ctx, name, "ok", args);
3533
- return textResult(progress);
3534
- }
3535
- case "get_project_governance": {
3536
- if (ctx.policy.profile !== "controller")
3537
- return errorResult("TOOL_DISABLED", "get_project_governance requires the controller profile");
3538
- const governance = inspectProjectGovernance(ctx.repoRoot);
3539
- audit(ctx, name, "ok", args);
3540
- return textResult(governance);
3541
- }
3542
- case "reconcile_project_governance": {
3543
- if (ctx.policy.profile !== "controller")
3544
- return errorResult("TOOL_DISABLED", "reconcile_project_governance requires the controller profile");
3545
- const result = reconcileProjectGovernance(ctx.repoRoot);
3546
- audit(ctx, name, "ok", args, ".ai/harness/controller/project-state.json");
3547
- return textResult(result);
3548
- }
3549
- case "get_project_state": {
3550
- if (ctx.policy.profile !== "controller")
3551
- return errorResult("TOOL_DISABLED", "get_project_state requires the controller profile");
3552
- const state = loadControllerProjectState(ctx.repoRoot);
3553
- audit(ctx, name, "ok", args);
3554
- return textResult(state);
3555
- }
3556
- case "set_current_issue": {
3557
- if (ctx.policy.profile !== "controller")
3558
- return errorResult("TOOL_DISABLED", "set_current_issue requires the controller profile");
3559
- const issue = getIssue(ctx.repoRoot, String(args.issue_id ?? ""));
3560
- if (issue.archivedAt || ["done", "cancelled"].includes(issue.status))
3561
- return errorResult("ISSUE_NOT_ACTIVE", `only an active, non-archived Issue can be selected (current: ${issue.status})`);
3562
- const state = saveControllerProjectState(ctx.repoRoot, { currentIssueId: issue.id }, "chatgpt-controller");
3563
- audit(ctx, name, "ok", args, ".ai/harness/controller/project-state.json");
3564
- return textResult(state);
3565
- }
3566
- case "archive_issue": {
3567
- if (ctx.policy.profile !== "controller")
3568
- return errorResult("TOOL_DISABLED", "archive_issue requires the controller profile");
3569
- const issue = archiveIssue(ctx.repoRoot, String(args.issue_id ?? ""));
3570
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
3571
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
3572
- }
3573
- case "restore_issue": {
3574
- if (ctx.policy.profile !== "controller")
3575
- return errorResult("TOOL_DISABLED", "restore_issue requires the controller profile");
3576
- const issue = restoreIssue(ctx.repoRoot, String(args.issue_id ?? ""));
3577
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
3578
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
3579
- }
3580
- case "get_task_progress_detail": {
3581
- if (ctx.policy.profile !== "controller")
3582
- return errorResult("TOOL_DISABLED", "get_task_progress_detail requires the controller profile");
3583
- const result = getTaskProgressReadView(
3584
- ctx.repoRoot,
3585
- String(args.issue_id ?? ""),
3586
- String(args.task_id ?? ""),
3587
- args.detail_level === "full" ? "full" : "summary",
3588
- );
3589
- audit(ctx, name, "ok", args, `tasks/issues/${result.issue.id}`);
3590
- return textResult(result);
3591
- }
3592
- case "get_worklog_timeline": {
3593
- if (ctx.policy.profile !== "controller")
3594
- return errorResult("TOOL_DISABLED", "get_worklog_timeline requires the controller profile");
3595
- const events = getControllerTimeline(ctx.repoRoot, {
3596
- category: parseWorklogCategory(args.category),
3597
- issueId: typeof args.issue_id === "string" ? args.issue_id : undefined,
3598
- taskId: typeof args.task_id === "string" ? args.task_id : undefined,
3599
- runId: typeof args.run_id === "string" ? args.run_id : undefined,
3600
- editSessionId: typeof args.edit_session_id === "string" ? args.edit_session_id : undefined,
3601
- since: typeof args.since === "string" ? args.since : undefined,
3602
- until: typeof args.until === "string" ? args.until : undefined,
3603
- limit: typeof args.limit === "number" ? Math.trunc(args.limit) : undefined,
3604
- });
3605
- audit(ctx, name, "ok", args);
3606
- return textResult({ events });
3607
- }
3608
- case "export_worklog": {
3609
- if (ctx.policy.profile !== "controller")
3610
- return errorResult("TOOL_DISABLED", "export_worklog requires the controller profile");
3611
- const result = exportControllerWorklog(ctx.repoRoot, {
3612
- format: args.format === "json" ? "json" : "markdown",
3613
- outputPath: typeof args.output_path === "string" ? args.output_path : undefined,
3614
- filter: {
3615
- issueId: typeof args.issue_id === "string" ? args.issue_id : undefined,
3616
- taskId: typeof args.task_id === "string" ? args.task_id : undefined,
3617
- runId: typeof args.run_id === "string" ? args.run_id : undefined,
3618
- editSessionId: typeof args.edit_session_id === "string" ? args.edit_session_id : undefined,
3619
- },
3620
- });
3621
- audit(ctx, name, "ok", args, result.path);
3622
- return textResult(result);
3623
- }
3624
- case "get_github_plugin_status": {
3625
- if (ctx.policy.profile !== "controller")
3626
- return errorResult("TOOL_DISABLED", "get_github_plugin_status requires the controller profile");
3627
- const status = getGitHubPluginStatus(ctx.repoRoot);
3628
- audit(ctx, name, "ok", args, status.repository);
3629
- return textResult(status);
3630
- }
3631
- case "configure_github_plugin": {
3632
- if (ctx.policy.profile !== "controller")
3633
- return errorResult("TOOL_DISABLED", "configure_github_plugin requires the controller profile");
3634
- const config = saveGitHubPluginConfig(ctx.repoRoot, {
3635
- enabled: typeof args.enabled === "boolean" ? args.enabled : undefined,
3636
- repository: args.clear_repository === true ? "" : typeof args.repository === "string" ? args.repository : undefined,
3637
- syncMode: args.sync_mode === "checkpoint" ? "checkpoint" : args.sync_mode === "manual" ? "manual" : undefined,
3638
- includeTasks: typeof args.include_tasks === "boolean" ? args.include_tasks : undefined,
3639
- projectOwner: args.clear_project === true ? "" : typeof args.project_owner === "string" ? args.project_owner : undefined,
3640
- projectNumber: args.clear_project === true ? null : typeof args.project_number === "number" ? args.project_number : undefined,
1489
+ "plans",
1490
+ "tasks/current.md",
1491
+ ".ai/harness/session",
1492
+ ".ai/harness/controller/packets",
1493
+ ".ai/harness/projections",
1494
+ ".ai/harness/checks",
1495
+ ];
1496
+ audit(ctx, name, "ok", args);
1497
+ return textResult({
1498
+ repoRoot: ctx.repoRoot,
1499
+ adopted: isForgeAdopted(ctx.repoRoot),
1500
+ profile: ctx.policy.profile,
1501
+ branch: currentGitBranch(ctx.repoRoot),
1502
+ workflowRoots: roots.map((path) => ({
1503
+ path,
1504
+ exists: existsSync(join(ctx.repoRoot, path)),
1505
+ })),
3641
1506
  });
3642
- audit(ctx, name, "ok", args, "repository-registry:github");
3643
- return textResult(config);
3644
- }
3645
- case "get_issue": {
3646
- if (ctx.policy.profile !== "controller")
3647
- return errorResult(
3648
- "TOOL_DISABLED",
3649
- "get_issue requires the controller profile",
3650
- );
3651
- const issue = getIssueReadView(
3652
- ctx.repoRoot,
3653
- String(args.issue_id ?? ""),
3654
- args.detail_level === "full" ? "full" : "summary",
3655
- );
3656
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
3657
- return textResult(issue);
3658
- }
3659
- case "github_status": {
3660
- if (ctx.policy.profile !== "controller")
3661
- return errorResult(
3662
- "TOOL_DISABLED",
3663
- "github_status requires the controller profile",
3664
- );
3665
- const status = getGitHubStatus(
3666
- ctx.repoRoot,
3667
- typeof args.repo === "string" ? args.repo : undefined,
3668
- );
3669
- audit(
3670
- ctx,
3671
- name,
3672
- status.available && status.authenticated ? "ok" : "failed",
3673
- args,
3674
- status.repository?.nameWithOwner,
3675
- status.errors.join("; "),
3676
- );
3677
- return textResult(status);
3678
1507
  }
3679
- case "publish_issue_to_github": {
3680
- if (ctx.policy.profile !== "controller")
3681
- return errorResult(
3682
- "TOOL_DISABLED",
3683
- "publish_issue_to_github requires the controller profile",
3684
- );
3685
- const issue = publishIssueToGitHub(
3686
- ctx.repoRoot,
3687
- String(args.issue_id ?? ""),
3688
- {
3689
- repo: typeof args.repo === "string" ? args.repo : undefined,
3690
- labels: stringList(args.labels),
3691
- includeTasks: args.include_tasks === true,
3692
- projectOwner:
3693
- typeof args.project_owner === "string"
3694
- ? args.project_owner
3695
- : undefined,
3696
- projectNumber:
3697
- typeof args.project_number === "number"
3698
- ? Math.trunc(args.project_number)
3699
- : undefined,
3700
- },
1508
+ case "harness_doctor": {
1509
+ const localConfig = existsSync(
1510
+ join(ctx.repoRoot, ".forge", "mcp.local.json"),
3701
1511
  );
3702
- audit(ctx, name, "ok", args, issue.github?.url);
3703
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
3704
- }
3705
- case "refresh_github_issue": {
3706
- if (ctx.policy.profile !== "controller")
3707
- return errorResult(
3708
- "TOOL_DISABLED",
3709
- "refresh_github_issue requires the controller profile",
3710
- );
3711
- const result = refreshGitHubIssue(
3712
- ctx.repoRoot,
3713
- String(args.issue_id ?? ""),
1512
+ const codexConfig = existsSync(
1513
+ join(ctx.repoRoot, ".codex", "config.toml"),
3714
1514
  );
3715
- audit(ctx, name, "ok", args, result.issue.github?.url);
1515
+ audit(ctx, name, "ok", args);
3716
1516
  return textResult({
3717
- ...result,
3718
- issue: projectIssueEffectiveView(ctx.repoRoot, result.issue),
1517
+ status: isForgeAdopted(ctx.repoRoot)
1518
+ ? "ready_local"
1519
+ : "not_adopted",
1520
+ repo: ctx.repoRoot,
1521
+ profile: ctx.policy.profile,
1522
+ mcp: {
1523
+ localConfig,
1524
+ policy: "builtin",
1525
+ profile: ctx.policy.profile,
1526
+ toolSurface:
1527
+ ctx.policy.profile === "controller"
1528
+ ? FORGE_TOOL_SURFACE
1529
+ : `${ctx.policy.profile}-legacy-v1`,
1530
+ toolCount: buildMcpToolDefinitions(ctx.policy, {
1531
+ enableChatgptBrowser: ctx.enableChatgptBrowser === true,
1532
+ }).length,
1533
+ repositoryRead:
1534
+ ctx.policy.profile === "controller"
1535
+ ? "ordinary repository source and document files are readable; immutable secret and runtime paths remain denied"
1536
+ : "workflow-scoped",
1537
+ deniedPaths: ctx.policy.denyGlobs.length,
1538
+ runner: {
1539
+ enabled: ctx.policy.execution.agentRunner,
1540
+ defaultTimeoutMs: ctx.policy.execution.runnerTimeoutMs,
1541
+ maxTimeoutMs: ctx.policy.execution.runnerMaxTimeoutMs,
1542
+ },
1543
+ },
1544
+ codex: {
1545
+ configured: codexConfig,
1546
+ fix: codexConfig
1547
+ ? null
1548
+ : "forge mcp setup codex --repo . --scope project",
1549
+ },
1550
+ chatgpt: {
1551
+ localEndpoint: "http://127.0.0.1:8765/mcp",
1552
+ manualStepsRequired: true,
1553
+ guide: "docs/forge-chatgpt-mcp-setup.md",
1554
+ },
3719
1555
  });
3720
1556
  }
3721
- case "close_github_issue": {
3722
- if (ctx.policy.profile !== "controller")
3723
- return errorResult(
3724
- "TOOL_DISABLED",
3725
- "close_github_issue requires the controller profile",
3726
- );
3727
- const issue = closeGitHubIssue(
3728
- ctx.repoRoot,
3729
- String(args.issue_id ?? ""),
3730
- );
3731
- audit(ctx, name, "ok", args, issue.github?.url);
3732
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
3733
- }
3734
- case "inspect_issue_readiness": {
1557
+ case "controller_capabilities": {
3735
1558
  if (ctx.policy.profile !== "controller")
3736
1559
  return errorResult(
3737
1560
  "TOOL_DISABLED",
3738
- "inspect_issue_readiness requires the controller profile",
1561
+ "controller_capabilities requires the controller profile",
3739
1562
  );
3740
- const readiness = inspectIssueReadiness(
3741
- ctx.repoRoot,
3742
- String(args.issue_id ?? ""),
3743
- );
3744
- audit(
3745
- ctx,
3746
- name,
3747
- readiness.ready ? "ok" : "blocked",
3748
- args,
3749
- `tasks/issues/${readiness.issueId}`,
3750
- [...readiness.blockers, ...readiness.taskBlockers].map((item) => item.message).join("; "),
1563
+ const expectedTools = ctx.publishedToolNames;
1564
+ if (!expectedTools) return errorResult(
1565
+ 'MCP_TOOL_SURFACE_UNAVAILABLE',
1566
+ 'Controller capability diagnostics require the authoritative MCP registry projection.',
3751
1567
  );
3752
- return textResult(readiness);
3753
- }
3754
- case "inspect_task_readiness": {
3755
- if (ctx.policy.profile !== "controller")
3756
- return errorResult(
3757
- "TOOL_DISABLED",
3758
- "inspect_task_readiness requires the controller profile",
3759
- );
3760
- const readiness = inspectTaskReadiness(
3761
- ctx.repoRoot,
3762
- String(args.issue_id ?? ""),
3763
- String(args.task_id ?? ""),
3764
- {
3765
- retryFromRunId: typeof args.retry_from_run_id === "string" ? args.retry_from_run_id : undefined,
3766
- approveDestructive: args.approve_destructive === true,
1568
+ const preferredTools: string[] = [...PREFERRED_FACADE_TOOL_NAMES];
1569
+ const capabilityDescriptors = listCapabilityDescriptors();
1570
+ const connectorSnapshot = Array.isArray(args.connector_tool_names)
1571
+ ? args.connector_tool_names.map(String)
1572
+ : Array.isArray(args.connectorToolNames)
1573
+ ? args.connectorToolNames.map(String)
1574
+ : undefined;
1575
+ const connectorFreshness = await buildLocalConnectorStatusForRepo({
1576
+ repoRoot: ctx.repoRoot,
1577
+ expectedTools,
1578
+ connectorToolNames: connectorSnapshot,
1579
+ });
1580
+ // staleConnector only when ChatGPT snapshot is supplied and missing rh_*.
1581
+ const missingFacadeTools = connectorFreshness.missingConnectorTools;
1582
+ const staleConnector = connectorFreshness.status === "chatgpt_snapshot_missing_facade";
1583
+ const payload = {
1584
+ schemaVersion: FORGE_MCP_SCHEMA_VERSION,
1585
+ toolSurface: FORGE_TOOL_SURFACE,
1586
+ version: FORGE_VERSION,
1587
+ toolSurfaceFingerprint: forgeToolSurfaceFingerprint(expectedTools),
1588
+ executionModel: "chatgpt-controller-execution-bridge",
1589
+ profile: ctx.policy.profile,
1590
+ capabilities: {
1591
+ repositoryInspection: true,
1592
+ issueLauncher: true,
1593
+ githubIssuesAndProjects: true,
1594
+ localAgentRuns: ctx.policy.execution.agentRunner,
1595
+ githubCopilotSessions: true,
1596
+ dynamicTaskGraph: true,
1597
+ runtimeAgentSelection: true,
1598
+ taskAgentBinding: false,
1599
+ structuredRunEvents: true,
1600
+ boundedDirectEdits: true,
1601
+ multiRevisionDirectEdits: true,
1602
+ editSavepoints: true,
1603
+ partialEditRollback: true,
1604
+ directEditFirstRouting: true,
1605
+ persistedEditDiffs: true,
1606
+ verifiedEditFinalization: true,
1607
+ verificationGate: true,
1608
+ localRunWatch: true,
1609
+ localVisualController: true,
1610
+ hierarchicalControllerUI: true,
1611
+ localApprovalQueue: false,
1612
+ localRiskApprovalGate: false,
1613
+ quickAgentSessions: true,
1614
+ persistentTimeouts: true,
1615
+ timeoutProgress: true,
1616
+ connectorSchemaDiagnostics: true,
1617
+ projectProgressAggregation: true,
1618
+ effectiveTaskStatus: true,
1619
+ unifiedWorklogTimeline: true,
1620
+ exportableWorklogReports: true,
1621
+ githubPluginConfiguration: true,
1622
+ serverSentEventDashboard: true,
1623
+ singleExecutionFocus: false,
1624
+ taskLocalReadiness: true,
1625
+ riskAdaptiveVerification: true,
1626
+ ephemeralQuickAgentSessions: true,
1627
+ automaticRunContinuation: true,
1628
+ reportedCommandEvidence: true,
1629
+ governanceDiagnostics: true,
1630
+ safeGovernanceReconciliation: true,
1631
+ evidenceGateProgress: true,
1632
+ directTaskActions: true,
1633
+ issueArchiving: true,
1634
+ retryableRunFailures: true,
1635
+ issueCreationGuardrails: true,
1636
+ persistedCheckEvidence: true,
1637
+ persistedCheckReuse: true,
1638
+ directVerification: true,
1639
+ controllerContextAggregation: true,
1640
+ taskLedgerProjection: true,
1641
+ assistantPluginRuntime: true,
1642
+ chatgptFacadeTools: true,
1643
+ goalWorkloop: true,
1644
+ handoffInbox: true,
1645
+ codexGrokDelegation: true,
1646
+ selfHealingRepair: true,
3767
1647
  },
3768
- );
3769
- audit(
3770
- ctx,
3771
- name,
3772
- readiness.ready ? "ok" : "blocked",
3773
- args,
3774
- `tasks/issues/${readiness.issueId}`,
3775
- readiness.blockers.map((item) => item.message).join("; "),
3776
- );
3777
- return textResult(readiness);
3778
- }
3779
- case "prepare_issue_launch": {
3780
- if (ctx.policy.profile !== "controller")
3781
- return errorResult(
3782
- "TOOL_DISABLED",
3783
- "prepare_issue_launch requires the controller profile",
3784
- );
3785
- const issueId = String(args.issue_id ?? "");
3786
- const issue = getIssue(ctx.repoRoot, issueId);
3787
- const readiness = inspectIssueReadiness(ctx.repoRoot, issueId);
3788
- const maxParallel = Math.min(
3789
- Math.max(
3790
- typeof args.max_parallel === "number"
3791
- ? Math.trunc(args.max_parallel)
3792
- : readiness.suggestedMaxParallel,
3793
- 1,
3794
- ),
3795
- 4,
3796
- );
3797
- const tasks = issue.tasks
3798
- .filter((task) => readiness.queueableTaskIds.includes(task.id))
3799
- .map((task) => ({
3800
- id: task.id,
3801
- title: task.title,
3802
- agent: task.recommendedAgent ?? "runtime-selected",
3803
- risk: task.risk,
3804
- allowedPaths: task.allowedPaths,
3805
- checks: task.checks,
3806
- acceptanceCriteria: task.acceptanceCriteria,
3807
- github: task.github,
3808
- }));
3809
- const preview = {
3810
- issue: {
3811
- id: issue.id,
3812
- title: issue.title,
3813
- status: issue.status,
3814
- github: issue.github,
1648
+ runner: {
1649
+ enabled: ctx.policy.execution.agentRunner,
1650
+ allowedAgents: ctx.policy.execution.allowedAgents,
1651
+ defaultTimeoutMs: ctx.policy.execution.runnerTimeoutMs,
1652
+ defaultTimeout: formatDurationMs(
1653
+ ctx.policy.execution.runnerTimeoutMs,
1654
+ ),
1655
+ maxTimeoutMs: ctx.policy.execution.runnerMaxTimeoutMs,
1656
+ maxTimeout: formatDurationMs(
1657
+ ctx.policy.execution.runnerMaxTimeoutMs,
1658
+ ),
1659
+ note: "Requested timeout values are validated explicitly and are never silently reduced to the default.",
1660
+ },
1661
+ preferredTools,
1662
+ capabilityGroups: summarizeCapabilityGroups(),
1663
+ toolArchitecture: {
1664
+ facadeTools: preferredTools,
1665
+ atomicTypedToolsRetained: true,
1666
+ internalHandlersRetained: true,
1667
+ domainSchemaLoading: 'static_stable_surface',
1668
+ dynamicDomainSchemaLoadingSupported: false,
1669
+ capabilityDescriptorCount: capabilityDescriptors.length,
1670
+ },
1671
+ toolExposure: {
1672
+ preferred: preferredTools,
1673
+ note: "Prefer the five rh_* workflow facades for orchestration. Typed repository, Git, Issue/Task, plugin, browser, iOS, artifact, and recovery tools remain available because the current MCP transport cannot load domain schemas per conversation.",
1674
+ exposedToolCount: expectedTools.length,
1675
+ },
1676
+ expectedTools,
1677
+ facadeToolsPresentInExpectedTools: preferredTools.every((tool: string) => expectedTools.includes(tool)),
1678
+ staleConnector,
1679
+ missingFacadeTools,
1680
+ connectorFreshness: {
1681
+ status: connectorFreshness.status,
1682
+ severity: connectorFreshness.severity,
1683
+ summary: connectorFreshness.summary,
1684
+ missingLocalTools: connectorFreshness.missingLocalTools,
1685
+ missingConnectorTools: connectorFreshness.missingConnectorTools,
1686
+ expectedFacadeTools: connectorFreshness.expectedFacadeTools,
1687
+ restartRecommended: connectorFreshness.restartRecommended,
1688
+ reconnectRecommended: connectorFreshness.reconnectRecommended,
1689
+ suggestedActions: connectorFreshness.suggestedActions,
3815
1690
  },
3816
- readiness,
3817
- maxParallel,
3818
- tasks,
1691
+ docs: [
1692
+ "docs/architecture/current/README.md",
1693
+ "docs/architecture/history.md",
1694
+ "docs/operations/controller-reliability-runbook.md",
1695
+ "docs/forge-github-issue-launcher.md",
1696
+ "docs/forge-chatgpt-mcp-setup.md",
1697
+ ],
1698
+ staleConnectorHint: staleConnector
1699
+ ? `Stale ChatGPT connector tool snapshot is missing facade tools: ${missingFacadeTools.join(", ")}. Refresh/reconnect MCP so tools/list reloads rh_status, rh_inbox, rh_context, and rh_work.`
1700
+ : connectorFreshness.status === "unable_to_verify_chatgpt_snapshot"
1701
+ ? `Local MCP tool surface includes preferred facade tools. ChatGPT connector snapshot was not supplied, so freshness cannot be verified from this call. If ChatGPT tools/list is missing rh_status/rh_inbox/rh_context/rh_work, reconnect MCP. Pass connector_tool_names for an exact check.`
1702
+ : connectorFreshness.status === "stale_fingerprint"
1703
+ ? `Local MCP process fingerprint/version may be stale. Restart controller/MCP, then reconnect ChatGPT if rh_* are still missing.`
1704
+ : connectorFreshness.status === "local_mcp_missing_facade"
1705
+ ? `Local MCP tool surface is missing facade tools: ${connectorFreshness.missingLocalTools.join(", ")}. Restart controller/MCP.`
1706
+ : `Connector clients must match ${FORGE_TOOL_SURFACE} version ${FORGE_VERSION} / schema ${FORGE_MCP_SCHEMA_VERSION}. Prefer rh_status/rh_inbox/rh_context/rh_work. Pass connector_tool_names to detect a stale ChatGPT tool snapshot precisely.`,
3819
1707
  };
3820
- audit(
3821
- ctx,
3822
- name,
3823
- readiness.queueable ? "ok" : "blocked",
3824
- args,
3825
- `tasks/issues/${issue.id}`,
3826
- );
3827
- return textResult(preview);
3828
- }
3829
- case "assess_work_request": {
3830
- if (ctx.policy.profile !== "controller")
3831
- return errorResult("TOOL_DISABLED", "assess_work_request requires the controller profile");
3832
- const assessment = assessWorkMode({
3833
- description: String(args.description ?? ""),
3834
- knownPaths: stringList(args.known_paths),
3835
- expectedFiles: typeof args.expected_files === "number" ? args.expected_files : undefined,
3836
- expectedChangedLines: typeof args.expected_changed_lines === "number" ? args.expected_changed_lines : undefined,
3837
- requiresInvestigation: args.requires_investigation === true,
3838
- requiresParallelism: args.requires_parallelism === true,
3839
- requiresLongRunningChecks: args.requires_long_running_checks === true,
3840
- needsDependencies: args.needs_dependencies === true,
3841
- risk: typeof args.risk === "string" ? args.risk as TaskRisk : undefined,
3842
- explicitMode: parseExplicitTaskMode(args.mode),
3843
- });
3844
1708
  audit(ctx, name, "ok", args);
3845
- return textResult(assessment);
3846
- }
3847
- case "create_issue": {
3848
- if (ctx.policy.profile !== "controller")
3849
- return errorResult(
3850
- "TOOL_DISABLED",
3851
- "create_issue requires the controller profile",
3852
- );
3853
- const issue = createIssue(ctx.repoRoot, {
3854
- title: String(args.title ?? ""),
3855
- kind: (args.kind ? String(args.kind) : "feature") as IssueKind,
3856
- summary: typeof args.summary === "string" ? args.summary : undefined,
3857
- goals: stringList(args.goals),
3858
- nonGoals: stringList(args.non_goals),
3859
- acceptanceCriteria: stringList(args.acceptance_criteria),
3860
- relatedArtifacts: stringList(args.related_artifacts),
3861
- tasks: controllerTaskDrafts(args.tasks),
3862
- allowWhileFocused: args.allow_while_focused === true,
3863
- allowDuplicate: args.allow_duplicate === true,
3864
- allowWhenPaused: args.allow_when_paused === true,
3865
- });
3866
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
3867
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
1709
+ return textResult(payload);
3868
1710
  }
3869
- case "update_issue": {
1711
+ case "local_bridge_status": {
3870
1712
  if (ctx.policy.profile !== "controller")
3871
1713
  return errorResult(
3872
1714
  "TOOL_DISABLED",
3873
- "update_issue requires the controller profile",
1715
+ "local_bridge_status requires the controller profile",
3874
1716
  );
3875
- const issueId = String(args.issue_id ?? "");
3876
- const patch = {
3877
- title: typeof args.title === "string" ? args.title : undefined,
3878
- status:
3879
- typeof args.status === "string"
3880
- ? (args.status as IssueStatus)
3881
- : undefined,
3882
- summary: typeof args.summary === "string" ? args.summary : undefined,
3883
- goals: Array.isArray(args.goals) ? stringList(args.goals) : undefined,
3884
- nonGoals: Array.isArray(args.non_goals)
3885
- ? stringList(args.non_goals)
3886
- : undefined,
3887
- acceptanceCriteria: Array.isArray(args.acceptance_criteria)
3888
- ? stringList(args.acceptance_criteria)
3889
- : undefined,
3890
- relatedArtifacts: Array.isArray(args.related_artifacts)
3891
- ? stringList(args.related_artifacts)
3892
- : undefined,
3893
- };
3894
- const migrated = applyMigratedIssueDecision(ctx.repoRoot, {
3895
- issueId,
3896
- ...patch,
1717
+ const detailLevel = args.detail_level === "detail" || args.detail === true
1718
+ ? "detail"
1719
+ : "summary";
1720
+ const controllerHome = resolveRepoPreferredControllerHome(ctx.repoRoot);
1721
+ const surface = resolveLocalBridgeSurface({
1722
+ controllerHome,
1723
+ repoRoot: ctx.repoRoot,
1724
+ allowProcessScan: detailLevel === "detail",
3897
1725
  });
3898
- if (migrated) {
3899
- audit(ctx, name, "ok", args, `requirements/${migrated.requirement.requirementId}`);
3900
- return textResult(migrated);
1726
+ const jobs = listLocalBridgeJobs(ctx.repoRoot, detailLevel === "detail" ? 12 : 20);
1727
+ const { activeJobCount, recentJobSummary } = summarizeRecentJobs(jobs);
1728
+ // Prefer authoritative surface; never invent a legacy 8766 endpoint.
1729
+ const endpoint = surface.endpoint ?? null;
1730
+ const running = surface.enabled
1731
+ && (surface.processRunning === true
1732
+ || (surface.endpointConfigured && surface.mode === "embedded"));
1733
+ if (detailLevel === "summary") {
1734
+ const payload = {
1735
+ detailLevel: "summary",
1736
+ repoId: ctx.repoId,
1737
+ running,
1738
+ ready: surface.enabled ? (surface.processRunning !== false) : true,
1739
+ health: surface.enabled
1740
+ ? (surface.processRunning === true || surface.mode === "embedded" ? "ready" : "warning")
1741
+ : "disabled",
1742
+ mode: surface.mode,
1743
+ endpoint,
1744
+ endpointConfigured: surface.endpointConfigured,
1745
+ endpointReachable: null as boolean | null,
1746
+ processRunning: surface.processRunning ?? null,
1747
+ expectedSurface: surface.expectedSurface,
1748
+ requiredForReadiness: surface.requiredForReadiness,
1749
+ warnings: surface.error && surface.requiredForReadiness
1750
+ ? [{ code: "LOCAL_BRIDGE_ERROR", message: surface.error.slice(0, 300) }]
1751
+ : [],
1752
+ activeJobCount,
1753
+ recentJobSummary,
1754
+ statusSource: surface.source,
1755
+ nonBlocking: !surface.requiredForReadiness,
1756
+ approvalQueue: false,
1757
+ };
1758
+ audit(ctx, name, "ok", args);
1759
+ return textResult(payload);
3901
1760
  }
3902
- const issue = updateIssue(ctx.repoRoot, issueId, patch);
3903
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
3904
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
1761
+ const reconciliation = reconcileLocalBridgeJobs(ctx.repoRoot);
1762
+ const payload = {
1763
+ detailLevel: "detail",
1764
+ endpoint,
1765
+ endpointConfigured: surface.endpointConfigured,
1766
+ running,
1767
+ error: surface.error,
1768
+ mode: surface.mode,
1769
+ requiredForReadiness: surface.requiredForReadiness,
1770
+ statusSource: surface.source,
1771
+ counts: recentJobSummary,
1772
+ activeJobCount,
1773
+ recentJobSummary,
1774
+ approvalQueue: false,
1775
+ reconciliation,
1776
+ recentJobs: jobs.map((job) => ({
1777
+ jobId: job.jobId,
1778
+ action: job.action,
1779
+ status: job.status,
1780
+ checkId: job.action === "run-check" ? (job.payload as { checkId?: string }).checkId : undefined,
1781
+ runId: job.runId,
1782
+ issueId: job.issueId,
1783
+ taskId: job.taskId,
1784
+ createdAt: job.createdAt,
1785
+ updatedAt: job.updatedAt,
1786
+ finishedAt: job.finishedAt,
1787
+ revision: job.revision,
1788
+ deadlineAt: job.deadlineAt,
1789
+ error: job.error?.slice(0, 300),
1790
+ })),
1791
+ nonBlocking: !surface.requiredForReadiness,
1792
+ fallback:
1793
+ "Open the localhost Local Controller to launch work or inspect execution when a ChatGPT write action is unavailable.",
1794
+ };
1795
+ audit(ctx, name, "ok", args);
1796
+ return textResult(payload);
3905
1797
  }
3906
- case "plan_issue": {
1798
+ case "controller_context": {
3907
1799
  if (ctx.policy.profile !== "controller")
3908
1800
  return errorResult(
3909
1801
  "TOOL_DISABLED",
3910
- "plan_issue requires the controller profile",
1802
+ "controller_context requires the controller profile",
3911
1803
  );
3912
- const issue = planIssue(
3913
- ctx.repoRoot,
3914
- String(args.issue_id ?? ""),
3915
- controllerTaskDrafts(args.tasks),
3916
- );
3917
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
3918
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
1804
+ const controllerHome = resolveRepoPreferredControllerHome(ctx.repoRoot);
1805
+ const requirementBoard = buildRequirementBoard({ controllerHome, repoId: ctx.repoId });
1806
+ const executionDiagnostics = buildExecutionDiagnostics({
1807
+ controllerHome,
1808
+ repoId: ctx.repoId,
1809
+ detailLevel: "detail",
1810
+ });
1811
+ const runs = listAgentJobs(ctx.repoRoot, 8);
1812
+ const reconciliation = reconcileLocalBridgeJobs(ctx.repoRoot);
1813
+ const jobs = listLocalBridgeJobs(ctx.repoRoot, 8);
1814
+ const checks = listControllerChecks(ctx.repoRoot).map((check) => ({
1815
+ id: check.id,
1816
+ description: check.description,
1817
+ timeoutMs: check.timeoutMs,
1818
+ source: check.source,
1819
+ }));
1820
+ const payload = {
1821
+ git: gitSnapshot(ctx.repoRoot),
1822
+ requirementBoard,
1823
+ executionDiagnostics,
1824
+ legacyCompatibility: {
1825
+ currentIssue: "deprecated_frozen",
1826
+ issueTaskBoard: "deprecated_frozen",
1827
+ authority: "controller-home-sqlite",
1828
+ },
1829
+ activeRuns: runs.map((run) => ({
1830
+ runId: run.runId,
1831
+ issueId: run.issueId,
1832
+ taskId: run.taskId,
1833
+ status: run.status,
1834
+ agent: run.agent,
1835
+ provider: run.provider,
1836
+ executionMode: run.executionMode,
1837
+ progress: run.progress,
1838
+ lastHeartbeatAt: run.lastHeartbeatAt,
1839
+ error: run.error?.slice(0, 300),
1840
+ })),
1841
+ localBridge: {
1842
+ reconciliation,
1843
+ recentJobs: jobs.map((job) => ({
1844
+ jobId: job.jobId,
1845
+ action: job.action,
1846
+ status: job.status,
1847
+ checkId: job.action === "run-check" ? (job.payload as { checkId?: string }).checkId : undefined,
1848
+ issueId: job.issueId,
1849
+ taskId: job.taskId,
1850
+ updatedAt: job.updatedAt,
1851
+ })),
1852
+ },
1853
+ checks,
1854
+ };
1855
+ audit(ctx, name, "ok", args);
1856
+ return textResult(payload);
3919
1857
  }
3920
- case "append_task": {
3921
- if (ctx.policy.profile !== "controller")
3922
- return errorResult(
3923
- "TOOL_DISABLED",
3924
- "append_task requires the controller profile",
3925
- );
3926
- const drafts = controllerTaskDrafts([args.task]);
3927
- if (drafts.length !== 1)
3928
- return errorResult(
3929
- "INVALID_TASK",
3930
- "append_task requires one valid task",
3931
- );
3932
- const issue = appendTask(
3933
- ctx.repoRoot,
3934
- String(args.issue_id ?? ""),
3935
- drafts[0],
1858
+ case "quick_agent_session":
1859
+ case "submit_local_job": {
1860
+ if (ctx.policy.profile !== "controller") {
1861
+ return errorResult("TOOL_DISABLED", `${name} requires the controller profile`);
1862
+ }
1863
+ audit(ctx, name, "blocked", args);
1864
+ return errorResult(
1865
+ name === "quick_agent_session" ? "AGENT_RUN_DEPRECATED" : "LOCAL_BRIDGE_JOB_RETIRED",
1866
+ "Kernel-managed Agent and Local Bridge Jobs are retired. Create or claim WorkContract and use Process Runtime or the Thin Launcher.",
3936
1867
  );
3937
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
3938
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
3939
1868
  }
3940
- case "split_task": {
1869
+ case "list_local_jobs": {
3941
1870
  if (ctx.policy.profile !== "controller")
3942
1871
  return errorResult(
3943
1872
  "TOOL_DISABLED",
3944
- "split_task requires the controller profile",
1873
+ "list_local_jobs requires the controller profile",
3945
1874
  );
3946
- const issue = splitTask(
3947
- ctx.repoRoot,
3948
- String(args.issue_id ?? ""),
3949
- String(args.task_id ?? ""),
3950
- controllerTaskDrafts(args.tasks),
3951
- );
3952
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
3953
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
1875
+ const limit = typeof args.limit === "number" ? args.limit : 50;
1876
+ const jobs = listLocalBridgeJobs(ctx.repoRoot, limit);
1877
+ audit(ctx, name, "ok", args);
1878
+ return textResult({ jobs });
3954
1879
  }
3955
- case "supersede_task": {
1880
+ case "get_local_job": {
3956
1881
  if (ctx.policy.profile !== "controller")
3957
1882
  return errorResult(
3958
1883
  "TOOL_DISABLED",
3959
- "supersede_task requires the controller profile",
1884
+ "get_local_job requires the controller profile",
3960
1885
  );
3961
- const issue = supersedeTask(
3962
- ctx.repoRoot,
3963
- String(args.issue_id ?? ""),
3964
- String(args.task_id ?? ""),
3965
- stringList(args.replacement_task_ids),
3966
- );
3967
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
3968
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
1886
+ const jobId = String(args.job_id ?? "").trim();
1887
+ const includeEvents = args.include_events === true;
1888
+ const includeOutput = args.include_output === true;
1889
+ const snapshot = getLocalBridgeJobSnapshot(ctx.repoRoot, jobId);
1890
+ const payload = {
1891
+ job: snapshot.job,
1892
+ ...(snapshot.status !== "ok" ? { lookup: snapshot } : {}),
1893
+ ...(includeEvents && snapshot.status === "ok" ? { events: getLocalBridgeJobEvents(ctx.repoRoot, jobId) } : {}),
1894
+ ...(includeOutput
1895
+ ? {
1896
+ output: readLocalBridgeJobOutput(ctx.repoRoot, jobId, {
1897
+ stream: args.stream === "stderr" ? "stderr" : "stdout",
1898
+ maxBytes: typeof args.max_bytes === "number" ? args.max_bytes : undefined,
1899
+ }),
1900
+ }
1901
+ : {}),
1902
+ };
1903
+ audit(ctx, name, "ok", args);
1904
+ return textResult(payload);
3969
1905
  }
3970
- case "set_task_dependencies": {
1906
+ case "get_local_job_output": {
3971
1907
  if (ctx.policy.profile !== "controller")
3972
1908
  return errorResult(
3973
1909
  "TOOL_DISABLED",
3974
- "set_task_dependencies requires the controller profile",
1910
+ "get_local_job_output requires the controller profile",
3975
1911
  );
3976
- const issue = setTaskDependencies(
3977
- ctx.repoRoot,
3978
- String(args.issue_id ?? ""),
3979
- String(args.task_id ?? ""),
3980
- stringList(args.depends_on),
3981
- );
3982
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
3983
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
1912
+ const jobId = String(args.job_id ?? "").trim();
1913
+ const payload = readLocalBridgeJobOutput(ctx.repoRoot, jobId, {
1914
+ stream: args.stream === "stderr" ? "stderr" : "stdout",
1915
+ maxBytes: typeof args.max_bytes === "number" ? args.max_bytes : undefined,
1916
+ });
1917
+ audit(ctx, name, "ok", args);
1918
+ return textResult(payload);
3984
1919
  }
3985
- case "update_task": {
3986
- if (ctx.policy.profile !== "controller")
3987
- return errorResult(
3988
- "TOOL_DISABLED",
3989
- "update_task requires the controller profile",
3990
- );
3991
- const startedAt = performance.now();
3992
- const taskId = String(args.task_id ?? "");
3993
- const issue = updateTask(
3994
- ctx.repoRoot,
3995
- String(args.issue_id ?? ""),
3996
- taskId,
3997
- {
3998
- status:
3999
- typeof args.status === "string"
4000
- ? (args.status as TaskStatus)
4001
- : undefined,
4002
- note: typeof args.note === "string" ? args.note : undefined,
4003
- },
4004
- );
4005
- const issueView = projectIssueEffectiveView(ctx.repoRoot, issue);
4006
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
4007
- if (args.detail_level === "detail" || args.detail_level === "full") return textResult(issueView);
1920
+ case "project_snapshot": {
1921
+ if (ctx.policy.profile !== "controller") return errorResult("TOOL_DISABLED", "project_snapshot requires the controller profile");
1922
+ const markerPaths = [
1923
+ ".ai/harness/active-plan",
1924
+ ".ai/harness/active-sprint",
1925
+ ".ai/harness/active-worktree",
1926
+ "tasks/current.md",
1927
+ ];
1928
+ const markers = markerPaths.map((path) => {
1929
+ const absolute = join(ctx.repoRoot, path);
1930
+ if (!existsSync(absolute)) return { path, exists: false };
1931
+ const raw = readFileSync(absolute, "utf-8");
1932
+ return {
1933
+ path,
1934
+ exists: true,
1935
+ bytes: Buffer.byteLength(raw),
1936
+ lines: raw.split(/\r?\n/).length,
1937
+ sha256: createHash("sha256").update(raw).digest("hex").slice(0, 16),
1938
+ preview: redactMcpText(raw).text.slice(0, 400),
1939
+ };
1940
+ });
1941
+ const controllerHome = resolveRepoPreferredControllerHome(ctx.repoRoot);
1942
+ const requirementBoard = buildRequirementBoard({ controllerHome, repoId: ctx.repoId });
1943
+ const executionDiagnostics = buildExecutionDiagnostics({
1944
+ controllerHome,
1945
+ repoId: ctx.repoId,
1946
+ detailLevel: "detail",
1947
+ });
1948
+ const snapshotRuns = listAgentJobs(ctx.repoRoot, 11);
1949
+ const runs = snapshotRuns.slice(0, 10).map((run) => ({
1950
+ runId: run.runId,
1951
+ issueId: run.issueId,
1952
+ taskId: run.taskId,
1953
+ agent: run.agent,
1954
+ provider: run.provider,
1955
+ executionMode: run.executionMode,
1956
+ status: run.status,
1957
+ progress: run.progress,
1958
+ createdAt: run.createdAt,
1959
+ finishedAt: run.finishedAt,
1960
+ error: run.error?.slice(0, 300),
1961
+ }));
4008
1962
  const payload = {
4009
- ...summarizeUpdatedIssue(issueView as unknown as Record<string, unknown>, taskId),
4010
- responseMeta: {
4011
- serverDurationMs: Number((performance.now() - startedAt).toFixed(2)),
4012
- structuredPayloadBytes: 0,
1963
+ git: gitSnapshot(ctx.repoRoot),
1964
+ requirementBoard,
1965
+ executionDiagnostics,
1966
+ legacyCompatibility: {
1967
+ currentIssue: "deprecated_frozen",
1968
+ issueTaskBoard: "deprecated_frozen",
1969
+ authority: "controller-home-sqlite",
1970
+ },
1971
+ runs,
1972
+ markers,
1973
+ truncated: {
1974
+ runs: snapshotRuns.length > 10,
4013
1975
  },
4014
1976
  };
4015
- payload.responseMeta.structuredPayloadBytes = Buffer.byteLength(JSON.stringify(payload), "utf8");
1977
+ audit(ctx, name, "ok", args);
4016
1978
  return textResult(payload);
4017
1979
  }
4018
- case "dispatch_task": {
4019
- if (ctx.policy.profile !== "controller") return errorResult("TOOL_DISABLED", "dispatch_task requires the controller profile");
4020
- audit(ctx, name, "blocked", args);
4021
- return errorResult(
4022
- "AGENT_RUN_DEPRECATED",
4023
- "Kernel-managed Agent Runs are retired. Create or claim a WorkContract through rh_work, then start an external SuperController session through the Thin Launcher.",
4024
- );
4025
- }
4026
- case "launch_issue": {
4027
- if (ctx.policy.profile !== "controller") return errorResult("TOOL_DISABLED", "launch_issue requires the controller profile");
4028
- audit(ctx, name, "blocked", args);
4029
- return errorResult(
4030
- "AGENT_RUN_DEPRECATED",
4031
- "Kernel-managed Agent Runs are retired. Convert the Issue tasks to WorkContracts and claim them through rh_work.",
4032
- );
4033
- }
4034
- case "dispatch_ready_tasks": {
4035
- if (ctx.policy.profile !== "controller") return errorResult("TOOL_DISABLED", "dispatch_ready_tasks requires the controller profile");
4036
- audit(ctx, name, "blocked", args);
4037
- return errorResult(
4038
- "AGENT_RUN_DEPRECATED",
4039
- "Kernel-managed batch dispatch is retired. Convert ready Tasks into WorkContracts, claim them explicitly, and launch peer external Controllers through the Thin Launcher.",
4040
- {
4041
- requestedIssue: typeof args.issue_id === "string" && args.issue_id.trim() ? args.issue_id.trim() : undefined,
4042
- currentFocus: loadControllerProjectState(ctx.repoRoot).currentIssueId,
4043
- focusIsInformational: true,
4044
- },
4045
- );
4046
- }
4047
- case "get_task_run": {
4048
- if (ctx.policy.profile !== "controller")
4049
- return errorResult(
4050
- "TOOL_DISABLED",
4051
- "get_task_run requires the controller profile",
4052
- );
4053
- const run = getAgentJob(ctx.repoRoot, String(args.run_id ?? ""));
4054
- audit(ctx, name, "ok", args, run.promptPath);
4055
- return textResult(summarizeAgentRun(ctx.repoRoot, run as unknown as Record<string, unknown>, {
4056
- includePaths: args.include_paths === true,
4057
- includeOutput: args.include_output !== false,
4058
- tailChars: typeof args.tail_chars === "number" ? Math.trunc(args.tail_chars) : undefined,
4059
- }));
4060
- }
4061
- case "get_task_run_events": {
1980
+ case "search_repository": {
4062
1981
  if (ctx.policy.profile !== "controller")
4063
1982
  return errorResult(
4064
1983
  "TOOL_DISABLED",
4065
- "get_task_run_events requires the controller profile",
1984
+ "search_repository requires the controller profile",
4066
1985
  );
4067
- const sinceEventIndex = typeof args.since_event_index === "number"
4068
- ? Math.trunc(args.since_event_index)
1986
+ const session = ctx.sessionId && ctx.repoId
1987
+ ? {
1988
+ sessionId: ctx.sessionId,
1989
+ repoId: ctx.repoId,
1990
+ checkoutId: ctx.checkoutId ?? ctx.repoId,
1991
+ }
4069
1992
  : undefined;
4070
- const events = getAgentJobEvents(
4071
- ctx.repoRoot,
4072
- String(args.run_id ?? ""),
4073
- typeof args.limit === "number" ? Math.trunc(args.limit) : 200,
4074
- {
4075
- sinceIndex: sinceEventIndex,
4076
- includeHeartbeats: args.include_heartbeats === true,
4077
- },
4078
- );
4079
- audit(ctx, name, "ok", args);
4080
- return textResult({
4081
- events,
4082
- // Keep an existing cursor stable when no new event is available. New events
4083
- // that race with a read are returned on the next call instead of being lost.
4084
- nextSinceEventIndex: events.at(-1)?.data?.eventIndex ?? sinceEventIndex,
4085
- heartbeatsCollapsed: args.include_heartbeats !== true,
1993
+ const result = searchRepository(ctx.repoRoot, ctx.policy, {
1994
+ query: String(args.query ?? ""),
1995
+ includeGlobs: stringList(args.include_globs),
1996
+ excludeGlobs: stringList(args.exclude_globs),
1997
+ maxResults:
1998
+ typeof args.max_results === "number" ? args.max_results : undefined,
1999
+ maxFiles:
2000
+ typeof args.max_files === "number" ? args.max_files : undefined,
2001
+ caseSensitive: args.case_sensitive === true,
2002
+ session,
4086
2003
  });
2004
+ audit(ctx, name, "ok", args);
2005
+ return textResult(result);
4087
2006
  }
4088
- case "get_task_run_log": {
2007
+ case "get_git_diff": {
4089
2008
  if (ctx.policy.profile !== "controller")
4090
2009
  return errorResult(
4091
2010
  "TOOL_DISABLED",
4092
- "get_task_run_log requires the controller profile",
2011
+ "get_git_diff requires the controller profile",
4093
2012
  );
4094
- const log = getAgentJobLog(
4095
- ctx.repoRoot,
4096
- String(args.run_id ?? ""),
4097
- false,
4098
- typeof args.max_bytes === "number" ? Math.trunc(args.max_bytes) : 32 * 1024,
4099
- );
4100
- audit(ctx, name, "ok", args, log.url);
4101
- const run = getAgentJob(ctx.repoRoot, String(args.run_id ?? ""));
4102
- const bounded = redactRunText(
4103
- log.log,
4104
- ctx.repoRoot,
4105
- {
4106
- executionRoot: run.executionRoot,
4107
- worktree: run.worktree,
4108
- worktreePath: run.worktreePath,
4109
- },
4110
- Math.max(
4111
- 4 * 1024,
4112
- Math.min(
4113
- typeof args.max_bytes === "number" ? Math.trunc(args.max_bytes) : 32 * 1024,
4114
- 256 * 1024,
4115
- ),
4116
- ),
4117
- );
2013
+ const path =
2014
+ typeof args.path === "string" && args.path.trim()
2015
+ ? args.path.trim()
2016
+ : undefined;
2017
+ if (path) {
2018
+ const decision = resolveMcpPath(
2019
+ ctx.repoRoot,
2020
+ path,
2021
+ ctx.policy,
2022
+ "read",
2023
+ );
2024
+ if (!decision.ok)
2025
+ return errorResult(
2026
+ "POLICY_DENIED",
2027
+ decision.reason ?? "path denied",
2028
+ { path },
2029
+ );
2030
+ }
2031
+ const maxBytes =
2032
+ typeof args.max_bytes === "number"
2033
+ ? Math.min(Math.max(Math.trunc(args.max_bytes), 1024), 512 * 1024)
2034
+ : 128 * 1024;
2035
+ const result = gitDiff(ctx.repoRoot, path, maxBytes);
2036
+ const redacted = redactMcpText(result.diff);
2037
+ audit(ctx, name, "ok", args, path);
4118
2038
  return textResult({
4119
- ...log,
4120
- log: bounded.text,
4121
- truncated: bounded.truncated,
4122
- redactions: bounded.redactions,
4123
- next: bounded.truncated
4124
- ? "Call get_task_run_log with a larger max_bytes value for more retained tail."
4125
- : undefined,
2039
+ ...result,
2040
+ diff: redacted.text,
2041
+ redactions: redacted.redactions,
4126
2042
  });
4127
2043
  }
4128
- case "get_task_diff": {
2044
+ case "list_checks": {
4129
2045
  if (ctx.policy.profile !== "controller")
4130
2046
  return errorResult(
4131
2047
  "TOOL_DISABLED",
4132
- "get_task_diff requires the controller profile",
2048
+ "list_checks requires the controller profile",
4133
2049
  );
4134
- const diff = taskRunDiff(
4135
- ctx.repoRoot,
4136
- String(args.run_id ?? ""),
4137
- typeof args.max_bytes === "number"
4138
- ? Math.min(Math.max(Math.trunc(args.max_bytes), 1024), 1024 * 1024)
4139
- : undefined,
4140
- );
4141
- audit(ctx, name, "ok", args, diff.worktree);
4142
- return textResult(diff);
2050
+ const checks = listControllerChecks(ctx.repoRoot);
2051
+ audit(ctx, name, "ok", args);
2052
+ return textResult({ checks });
4143
2053
  }
4144
- case "integrate_task_run": {
2054
+ case "get_worklog_timeline": {
4145
2055
  if (ctx.policy.profile !== "controller")
4146
- return errorResult(
4147
- "TOOL_DISABLED",
4148
- "integrate_task_run requires the controller profile",
4149
- );
4150
- const integrated = integrateAgentJob(
4151
- ctx.repoRoot,
4152
- ctx.policy,
4153
- String(args.run_id ?? ""),
4154
- );
4155
- audit(
4156
- ctx,
4157
- name,
4158
- "ok",
4159
- args,
4160
- `controller-home://repositories/${editExecutionIdentity(ctx).repoId}/edit-sessions/${integrated.session.sessionId}`,
4161
- );
4162
- return textResult(integrated);
2056
+ return errorResult("TOOL_DISABLED", "get_worklog_timeline requires the controller profile");
2057
+ const events = listControllerWorklogEvents(ctx.repoRoot, {
2058
+ category: parseWorklogCategory(args.category),
2059
+ issueId: typeof args.issue_id === "string" ? args.issue_id : undefined,
2060
+ taskId: typeof args.task_id === "string" ? args.task_id : undefined,
2061
+ runId: typeof args.run_id === "string" ? args.run_id : undefined,
2062
+ editSessionId: typeof args.edit_session_id === "string" ? args.edit_session_id : undefined,
2063
+ since: typeof args.since === "string" ? args.since : undefined,
2064
+ until: typeof args.until === "string" ? args.until : undefined,
2065
+ limit: typeof args.limit === "number" ? Math.trunc(args.limit) : undefined,
2066
+ });
2067
+ audit(ctx, name, "ok", args);
2068
+ return textResult({ events });
4163
2069
  }
4164
- case "list_task_runs": {
2070
+ case "export_worklog": {
4165
2071
  if (ctx.policy.profile !== "controller")
4166
- return errorResult(
4167
- "TOOL_DISABLED",
4168
- "list_task_runs requires the controller profile",
4169
- );
4170
- const runs = listAgentJobs(
4171
- ctx.repoRoot,
4172
- typeof args.limit === "number" ? args.limit : 50,
4173
- );
4174
- audit(ctx, name, "ok", args);
4175
- return textResult({
4176
- runs: summarizeRunList(
4177
- ctx.repoRoot,
4178
- runs as unknown as Array<Record<string, unknown>>,
4179
- args.include_paths === true,
4180
- ),
2072
+ return errorResult("TOOL_DISABLED", "export_worklog requires the controller profile");
2073
+ const result = exportControllerWorklog(ctx.repoRoot, {
2074
+ format: args.format === "json" ? "json" : "markdown",
2075
+ outputPath: typeof args.output_path === "string" ? args.output_path : undefined,
2076
+ filter: {
2077
+ issueId: typeof args.issue_id === "string" ? args.issue_id : undefined,
2078
+ taskId: typeof args.task_id === "string" ? args.task_id : undefined,
2079
+ runId: typeof args.run_id === "string" ? args.run_id : undefined,
2080
+ editSessionId: typeof args.edit_session_id === "string" ? args.edit_session_id : undefined,
2081
+ },
4181
2082
  });
2083
+ audit(ctx, name, "ok", args, result.path);
2084
+ return textResult(result);
4182
2085
  }
4183
- case "cancel_task_run": {
2086
+ case "get_github_plugin_status": {
4184
2087
  if (ctx.policy.profile !== "controller")
4185
- return errorResult(
4186
- "TOOL_DISABLED",
4187
- "cancel_task_run requires the controller profile",
4188
- );
4189
- const run = cancelAgentJob(ctx.repoRoot, String(args.run_id ?? ""));
4190
- audit(ctx, name, "ok", args, run.promptPath);
4191
- return textResult(run);
2088
+ return errorResult("TOOL_DISABLED", "get_github_plugin_status requires the controller profile");
2089
+ const status = getGitHubPluginStatus(ctx.repoRoot);
2090
+ audit(ctx, name, "ok", args, status.repository);
2091
+ return textResult(status);
4192
2092
  }
4193
- case "retry_task_run": {
2093
+ case "configure_github_plugin": {
4194
2094
  if (ctx.policy.profile !== "controller")
4195
- return errorResult(
4196
- "TOOL_DISABLED",
4197
- "retry_task_run requires the controller profile",
4198
- );
4199
- // Fail closed before Task/provider side effects. Historical Runs remain readable.
4200
- audit(ctx, name, "failed", args, undefined, "AGENT_RUN_RETIRED");
4201
- return errorResult(
4202
- "AGENT_RUN_RETIRED",
4203
- "Kernel-managed Agent Run retry is disabled. Create or claim a WorkContract and start an external SuperController through Thin Launcher.",
4204
- );
4205
- }
4206
- case "finish_task_run": {
4207
- if (ctx.policy.profile !== "controller") return errorResult("TOOL_DISABLED", "finish_task_run requires the controller profile");
4208
- const runId = String(args.run_id ?? "").trim();
4209
- if (!runId) return errorResult("RUN_ID_REQUIRED", "finish_task_run requires run_id");
4210
- const decision = typeof args.decision === "string" ? args.decision : "auto";
4211
- if (!["auto", "approve_and_finish", "request_changes", "discard"].includes(decision)) {
4212
- return errorResult("INVALID_DECISION", "decision must be auto, approve_and_finish, request_changes, or discard");
4213
- }
4214
- const finished = finishTaskRun(ctx.repoRoot, {
4215
- runId,
4216
- decision: decision as "auto" | "approve_and_finish" | "request_changes" | "discard",
4217
- reviewer: typeof args.reviewer === "string" ? args.reviewer : undefined,
4218
- note: typeof args.note === "string" ? args.note : undefined,
4219
- cleanup: typeof args.cleanup === "boolean" ? args.cleanup : undefined,
4220
- commit: typeof args.commit === "boolean" ? args.commit : undefined,
4221
- });
4222
- audit(ctx, name, ["blocked", "needs_decision"].includes(finished.action) ? "failed" : "ok", args, `tasks/issues/${finished.issueId}`, finished.reason);
4223
- return textResult(finished);
4224
- }
4225
- case "verify_task": {
4226
- if (ctx.policy.profile !== "controller") return errorResult("TOOL_DISABLED", "verify_task requires the controller profile");
4227
- const issueId = String(args.issue_id ?? "");
4228
- const taskId = String(args.task_id ?? "");
4229
- const current = getIssue(ctx.repoRoot, issueId);
4230
- const task = current.tasks.find((entry) => entry.id === taskId);
4231
- if (!task) return errorResult("TASK_NOT_FOUND", `task not found: ${issueId}/${taskId}`);
4232
- const policy = taskExecutionPolicy(task);
4233
- const evidenceRunId = typeof args.run_id === "string" ? args.run_id : task.runIds.at(-1);
4234
- let evidenceRun: ReturnType<typeof getAgentJob> | undefined;
4235
- if (evidenceRunId) {
4236
- evidenceRun = getAgentJob(ctx.repoRoot, evidenceRunId);
4237
- if (evidenceRun.issueId !== issueId || evidenceRun.taskId !== taskId) return errorResult("RUN_TASK_MISMATCH", `Run ${evidenceRunId} does not belong to ${issueId}/${taskId}`);
4238
- if (evidenceRun.status !== "succeeded") return errorResult("RUN_NOT_SUCCEEDED", `verification requires a succeeded Run (current: ${evidenceRun.status})`);
4239
- if (evidenceRun.provider === "local" && evidenceRun.worktree !== ctx.repoRoot && !evidenceRun.integratedSessionId) return errorResult("RUN_NOT_INTEGRATED", "integrate the isolated local Run before recording verification");
4240
- if (evidenceRun.provider === "github" && evidenceRun.github?.createPullRequest !== false && !evidenceRun.github?.pullRequestUrl) return errorResult("GITHUB_PR_NOT_READY", "GitHub cloud verification requires the linked pull request to be available");
4241
- }
4242
-
4243
- const requestedCheckInputs = taskObjects(args.check_results).map((entry) => ({
4244
- checkId: String(entry.check_id ?? "").trim(),
4245
- requestedSummary: typeof entry.summary === "string" ? entry.summary : undefined,
4246
- })).filter((entry) => entry.checkId);
4247
- const registryChecks = listControllerChecks(ctx.repoRoot);
4248
- const requestedCheckIds = task.checks.length > 0 ? task.checks : requestedCheckInputs.map((entry) => entry.checkId);
4249
- const normalizedChecks = normalizeCheckIds(requestedCheckIds, registryChecks);
4250
- const checkIds = normalizedChecks.validCheckIds;
4251
- const checkResults: TaskVerification['checkResults'] = [];
4252
- if (checkIds.length > 0) {
4253
- const controllerHome = resolveRepoPreferredControllerHome(ctx.repoRoot);
4254
- const repository = resolveRepositorySelection({
4255
- explicitPath: ctx.repoRoot,
4256
- controllerHome,
4257
- allowSoleRepository: true,
4258
- });
4259
- if (!repository) return errorResult('TASK_CHECK_REPOSITORY_UNRESOLVED', 'verify_task could not resolve the repository checkout');
4260
- const taskCheckRequestBase = typeof args.request_id === 'string' && args.request_id.trim()
4261
- ? args.request_id.trim()
4262
- : `verify-task:${issueId}:${taskId}:${createHash('sha256').update(JSON.stringify({ checkIds, revision: args.integrated_revision ?? evidenceRun?.integratedSessionId ?? 'current' })).digest('hex').slice(0, 16)}`;
4263
- for (const [index, checkId] of checkIds.entries()) {
4264
- const processRequestId = `${taskCheckRequestBase}:check:${index + 1}:${createHash('sha256').update(checkId).digest('hex').slice(0, 12)}`;
4265
- const facade = await runPersistedCheckViaProcessRuntime({
4266
- controllerHome,
4267
- repoId: repository.repoId,
4268
- checkoutId: repository.activeCheckoutId,
4269
- repoRoot: repository.canonicalRoot,
4270
- executionIdentity: executionIdentityForRepository(repository),
4271
- checkId,
4272
- requestId: processRequestId,
4273
- commandId: processRequestId,
4274
- verificationBinding: { issueId, taskId },
4275
- });
4276
- if (facade.mode === 'durable' || !facade.process) {
4277
- return errorResult('TASK_CHECK_DURABLE_REQUIRED', facade.durable?.reason ?? `check requires durable workflow: ${checkId}`);
4278
- }
4279
- if (!facade.process.completed) {
4280
- return textResult({
4281
- accepted: true,
4282
- status: 'verification_running',
4283
- issueId,
4284
- taskId,
4285
- checkId,
4286
- requestId: taskCheckRequestBase,
4287
- processId: facade.process.processId,
4288
- next: `Retry verify_task with request_id=${taskCheckRequestBase}; the original Process will be reused.`,
4289
- durableSideEffects: { executionJobCount: 0, localJobCount: 0, workerSpawnCount: 0, projectionUpdateCount: 0 },
4290
- });
4291
- }
4292
- const record = getProcessRecord(controllerHome, repository.repoId, facade.process.processId);
4293
- if (!record) return errorResult('TASK_CHECK_PROCESS_MISSING', `process record not found: ${facade.process.processId}`);
4294
- const leaseConflict = taskVerificationLeaseConflict(record);
4295
- if (leaseConflict) {
4296
- const retryRequestId = taskVerificationRetryRequestId(
4297
- taskCheckRequestBase,
4298
- checkId,
4299
- facade.process.processId,
4300
- );
4301
- return textResult({
4302
- accepted: true,
4303
- status: 'verification_deferred',
4304
- reason: 'repository_resource_busy',
4305
- issueId,
4306
- taskId,
4307
- checkId,
4308
- requestId: taskCheckRequestBase,
4309
- processId: facade.process.processId,
4310
- conflict: {
4311
- code: 'PROCESS_LEASE_CONFLICT',
4312
- resourceKey: leaseConflict.resourceKey,
4313
- blockingProcessId: leaseConflict.blockingProcessId,
4314
- },
4315
- retryRequestId,
4316
- next: `Retry verify_task with request_id=${retryRequestId} after the conflicting repository process settles.`,
4317
- durableSideEffects: { executionJobCount: 0, localJobCount: 0, workerSpawnCount: 0, projectionUpdateCount: 0 },
4318
- });
4319
- }
4320
- const receipt = processCheckCompletionReceipt(record, {
4321
- repoId: repository.repoId,
4322
- checkoutId: repository.activeCheckoutId,
4323
- issueId,
4324
- taskId,
4325
- checkId,
4326
- processId: facade.process.processId,
4327
- });
4328
- checkResults.push({
4329
- checkId,
4330
- ok: receipt.ok,
4331
- summary: receipt.summary,
4332
- receipt,
4333
- });
4334
- }
4335
- }
4336
-
4337
- const commandEvidence: TaskCommandEvidence[] = [];
4338
- for (const entry of taskObjects(args.reported_commands)) {
4339
- const command = Array.isArray(entry.command) ? entry.command.map(String).map((part) => part.trim()).filter(Boolean) : [];
4340
- if (command.length === 0) return errorResult("INVALID_COMMAND_EVIDENCE", "reported command evidence requires a non-empty command argv");
4341
- const cwdInput = typeof entry.cwd === "string" && entry.cwd.trim() ? entry.cwd.trim() : undefined;
4342
- if (cwdInput && cwdInput !== "." && cwdInput !== "./") {
4343
- const cwdDecision = resolveMcpPath(ctx.repoRoot, cwdInput, ctx.policy, "read");
4344
- if (!cwdDecision.ok) {
4345
- const normalizedCwd = normalizeMcpRelativePath(cwdInput);
4346
- const missingHarnessEvidence = normalizedCwd.ok &&
4347
- normalizedCwd.relativePath?.startsWith(".ai/harness/") &&
4348
- cwdDecision.reason?.startsWith("path does not exist:");
4349
- if (!missingHarnessEvidence) return errorResult("COMMAND_EVIDENCE_PATH_DENIED", cwdDecision.reason ?? "reported command cwd is denied");
4350
- }
4351
- }
4352
- const artifactInput = typeof entry.artifact_path === "string" && entry.artifact_path.trim() ? entry.artifact_path.trim() : undefined;
4353
- if (artifactInput) {
4354
- const artifactDecision = resolveMcpPath(ctx.repoRoot, artifactInput, ctx.policy, "read");
4355
- if (!artifactDecision.ok) {
4356
- const normalizedArtifact = normalizeMcpRelativePath(artifactInput);
4357
- const missingHarnessEvidence = normalizedArtifact.ok &&
4358
- normalizedArtifact.relativePath?.startsWith(".ai/harness/") &&
4359
- artifactDecision.reason?.startsWith("path does not exist:");
4360
- if (!missingHarnessEvidence) return errorResult("COMMAND_EVIDENCE_ARTIFACT_DENIED", artifactDecision.reason ?? "reported command artifact is denied");
4361
- }
4362
- }
4363
- commandEvidence.push({
4364
- command,
4365
- cwd: cwdInput,
4366
- ok: entry.ok === true,
4367
- exitCode: typeof entry.exit_code === "number" ? Math.trunc(entry.exit_code) : undefined,
4368
- stdout: typeof entry.stdout === "string" ? entry.stdout.slice(-32 * 1024) : undefined,
4369
- stderr: typeof entry.stderr === "string" ? entry.stderr.slice(-32 * 1024) : undefined,
4370
- artifactPath: artifactInput,
4371
- reportedBy: String(args.reviewer ?? "").trim(),
4372
- executedAt: typeof entry.executed_at === "string" ? entry.executed_at : new Date().toISOString(),
4373
- source: "reported",
4374
- });
4375
- }
4376
-
4377
- let acceptanceResults: TaskVerification["acceptanceResults"] = taskObjects(args.acceptance_results).map((entry) => ({
4378
- criterion: String(entry.criterion ?? ""),
4379
- ok: entry.ok === true,
4380
- outcome: entry.ok === true ? "passed" as const : "failed" as const,
4381
- source: "reported" as const,
4382
- evidence: typeof entry.evidence === "string" ? entry.evidence : undefined,
4383
- }));
4384
- if (acceptanceResults.length === 0 && evidenceRun && task.acceptanceCriteria.length > 0) {
4385
- acceptanceResults = task.acceptanceCriteria.map((criterion) => ({
4386
- criterion,
4387
- ok: false,
4388
- outcome: "not_evaluated" as const,
4389
- source: "run_completion" as const,
4390
- evidence: `Successful Run ${evidenceRun!.runId}; acceptance was not independently evaluated.`,
4391
- }));
4392
- }
4393
- const verification: TaskVerification = {
4394
- runId: evidenceRunId,
4395
- integratedRevision: typeof args.integrated_revision === "string" ? args.integrated_revision : evidenceRun?.integratedSessionId,
4396
- reviewedDiffHash: typeof args.reviewed_diff_hash === "string" ? args.reviewed_diff_hash : undefined,
4397
- reviewer: String(args.reviewer ?? ""),
4398
- checkResults,
4399
- commandEvidence,
4400
- acceptanceResults,
4401
- verifiedAt: new Date().toISOString(),
4402
- };
4403
- const issue = recordTaskVerification(ctx.repoRoot, issueId, taskId, verification);
4404
- const issueView = projectIssueEffectiveView(ctx.repoRoot, issue);
4405
- const verifiedTask = issueView.tasks.find((entry) => entry.id === taskId);
4406
- audit(ctx, name, ["verified", "done"].includes(verifiedTask?.status ?? "") ? "ok" : "failed", args, `tasks/issues/${issue.id}`);
4407
- return textResult({
4408
- issueId,
4409
- taskId,
4410
- policy,
4411
- checkNormalization: {
4412
- validCheckIds: normalizedChecks.validCheckIds,
4413
- invalidCheckIds: normalizedChecks.invalidCheckIds,
4414
- warnings: normalizedChecks.warnings,
4415
- },
4416
- task: verifiedTask ? compactTaskView(verifiedTask as unknown as Record<string, unknown>) : undefined,
4417
- tasks: issueView.tasks.map((entry) => compactTaskView(entry as unknown as Record<string, unknown>)),
4418
- issue: compactIssueView(issueView as unknown as Record<string, unknown>),
2095
+ return errorResult("TOOL_DISABLED", "configure_github_plugin requires the controller profile");
2096
+ const config = saveGitHubPluginConfig(ctx.repoRoot, {
2097
+ enabled: typeof args.enabled === "boolean" ? args.enabled : undefined,
2098
+ repository: args.clear_repository === true ? "" : typeof args.repository === "string" ? args.repository : undefined,
2099
+ syncMode: args.sync_mode === "checkpoint" ? "checkpoint" : args.sync_mode === "manual" ? "manual" : undefined,
2100
+ includeTasks: typeof args.include_tasks === "boolean" ? args.include_tasks : undefined,
2101
+ projectOwner: args.clear_project === true ? "" : typeof args.project_owner === "string" ? args.project_owner : undefined,
2102
+ projectNumber: args.clear_project === true ? null : typeof args.project_number === "number" ? args.project_number : undefined,
4419
2103
  });
2104
+ audit(ctx, name, "ok", args, "repository-registry:github");
2105
+ return textResult(config);
4420
2106
  }
4421
- case "accept_task": {
4422
- if (ctx.policy.profile !== "controller") return errorResult("TOOL_DISABLED", "accept_task requires the controller profile");
4423
- const issueId = String(args.issue_id ?? "");
4424
- const taskId = String(args.task_id ?? "");
4425
- const current = getIssue(ctx.repoRoot, issueId);
4426
- const task = current.tasks.find((entry) => entry.id === taskId);
4427
- if (!task) return errorResult("TASK_NOT_FOUND", `task not found: ${issueId}/${taskId}`);
4428
- if (task.status === "done") return textResult(projectIssueEffectiveView(ctx.repoRoot, current));
4429
- if (task.status !== "verified" || !task.verification) return errorResult("TASK_NOT_VERIFIED", `task has not passed its risk-adaptive verification requirements (current: ${task.status})`);
4430
- const workId = typeof args.work_id === "string" ? args.work_id.trim() : "";
4431
- const issue = workId
4432
- ? acceptVerifiedTaskFromControllerWork({
4433
- controllerHome: resolveRepoPreferredControllerHome(ctx.repoRoot),
4434
- repoId: ctx.repoId ?? String(args.repo_id ?? "").trim(),
4435
- repoRoot: ctx.repoRoot,
4436
- issueId,
4437
- taskId,
4438
- workId,
4439
- note: typeof args.note === "string" ? args.note : undefined,
4440
- }).issue
4441
- : acceptVerifiedTask(ctx.repoRoot, issueId, taskId, typeof args.note === "string" ? args.note : "Accepted after required verification evidence.");
4442
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
4443
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
4444
- }
4445
- case "request_task_changes": {
2107
+ case "github_status": {
4446
2108
  if (ctx.policy.profile !== "controller")
4447
2109
  return errorResult(
4448
2110
  "TOOL_DISABLED",
4449
- "request_task_changes requires the controller profile",
4450
- );
4451
- const issueId = String(args.issue_id ?? "");
4452
- const taskId = String(args.task_id ?? "");
4453
- const current = getIssue(ctx.repoRoot, issueId);
4454
- const task = current.tasks.find((entry) => entry.id === taskId);
4455
- if (!task)
4456
- return errorResult(
4457
- "TASK_NOT_FOUND",
4458
- `task not found: ${issueId}/${taskId}`,
4459
- );
4460
- if (
4461
- !["review", "integrated", "verifying", "verified"].includes(
4462
- task.status,
4463
- )
4464
- )
4465
- return errorResult(
4466
- "TASK_NOT_REVIEWABLE",
4467
- `task must be in review, integrated, verifying, or verified before requesting changes (current: ${task.status})`,
2111
+ "github_status requires the controller profile",
4468
2112
  );
4469
- const issue = updateTask(ctx.repoRoot, issueId, taskId, {
4470
- status: "changes_requested",
4471
- note: String(args.note ?? ""),
4472
- });
4473
- audit(ctx, name, "ok", args, `tasks/issues/${issue.id}`);
4474
- return textResult(projectIssueEffectiveView(ctx.repoRoot, issue));
2113
+ const status = getGitHubStatus(
2114
+ ctx.repoRoot,
2115
+ typeof args.repo === "string" ? args.repo : undefined,
2116
+ );
2117
+ audit(
2118
+ ctx,
2119
+ name,
2120
+ status.available && status.authenticated ? "ok" : "failed",
2121
+ args,
2122
+ status.repository?.nameWithOwner,
2123
+ status.errors.join("; "),
2124
+ );
2125
+ return textResult(status);
4475
2126
  }
4476
2127
  case "begin_edit_session": {
4477
2128
  if (ctx.policy.profile !== "controller")
@@ -4487,19 +2138,6 @@ export async function callMcpTool(
4487
2138
  const checks = stringList(args.checks);
4488
2139
  const git = gitSnapshot(ctx.repoRoot);
4489
2140
  const workspaceIdentity = createHash("sha256").update(JSON.stringify(git)).digest("hex");
4490
- const routeDecision = decideRoute({
4491
- intent: {
4492
- objective: purpose,
4493
- scopeClear: allowedPaths.length > 0,
4494
- mutation: true,
4495
- expectedFiles: typeof args.max_files === "number" ? args.max_files : allowedPaths.length,
4496
- expectedChangedLines: typeof args.max_changed_lines === "number" ? args.max_changed_lines : undefined,
4497
- },
4498
- workspace: { knownPaths: allowedPaths, checkoutId: identity.checkoutId, fingerprint: workspaceIdentity },
4499
- policy: { risk: "local_repo_write", approvalConfirmed: true },
4500
- capabilities: {},
4501
- recovery: {},
4502
- });
4503
2141
  const requestedWorkId = typeof args.work_id === "string" ? args.work_id.trim() : "";
4504
2142
  const workId = requestedWorkId || `work-direct-${createHash("sha256").update(`${repoId}\0${purpose}\0${Date.now()}`).digest("hex").slice(0, 16)}`;
4505
2143
  const workStore = { controllerHome, repoId };
@@ -4514,7 +2152,6 @@ export async function callMcpTool(
4514
2152
  controllerInstanceId: identity.controllerInstanceId,
4515
2153
  baseRevision: git.head ?? undefined,
4516
2154
  workspaceFingerprint: workspaceIdentity,
4517
- routeDecision,
4518
2155
  objective: purpose,
4519
2156
  issueId: typeof args.issue_id === "string" ? args.issue_id : undefined,
4520
2157
  taskId: typeof args.task_id === "string" ? args.task_id : undefined,
@@ -4546,7 +2183,6 @@ export async function callMcpTool(
4546
2183
  checkoutId: identity.checkoutId,
4547
2184
  principalId: identity.principalId,
4548
2185
  controllerInstanceId: identity.controllerInstanceId,
4549
- routeDecisionFingerprint: routeDecision.inputFingerprint,
4550
2186
  },
4551
2187
  });
4552
2188
  audit(
@@ -4585,7 +2221,6 @@ export async function callMcpTool(
4585
2221
  checkoutId: identity.checkoutId,
4586
2222
  principalId: identity.principalId,
4587
2223
  controllerInstanceId: identity.controllerInstanceId,
4588
- routeDecisionFingerprint: current.routeDecisionFingerprint,
4589
2224
  },
4590
2225
  },
4591
2226
  );
@@ -4705,7 +2340,6 @@ export async function callMcpTool(
4705
2340
  checkoutId: identity.checkoutId,
4706
2341
  principalId: identity.principalId,
4707
2342
  controllerInstanceId: identity.controllerInstanceId,
4708
- routeDecisionFingerprint: current.routeDecisionFingerprint,
4709
2343
  },
4710
2344
  },
4711
2345
  );
@@ -4718,29 +2352,6 @@ export async function callMcpTool(
4718
2352
  );
4719
2353
  return textResult(session);
4720
2354
  }
4721
- case "finish_edit_session": {
4722
- if (ctx.policy.profile !== "controller")
4723
- return errorResult(
4724
- "TOOL_DISABLED",
4725
- "finish_edit_session requires the controller profile",
4726
- );
4727
- const sessionId = String(args.session_id ?? "").trim();
4728
- if (!sessionId) return errorResult("SESSION_ID_REQUIRED", "finish_edit_session requires session_id");
4729
- const decision = typeof args.decision === "string" ? args.decision : "auto";
4730
- if (!["auto", "approve_and_finish", "request_changes", "discard"].includes(decision)) {
4731
- return errorResult("INVALID_DECISION", "decision must be auto, approve_and_finish, request_changes, or discard");
4732
- }
4733
- const finished = finishEditSession(ctx.repoRoot, {
4734
- sessionId,
4735
- decision: decision as "auto" | "approve_and_finish" | "request_changes" | "discard",
4736
- reviewer: typeof args.reviewer === "string" ? args.reviewer : undefined,
4737
- note: typeof args.note === "string" ? args.note : undefined,
4738
- checkIds: Array.isArray(args.check_ids) ? args.check_ids.map(String) : undefined,
4739
- commit: typeof args.commit === "boolean" ? args.commit : undefined,
4740
- });
4741
- audit(ctx, name, ["blocked", "needs_decision"].includes(finished.action) ? "failed" : "ok", args, `tasks/issues/${finished.issueId}`, finished.reason);
4742
- return textResult(finished);
4743
- }
4744
2355
  case "list_workflow_files": {
4745
2356
  const files = workflowFileCandidates(ctx.repoRoot)
4746
2357
  .filter(