@moretea-labs/forge 1.5.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (903) hide show
  1. package/AGENTS.md +120 -0
  2. package/CHANGELOG.md +36 -0
  3. package/CLAUDE.md +120 -0
  4. package/CODE_OF_CONDUCT.md +23 -0
  5. package/CONTRIBUTING.md +77 -0
  6. package/LICENSE +22 -0
  7. package/NOTICE +9 -0
  8. package/README.en.md +5 -0
  9. package/README.es.md +12 -0
  10. package/README.fr.md +12 -0
  11. package/README.ja.md +12 -0
  12. package/README.md +132 -0
  13. package/README.zh-CN.md +84 -0
  14. package/SECURITY.md +50 -0
  15. package/SKILL.md +431 -0
  16. package/SUPPORT.md +31 -0
  17. package/THIRD_PARTY_NOTICES.md +18 -0
  18. package/agents/openai.yaml +4 -0
  19. package/assets/AGENTS.md +68 -0
  20. package/assets/CLAUDE.md +68 -0
  21. package/assets/branding/README.md +10 -0
  22. package/assets/branding/forge-controller-flow.mmd +8 -0
  23. package/assets/hooks/AGENTS.md +75 -0
  24. package/assets/hooks/CLAUDE.md +75 -0
  25. package/assets/hooks/anti-simplification.sh +12 -0
  26. package/assets/hooks/changelog-guard.sh +80 -0
  27. package/assets/hooks/codex.hooks.template.json +60 -0
  28. package/assets/hooks/first-principles-guard.sh +72 -0
  29. package/assets/hooks/hook-input.sh +618 -0
  30. package/assets/hooks/lib/session-state.sh +106 -0
  31. package/assets/hooks/lib/workflow-state.sh +1730 -0
  32. package/assets/hooks/post-bash.sh +215 -0
  33. package/assets/hooks/post-edit-guard.sh +256 -0
  34. package/assets/hooks/post-tool-observer.sh +99 -0
  35. package/assets/hooks/pre-edit-guard.sh +240 -0
  36. package/assets/hooks/prompt-guard.sh +1077 -0
  37. package/assets/hooks/run-hook.sh +67 -0
  38. package/assets/hooks/security-sentinel.sh +115 -0
  39. package/assets/hooks/session-start-context.sh +593 -0
  40. package/assets/hooks/settings.template.json +60 -0
  41. package/assets/hooks/stop-orchestrator.sh +171 -0
  42. package/assets/hooks/subagent-return-channel-guard.sh +107 -0
  43. package/assets/hooks/worktree-guard.sh +39 -0
  44. package/assets/initializer-question-pack.v1.json +132 -0
  45. package/assets/initializer-question-pack.v1.schema.json +46 -0
  46. package/assets/initializer-question-pack.v2.json +174 -0
  47. package/assets/initializer-question-pack.v3.json +206 -0
  48. package/assets/initializer-question-pack.v3.schema.json +92 -0
  49. package/assets/initializer-question-pack.v4.json +557 -0
  50. package/assets/initializer-question-pack.v4.schema.json +169 -0
  51. package/assets/partials/01-header.partial.md +10 -0
  52. package/assets/partials/02-iron-rules.partial.md +30 -0
  53. package/assets/partials/03-philosophy.partial.md +32 -0
  54. package/assets/partials/04-project-structure.partial.md +56 -0
  55. package/assets/partials/05-workflow.partial.md +79 -0
  56. package/assets/partials/06-cloudflare.partial.md +20 -0
  57. package/assets/partials/07-footer.partial.md +38 -0
  58. package/assets/partials/08-orchestration.partial.md +49 -0
  59. package/assets/partials/09-compact-instructions.partial.md +11 -0
  60. package/assets/partials/_assembly-order.md +40 -0
  61. package/assets/partials-agents/01-header.partial.md +8 -0
  62. package/assets/partials-agents/02-operating-mode.partial.md +44 -0
  63. package/assets/partials-agents/03-orchestration.partial.md +45 -0
  64. package/assets/partials-agents/04-task-protocol.partial.md +69 -0
  65. package/assets/partials-agents/05-coding-constraints.partial.md +20 -0
  66. package/assets/partials-agents/06-quality-safety.partial.md +23 -0
  67. package/assets/partials-agents/07-cloudflare.partial.md +9 -0
  68. package/assets/partials-agents/08-deep-docs.partial.md +32 -0
  69. package/assets/plan-map.json +301 -0
  70. package/assets/plugin-registry.v1.json +8 -0
  71. package/assets/project-structures/ai-native-collaborative-editor.txt +25 -0
  72. package/assets/project-structures/ai-native-product-copilot.txt +21 -0
  73. package/assets/project-structures/ai-native-runtime-console.txt +25 -0
  74. package/assets/project-structures/ai-native-sidecar-kernel.txt +19 -0
  75. package/assets/project-structures/astro-ssr.txt +25 -0
  76. package/assets/project-structures/expo-nativewind.txt +52 -0
  77. package/assets/project-structures/monorepo.txt +63 -0
  78. package/assets/project-structures/remix.txt +50 -0
  79. package/assets/project-structures/tanstack-start-workers.txt +51 -0
  80. package/assets/project-structures/vite-tanstack.txt +58 -0
  81. package/assets/reference-configs/AGENTS.md +13 -0
  82. package/assets/reference-configs/CLAUDE.md +13 -0
  83. package/assets/reference-configs/agentic-development-flow.md +94 -0
  84. package/assets/reference-configs/ai-workflows.md +14 -0
  85. package/assets/reference-configs/changelog-versioning.md +13 -0
  86. package/assets/reference-configs/coding-standards.md +14 -0
  87. package/assets/reference-configs/development-protocol.md +21 -0
  88. package/assets/reference-configs/document-generation.md +38 -0
  89. package/assets/reference-configs/evaluator-rubric.md +19 -0
  90. package/assets/reference-configs/external-tooling.md +374 -0
  91. package/assets/reference-configs/git-strategy.md +14 -0
  92. package/assets/reference-configs/global-working-rules.md +64 -0
  93. package/assets/reference-configs/handoff-protocol.md +38 -0
  94. package/assets/reference-configs/harness-overview.md +124 -0
  95. package/assets/reference-configs/heartbeat-triage.md +57 -0
  96. package/assets/reference-configs/hook-operations.md +96 -0
  97. package/assets/reference-configs/release-deploy.md +40 -0
  98. package/assets/reference-configs/spa-day-protocol.md +14 -0
  99. package/assets/reference-configs/sprint-contracts.md +89 -0
  100. package/assets/reference-configs/workflow-orchestration.md +14 -0
  101. package/assets/skill-hooks.json +41 -0
  102. package/assets/skill-version.json +189 -0
  103. package/assets/skills/claude-review/SKILL.md +231 -0
  104. package/assets/skills/codex-review/SKILL.md +103 -0
  105. package/assets/templates/AGENTS.md +13 -0
  106. package/assets/templates/CLAUDE.md +13 -0
  107. package/assets/templates/architecture.template.md +80 -0
  108. package/assets/templates/brief.template.md +38 -0
  109. package/assets/templates/contract.template.md +104 -0
  110. package/assets/templates/decisions.template.md +85 -0
  111. package/assets/templates/factor-factory/factor-backtest-report.template.md +36 -0
  112. package/assets/templates/factor-factory/factor-hypothesis.template.md +30 -0
  113. package/assets/templates/factor-factory/factor-registry.template.json +5 -0
  114. package/assets/templates/gitignore.template +53 -0
  115. package/assets/templates/guides/metro-esm-gotchas.template.md +46 -0
  116. package/assets/templates/helpers/architecture-event.ts +822 -0
  117. package/assets/templates/helpers/architecture-queue.sh +647 -0
  118. package/assets/templates/helpers/archive-architecture-request.sh +244 -0
  119. package/assets/templates/helpers/archive-workflow.sh +295 -0
  120. package/assets/templates/helpers/capability-config.ts +394 -0
  121. package/assets/templates/helpers/capability-resolver.ts +505 -0
  122. package/assets/templates/helpers/capture-plan.sh +335 -0
  123. package/assets/templates/helpers/check-agent-tooling.sh +1533 -0
  124. package/assets/templates/helpers/check-architecture-sync.sh +287 -0
  125. package/assets/templates/helpers/check-brain-manifest.sh +255 -0
  126. package/assets/templates/helpers/check-context-files.sh +49 -0
  127. package/assets/templates/helpers/check-deploy-sql-order.sh +117 -0
  128. package/assets/templates/helpers/check-skill-version.ts +193 -0
  129. package/assets/templates/helpers/check-task-sync.sh +65 -0
  130. package/assets/templates/helpers/check-task-workflow.sh +1002 -0
  131. package/assets/templates/helpers/codex-handoff-resume.sh +269 -0
  132. package/assets/templates/helpers/context-contract-sync.sh +516 -0
  133. package/assets/templates/helpers/contract-run.ts +465 -0
  134. package/assets/templates/helpers/contract-worktree.sh +725 -0
  135. package/assets/templates/helpers/ensure-task-workflow.sh +1218 -0
  136. package/assets/templates/helpers/harness-trace-grade.sh +150 -0
  137. package/assets/templates/helpers/heartbeat-triage.sh +361 -0
  138. package/assets/templates/helpers/inspect-project-state.ts +280 -0
  139. package/assets/templates/helpers/maintenance-triage.sh +167 -0
  140. package/assets/templates/helpers/migrate-project-template.sh +54 -0
  141. package/assets/templates/helpers/migrate-workflow-docs.ts +413 -0
  142. package/assets/templates/helpers/new-plan.sh +183 -0
  143. package/assets/templates/helpers/new-spec.sh +50 -0
  144. package/assets/templates/helpers/new-sprint.sh +49 -0
  145. package/assets/templates/helpers/plan-to-todo.sh +799 -0
  146. package/assets/templates/helpers/prepare-codex-handoff.sh +203 -0
  147. package/assets/templates/helpers/prepare-handoff.sh +81 -0
  148. package/assets/templates/helpers/refresh-current-status.sh +469 -0
  149. package/assets/templates/helpers/select-agent-context-blocks.sh +66 -0
  150. package/assets/templates/helpers/ship-worktrees.sh +561 -0
  151. package/assets/templates/helpers/sprint-backlog.sh +837 -0
  152. package/assets/templates/helpers/summarize-failures.sh +125 -0
  153. package/assets/templates/helpers/switch-plan.sh +153 -0
  154. package/assets/templates/helpers/sync-brain-docs.sh +381 -0
  155. package/assets/templates/helpers/verify-contract.sh +705 -0
  156. package/assets/templates/helpers/verify-sprint.sh +680 -0
  157. package/assets/templates/helpers/workflow-contract.ts +202 -0
  158. package/assets/templates/helpers/workstream-sync.sh +263 -0
  159. package/assets/templates/implementation-notes.template.md +37 -0
  160. package/assets/templates/packages.template.md +45 -0
  161. package/assets/templates/plan.template.md +77 -0
  162. package/assets/templates/prd.template.md +165 -0
  163. package/assets/templates/research.template.md +25 -0
  164. package/assets/templates/review.template.md +78 -0
  165. package/assets/templates/spec.template.md +31 -0
  166. package/assets/templates/sprint.template.md +69 -0
  167. package/assets/templates/tech-stack.template.md +57 -0
  168. package/assets/versions.json +77 -0
  169. package/assets/workflow-contract.v1.json +671 -0
  170. package/bin/forge-desktop-helper.mjs +204 -0
  171. package/bin/forge-hook.mjs +32 -0
  172. package/bin/forge-runtime-service.mjs +34 -0
  173. package/bin/forge-runtime.mjs +30 -0
  174. package/bin/forge.mjs +32 -0
  175. package/docs/CHANGELOG.md +978 -0
  176. package/docs/README.md +72 -0
  177. package/docs/README.zh-CN.md +58 -0
  178. package/docs/architecture/chatgpt-handoff-facade.md +393 -0
  179. package/docs/architecture/current/README.md +158 -0
  180. package/docs/architecture/current/approved-target-architecture.zh-CN.md +1501 -0
  181. package/docs/architecture/current/architecture-invariants.md +331 -0
  182. package/docs/architecture/current/automation-and-schedule-engine.md +453 -0
  183. package/docs/architecture/current/codegraph-context-provider.md +226 -0
  184. package/docs/architecture/current/control-plane-authority-inventory.md +82 -0
  185. package/docs/architecture/current/controller-repository-decoupling.md +148 -0
  186. package/docs/architecture/current/dispatch-and-agent-strategy.md +354 -0
  187. package/docs/architecture/current/entity-model.md +619 -0
  188. package/docs/architecture/current/external-plugin-governance.md +396 -0
  189. package/docs/architecture/current/failure-recovery.md +471 -0
  190. package/docs/architecture/current/governance.md +213 -0
  191. package/docs/architecture/current/human-interaction-plane.md +87 -0
  192. package/docs/architecture/current/implementation-status.md +128 -0
  193. package/docs/architecture/current/job-and-run-lifecycle.md +413 -0
  194. package/docs/architecture/current/local-workspace-target-grants.md +70 -0
  195. package/docs/architecture/current/migration-roadmap.md +105 -0
  196. package/docs/architecture/current/multi-repository-execution.md +371 -0
  197. package/docs/architecture/current/operations-runbook.md +45 -0
  198. package/docs/architecture/current/peer-supercontroller-runtime.md +32 -0
  199. package/docs/architecture/current/personal-assistant-plugin-baseline.md +207 -0
  200. package/docs/architecture/current/repository-access-modes.md +102 -0
  201. package/docs/architecture/current/route-policy.md +15 -0
  202. package/docs/architecture/current/route-session-concurrency.md +85 -0
  203. package/docs/architecture/current/runtime-architecture-simplification.md +214 -0
  204. package/docs/architecture/current/runtime-directory-map.md +49 -0
  205. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +85 -0
  206. package/docs/architecture/current/scheduler-and-resource-claims.md +432 -0
  207. package/docs/architecture/current/session-aware-execution-and-authorization.md +77 -0
  208. package/docs/architecture/current/system-overview.md +144 -0
  209. package/docs/architecture/current/target-requirements-traceability.md +202 -0
  210. package/docs/architecture/current/thin-harness-governance-policy.md +32 -0
  211. package/docs/architecture/current/thin-harness-v1.md +313 -0
  212. package/docs/architecture/current/verification-and-release-gates.md +391 -0
  213. package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +100 -0
  214. package/docs/architecture/decisions/20260803-canonical-skill-surface.md +40 -0
  215. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +53 -0
  216. package/docs/architecture/diagrams/.gitkeep +1 -0
  217. package/docs/architecture/diagrams/agentic-dev-plugin-architecture.html +186 -0
  218. package/docs/architecture/domains/.gitkeep +0 -0
  219. package/docs/architecture/domains/public-surface.md +30 -0
  220. package/docs/architecture/domains/runtime-harness.md +33 -0
  221. package/docs/architecture/domains/verification.md +33 -0
  222. package/docs/architecture/domains/workflow-engine.md +30 -0
  223. package/docs/architecture/global-hook-runtime.md +162 -0
  224. package/docs/architecture/history.md +38 -0
  225. package/docs/architecture/index.md +114 -0
  226. package/docs/architecture/ios-semantic-automation-provider-v2.md +166 -0
  227. package/docs/architecture/modules/.gitkeep +0 -0
  228. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +24 -0
  229. package/docs/architecture/requests/.gitkeep +1 -0
  230. package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +59 -0
  231. package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +59 -0
  232. package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +59 -0
  233. package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +59 -0
  234. package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +59 -0
  235. package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +59 -0
  236. package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +59 -0
  237. package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +59 -0
  238. package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +59 -0
  239. package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +59 -0
  240. package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
  241. package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
  242. package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +59 -0
  243. package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
  244. package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +59 -0
  245. package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
  246. package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +59 -0
  247. package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +59 -0
  248. package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +59 -0
  249. package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +59 -0
  250. package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +59 -0
  251. package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +63 -0
  252. package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +60 -0
  253. package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +60 -0
  254. package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +60 -0
  255. package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +60 -0
  256. package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +60 -0
  257. package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +60 -0
  258. package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +68 -0
  259. package/docs/architecture/requests/archive/2026/root.md +68 -0
  260. package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +76 -0
  261. package/docs/architecture/requests/archive/2026/verification-evals-checks.md +68 -0
  262. package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +69 -0
  263. package/docs/architecture/reviews/2026-07-11-access-surface-review.md +99 -0
  264. package/docs/architecture/snapshots/.gitkeep +1 -0
  265. package/docs/forge-autonomous-goal-loop.md +7 -0
  266. package/docs/forge-chatgpt-browser-engine.md +11 -0
  267. package/docs/forge-chatgpt-mcp-setup.md +239 -0
  268. package/docs/forge-chatgpt-supervised-automation.md +11 -0
  269. package/docs/forge-github-issue-launcher.md +7 -0
  270. package/docs/forge-plugin-management.md +95 -0
  271. package/docs/forge-provider-configuration.md +7 -0
  272. package/docs/forge-runtime-self-healing-loop.md +7 -0
  273. package/docs/images/forge-banner-cn.svg +1 -0
  274. package/docs/images/forge-banner.svg +1 -0
  275. package/docs/images/forge-controller-flow.svg +1 -0
  276. package/docs/images/image.png +0 -0
  277. package/docs/images/repo-harness-gptpro-cn.png +0 -0
  278. package/docs/images/repo-harness-gptpro.png +0 -0
  279. package/docs/images/repo-harness-hook-carrot.png +0 -0
  280. package/docs/operations/chatgpt-personal-assistant-runtime.md +83 -0
  281. package/docs/operations/checkout-route-integrity.md +22 -0
  282. package/docs/operations/controller-browser-plugin.md +299 -0
  283. package/docs/operations/controller-desktop-plugin.md +112 -0
  284. package/docs/operations/controller-performance-and-502.md +126 -0
  285. package/docs/operations/controller-reliability-runbook.md +134 -0
  286. package/docs/operations/environment-hardening.md +50 -0
  287. package/docs/operations/features.md +45 -0
  288. package/docs/operations/features.zh-CN.md +45 -0
  289. package/docs/operations/github-repository.md +60 -0
  290. package/docs/operations/gmail-assistant-routines.md +186 -0
  291. package/docs/operations/homebrew.md +26 -0
  292. package/docs/operations/local-system-assistant.md +46 -0
  293. package/docs/operations/local-system-assistant.zh-CN.md +46 -0
  294. package/docs/operations/mcp-tool-exposure.md +58 -0
  295. package/docs/operations/mobile-intents-shortcuts.md +117 -0
  296. package/docs/operations/open-source-release-hygiene.md +31 -0
  297. package/docs/operations/personal-assistant-operator-guide.md +61 -0
  298. package/docs/operations/platform-support.md +62 -0
  299. package/docs/operations/platform-support.zh-CN.md +61 -0
  300. package/docs/operations/provider-configuration.md +31 -0
  301. package/docs/operations/releasing.md +86 -0
  302. package/docs/operations/releasing.zh-CN.md +86 -0
  303. package/docs/operations/route-session-concurrency-baseline.json +893 -0
  304. package/docs/operations/route-session-concurrency-baseline.md +94 -0
  305. package/docs/operations/runtime-performance-diagnostics.md +14 -0
  306. package/docs/operations/sqlite-control-plane-recovery.md +65 -0
  307. package/docs/operations/standalone-disaster-recovery.md +136 -0
  308. package/docs/operations/troubleshooting.md +72 -0
  309. package/docs/operations/troubleshooting.zh-CN.md +70 -0
  310. package/docs/public-usage-guide.md +33 -0
  311. package/docs/public-usage-guide.zh-CN.md +33 -0
  312. package/docs/tutorials/01-install-and-start.md +93 -0
  313. package/docs/tutorials/01-install-and-start.zh-CN.md +93 -0
  314. package/docs/tutorials/02-connect-chatgpt.md +82 -0
  315. package/docs/tutorials/02-connect-chatgpt.zh-CN.md +80 -0
  316. package/docs/tutorials/03-first-repository-task.md +57 -0
  317. package/docs/tutorials/03-first-repository-task.zh-CN.md +57 -0
  318. package/docs/tutorials/README.md +13 -0
  319. package/docs/tutorials/README.zh-CN.md +13 -0
  320. package/docs/wiki/Architecture.md +85 -0
  321. package/docs/wiki/Core-Concepts.md +27 -0
  322. package/docs/wiki/Home.md +36 -0
  323. package/docs/wiki/Implementation.md +172 -0
  324. package/docs/wiki/Installation.md +50 -0
  325. package/docs/wiki/Integrations.md +29 -0
  326. package/docs/wiki/Operations.md +26 -0
  327. package/docs/wiki/Quick-Start.md +46 -0
  328. package/docs/wiki/Releases-and-Upgrades.md +34 -0
  329. package/docs/wiki/Runtime-Architecture.md +50 -0
  330. package/docs/wiki/Security-Model.md +31 -0
  331. package/docs/wiki/Troubleshooting.md +38 -0
  332. package/docs/wiki/Work-Lifecycle.md +31 -0
  333. package/docs/wiki/_Sidebar.md +21 -0
  334. package/install.ps1 +48 -0
  335. package/install.sh +66 -0
  336. package/package.json +156 -0
  337. package/scripts/activate-source-baseline.ts +335 -0
  338. package/scripts/architecture-event.ts +822 -0
  339. package/scripts/architecture-queue.sh +647 -0
  340. package/scripts/archive-architecture-request.sh +244 -0
  341. package/scripts/archive-workflow.sh +295 -0
  342. package/scripts/assemble-template.ts +1172 -0
  343. package/scripts/benchmark-controller-hotpath.ts +212 -0
  344. package/scripts/benchmark-controller-round2.ts +139 -0
  345. package/scripts/benchmark-ios-agent-device.ts +514 -0
  346. package/scripts/benchmark-route-session-concurrency.ts +714 -0
  347. package/scripts/benchmark-thin-harness-gateway-ab.ts +632 -0
  348. package/scripts/benchmark-thin-harness-mcp-e2e.ts +479 -0
  349. package/scripts/benchmark-thin-harness.ts +251 -0
  350. package/scripts/bootstrap-runtime-maintenance-recovery.sh +183 -0
  351. package/scripts/canary-global-hook.sh +263 -0
  352. package/scripts/capability-config.ts +394 -0
  353. package/scripts/capability-resolver.ts +505 -0
  354. package/scripts/capture-plan.sh +335 -0
  355. package/scripts/check-agent-tooling.sh +1549 -0
  356. package/scripts/check-architecture-sync.sh +373 -0
  357. package/scripts/check-brain-manifest.sh +255 -0
  358. package/scripts/check-ci.sh +16 -0
  359. package/scripts/check-context-files.sh +49 -0
  360. package/scripts/check-deploy-sql-order.sh +117 -0
  361. package/scripts/check-mcp-compatibility.ts +93 -0
  362. package/scripts/check-npm-release.sh +32 -0
  363. package/scripts/check-open-source-tracked-surface.sh +146 -0
  364. package/scripts/check-package-identity.mjs +117 -0
  365. package/scripts/check-platform-support.mjs +79 -0
  366. package/scripts/check-public-docs.sh +105 -0
  367. package/scripts/check-public-release-surface.sh +19 -0
  368. package/scripts/check-release-published.sh +57 -0
  369. package/scripts/check-release-readiness.sh +43 -0
  370. package/scripts/check-release-version.mjs +74 -0
  371. package/scripts/check-runtime-architecture.mjs +593 -0
  372. package/scripts/check-runtime-env.mjs +89 -0
  373. package/scripts/check-skill-version.ts +193 -0
  374. package/scripts/check-tarball-install-smoke.sh +66 -0
  375. package/scripts/check-task-sync.sh +65 -0
  376. package/scripts/check-task-workflow.sh +1002 -0
  377. package/scripts/check-third-party-notices.mjs +36 -0
  378. package/scripts/codex-handoff-resume.sh +269 -0
  379. package/scripts/complete-control-plane-cutover.ts +126 -0
  380. package/scripts/context-contract-sync.sh +516 -0
  381. package/scripts/contract-run.ts +465 -0
  382. package/scripts/contract-worktree.sh +725 -0
  383. package/scripts/controller-ngrok-rotation.sh +678 -0
  384. package/scripts/create-project-dirs.sh +282 -0
  385. package/scripts/ensure-codegraph.sh +16 -0
  386. package/scripts/ensure-task-workflow.sh +1218 -0
  387. package/scripts/export-requirement-portfolio.ts +28 -0
  388. package/scripts/factor-lab-check.sh +73 -0
  389. package/scripts/factor-lab-new.sh +96 -0
  390. package/scripts/factor-lab-promote.sh +86 -0
  391. package/scripts/factor-lab-reject.sh +68 -0
  392. package/scripts/harness-trace-grade.sh +150 -0
  393. package/scripts/heartbeat-triage.sh +361 -0
  394. package/scripts/hook-dispatch-diet-report.ts +234 -0
  395. package/scripts/hook-shim.sh +93 -0
  396. package/scripts/init-project.sh +505 -0
  397. package/scripts/initializer-question-pack.ts +200 -0
  398. package/scripts/inspect-project-state.ts +280 -0
  399. package/scripts/install-desktop-operator-registration.ts +45 -0
  400. package/scripts/install-standalone-recovery.ts +74 -0
  401. package/scripts/lib/AGENTS.md +13 -0
  402. package/scripts/lib/CLAUDE.md +13 -0
  403. package/scripts/lib/controller-home-env.sh +69 -0
  404. package/scripts/lib/project-init-lib.sh +2759 -0
  405. package/scripts/load-standalone-recovery.sh +17 -0
  406. package/scripts/loop-engine-cutover-gate.ts +324 -0
  407. package/scripts/maintenance-triage.sh +167 -0
  408. package/scripts/migrate-project-template.sh +1189 -0
  409. package/scripts/migrate-requirement-portfolio.ts +86 -0
  410. package/scripts/migrate-workflow-docs.ts +413 -0
  411. package/scripts/new-plan.sh +183 -0
  412. package/scripts/new-spec.sh +50 -0
  413. package/scripts/new-sprint.sh +49 -0
  414. package/scripts/open-source-audit-allowlist.txt +46 -0
  415. package/scripts/package-source-archive.sh +147 -0
  416. package/scripts/plan-to-todo.sh +799 -0
  417. package/scripts/prepare-codex-handoff.sh +203 -0
  418. package/scripts/prepare-handoff.sh +81 -0
  419. package/scripts/public-release-files.txt +39 -0
  420. package/scripts/public-release.ts +174 -0
  421. package/scripts/publish-release-tarball.sh +27 -0
  422. package/scripts/refresh-current-status.sh +469 -0
  423. package/scripts/refresh-ios-physical-runtime.ts +196 -0
  424. package/scripts/repair-executable-modes.ts +8 -0
  425. package/scripts/rollout-all-registered-repos.sh +7 -0
  426. package/scripts/route-nl-vs-ts-eval.ts +679 -0
  427. package/scripts/run-bun-test-file.ts +182 -0
  428. package/scripts/run-governed-gate.ts +140 -0
  429. package/scripts/run-mobile-monitor-bridge.ts +71 -0
  430. package/scripts/run-skill-evals.ts +1022 -0
  431. package/scripts/run-skill-hook.ts +244 -0
  432. package/scripts/run-supervised-command.ts +24 -0
  433. package/scripts/run-tests-portable.sh +20 -0
  434. package/scripts/select-agent-context-blocks.sh +66 -0
  435. package/scripts/setup-plugins.sh +43 -0
  436. package/scripts/ship-worktrees.sh +561 -0
  437. package/scripts/smoke-browser-cdp-bridge.ts +93 -0
  438. package/scripts/smoke-chatgpt-supervised-automation.ts +208 -0
  439. package/scripts/smoke-ios-physical-device.ts +114 -0
  440. package/scripts/smoke-mcp-http-runtime.ts +216 -0
  441. package/scripts/smoke-mcp-tool-surface.ts +117 -0
  442. package/scripts/smoke-runtime-recovery.ts +220 -0
  443. package/scripts/smoke-schedule-engine.ts +283 -0
  444. package/scripts/sprint-backlog.sh +837 -0
  445. package/scripts/stage-runtime-release.ts +38 -0
  446. package/scripts/summarize-failures.sh +125 -0
  447. package/scripts/switch-plan.sh +153 -0
  448. package/scripts/sync-brain-docs.sh +381 -0
  449. package/scripts/sync-codex-installed-copies.sh +183 -0
  450. package/scripts/test-governance.ts +75 -0
  451. package/scripts/verify-contract.sh +705 -0
  452. package/scripts/verify-forge-runtime.sh +31 -0
  453. package/scripts/verify-sprint.sh +680 -0
  454. package/scripts/windows-paths.d.mts +7 -0
  455. package/scripts/windows-paths.mjs +28 -0
  456. package/scripts/workflow-contract.ts +206 -0
  457. package/scripts/workstream-sync.sh +263 -0
  458. package/src/cli/agent-jobs/executable-resolver.ts +289 -0
  459. package/src/cli/agent-jobs/executor-health.ts +252 -0
  460. package/src/cli/agent-jobs/integration.ts +963 -0
  461. package/src/cli/agent-jobs/job-manager.ts +2030 -0
  462. package/src/cli/agent-jobs/types.ts +219 -0
  463. package/src/cli/agent-jobs/worker-lifecycle.ts +288 -0
  464. package/src/cli/chatgpt-browser/bind-server.ts +504 -0
  465. package/src/cli/chatgpt-browser/binding.ts +140 -0
  466. package/src/cli/chatgpt-browser/bridge-extension.ts +221 -0
  467. package/src/cli/chatgpt-browser/bridge-provider.ts +268 -0
  468. package/src/cli/chatgpt-browser/engine.ts +569 -0
  469. package/src/cli/chatgpt-browser/file-policy.ts +158 -0
  470. package/src/cli/chatgpt-browser/native-provider.ts +553 -0
  471. package/src/cli/chatgpt-browser/oracle-provider.ts +437 -0
  472. package/src/cli/chatgpt-browser/prompt-assembler.ts +29 -0
  473. package/src/cli/chatgpt-browser/session-store.ts +301 -0
  474. package/src/cli/chatgpt-browser/types.ts +172 -0
  475. package/src/cli/commands/adopt-plan.ts +186 -0
  476. package/src/cli/commands/brain-root.ts +132 -0
  477. package/src/cli/commands/brain.ts +609 -0
  478. package/src/cli/commands/capability-context.ts +613 -0
  479. package/src/cli/commands/chatgpt.ts +424 -0
  480. package/src/cli/commands/controller.ts +680 -0
  481. package/src/cli/commands/docs.ts +103 -0
  482. package/src/cli/commands/doctor.ts +476 -0
  483. package/src/cli/commands/global-runtime.ts +323 -0
  484. package/src/cli/commands/hook.ts +34 -0
  485. package/src/cli/commands/init-hook.ts +870 -0
  486. package/src/cli/commands/init.ts +829 -0
  487. package/src/cli/commands/install.ts +86 -0
  488. package/src/cli/commands/mcp.ts +315 -0
  489. package/src/cli/commands/migrate.ts +154 -0
  490. package/src/cli/commands/plugin.ts +74 -0
  491. package/src/cli/commands/prompt-guard-decision.ts +238 -0
  492. package/src/cli/commands/recovery.ts +667 -0
  493. package/src/cli/commands/repository.ts +224 -0
  494. package/src/cli/commands/run.ts +23 -0
  495. package/src/cli/commands/runtime.ts +191 -0
  496. package/src/cli/commands/security.ts +440 -0
  497. package/src/cli/commands/status.ts +137 -0
  498. package/src/cli/commands/tools.ts +167 -0
  499. package/src/cli/controller/check-runner.ts +1022 -0
  500. package/src/cli/controller/codex-continuation.ts +153 -0
  501. package/src/cli/controller/completion-backlog.ts +380 -0
  502. package/src/cli/controller/completion-orchestrator.ts +1807 -0
  503. package/src/cli/controller/completion-target.ts +96 -0
  504. package/src/cli/controller/composite-operations.ts +196 -0
  505. package/src/cli/controller/composite-result.ts +130 -0
  506. package/src/cli/controller/context-pack.ts +652 -0
  507. package/src/cli/controller/execution-completion.ts +175 -0
  508. package/src/cli/controller/execution-policy.ts +392 -0
  509. package/src/cli/controller/governance.ts +594 -0
  510. package/src/cli/controller/issue-store.ts +1421 -0
  511. package/src/cli/controller/launch-agents.ts +410 -0
  512. package/src/cli/controller/legacy-issue-cutover.ts +415 -0
  513. package/src/cli/controller/lifecycle-authority.ts +16 -0
  514. package/src/cli/controller/migrated-issue-decision.ts +237 -0
  515. package/src/cli/controller/operational-plan.ts +226 -0
  516. package/src/cli/controller/postcondition.ts +134 -0
  517. package/src/cli/controller/progress.ts +656 -0
  518. package/src/cli/controller/project-state.ts +147 -0
  519. package/src/cli/controller/requirement-portfolio-export.ts +146 -0
  520. package/src/cli/controller/requirement-portfolio-migration.ts +613 -0
  521. package/src/cli/controller/run-evidence.ts +43 -0
  522. package/src/cli/controller/runtime-config.ts +92 -0
  523. package/src/cli/controller/stuck-state-migration.ts +396 -0
  524. package/src/cli/controller/task-ledger.ts +632 -0
  525. package/src/cli/controller/task-status-resolver.ts +403 -0
  526. package/src/cli/controller/types.ts +273 -0
  527. package/src/cli/controller/work-mode.ts +186 -0
  528. package/src/cli/controller/worklog.ts +201 -0
  529. package/src/cli/diagnostic-entry.ts +39 -0
  530. package/src/cli/editing/edit-session.ts +1606 -0
  531. package/src/cli/editing/executable-modes.ts +70 -0
  532. package/src/cli/github/github.ts +274 -0
  533. package/src/cli/github/plugin.ts +345 -0
  534. package/src/cli/github/session.ts +104 -0
  535. package/src/cli/hook/prompt-guard-decision.ts +238 -0
  536. package/src/cli/hook/prompt-intents.ts +534 -0
  537. package/src/cli/hook/route-registry.ts +107 -0
  538. package/src/cli/hook/runtime.ts +303 -0
  539. package/src/cli/hook/state-snapshot.ts +388 -0
  540. package/src/cli/hook-entry.ts +51 -0
  541. package/src/cli/index.ts +574 -0
  542. package/src/cli/installer/managed-entries.ts +78 -0
  543. package/src/cli/installer/shared.ts +65 -0
  544. package/src/cli/installer/targets/claude.ts +136 -0
  545. package/src/cli/installer/targets/codex.ts +174 -0
  546. package/src/cli/installer/targets/registry.ts +28 -0
  547. package/src/cli/installer/types.ts +72 -0
  548. package/src/cli/local-bridge/connector-freshness.ts +529 -0
  549. package/src/cli/local-bridge/console-view-models.ts +324 -0
  550. package/src/cli/local-bridge/dashboard.ts +1312 -0
  551. package/src/cli/local-bridge/facade-api.ts +1877 -0
  552. package/src/cli/local-bridge/job-store.ts +1451 -0
  553. package/src/cli/local-bridge/mobile-intents.ts +344 -0
  554. package/src/cli/local-bridge/mobile-monitor.ts +209 -0
  555. package/src/cli/local-bridge/server.ts +3183 -0
  556. package/src/cli/local-bridge/types.ts +176 -0
  557. package/src/cli/mcp/access-mode.ts +132 -0
  558. package/src/cli/mcp/access-tools.ts +387 -0
  559. package/src/cli/mcp/audit.ts +32 -0
  560. package/src/cli/mcp/auth.ts +402 -0
  561. package/src/cli/mcp/instructions.ts +33 -0
  562. package/src/cli/mcp/legacy-context.ts +16 -0
  563. package/src/cli/mcp/legacy-tool-service.ts +5516 -0
  564. package/src/cli/mcp/multi-repository.ts +423 -0
  565. package/src/cli/mcp/oauth.ts +226 -0
  566. package/src/cli/mcp/paths.ts +134 -0
  567. package/src/cli/mcp/policy.ts +230 -0
  568. package/src/cli/mcp/proxy-env.ts +62 -0
  569. package/src/cli/mcp/readiness-projection.ts +24 -0
  570. package/src/cli/mcp/redaction.ts +21 -0
  571. package/src/cli/mcp/repo.ts +24 -0
  572. package/src/cli/mcp/repository-tools.ts +1295 -0
  573. package/src/cli/mcp/server.ts +341 -0
  574. package/src/cli/mcp/setup.ts +793 -0
  575. package/src/cli/mcp/tool-capability-cache.ts +87 -0
  576. package/src/cli/mcp/tools.ts +9 -0
  577. package/src/cli/mcp/toolset-names.ts +70 -0
  578. package/src/cli/mcp/toolset-selection.ts +41 -0
  579. package/src/cli/mcp/toolset.ts +300 -0
  580. package/src/cli/mcp/transports/AGENTS.md +39 -0
  581. package/src/cli/mcp/transports/CLAUDE.md +39 -0
  582. package/src/cli/mcp/transports/http.ts +1234 -0
  583. package/src/cli/mcp/transports/session-registry.ts +428 -0
  584. package/src/cli/mcp/transports/stdio.ts +8 -0
  585. package/src/cli/mcp/types.ts +41 -0
  586. package/src/cli/mcp/umbrella-tools.ts +117 -0
  587. package/src/cli/repo-adoption/reclaim-runtime.ts +681 -0
  588. package/src/cli/repositories/branch-name-policy.ts +57 -0
  589. package/src/cli/repositories/command-classifier.ts +600 -0
  590. package/src/cli/repositories/command-executor.ts +1066 -0
  591. package/src/cli/repositories/command-normalization.ts +53 -0
  592. package/src/cli/repositories/command-scope.ts +264 -0
  593. package/src/cli/repositories/controller-home.ts +88 -0
  594. package/src/cli/repositories/entity-migration.ts +199 -0
  595. package/src/cli/repositories/ephemeral-workspace.ts +62 -0
  596. package/src/cli/repositories/git-command-executor.ts +301 -0
  597. package/src/cli/repositories/goal-registry.ts +239 -0
  598. package/src/cli/repositories/identity.ts +64 -0
  599. package/src/cli/repositories/local-project-onboarding.ts +539 -0
  600. package/src/cli/repositories/locks.ts +262 -0
  601. package/src/cli/repositories/registry.ts +1057 -0
  602. package/src/cli/repositories/runtime-storage.ts +411 -0
  603. package/src/cli/repositories/safe-patch.ts +421 -0
  604. package/src/cli/repositories/selected-path-actions.ts +341 -0
  605. package/src/cli/repositories/structured-git.ts +257 -0
  606. package/src/cli/repositories/types.ts +155 -0
  607. package/src/cli/repositories/umbrella.ts +134 -0
  608. package/src/cli/repositories/workbench.ts +140 -0
  609. package/src/cli/repositories/worktree-storage.ts +137 -0
  610. package/src/cli/repository/inspector.ts +637 -0
  611. package/src/cli/repository/session-cache.ts +453 -0
  612. package/src/cli/runtime/helper-runner.ts +172 -0
  613. package/src/cli/runtime-invocation.ts +318 -0
  614. package/src/cli/tools/codegraph.ts +676 -0
  615. package/src/cli/v81-entry.ts +29 -0
  616. package/src/core/adoption/gitignore-plan.ts +127 -0
  617. package/src/core/adoption/helper-wrapper-plan.ts +159 -0
  618. package/src/core/adoption/manifest-templates.ts +60 -0
  619. package/src/core/adoption/modes.ts +7 -0
  620. package/src/core/adoption/operations.ts +126 -0
  621. package/src/core/adoption/plan.ts +149 -0
  622. package/src/core/adoption/render.ts +70 -0
  623. package/src/core/adoption/rollback.ts +62 -0
  624. package/src/core/adoption/summary.ts +43 -0
  625. package/src/core/adoption/workflow-contract-asset.ts +14 -0
  626. package/src/core/adoption/workflow-contract-plan.ts +31 -0
  627. package/src/effects/fs-transaction.ts +540 -0
  628. package/src/effects/git-executable.ts +59 -0
  629. package/src/effects/managed-block.ts +86 -0
  630. package/src/effects/path-safety.ts +47 -0
  631. package/src/effects/process-runner.ts +192 -0
  632. package/src/runtime/assistant/action-proposals.ts +279 -0
  633. package/src/runtime/assistant/intent.ts +395 -0
  634. package/src/runtime/assistant/model-provider.ts +460 -0
  635. package/src/runtime/assistant/openapi.ts +223 -0
  636. package/src/runtime/assistant/policy.ts +89 -0
  637. package/src/runtime/assistant/readiness.ts +258 -0
  638. package/src/runtime/assistant/routine-runtime.ts +477 -0
  639. package/src/runtime/assistant/schedule-binding.ts +171 -0
  640. package/src/runtime/assistant/standing-grants.ts +375 -0
  641. package/src/runtime/assistant/store.ts +243 -0
  642. package/src/runtime/assistant/types.ts +135 -0
  643. package/src/runtime/context/codegraph-read-provider.ts +347 -0
  644. package/src/runtime/context/codegraph-sidecar.cjs +207 -0
  645. package/src/runtime/control-plane/dispatch-priority.ts +67 -0
  646. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +151 -0
  647. package/src/runtime/control-plane/execution/execution-identity.ts +456 -0
  648. package/src/runtime/control-plane/execution/session-store.ts +175 -0
  649. package/src/runtime/control-plane/execution/validation.ts +117 -0
  650. package/src/runtime/control-plane/execution/verification-evidence.ts +191 -0
  651. package/src/runtime/control-plane/execution/work-handle-store.ts +225 -0
  652. package/src/runtime/control-plane/execution/work-prepare-request-store.ts +159 -0
  653. package/src/runtime/control-plane/execution/work-task-receipt.ts +445 -0
  654. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +821 -0
  655. package/src/runtime/control-plane/facade/capability-registry.ts +279 -0
  656. package/src/runtime/control-plane/facade/check-normalization.ts +233 -0
  657. package/src/runtime/control-plane/facade/codex-delegation.ts +196 -0
  658. package/src/runtime/control-plane/facade/controller-session-store.ts +199 -0
  659. package/src/runtime/control-plane/facade/facade-result.ts +41 -0
  660. package/src/runtime/control-plane/facade/goal-workloop-access.ts +177 -0
  661. package/src/runtime/control-plane/facade/goal-workloop.ts +1216 -0
  662. package/src/runtime/control-plane/facade/handoff-inbox-store.ts +221 -0
  663. package/src/runtime/control-plane/facade/index.ts +16 -0
  664. package/src/runtime/control-plane/facade/operation-digest.ts +528 -0
  665. package/src/runtime/control-plane/facade/plan-contract-store.ts +403 -0
  666. package/src/runtime/control-plane/facade/policy-gate.ts +149 -0
  667. package/src/runtime/control-plane/facade/requirement-board.ts +280 -0
  668. package/src/runtime/control-plane/facade/self-healing-loop.ts +555 -0
  669. package/src/runtime/control-plane/facade/suggested-actions.ts +108 -0
  670. package/src/runtime/control-plane/facade/types.ts +729 -0
  671. package/src/runtime/control-plane/facade/work-admission-policy.ts +93 -0
  672. package/src/runtime/control-plane/facade/work-continuation.ts +104 -0
  673. package/src/runtime/control-plane/facade/work-contract-store.ts +1118 -0
  674. package/src/runtime/control-plane/facade/work-task-projection.ts +66 -0
  675. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +365 -0
  676. package/src/runtime/control-plane/global-scheduler/scheduler.ts +920 -0
  677. package/src/runtime/control-plane/global-scheduler/wake-signal.ts +89 -0
  678. package/src/runtime/control-plane/goal-loop/config-facade.ts +700 -0
  679. package/src/runtime/control-plane/goal-loop/config-store.ts +570 -0
  680. package/src/runtime/control-plane/goal-loop/config-types.ts +213 -0
  681. package/src/runtime/control-plane/goal-loop/executor-router.ts +109 -0
  682. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +149 -0
  683. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +304 -0
  684. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +1001 -0
  685. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +160 -0
  686. package/src/runtime/control-plane/goal-loop/index.ts +13 -0
  687. package/src/runtime/control-plane/goal-loop/local-tools.ts +290 -0
  688. package/src/runtime/control-plane/goal-loop/policy-gates.ts +203 -0
  689. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +93 -0
  690. package/src/runtime/control-plane/goal-loop/provider-registry.ts +545 -0
  691. package/src/runtime/control-plane/goal-loop/types.ts +378 -0
  692. package/src/runtime/control-plane/governance/access-policy.ts +147 -0
  693. package/src/runtime/control-plane/governance/authorization.ts +195 -0
  694. package/src/runtime/control-plane/governance/external-effects.ts +43 -0
  695. package/src/runtime/control-plane/launcher/thin-launcher.ts +97 -0
  696. package/src/runtime/control-plane/persistence/metadata-payload-policy.ts +53 -0
  697. package/src/runtime/control-plane/persistence/requirement-store.ts +241 -0
  698. package/src/runtime/control-plane/persistence/sqlite-store.ts +539 -0
  699. package/src/runtime/control-plane/release-retention.ts +447 -0
  700. package/src/runtime/control-plane/repo-actor/actor.ts +181 -0
  701. package/src/runtime/control-plane/repo-actor/registry.ts +20 -0
  702. package/src/runtime/control-plane/routing/index.ts +1 -0
  703. package/src/runtime/control-plane/routing/route-policy.ts +379 -0
  704. package/src/runtime/control-plane/runtime-cleanup.ts +727 -0
  705. package/src/runtime/control-plane/runtime-generation.ts +509 -0
  706. package/src/runtime/control-plane/runtime-pid-ownership.ts +11 -0
  707. package/src/runtime/control-plane/runtime-status-client.ts +90 -0
  708. package/src/runtime/control-plane/startup-recovery.ts +198 -0
  709. package/src/runtime/diagnostics/connectivity-observability.ts +475 -0
  710. package/src/runtime/diagnostics/mcp-timing.ts +64 -0
  711. package/src/runtime/diagnostics/performance.ts +379 -0
  712. package/src/runtime/diagnostics/process-facade.ts +350 -0
  713. package/src/runtime/diagnostics/read-only-tool.ts +226 -0
  714. package/src/runtime/evidence/artifact-store.ts +154 -0
  715. package/src/runtime/evidence/event-ledger.ts +80 -0
  716. package/src/runtime/evidence/evidence-store.ts +68 -0
  717. package/src/runtime/evidence/process-check-receipt.ts +43 -0
  718. package/src/runtime/evidence/result-store.ts +209 -0
  719. package/src/runtime/evidence/sensitive-output.ts +353 -0
  720. package/src/runtime/execution/jobs/child-reference.ts +124 -0
  721. package/src/runtime/execution/jobs/legacy-adapter.ts +22 -0
  722. package/src/runtime/execution/jobs/receipt-recovery.ts +119 -0
  723. package/src/runtime/execution/jobs/receipt-store.ts +243 -0
  724. package/src/runtime/execution/jobs/store.ts +737 -0
  725. package/src/runtime/execution/jobs/timeouts.ts +109 -0
  726. package/src/runtime/execution/jobs/types.ts +230 -0
  727. package/src/runtime/execution/jobs/wait.ts +45 -0
  728. package/src/runtime/execution/process-runtime/canary.ts +26 -0
  729. package/src/runtime/execution/process-runtime/check-facade.ts +272 -0
  730. package/src/runtime/execution/process-runtime/check-receipt.ts +185 -0
  731. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +60 -0
  732. package/src/runtime/execution/process-runtime/command-facade.ts +321 -0
  733. package/src/runtime/execution/process-runtime/gc.ts +171 -0
  734. package/src/runtime/execution/process-runtime/index.ts +9 -0
  735. package/src/runtime/execution/process-runtime/interactive-admission.ts +23 -0
  736. package/src/runtime/execution/process-runtime/process-runner-entry.ts +466 -0
  737. package/src/runtime/execution/process-runtime/resource-claims.ts +440 -0
  738. package/src/runtime/execution/process-runtime/runtime.ts +1816 -0
  739. package/src/runtime/execution/process-runtime/store.ts +687 -0
  740. package/src/runtime/execution/process-runtime/types.ts +352 -0
  741. package/src/runtime/execution/thin-harness/async-process.ts +181 -0
  742. package/src/runtime/execution/thin-harness/batch-executor.ts +671 -0
  743. package/src/runtime/execution/thin-harness/execution-router.ts +700 -0
  744. package/src/runtime/execution/thin-harness/fast-executor.ts +1263 -0
  745. package/src/runtime/execution/thin-harness/fast-receipt.ts +235 -0
  746. package/src/runtime/execution/thin-harness/fingerprint-worker.ts +191 -0
  747. package/src/runtime/execution/thin-harness/index.ts +13 -0
  748. package/src/runtime/execution/thin-harness/latency-trace.ts +107 -0
  749. package/src/runtime/execution/thin-harness/lightweight-lanes.ts +745 -0
  750. package/src/runtime/execution/thin-harness/mutation-gate.ts +578 -0
  751. package/src/runtime/execution/thin-harness/proposal-store.ts +409 -0
  752. package/src/runtime/execution/thin-harness/request-ledger.ts +595 -0
  753. package/src/runtime/execution/thin-harness/search-worker.ts +114 -0
  754. package/src/runtime/execution/thin-harness/types.ts +313 -0
  755. package/src/runtime/execution/thin-harness/workspace-savepoint.ts +333 -0
  756. package/src/runtime/execution/workers/executor.ts +687 -0
  757. package/src/runtime/execution/workers/ownership.ts +94 -0
  758. package/src/runtime/execution/workers/worker-entry.ts +277 -0
  759. package/src/runtime/gateway/mcp/execution-tools.ts +2009 -0
  760. package/src/runtime/gateway/mcp/persisted-check-process.ts +255 -0
  761. package/src/runtime/gateway/mcp/process-tools.ts +230 -0
  762. package/src/runtime/gateway/mcp/resource-policy.ts +195 -0
  763. package/src/runtime/gateway/mcp/router.ts +1088 -0
  764. package/src/runtime/gateway/mcp/runtime-tools.ts +5994 -0
  765. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +219 -0
  766. package/src/runtime/health/evaluator.ts +474 -0
  767. package/src/runtime/health/index.ts +2 -0
  768. package/src/runtime/health/operational-view.ts +198 -0
  769. package/src/runtime/maintenance/cleanup.ts +419 -0
  770. package/src/runtime/model-clients/deepseek-adapter.ts +312 -0
  771. package/src/runtime/model-clients/index.ts +2 -0
  772. package/src/runtime/model-clients/types.ts +101 -0
  773. package/src/runtime/observability/gateway-contention-metrics.ts +129 -0
  774. package/src/runtime/observability/route-session-benchmark.ts +110 -0
  775. package/src/runtime/personal-assistant/gmail-triage-manager.ts +231 -0
  776. package/src/runtime/personal-assistant/reporting-runtime.ts +344 -0
  777. package/src/runtime/personal-assistant/triage-runtime.ts +748 -0
  778. package/src/runtime/plugins/app-store-connect-adapter.ts +868 -0
  779. package/src/runtime/plugins/browser-adapter.ts +2812 -0
  780. package/src/runtime/plugins/browser-automation-service.ts +46 -0
  781. package/src/runtime/plugins/browser-handoff-host.ts +304 -0
  782. package/src/runtime/plugins/browser-handoff.ts +371 -0
  783. package/src/runtime/plugins/browser-macos-bridge.ts +865 -0
  784. package/src/runtime/plugins/browser-node-bridge-host.ts +64 -0
  785. package/src/runtime/plugins/browser-node-bridge.ts +249 -0
  786. package/src/runtime/plugins/desktop-adapter.ts +374 -0
  787. package/src/runtime/plugins/desktop-operator-registration.ts +418 -0
  788. package/src/runtime/plugins/errors.ts +39 -0
  789. package/src/runtime/plugins/execution-origin.ts +26 -0
  790. package/src/runtime/plugins/external-adapter.ts +322 -0
  791. package/src/runtime/plugins/external-registration.ts +308 -0
  792. package/src/runtime/plugins/external-unix-socket-probe.cjs +79 -0
  793. package/src/runtime/plugins/external-unix-socket.ts +238 -0
  794. package/src/runtime/plugins/first-party-registry.ts +37 -0
  795. package/src/runtime/plugins/github-adapter.ts +266 -0
  796. package/src/runtime/plugins/gmail-adapter.ts +729 -0
  797. package/src/runtime/plugins/google-calendar-adapter.ts +468 -0
  798. package/src/runtime/plugins/google-shared.ts +578 -0
  799. package/src/runtime/plugins/google-tasks-adapter.ts +535 -0
  800. package/src/runtime/plugins/interaction-session.ts +252 -0
  801. package/src/runtime/plugins/ios/agent-device-capabilities.ts +258 -0
  802. package/src/runtime/plugins/ios/agent-device-failures.ts +95 -0
  803. package/src/runtime/plugins/ios/agent-device-provider.ts +63 -0
  804. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +268 -0
  805. package/src/runtime/plugins/ios/app-adapters.ts +122 -0
  806. package/src/runtime/plugins/ios-adapter.ts +509 -0
  807. package/src/runtime/plugins/ios-agent-device.ts +2311 -0
  808. package/src/runtime/plugins/ios-physical-device.ts +955 -0
  809. package/src/runtime/plugins/local-system-adapter.ts +1016 -0
  810. package/src/runtime/plugins/macos-capability-broker.ts +73 -0
  811. package/src/runtime/plugins/managed-process-adapter.ts +366 -0
  812. package/src/runtime/plugins/resend-adapter.ts +506 -0
  813. package/src/runtime/plugins/store.ts +1025 -0
  814. package/src/runtime/plugins/types.ts +138 -0
  815. package/src/runtime/projections/controller-context.ts +596 -0
  816. package/src/runtime/projections/git-status-sampler.ts +84 -0
  817. package/src/runtime/projections/invalidation.ts +250 -0
  818. package/src/runtime/projections/materialized-view.ts +694 -0
  819. package/src/runtime/recovery/actions.ts +158 -0
  820. package/src/runtime/recovery/audit.ts +38 -0
  821. package/src/runtime/recovery/classifier.ts +163 -0
  822. package/src/runtime/recovery/index.ts +9 -0
  823. package/src/runtime/recovery/local-jobs-repair.ts +393 -0
  824. package/src/runtime/recovery/maintenance-executor.ts +822 -0
  825. package/src/runtime/recovery/patch-handoff.ts +50 -0
  826. package/src/runtime/recovery/probe.ts +308 -0
  827. package/src/runtime/recovery/store.ts +35 -0
  828. package/src/runtime/recovery/types.ts +175 -0
  829. package/src/runtime/release/identity.ts +101 -0
  830. package/src/runtime/release/release-gate.ts +137 -0
  831. package/src/runtime/resources/claims/conflicts.ts +205 -0
  832. package/src/runtime/resources/index.ts +1 -0
  833. package/src/runtime/resources/leases/store.ts +539 -0
  834. package/src/runtime/resources/leases/types.ts +50 -0
  835. package/src/runtime/resources/ownership.ts +72 -0
  836. package/src/runtime/root/controller-services.ts +42 -0
  837. package/src/runtime/root/entry.ts +70 -0
  838. package/src/runtime/root/gateway-adapter.ts +46 -0
  839. package/src/runtime/root/local-bridge.ts +72 -0
  840. package/src/runtime/root/mcp-transport.ts +198 -0
  841. package/src/runtime/root/ownership.ts +94 -0
  842. package/src/runtime/root/readiness.ts +69 -0
  843. package/src/runtime/root/release-manifest.ts +147 -0
  844. package/src/runtime/root/release-materialize.ts +509 -0
  845. package/src/runtime/root/release-store.ts +236 -0
  846. package/src/runtime/root/runtime.ts +318 -0
  847. package/src/runtime/root/scheduler.ts +58 -0
  848. package/src/runtime/root/service-runner-entry.ts +20 -0
  849. package/src/runtime/root/service-runner.ts +69 -0
  850. package/src/runtime/root/service.ts +291 -0
  851. package/src/runtime/root/status.ts +127 -0
  852. package/src/runtime/root/types.ts +112 -0
  853. package/src/runtime/root/write-fence.ts +309 -0
  854. package/src/runtime/safe-tooling/browser-safe.ts +133 -0
  855. package/src/runtime/safe-tooling/external-filesystem.ts +304 -0
  856. package/src/runtime/safe-tooling/google-credential-store.ts +95 -0
  857. package/src/runtime/safe-tooling/google-oauth-broker.ts +286 -0
  858. package/src/runtime/safe-tooling/index.ts +11 -0
  859. package/src/runtime/safe-tooling/ios-development.ts +1039 -0
  860. package/src/runtime/safe-tooling/job-summary.ts +361 -0
  861. package/src/runtime/safe-tooling/plugin-summary.ts +53 -0
  862. package/src/runtime/safe-tooling/resend-oauth.ts +387 -0
  863. package/src/runtime/safe-tooling/review-artifacts.ts +133 -0
  864. package/src/runtime/safe-tooling/types.ts +103 -0
  865. package/src/runtime/safe-tooling/workspace-auth.ts +116 -0
  866. package/src/runtime/shared/bounded-child-supervisor.ts +235 -0
  867. package/src/runtime/shared/idempotent-retry.ts +128 -0
  868. package/src/runtime/shared/json-files.ts +43 -0
  869. package/src/runtime/shared/local-bridge-surface.ts +231 -0
  870. package/src/runtime/shared/managed-env.ts +154 -0
  871. package/src/runtime/shared/node-ts-loader.mjs +50 -0
  872. package/src/runtime/shared/process-environment.ts +97 -0
  873. package/src/runtime/shared/process-identity.ts +72 -0
  874. package/src/runtime/shared/process-tree.ts +227 -0
  875. package/src/runtime/shared/response-budget.ts +246 -0
  876. package/src/runtime/standalone-recovery/core.ts +2275 -0
  877. package/src/runtime/standalone-recovery/entry.ts +686 -0
  878. package/src/runtime/standalone-recovery/http-transport.ts +317 -0
  879. package/src/runtime/standalone-recovery/installer.ts +747 -0
  880. package/src/runtime/standalone-recovery/release.ts +241 -0
  881. package/src/runtime/watchdog/workflow-watchdog.ts +118 -0
  882. package/src/runtime/workflow/campaigns/cleanup.ts +263 -0
  883. package/src/runtime/workflow/campaigns/engine.ts +112 -0
  884. package/src/runtime/workflow/campaigns/normalize.ts +100 -0
  885. package/src/runtime/workflow/campaigns/review.ts +315 -0
  886. package/src/runtime/workflow/campaigns/store.ts +526 -0
  887. package/src/runtime/workflow/campaigns/supervisor.ts +112 -0
  888. package/src/runtime/workflow/campaigns/types.ts +290 -0
  889. package/src/runtime/workflow/campaigns/workspace-agent.ts +155 -0
  890. package/src/runtime/workflow/campaigns/workspace.ts +200 -0
  891. package/src/runtime/workflow/findings/store.ts +165 -0
  892. package/src/runtime/workflow/findings/types.ts +36 -0
  893. package/src/runtime/workflow/portfolio/engine.ts +20 -0
  894. package/src/runtime/workflow/portfolio/store.ts +185 -0
  895. package/src/runtime/workflow/portfolio/types.ts +29 -0
  896. package/src/runtime/workflow/schedules/engine.ts +620 -0
  897. package/src/runtime/workflow/schedules/settlement.ts +144 -0
  898. package/src/runtime/workflow/schedules/store.ts +417 -0
  899. package/src/runtime/workflow/schedules/types.ts +124 -0
  900. package/src/runtime/workspace-targets/index.ts +24 -0
  901. package/src/runtime/workspace-targets/target-grant.ts +721 -0
  902. package/src/testing/test-governance.ts +626 -0
  903. package/src/version.ts +21 -0
@@ -0,0 +1,1877 @@
1
+ import type { RepositoryRecord } from '../repositories/types';
2
+ import {
3
+ accessModeDescriptor,
4
+ isAccessMode,
5
+ readRepositoryAccessPolicy,
6
+ writeRepositoryAccessPolicy,
7
+ type AccessMode,
8
+ } from '../../runtime/control-plane/governance/access-policy';
9
+ import {
10
+ persistControllerAccessMode,
11
+ resolveControllerAccessState,
12
+ } from '../mcp/access-mode';
13
+ import { listControllerChecks, runControllerCheck } from '../controller/check-runner';
14
+ import { createMcpToolContext, type MultiRepositoryMcpToolContext } from '../mcp/multi-repository';
15
+ import { readSchedulerHealthSnapshot } from '../../runtime/control-plane/global-scheduler/scheduler';
16
+ import { observeRuntimeStatus } from '../../runtime/root/status';
17
+ import { readForgeRuntimeStatus } from '../../runtime/control-plane/runtime-status-client';
18
+ import { evaluateActiveRuntimeSourceDrift, readRuntimeGeneration } from '../../runtime/control-plane/runtime-generation';
19
+ import {
20
+ getAssistantPluginManifest,
21
+ listAssistantPluginManifests,
22
+ } from '../../runtime/plugins/store';
23
+ import type { AssistantPluginManifest } from '../../runtime/plugins/types';
24
+ import {
25
+ acknowledgeHandoffItem,
26
+ buildFacadeResult,
27
+ buildSyncOperationDigest,
28
+ classifyUserFacingError,
29
+ classifyVerificationOutcome,
30
+ continueGoalWorkloop,
31
+ delegateToCodexCerebellum,
32
+ dismissHandoffItem,
33
+ finalizeGoalWorkloop,
34
+ getHandoffItem,
35
+ getWorkContract,
36
+ listHandoffItems,
37
+ listWorkContracts,
38
+ normalizeCheckIds,
39
+ resolveHandoffItem,
40
+ routeWorkStart,
41
+ runSelfHealingLoop,
42
+ selectExecutionMode,
43
+ stopGoalWorkloop,
44
+ summarizeHandoffItem,
45
+ summarizeWorkContract,
46
+ verifyGoalWorkloop,
47
+ type ExecutionModeSelectionInput,
48
+ type FacadeResult,
49
+ type HandoffItem,
50
+ type SuggestedNextAction,
51
+ type WorkContract,
52
+ } from '../../runtime/control-plane/facade';
53
+ import { buildRuntimeMaintenanceStatus } from '../../runtime/recovery';
54
+ import { applySafePatch } from '../repositories/safe-patch';
55
+ import { withControllerLock } from '../repositories/locks';
56
+ import { loadMcpServiceLocalConfig, loadMcpServiceRuntimeState } from '../mcp/auth';
57
+ import {
58
+ buildLocalConnectorStatusForRepo,
59
+ EXPECTED_FACADE_TOOLS,
60
+ type ConnectorFreshnessReport,
61
+ } from './connector-freshness';
62
+ import { controllerExposedToolNames } from '../mcp/toolset';
63
+ import {
64
+ statusLabelForPhase,
65
+ type UserFacingErrorClass,
66
+ type UserFacingPhase,
67
+ } from '../../runtime/control-plane/facade/operation-digest';
68
+ import { repositoryGitStatus } from '../repositories/structured-git';
69
+ import { listExecutionJobs } from '../../runtime/execution/jobs/store';
70
+ import {
71
+ createConnectivityObservation,
72
+ deriveExecutionJobTimingSnapshots,
73
+ recordConnectivityProbe,
74
+ type ConnectivityComponentObservation,
75
+ } from '../../runtime/diagnostics/connectivity-observability';
76
+ import type {
77
+ ChangedFileEntryViewModel,
78
+ ChangedFilesSummaryViewModel,
79
+ CommandCenterViewModel,
80
+ ConnectorFreshnessViewModel,
81
+ ConsoleErrorViewModel,
82
+ GoalLoopStatusViewModel,
83
+ HandoffCardViewModel,
84
+ ModePreviewViewModel,
85
+ PlainStatusTone,
86
+ PluginActionViewModel,
87
+ AccessStateViewModel,
88
+ PluginCardViewModel,
89
+ PluginSummaryViewModel,
90
+ RepositoryCardViewModel,
91
+ SuggestedActionViewModel,
92
+ SystemReadinessViewModel,
93
+ VerificationViewModel,
94
+ WorkSummaryViewModel,
95
+ } from './console-view-models';
96
+ import {
97
+ buildAutomationSettingsView,
98
+ executorRoutePreviewWithConfig,
99
+ executorRoutingConfigGet,
100
+ executorRoutingConfigReset,
101
+ executorRoutingConfigUpdate,
102
+ goalLoopPolicyGet,
103
+ goalLoopPolicyUpdate,
104
+ goalStatus as goalLoopStatusSnapshot,
105
+ localToolDisable,
106
+ localToolEnable,
107
+ localToolHealthCheck,
108
+ localToolList,
109
+ localToolConfigGet,
110
+ localToolConfigUpdate,
111
+ providerApiSettingsGet,
112
+ providerApiSettingsUpdate,
113
+ providerConfigGet,
114
+ providerConfigUpdate,
115
+ providerCredentialsStatus,
116
+ providerDisable,
117
+ providerEnable,
118
+ providerHealthCheck,
119
+ providerPriorityUpdate,
120
+ providerResetDefaults,
121
+ type ConfigFacadeContext,
122
+ type GoalLoopContext,
123
+ } from '../../runtime/control-plane/goal-loop';
124
+ import { listActiveOccurrences, listSchedules } from '../../runtime/workflow/schedules/store';
125
+
126
+ export type ConsoleFacadeContext = {
127
+ controllerHome: string;
128
+ repository: RepositoryRecord;
129
+ };
130
+
131
+ function store(ctx: ConsoleFacadeContext) {
132
+ return { controllerHome: ctx.controllerHome, repoId: ctx.repository.repoId };
133
+ }
134
+
135
+ function plainMode(mode: ModePreviewViewModel['mode']): ModePreviewViewModel {
136
+ if (mode === 'direct_control') {
137
+ return {
138
+ mode,
139
+ label: '直接执行',
140
+ explanation: '小范围、目标清晰的改动;适合由 ChatGPT 直接监督完成。',
141
+ createWorkContract: false,
142
+ createHandoff: false,
143
+ };
144
+ }
145
+ if (mode === 'handoff_only') {
146
+ return {
147
+ mode,
148
+ label: '需要你先决定',
149
+ explanation: '目标不清、风险较高或缺少授权;执行前需要你的判断。',
150
+ createWorkContract: false,
151
+ createHandoff: true,
152
+ };
153
+ }
154
+ return {
155
+ mode: 'goal_workloop',
156
+ label: '可恢复的后台任务',
157
+ explanation: '多步骤或需要恢复/隔离的任务;会创建工作项并支持继续、验证与收尾。',
158
+ createWorkContract: true,
159
+ createHandoff: false,
160
+ };
161
+ }
162
+
163
+ function mapSuggested(actions: SuggestedNextAction[] = []): SuggestedActionViewModel[] {
164
+ return actions.slice(0, 8).map((action, index) => {
165
+ const op = action.operation;
166
+ const kind: SuggestedActionViewModel['kind'] =
167
+ op === 'continue' ? 'continue'
168
+ : op === 'verify' ? 'verify'
169
+ : op === 'finalize' ? 'finalize'
170
+ : op === 'stop' ? 'stop'
171
+ : op === 'delegate' ? 'delegate'
172
+ : op === 'repair' || op === 'diagnose' ? 'repair'
173
+ : op === 'resolve' ? 'resolve'
174
+ : op === 'dismiss' ? 'dismiss'
175
+ : op === 'get' || op === 'list' ? 'open'
176
+ : 'other';
177
+ return {
178
+ id: `${action.tool}-${op}-${index}`,
179
+ label: action.label,
180
+ kind,
181
+ tool: action.tool,
182
+ operation: action.operation,
183
+ payload: action.payload,
184
+ primary: index === 0,
185
+ };
186
+ });
187
+ }
188
+
189
+ function workStatusLabel(status: WorkContract['status']): { label: string; tone: PlainStatusTone; phase: UserFacingPhase } {
190
+ switch (status) {
191
+ case 'running': return { label: '进行中', tone: 'blue', phase: 'running' };
192
+ case 'open': return { label: '待开始', tone: 'gray', phase: 'queued' };
193
+ case 'blocked': return { label: '已阻塞', tone: 'red', phase: 'blocked' };
194
+ case 'ready': return { label: '需要你审阅', tone: 'amber', phase: 'needs_attention' };
195
+ case 'completed': return { label: '已完成', tone: 'green', phase: 'succeeded' };
196
+ case 'failed': return { label: '未通过验收', tone: 'red', phase: 'failed' };
197
+ case 'cancelled': return { label: '已停止', tone: 'gray', phase: 'cancelled' };
198
+ default: return { label: String(status), tone: 'gray', phase: 'running' };
199
+ }
200
+ }
201
+
202
+ const ERROR_COPY: Record<UserFacingErrorClass, { title: string; explanation: string; nextActions: string[] }> = {
203
+ controller_unavailable: {
204
+ title: '控制器暂不可用',
205
+ explanation: '后台控制器未就绪,暂时无法执行任务。',
206
+ nextActions: ['查看系统状态', '运行诊断', '重启 controller'],
207
+ },
208
+ connector_stale: {
209
+ title: '连接器可能过期',
210
+ explanation: '本地 MCP 与 ChatGPT 工具快照可能不一致;若 ChatGPT 里看不到 rh_* 请重连。',
211
+ nextActions: ['查看连接器诊断', '重启 MCP', '重连 ChatGPT Connector'],
212
+ },
213
+ infrastructure_failure: {
214
+ title: '环境/基础设施问题',
215
+ explanation: '失败来自运行环境(进程、存储、检查命令),不是业务验收本身。',
216
+ nextActions: ['运行修复诊断', '查看高级诊断', '稍后重试'],
217
+ },
218
+ acceptance_failure: {
219
+ title: '验收未通过',
220
+ explanation: '检查已运行,但结果不符合验收标准。',
221
+ nextActions: ['查看失败原因', '继续修改', '再次验证'],
222
+ },
223
+ invalid_check_id: {
224
+ title: '检查项无效',
225
+ explanation: '指定的检查 ID 未注册或不存在。',
226
+ nextActions: ['选择有效检查', '查看系统状态'],
227
+ },
228
+ approval_required: {
229
+ title: '需要你授权',
230
+ explanation: '该操作风险较高,需明确授权后才能继续。',
231
+ nextActions: ['查看待决定', '确认后继续'],
232
+ },
233
+ handoff_required: {
234
+ title: '需要你做决定',
235
+ explanation: '任务停在决策点,不能自动往下走。',
236
+ nextActions: ['打开待决定', '记录决定后继续'],
237
+ },
238
+ timeout: {
239
+ title: '执行超时',
240
+ explanation: '操作在限定时间内未完成。',
241
+ nextActions: ['重试', '运行诊断', '查看高级详情'],
242
+ },
243
+ policy_denied: {
244
+ title: '策略拒绝',
245
+ explanation: '当前策略不允许该操作。',
246
+ nextActions: ['调整范围', '查看高级诊断'],
247
+ },
248
+ not_found: {
249
+ title: '未找到目标',
250
+ explanation: '任务、仓库或资源不存在或已清理。',
251
+ nextActions: ['刷新控制台', '重新选择仓库或任务'],
252
+ },
253
+ unknown_failure: {
254
+ title: '出现未知错误',
255
+ explanation: '操作失败,但尚未归类到具体原因。',
256
+ nextActions: ['重试', '运行诊断', '查看高级详情'],
257
+ },
258
+ };
259
+
260
+ export function describeConsoleError(
261
+ errorClass: UserFacingErrorClass,
262
+ detail?: string,
263
+ ): ConsoleErrorViewModel {
264
+ const copy = ERROR_COPY[errorClass] ?? ERROR_COPY.unknown_failure;
265
+ const explanation = detail && detail.trim() && detail.trim() !== 'undefined'
266
+ ? `${copy.explanation} ${detail.trim().slice(0, 180)}`
267
+ : copy.explanation;
268
+ return {
269
+ errorClass,
270
+ title: copy.title,
271
+ explanation,
272
+ nextActions: copy.nextActions,
273
+ };
274
+ }
275
+
276
+ export function summarizeChangedFiles(paths: readonly string[]): ChangedFilesSummaryViewModel | undefined {
277
+ const unique = [...new Set(paths.map((entry) => String(entry || '').trim()).filter(Boolean))].slice(0, 40);
278
+ if (!unique.length) return undefined;
279
+ const files: ChangedFileEntryViewModel[] = unique.map((path) => {
280
+ let status: ChangedFileEntryViewModel['status'] = 'modified';
281
+ if (path.startsWith('A ') || path.includes('(new)') || path.startsWith('+')) status = 'added';
282
+ else if (path.startsWith('D ') || path.includes('(deleted)') || path.startsWith('-')) status = 'deleted';
283
+ else if (path.startsWith('R ') || path.includes(' -> ')) status = 'renamed';
284
+ const clean = path.replace(/^[AMD?R]\s+/, '').replace(/^\+/, '').replace(/^-/, '').trim();
285
+ return {
286
+ path: clean || path,
287
+ status,
288
+ statusLabel: status === 'added' ? '新增' : status === 'deleted' ? '删除' : status === 'renamed' ? '重命名' : '修改',
289
+ };
290
+ });
291
+ const added = files.filter((entry) => entry.status === 'added').length;
292
+ const deleted = files.filter((entry) => entry.status === 'deleted').length;
293
+ const modified = files.length - added - deleted;
294
+ const parts: string[] = [];
295
+ if (modified) parts.push(`修改 ${modified}`);
296
+ if (added) parts.push(`新增 ${added}`);
297
+ if (deleted) parts.push(`删除 ${deleted}`);
298
+ if (!parts.length) parts.push(`${files.length} 个文件`);
299
+ return {
300
+ total: files.length,
301
+ modified,
302
+ added,
303
+ deleted,
304
+ files,
305
+ summaryLabel: parts.join(' · '),
306
+ };
307
+ }
308
+
309
+ function extractChangedPathsFromWork(
310
+ work: WorkContract,
311
+ opts: { controllerHome: string; repoId: string } | null = null,
312
+ ): string[] {
313
+ const paths: string[] = [];
314
+ for (const evidence of work.evidenceRefs ?? []) {
315
+ const blob = `${evidence.title ?? ''} ${evidence.summary ?? ''}`;
316
+ for (const match of blob.matchAll(/(?:^|[\s`])([A-Za-z0-9_./-]+\.[A-Za-z0-9]+)(?:$|[\s`,])/g)) {
317
+ if (match[1] && !match[1].startsWith('http')) paths.push(match[1]);
318
+ }
319
+ }
320
+ if (opts) {
321
+ for (const handoffId of work.handoffRefs ?? []) {
322
+ try {
323
+ const item = getHandoffItem({ controllerHome: opts.controllerHome, repoId: opts.repoId }, handoffId);
324
+ for (const path of item?.currentState?.changedFiles ?? []) {
325
+ if (path) paths.push(path);
326
+ }
327
+ } catch {
328
+ // ignore missing handoffs
329
+ }
330
+ }
331
+ }
332
+ return paths;
333
+ }
334
+
335
+ function progressSteps(work: WorkContract): WorkSummaryViewModel['progressSteps'] {
336
+ const checks = work.checkRefs ?? [];
337
+ const hasPass = checks.some((entry) => entry.outcome === 'valid_pass');
338
+ const hasFail = checks.some((entry) => entry.outcome === 'valid_fail');
339
+ const hasDelegate = Boolean(work.workerRef);
340
+ const review = work.status === 'ready';
341
+ const done = work.status === 'completed';
342
+ const failed = work.status === 'failed';
343
+ const cancelled = work.status === 'cancelled';
344
+ return [
345
+ { label: '已开始', done: true, active: work.status === 'running' && !hasDelegate && !hasPass },
346
+ { label: '已委派助手', done: hasDelegate, active: hasDelegate && !hasPass && !review },
347
+ { label: '已提出修改', done: hasDelegate || hasPass || hasFail, active: false },
348
+ { label: '已验证', done: hasPass || hasFail, active: hasFail && !review },
349
+ { label: '等待审阅', done: review || done, active: review },
350
+ { label: done ? '已完成' : failed ? '验收失败' : cancelled ? '已停止' : '收尾', done: done || failed || cancelled, active: false },
351
+ ];
352
+ }
353
+
354
+ function latestVerification(work: WorkContract): VerificationViewModel | undefined {
355
+ const record = work.checkRefs[0];
356
+ if (!record) return undefined;
357
+ const isAcceptanceFailure = record.outcome === 'valid_fail';
358
+ const isInfrastructureIssue = record.outcome === 'infrastructure_failure' || record.outcome === 'invalid_check_id';
359
+ const tone: PlainStatusTone = record.outcome === 'valid_pass' ? 'green'
360
+ : isAcceptanceFailure ? 'red'
361
+ : isInfrastructureIssue ? 'amber'
362
+ : 'gray';
363
+ const label = record.outcome === 'valid_pass' ? '检查通过'
364
+ : record.outcome === 'valid_fail' ? '检查未通过'
365
+ : record.outcome === 'infrastructure_failure' ? '环境问题'
366
+ : record.outcome === 'invalid_check_id' ? '检查项无效'
367
+ : record.outcome === 'superseded' ? '已被后续结果覆盖'
368
+ : '已跳过';
369
+ return {
370
+ label,
371
+ tone,
372
+ outcome: record.outcome,
373
+ checkLabel: record.checkId,
374
+ isAcceptanceFailure,
375
+ isInfrastructureIssue,
376
+ summary: record.summary,
377
+ };
378
+ }
379
+
380
+ export function mapWorkSummary(
381
+ work: WorkContract,
382
+ opts: { controllerHome?: string; repoId?: string } = {},
383
+ ): WorkSummaryViewModel {
384
+ const status = workStatusLabel(work.status);
385
+ const mode = plainMode(work.mode === 'direct_control' || work.mode === 'handoff_only' ? work.mode : 'goal_workloop');
386
+ const verification = latestVerification(work);
387
+ const suggested = mapSuggested(work.suggestedNextActions);
388
+ const nextAction = suggested[0]?.label
389
+ ?? (work.status === 'ready' ? '请审阅后继续或收尾'
390
+ : work.status === 'running' ? '继续或运行检查'
391
+ : work.status === 'completed' ? '任务已完成'
392
+ : work.status === 'failed' ? '查看失败原因并决定是否重试'
393
+ : '查看任务状态');
394
+ const storeOpts = opts.controllerHome && opts.repoId
395
+ ? { controllerHome: opts.controllerHome, repoId: opts.repoId }
396
+ : null;
397
+ const changedFiles = summarizeChangedFiles(extractChangedPathsFromWork(work, storeOpts));
398
+ let error: ConsoleErrorViewModel | undefined;
399
+ if (work.status === 'failed') {
400
+ error = describeConsoleError(
401
+ verification?.isInfrastructureIssue ? 'infrastructure_failure' : 'acceptance_failure',
402
+ verification?.summary,
403
+ );
404
+ } else if (work.status === 'blocked' || work.status === 'ready') {
405
+ error = describeConsoleError(
406
+ 'handoff_required',
407
+ work.suggestedNextActions[0]?.reason,
408
+ );
409
+ }
410
+ const latestSummary = verification?.summary
411
+ || work.evidenceRefs[0]?.summary
412
+ || work.evidenceRefs[0]?.title
413
+ || (work.status === 'running' ? '任务执行中…' : status.label);
414
+ return {
415
+ id: work.workId,
416
+ title: work.objective.slice(0, 160) || '未命名任务',
417
+ objective: work.objective,
418
+ modeLabel: mode.label,
419
+ mode: mode.mode,
420
+ accessMode: work.constraints.accessMode ?? 'full_access',
421
+ accessModeLabel: accessModeDescriptor(work.constraints.accessMode ?? 'full_access').shortLabel,
422
+ statusLabel: status.label,
423
+ tone: status.tone,
424
+ phase: status.phase,
425
+ phaseLabel: statusLabelForPhase(status.phase),
426
+ nextAction,
427
+ latestAction: suggested[0]?.label || nextAction,
428
+ latestSummary,
429
+ progressSteps: progressSteps(work),
430
+ latestVerification: verification,
431
+ acceptanceCriteria: work.acceptanceCriteria.slice(0, 8),
432
+ evidenceLabels: work.evidenceRefs.slice(0, 6).map((entry) => entry.title || entry.summary || '证据'),
433
+ changedFiles,
434
+ error,
435
+ delegateSummary: work.workerRef
436
+ ? `已委派小助手(${work.workerRef.split(':')[0] ?? 'worker'}),等待 ChatGPT 审阅后才能收尾。`
437
+ : undefined,
438
+ suggestedActions: suggested,
439
+ primaryActionLabel: suggested[0]?.label,
440
+ advanced: {
441
+ workId: work.workId,
442
+ status: work.status,
443
+ checkIds: work.checks,
444
+ handoffRefs: work.handoffRefs,
445
+ },
446
+ };
447
+ }
448
+
449
+ function handoffTone(severity: HandoffItem['severity']): PlainStatusTone {
450
+ if (severity === 'blocked' || severity === 'failed') return 'red';
451
+ if (severity === 'needs_review') return 'amber';
452
+ if (severity === 'ready_to_continue') return 'green';
453
+ return 'blue';
454
+ }
455
+
456
+ function describeHandoffDecision(item: HandoffItem): HandoffCardViewModel['decision'] {
457
+ const reason = item.creationReason;
458
+ const isApproval = reason === 'policy_approval_required' || reason === 'destructive_action_requires_confirmation';
459
+ const approvalAction = item.approvalAction;
460
+ const canApproveAndContinue = isApproval
461
+ && (item.status === 'pending' || item.status === 'acknowledged')
462
+ && approvalAction?.operation === 'start';
463
+ if (isApproval) {
464
+ const destructive = reason === 'destructive_action_requires_confirmation' || approvalAction?.risk === 'destructive';
465
+ const payload = approvalAction?.payload ?? {};
466
+ const objective = typeof payload.objective === 'string' ? payload.objective.trim() : '';
467
+ const allowedPaths = Array.isArray(payload.allowedPaths) ? payload.allowedPaths.map(String).filter(Boolean) : [];
468
+ const expectedFiles = typeof payload.expectedFiles === 'number' ? payload.expectedFiles : undefined;
469
+ const scopeSummary = allowedPaths.length
470
+ ? `允许修改:${allowedPaths.slice(0, 4).join('、')}${allowedPaths.length > 4 ? ' 等' : ''}`
471
+ : '修改范围仍受原任务描述和仓库策略限制';
472
+ const estimate = expectedFiles === undefined ? '' : `;预计涉及约 ${expectedFiles} 个文件`;
473
+ return {
474
+ type: 'approval',
475
+ typeLabel: destructive ? '高风险操作审批' : '执行授权',
476
+ requestedAction: approvalAction?.operation === 'start' && objective
477
+ ? `创建任务并继续处理:“${objective.slice(0, 240)}”`
478
+ : item.blockingDecision || item.summary,
479
+ necessityLabel: '必须确认',
480
+ necessityExplanation: destructive
481
+ ? '此操作包含高风险或潜在不可逆副作用,系统不能替你默认同意。'
482
+ : '当前安全策略要求显式授权;缩小范围或改为只读预览后可避免本次审批。',
483
+ impact: approvalAction?.operation === 'start'
484
+ ? `批准后会创建正式任务。${scopeSummary}${estimate}。`
485
+ : '当前审批没有保存可安全重放的具体动作,因此不能自动续跑。',
486
+ afterApproval: canApproveAndContinue
487
+ ? '系统会记录批准、立即创建任务并打开任务详情;接下来会明确显示继续、委派、验证或收尾动作,不代表任务已经完成。'
488
+ : '只会记录你的决定,不会自动执行。请按人工继续提示处理,或重新提交范围明确的任务。',
489
+ ifRejected: '相关操作保持未执行;你可以缩小范围、改成预览、补充信息或取消。',
490
+ canApproveAndContinue,
491
+ primaryActionLabel: canApproveAndContinue ? '批准并创建任务' : '仅记录决定',
492
+ };
493
+ }
494
+ if (reason === 'missing_authorization') {
495
+ return {
496
+ type: 'authorization',
497
+ typeLabel: '缺少权限',
498
+ requestedAction: item.blockingDecision || item.summary,
499
+ necessityLabel: '不是审批',
500
+ necessityExplanation: '系统缺少执行所需的账号、插件或仓库权限;单击“批准”不能补齐权限。',
501
+ impact: '补齐权限后需要重新检查任务,系统才会决定是否继续。',
502
+ afterApproval: '配置或授权完成后,返回任务页重新运行检查或继续任务。',
503
+ ifRejected: '任务保持阻塞,但不会产生额外副作用。',
504
+ canApproveAndContinue: false,
505
+ primaryActionLabel: '记录处理结果',
506
+ };
507
+ }
508
+ if (reason === 'invalid_objective') {
509
+ return {
510
+ type: 'clarification',
511
+ typeLabel: '需要补充范围',
512
+ requestedAction: item.blockingDecision || item.summary,
513
+ necessityLabel: '需要澄清',
514
+ necessityExplanation: '目标或修改边界不足,继续执行可能偏离你的真实需求。',
515
+ impact: '补充目标、允许路径和验收标准后,系统会重新选择执行方式。',
516
+ afterApproval: '此项不能通过“批准”自动解决,需要修改任务描述后重新提交。',
517
+ ifRejected: '当前请求不会执行。',
518
+ canApproveAndContinue: false,
519
+ primaryActionLabel: '记录补充说明',
520
+ };
521
+ }
522
+ return {
523
+ type: 'review',
524
+ typeLabel: '结果审阅',
525
+ requestedAction: item.blockingDecision || item.summary,
526
+ necessityLabel: '需要判断',
527
+ necessityExplanation: '系统遇到了验收失败、结果歧义或助手产出,需要你选择修复、调整范围或停止。',
528
+ impact: '你的决定会影响下一步采用修复、重新验证还是结束任务。',
529
+ afterApproval: '记录决定后,请从相关任务进入对应的修复、验证或收尾动作。',
530
+ ifRejected: '任务保持在审阅状态,不会自动继续。',
531
+ canApproveAndContinue: false,
532
+ primaryActionLabel: '记录审阅决定',
533
+ };
534
+ }
535
+
536
+ export function mapHandoffCard(item: HandoffItem): HandoffCardViewModel {
537
+ return {
538
+ id: item.id,
539
+ title: item.title,
540
+ reason: item.reason,
541
+ recommendedDecision: item.recommendedDecision,
542
+ continuationPrompt: item.recommendedContinuationPrompt || item.recommendedPrompt,
543
+ severityLabel: item.severity === 'needs_review' ? '需要判断'
544
+ : item.severity === 'blocked' ? '已阻塞'
545
+ : item.severity === 'failed' ? '失败'
546
+ : item.severity === 'ready_to_continue' ? '可继续'
547
+ : '提示',
548
+ tone: handoffTone(item.severity),
549
+ statusLabel: item.status === 'pending' ? '待处理'
550
+ : item.status === 'acknowledged' ? '已确认'
551
+ : item.status === 'resolved' ? '已解决'
552
+ : item.status === 'dismissed' ? '已忽略'
553
+ : item.status,
554
+ workTitle: item.currentState.statusSummary,
555
+ attemptedActions: item.attemptedActions ?? [],
556
+ evidenceLabels: item.evidenceRefs.map((entry) => entry.title || '证据').slice(0, 6),
557
+ suggestedActions: mapSuggested(item.suggestedNextActions),
558
+ decision: describeHandoffDecision(item),
559
+ advanced: {
560
+ handoffId: item.id,
561
+ workId: item.workId,
562
+ creationReason: item.creationReason,
563
+ },
564
+ };
565
+ }
566
+
567
+ export function previewExecutionMode(input: ExecutionModeSelectionInput & { objective?: string }): ModePreviewViewModel {
568
+ const selection = selectExecutionMode(input);
569
+ return {
570
+ ...plainMode(selection.mode),
571
+ createWorkContract: selection.createWorkContract,
572
+ createHandoff: selection.createHandoff,
573
+ explanation: selection.reason.includes('Small') || selection.reason.includes('small')
574
+ ? plainMode(selection.mode).explanation
575
+ : plainMode(selection.mode).explanation,
576
+ };
577
+ }
578
+
579
+ function displayPath(path: string): string {
580
+ const home = process.env.HOME?.trim();
581
+ if (home && path.startsWith(home)) return `~${path.slice(home.length)}`;
582
+ return path;
583
+ }
584
+
585
+ export function mapRepositoryCard(
586
+ repository: RepositoryRecord,
587
+ current: boolean,
588
+ ): RepositoryCardViewModel {
589
+ let branchLabel: string | undefined;
590
+ let dirtyLabel: string | undefined;
591
+ let readinessLabel: string | undefined;
592
+ if (current || repository.enabled !== false) {
593
+ try {
594
+ const git = repositoryGitStatus(repository);
595
+ branchLabel = git.branch || repository.defaultBranch || 'detached';
596
+ dirtyLabel = git.clean ? '工作区干净' : '有未提交变更';
597
+ readinessLabel = repository.enabled === false ? '已停用' : git.clean ? '可执行' : '可执行(脏工作区)';
598
+ } catch {
599
+ branchLabel = repository.defaultBranch || undefined;
600
+ readinessLabel = repository.enabled === false ? '已停用' : '可用';
601
+ }
602
+ }
603
+ return {
604
+ id: repository.repoId,
605
+ name: repository.displayName || repository.repoId,
606
+ path: displayPath(repository.canonicalRoot),
607
+ statusLabel: current ? '当前' : repository.enabled === false ? '已停用' : '可用',
608
+ tone: current ? 'green' : repository.enabled === false ? 'gray' : 'blue',
609
+ current,
610
+ branchLabel,
611
+ dirtyLabel,
612
+ readinessLabel,
613
+ advanced: {
614
+ repoId: repository.repoId,
615
+ remote: repository.remoteUrl,
616
+ defaultBranch: repository.defaultBranch,
617
+ checkoutId: repository.activeCheckoutId,
618
+ },
619
+ };
620
+ }
621
+
622
+ function mapConnectorFreshnessView(report: ConnectorFreshnessReport): ConnectorFreshnessViewModel {
623
+ return {
624
+ status: report.status,
625
+ severity: report.severity,
626
+ summary: report.summary,
627
+ expectedFacadeTools: report.expectedFacadeTools,
628
+ missingLocalTools: report.missingLocalTools,
629
+ missingConnectorTools: report.missingConnectorTools,
630
+ restartRecommended: report.restartRecommended,
631
+ reconnectRecommended: report.reconnectRecommended,
632
+ howToFix: report.howToFix,
633
+ suggestedActions: report.suggestedActions,
634
+ diagnosticScope: report.diagnosticScope,
635
+ localRegistryVerified: report.localRegistryVerified,
636
+ connectorCallability: report.connectorCallability,
637
+ connectorMismatch: report.connectorMismatch,
638
+ };
639
+ }
640
+
641
+ function accessToolGroups(_mode: AccessMode): string[] {
642
+ // Access mode controls approval behavior only. Tool discovery stays stable so
643
+ // changing Request/Full Access never requires an MCP reconnect.
644
+ return [
645
+ 'status_and_readiness',
646
+ 'repository_selection',
647
+ 'approval_and_handoffs',
648
+ 'access_control_plane',
649
+ 'repository_reads',
650
+ 'repository_writes_and_patches',
651
+ 'commands_and_checks',
652
+ 'git_and_branches',
653
+ 'worktrees_and_task_runs',
654
+ 'direct_edit_sessions',
655
+ 'ios_xcode_and_simulator',
656
+ 'screenshots_and_review_artifacts',
657
+ ];
658
+ }
659
+
660
+ function controllerAccessStateView(ctx: ConsoleFacadeContext): AccessStateViewModel {
661
+ const configured = resolveControllerAccessState({
662
+ controllerHome: ctx.controllerHome,
663
+ repoRoot: ctx.repository.canonicalRoot,
664
+ });
665
+ const toolCtx = createMcpToolContext({
666
+ repo: ctx.repository.canonicalRoot,
667
+ controllerHome: ctx.controllerHome,
668
+ profile: 'controller',
669
+ }) as MultiRepositoryMcpToolContext;
670
+ const effective = resolveControllerAccessState({
671
+ controllerHome: ctx.controllerHome,
672
+ repoRoot: ctx.repository.canonicalRoot,
673
+ toolsetOverride: toolCtx.toolset,
674
+ toolsetLocked: toolCtx.toolsetLocked,
675
+ });
676
+ const repositoryPolicy = readRepositoryAccessPolicy(ctx.controllerHome, ctx.repository.repoId);
677
+ const runtimeGeneration = readRuntimeGeneration(ctx.controllerHome);
678
+ // Runtime Source is controller-scoped; execution repository is not the current source.
679
+ const drift = evaluateActiveRuntimeSourceDrift(runtimeGeneration?.source);
680
+ return {
681
+ configuredAccessMode: configured.configuredAccessMode,
682
+ configuredAccessModeLabel: accessModeDescriptor(configured.configuredAccessMode).shortLabel,
683
+ effectiveAccessMode: effective.effectiveAccessMode,
684
+ effectiveAccessModeLabel: accessModeDescriptor(effective.effectiveAccessMode).shortLabel,
685
+ effectiveToolset: effective.effectiveToolset,
686
+ exposureRevision: configured.exposureRevision,
687
+ lastAppliedAt: configured.lastAppliedAt,
688
+ source: effective.source,
689
+ reconnectRequired: false,
690
+ schemaRefreshRequired: false,
691
+ toolSchemaStable: true,
692
+ restartRequired: drift.restartRequired,
693
+ repositoryPolicyMode: repositoryPolicy.mode,
694
+ repositoryPolicyLabel: accessModeDescriptor(repositoryPolicy.mode).shortLabel,
695
+ toolGroups: accessToolGroups(effective.effectiveAccessMode),
696
+ };
697
+ }
698
+
699
+ /**
700
+ * Evaluate local MCP tool-surface freshness for the console.
701
+ * Probes live MCP /health; does not invent ChatGPT connector tool names.
702
+ */
703
+ export async function evaluateConsoleConnectorFreshness(
704
+ ctx: ConsoleFacadeContext,
705
+ opts: { connectorToolNames?: readonly string[] | null } = {},
706
+ ): Promise<ConnectorFreshnessReport> {
707
+ const toolCtx = createMcpToolContext({
708
+ repo: ctx.repository.canonicalRoot,
709
+ controllerHome: ctx.controllerHome,
710
+ profile: 'controller',
711
+ }) as MultiRepositoryMcpToolContext;
712
+ const expected = controllerExposedToolNames(toolCtx);
713
+ return buildLocalConnectorStatusForRepo({
714
+ repoRoot: ctx.repository.canonicalRoot,
715
+ expectedTools: expected,
716
+ connectorToolNames: opts.connectorToolNames,
717
+ });
718
+ }
719
+
720
+ export async function buildSystemReadiness(
721
+ ctx: ConsoleFacadeContext,
722
+ opts: { connectorToolNames?: readonly string[] | null } = {},
723
+ ): Promise<SystemReadinessViewModel> {
724
+ const runtime = observeRuntimeStatus(ctx.controllerHome);
725
+ const runtimeHealth = undefined;
726
+ const freshness = await evaluateConsoleConnectorFreshness(ctx, {
727
+ connectorToolNames: opts.connectorToolNames,
728
+ });
729
+ const handoffs = listHandoffItems({ ...store(ctx), status: 'all', limit: 100 });
730
+ const pendingHandoffCount = handoffs.filter((item) => item.status === 'pending').length;
731
+ const checks = listControllerChecks(ctx.repository.canonicalRoot);
732
+ const automation = buildAutomationReadinessSection(ctx);
733
+ const sections: SystemReadinessViewModel['sections'] = [
734
+ {
735
+ id: 'controller',
736
+ title: '控制器',
737
+ statusLabel: runtime.ready ? '就绪' : runtime.running ? '启动中' : '未运行',
738
+ tone: runtime.ready ? 'green' : 'red',
739
+ detail: runtime.ready
740
+ ? 'Canonical Runtime 已通过完整就绪检查。'
741
+ : runtime.reasonCodes.join('、') || 'Canonical Runtime 尚未就绪。',
742
+ },
743
+ {
744
+ id: 'connector',
745
+ title: 'ChatGPT 连接',
746
+ statusLabel: freshness.sectionStatusLabel,
747
+ tone: freshness.connectorTone,
748
+ detail: freshness.sectionDetail,
749
+ },
750
+ {
751
+ id: 'bridge',
752
+ title: '本地控制台',
753
+ statusLabel: runtime.running ? '已连接' : '不可用',
754
+ tone: runtime.running ? 'green' : 'red',
755
+ detail: runtime.running
756
+ ? '本地控制台正在读取同一 Canonical Runtime 状态。'
757
+ : 'Canonical Runtime 未运行,本地控制台不发布独立健康状态。',
758
+ },
759
+ {
760
+ id: 'repository',
761
+ title: '仓库',
762
+ statusLabel: ctx.repository.enabled === false ? '不可用' : '可用',
763
+ tone: ctx.repository.enabled === false ? 'red' : 'green',
764
+ detail: displayPath(ctx.repository.canonicalRoot),
765
+ },
766
+ {
767
+ id: 'checks',
768
+ title: '检查项',
769
+ statusLabel: checks.length ? `${checks.length} 个可用` : '未配置',
770
+ tone: checks.length ? 'green' : 'amber',
771
+ detail: checks.length ? '可运行注册检查验证结果。' : '尚未发现可用检查脚本。',
772
+ },
773
+ {
774
+ id: 'handoffs',
775
+ title: '待你决定',
776
+ statusLabel: pendingHandoffCount ? `${pendingHandoffCount} 项` : '无',
777
+ tone: pendingHandoffCount ? 'amber' : 'green',
778
+ detail: pendingHandoffCount ? '有事项需要你的判断后才能继续。' : '没有待处理决策。',
779
+ },
780
+ automation,
781
+ ];
782
+ const blocked = !runtime.ready;
783
+ const needsSetup = ctx.repository.enabled === false;
784
+ const state: SystemReadinessViewModel['state'] = blocked ? 'blocked' : needsSetup ? 'needs_setup' : 'ready';
785
+ return {
786
+ state,
787
+ label: state === 'ready' ? '就绪' : state === 'needs_setup' ? '需要设置' : '暂不可用',
788
+ headline: state === 'ready' ? '系统可用' : state === 'needs_setup' ? '完成设置后即可开始' : '系统暂不可用',
789
+ description: state === 'ready'
790
+ ? '你可以输入自然语言任务,或先处理待决定事项。'
791
+ : state === 'needs_setup'
792
+ ? '请先选择或注册一个可用仓库。'
793
+ : '请先查看系统状态并尝试诊断/修复。',
794
+ connectorLabel: freshness.connectorLabel,
795
+ connectorTone: freshness.connectorTone,
796
+ connectorFreshness: mapConnectorFreshnessView(freshness),
797
+ pendingHandoffCount,
798
+ runtimeHealth,
799
+ operationalView: undefined,
800
+ sections,
801
+ };
802
+ }
803
+
804
+ function buildAutomationReadinessSection(
805
+ ctx: ConsoleFacadeContext,
806
+ ): SystemReadinessViewModel['sections'][number] {
807
+ const schedules = listSchedules(ctx.controllerHome, ctx.repository.repoId).filter((schedule) => schedule.enabled);
808
+ const shadowSchedules = schedules.filter((schedule) => schedule.policy.shadowMode);
809
+ const liveSchedules = schedules.filter((schedule) => !schedule.policy.shadowMode);
810
+ const liveScheduleIds = new Set(liveSchedules.map((schedule) => schedule.scheduleId));
811
+ const activeLiveOccurrences = listActiveOccurrences(ctx.controllerHome, ctx.repository.repoId)
812
+ .filter((occurrence) => liveScheduleIds.has(occurrence.scheduleId)
813
+ && ['created', 'queued', 'running'].includes(occurrence.status))
814
+ .length;
815
+
816
+ if (schedules.length === 0) {
817
+ return {
818
+ id: 'automation',
819
+ title: '自治调度',
820
+ statusLabel: '未配置',
821
+ tone: 'gray',
822
+ detail: '当前“就绪”只表示控制器可以接受任务;没有启用中的 live schedule 在后台自治执行。',
823
+ };
824
+ }
825
+
826
+ if (liveSchedules.length === 0) {
827
+ return {
828
+ id: 'automation',
829
+ title: '自治调度',
830
+ statusLabel: `${shadowSchedules.length} 个影子计划`,
831
+ tone: 'amber',
832
+ detail: '已启用的 schedule 全部处于 shadow mode:它们只记录 would_execute / shadowed 结果,不会排队或启动 Execution Job。',
833
+ };
834
+ }
835
+
836
+ const liveLabel = shadowSchedules.length > 0
837
+ ? `live ${liveSchedules.length} / shadow ${shadowSchedules.length}`
838
+ : `${liveSchedules.length} 个 live 计划`;
839
+ const liveDetail = activeLiveOccurrences > 0
840
+ ? `当前有 ${activeLiveOccurrences} 个 live occurrence 正在 created/queued/running。`
841
+ : '当前没有 live occurrence 在 created/queued/running。';
842
+ return {
843
+ id: 'automation',
844
+ title: '自治调度',
845
+ statusLabel: liveLabel,
846
+ tone: 'green',
847
+ detail: `只有 live schedule 在触发条件满足时才会排队 bounded execution。${shadowSchedules.length > 0 ? ' shadow schedule 仍然只做记录。' : ''} ${liveDetail}`,
848
+ };
849
+ }
850
+
851
+ function riskLabel(risk: string): string {
852
+ if (risk === 'readonly') return '只读';
853
+ if (risk === 'workspace_write') return '本地写入';
854
+ if (risk === 'remote_write') return '远程写入';
855
+ if (risk === 'destructive') return '破坏性';
856
+ return risk || '未知';
857
+ }
858
+
859
+ function confirmationLabel(confirmation: string): string {
860
+ if (confirmation === 'none') return '无需确认';
861
+ if (confirmation === 'authorization') return '需要授权确认';
862
+ if (confirmation === 'strong_confirmation') return '需要强确认';
863
+ return confirmation || '未知';
864
+ }
865
+
866
+ function mapPluginAction(action: AssistantPluginManifest['actions'][number]): PluginActionViewModel {
867
+ return {
868
+ id: action.actionId,
869
+ title: action.title || action.actionId,
870
+ description: action.description || '',
871
+ risk: action.risk,
872
+ riskLabel: riskLabel(action.risk),
873
+ readOnly: action.readOnly === true || action.risk === 'readonly',
874
+ confirmation: action.confirmation,
875
+ confirmationLabel: confirmationLabel(action.confirmation),
876
+ canPreview: action.readOnly === true || action.risk === 'readonly' || action.actionId === 'configure' || action.actionId === 'auth_status',
877
+ requiredConfirmationText: action.requiredConfirmationText,
878
+ };
879
+ }
880
+
881
+ export function mapPluginCard(manifest: AssistantPluginManifest): PluginCardViewModel {
882
+ const health = manifest.health;
883
+ const lifecycle = manifest.lifecycle;
884
+ const ready = manifest.enabled !== false
885
+ && health.ready !== false
886
+ && health.state !== 'error'
887
+ && lifecycle.state !== 'error';
888
+ const needsAuthorization = [...(health.errors ?? []), ...(health.warnings ?? []), lifecycle.reason ?? '']
889
+ .some((entry) => /auth|token|credential|permission|scope|授权|登录/i.test(String(entry)));
890
+ const status: PluginCardViewModel['status'] = manifest.enabled === false
891
+ ? 'disabled'
892
+ : ready
893
+ ? 'ready'
894
+ : needsAuthorization
895
+ ? 'authorization_required'
896
+ : health.state === 'error' || lifecycle.state === 'error'
897
+ ? 'failed'
898
+ : 'needs_setup';
899
+ const tone: PlainStatusTone = status === 'ready'
900
+ ? 'green'
901
+ : status === 'failed'
902
+ ? 'red'
903
+ : status === 'disabled'
904
+ ? 'gray'
905
+ : 'amber';
906
+ const nextStep = status === 'ready'
907
+ ? '已连接,可用于任务执行'
908
+ : status === 'authorization_required'
909
+ ? '需要完成授权后才能使用'
910
+ : status === 'disabled'
911
+ ? '插件已禁用'
912
+ : '需要配置并测试连接';
913
+ const actions = manifest.actions.map(mapPluginAction);
914
+ return {
915
+ id: manifest.pluginId,
916
+ name: manifest.displayName || manifest.pluginId,
917
+ provider: manifest.provider,
918
+ status,
919
+ statusLabel: status === 'ready'
920
+ ? '可用'
921
+ : status === 'authorization_required'
922
+ ? '待授权'
923
+ : status === 'failed'
924
+ ? '异常'
925
+ : status === 'disabled'
926
+ ? '已禁用'
927
+ : '需配置',
928
+ tone,
929
+ enabled: manifest.enabled !== false,
930
+ actionCount: actions.length,
931
+ description: `${actions.length} 个可用动作 · ${manifest.provider}`,
932
+ nextStep,
933
+ healthLabel: health.state || 'unknown',
934
+ lifecycleLabel: lifecycle.state || 'unknown',
935
+ capabilityLabels: (manifest.capabilities ?? []).slice(0, 6).map((entry) => entry.title || entry.capabilityId),
936
+ actions,
937
+ warnings: [...(health.warnings ?? []), ...(health.errors ?? [])].slice(0, 6).map(String),
938
+ advanced: {
939
+ pluginId: manifest.pluginId,
940
+ provider: manifest.provider,
941
+ revision: manifest.revision,
942
+ },
943
+ };
944
+ }
945
+
946
+ export function listConsolePlugins(ctx: ConsoleFacadeContext): PluginCardViewModel[] {
947
+ return listAssistantPluginManifests(ctx.controllerHome, ctx.repository, {
948
+ preferStored: true,
949
+ }).map(mapPluginCard);
950
+ }
951
+
952
+ export function getConsolePlugin(ctx: ConsoleFacadeContext, pluginId: string): PluginCardViewModel | null {
953
+ try {
954
+ return mapPluginCard(getAssistantPluginManifest(ctx.controllerHome, ctx.repository, pluginId));
955
+ } catch {
956
+ return null;
957
+ }
958
+ }
959
+
960
+ export function buildPluginSummary(plugins: PluginCardViewModel[]): PluginSummaryViewModel {
961
+ const ready = plugins.filter((entry) => entry.status === 'ready').length;
962
+ const needsAttention = plugins.filter((entry) =>
963
+ entry.status === 'authorization_required' || entry.status === 'failed' || entry.status === 'needs_setup').length;
964
+ return {
965
+ ready,
966
+ total: plugins.length,
967
+ needsAttention,
968
+ lines: plugins.slice(0, 6).map((entry) => `${entry.name} · ${entry.statusLabel}`),
969
+ };
970
+ }
971
+
972
+ export async function buildCommandCenter(
973
+ ctx: ConsoleFacadeContext,
974
+ repositories: RepositoryCardViewModel[],
975
+ ): Promise<CommandCenterViewModel> {
976
+ const readiness = await buildSystemReadiness(ctx);
977
+ const mapWork = (work: WorkContract) => mapWorkSummary(work, {
978
+ controllerHome: ctx.controllerHome,
979
+ repoId: ctx.repository.repoId,
980
+ });
981
+ const currentRepository = repositories.find((entry) => entry.current) ?? mapRepositoryCard(ctx.repository, true);
982
+ const activeWork = listWorkContracts({ ...store(ctx), status: 'active', limit: 20 }).map(mapWork);
983
+ const allRecent = listWorkContracts({ ...store(ctx), status: 'all', limit: 12 }).map(mapWork);
984
+ const handoffs = listHandoffItems({ ...store(ctx), status: 'pending', limit: 20 }).map(mapHandoffCard);
985
+ const plugins = listConsolePlugins(ctx);
986
+ const pluginSummary = buildPluginSummary(plugins);
987
+ const goalLoop = buildGoalLoopStatusView(ctx);
988
+ const access = controllerAccessStateView(ctx);
989
+ const banner = readiness.connectorFreshness?.severity === 'warning' || readiness.connectorFreshness?.severity === 'error'
990
+ ? readiness.connectorFreshness.summary
991
+ : undefined;
992
+ const needsSetup = readiness.state === 'needs_setup' || ctx.repository.enabled === false || !currentRepository;
993
+ return {
994
+ schemaVersion: 1,
995
+ generatedAt: new Date().toISOString(),
996
+ accessMode: access.configuredAccessMode,
997
+ accessModeLabel: access.configuredAccessModeLabel,
998
+ accessModeDescription: accessModeDescriptor(access.configuredAccessMode).description,
999
+ access,
1000
+ readiness,
1001
+ currentRepository,
1002
+ repositories,
1003
+ currentWork: activeWork[0],
1004
+ recentWork: allRecent,
1005
+ handoffs,
1006
+ goalLoop,
1007
+ pluginSummary,
1008
+ plugins,
1009
+ modePreviewDefault: plainMode('direct_control'),
1010
+ // Only surface confirmed warnings — never "maybe missing" when ChatGPT snapshot is unobserved.
1011
+ warnings: banner ? [banner] : [],
1012
+ setupGuide: needsSetup
1013
+ ? {
1014
+ needed: true,
1015
+ title: '先选择或注册一个仓库',
1016
+ body: '没有可用仓库时,无法开始开发任务。请在“仓库”页添加本地 Git 仓库并设为当前。',
1017
+ actionLabel: '去设置仓库',
1018
+ }
1019
+ : { needed: false, title: '', body: '', actionLabel: '' },
1020
+ };
1021
+ }
1022
+
1023
+ function configCtx(ctx: ConsoleFacadeContext): ConfigFacadeContext {
1024
+ return { controllerHome: ctx.controllerHome };
1025
+ }
1026
+
1027
+ export function buildGoalLoopStatusView(ctx: ConsoleFacadeContext): GoalLoopStatusViewModel {
1028
+ const goalCtx: GoalLoopContext = {
1029
+ goalStore: store(ctx),
1030
+ packetStore: store(ctx),
1031
+ repoId: ctx.repository.repoId,
1032
+ configLocation: { controllerHome: ctx.controllerHome },
1033
+ providerEnv: { configLocation: { controllerHome: ctx.controllerHome } },
1034
+ };
1035
+ const snapshot = goalLoopStatusSnapshot(goalCtx) as {
1036
+ activeGoals?: Array<Record<string, unknown>>;
1037
+ activeCount?: number;
1038
+ invokableProviders?: string[];
1039
+ handoffOnlyProviders?: string[];
1040
+ providers?: Array<Record<string, unknown>>;
1041
+ };
1042
+ let automationSummary: string | undefined;
1043
+ let liveEffective: boolean | undefined;
1044
+ try {
1045
+ const settings = buildAutomationSettingsView(configCtx(ctx));
1046
+ automationSummary = settings.overview.plainLanguageSummary;
1047
+ liveEffective = settings.overview.liveModelProvidersEffective;
1048
+ } catch {
1049
+ automationSummary = undefined;
1050
+ }
1051
+
1052
+ const goals = (snapshot.activeGoals ?? []).map((goal) => {
1053
+ const stage = String(goal.stage ?? '');
1054
+ const providerSelected = typeof goal.providerSelected === 'string' ? goal.providerSelected : undefined;
1055
+ const waitingReason = typeof goal.waitingReason === 'string' ? goal.waitingReason : undefined;
1056
+ const nextSafeAction = typeof goal.nextSafeAction === 'string' ? goal.nextSafeAction : undefined;
1057
+ const handoffPacketAvailable = goal.handoffPacketAvailable === true;
1058
+ return {
1059
+ title: String(goal.title ?? ''),
1060
+ stage,
1061
+ currentStep: String(goal.currentStep ?? ''),
1062
+ providerSelected,
1063
+ waitingReason,
1064
+ nextSafeAction,
1065
+ handoffPacketAvailable,
1066
+ approvalRequired: Boolean(waitingReason) || stage === 'waiting_for_user',
1067
+ whyThisProvider: providerSelected
1068
+ ? `Last selected provider: ${providerSelected}`
1069
+ : 'No provider selected yet',
1070
+ whatHappensNext: nextSafeAction
1071
+ ?? (handoffPacketAvailable
1072
+ ? 'Open handoff packet or configure a direct provider'
1073
+ : 'Wait for next daemon tick or open Automation Settings'),
1074
+ whatIsBlocked: waitingReason
1075
+ ?? (stage === 'handoff_ready'
1076
+ ? 'Waiting for handoff supervisor (ChatGPT is not auto-invokable)'
1077
+ : undefined),
1078
+ };
1079
+ });
1080
+ return {
1081
+ activeCount: snapshot.activeCount ?? goals.length,
1082
+ goals,
1083
+ invokableProviders: snapshot.invokableProviders ?? [],
1084
+ handoffOnlyProviders: snapshot.handoffOnlyProviders ?? [],
1085
+ providerHealth: (snapshot.providers ?? []).map((provider) => ({
1086
+ providerId: String(provider.providerId ?? ''),
1087
+ status: String(provider.status ?? ''),
1088
+ directDispatchAllowed: provider.directDispatchAllowed === true,
1089
+ handoffOnly: provider.handoffOnly === true,
1090
+ summary: String(provider.summary ?? ''),
1091
+ })),
1092
+ automationSummary,
1093
+ liveModelProvidersEffective: liveEffective,
1094
+ settingsPathHint: 'Model & Tool Providers',
1095
+ nextTickHint: 'Daemon ticks active goals about every 5 seconds when the controller is running.',
1096
+ };
1097
+ }
1098
+
1099
+ export function getAutomationSettings(ctx: ConsoleFacadeContext) {
1100
+ return buildAutomationSettingsView(configCtx(ctx));
1101
+ }
1102
+
1103
+ export function consoleProviderConfigGet(ctx: ConsoleFacadeContext) {
1104
+ return providerConfigGet(configCtx(ctx));
1105
+ }
1106
+
1107
+ export function consoleProviderConfigUpdate(ctx: ConsoleFacadeContext, body: Record<string, unknown>) {
1108
+ return providerConfigUpdate(configCtx(ctx), body as unknown as Parameters<typeof providerConfigUpdate>[1]);
1109
+ }
1110
+
1111
+ export function consoleProviderEnable(ctx: ConsoleFacadeContext, providerId: string) {
1112
+ return providerEnable(configCtx(ctx), providerId);
1113
+ }
1114
+
1115
+ export function consoleProviderDisable(ctx: ConsoleFacadeContext, providerId: string) {
1116
+ return providerDisable(configCtx(ctx), providerId);
1117
+ }
1118
+
1119
+ export function consoleProviderPriority(ctx: ConsoleFacadeContext, providerId: string, direction: 'up' | 'down' | number) {
1120
+ return providerPriorityUpdate(configCtx(ctx), providerId, direction);
1121
+ }
1122
+
1123
+ export function consoleProviderHealth(ctx: ConsoleFacadeContext, providerId?: string) {
1124
+ return providerHealthCheck(configCtx(ctx), providerId);
1125
+ }
1126
+
1127
+ export function consoleProviderCredentials(ctx: ConsoleFacadeContext) {
1128
+ return providerCredentialsStatus(configCtx(ctx));
1129
+ }
1130
+
1131
+ export function consoleProviderReset(ctx: ConsoleFacadeContext) {
1132
+ return providerResetDefaults(configCtx(ctx));
1133
+ }
1134
+
1135
+ export function consoleProviderApiSettingsGet(ctx: ConsoleFacadeContext, providerId: string) {
1136
+ return providerApiSettingsGet(configCtx(ctx), providerId);
1137
+ }
1138
+
1139
+ export function consoleProviderApiSettingsUpdate(
1140
+ ctx: ConsoleFacadeContext,
1141
+ providerId: string,
1142
+ body: Record<string, unknown>,
1143
+ ) {
1144
+ return providerApiSettingsUpdate(configCtx(ctx), providerId, {
1145
+ baseUrl: typeof body.baseUrl === 'string' ? body.baseUrl : typeof body.base_url === 'string' ? body.base_url : undefined,
1146
+ model: typeof body.model === 'string' ? body.model : undefined,
1147
+ apiKey: typeof body.apiKey === 'string'
1148
+ ? body.apiKey
1149
+ : typeof body.api_key === 'string'
1150
+ ? body.api_key
1151
+ : undefined,
1152
+ clearApiKey: body.clearApiKey === true || body.clear_api_key === true,
1153
+ });
1154
+ }
1155
+
1156
+ export function consoleLocalToolList(ctx: ConsoleFacadeContext) {
1157
+ return localToolList(configCtx(ctx));
1158
+ }
1159
+
1160
+ export function consoleLocalToolEnable(ctx: ConsoleFacadeContext, toolId: string) {
1161
+ return localToolEnable(configCtx(ctx), toolId);
1162
+ }
1163
+
1164
+ export function consoleLocalToolDisable(ctx: ConsoleFacadeContext, toolId: string) {
1165
+ return localToolDisable(configCtx(ctx), toolId);
1166
+ }
1167
+
1168
+ export function consoleLocalToolHealth(ctx: ConsoleFacadeContext, toolId?: string) {
1169
+ return localToolHealthCheck(configCtx(ctx), toolId);
1170
+ }
1171
+
1172
+ export function consoleLocalToolConfigGet(ctx: ConsoleFacadeContext) {
1173
+ return localToolConfigGet(configCtx(ctx));
1174
+ }
1175
+
1176
+ export function consoleLocalToolConfigUpdate(ctx: ConsoleFacadeContext, body: Record<string, unknown>) {
1177
+ return localToolConfigUpdate(configCtx(ctx), body as unknown as Parameters<typeof localToolConfigUpdate>[1]);
1178
+ }
1179
+
1180
+ export function consoleRoutingGet(ctx: ConsoleFacadeContext) {
1181
+ return executorRoutingConfigGet(configCtx(ctx));
1182
+ }
1183
+
1184
+ export function consoleRoutingUpdate(ctx: ConsoleFacadeContext, body: Record<string, unknown>) {
1185
+ return executorRoutingConfigUpdate(configCtx(ctx), body as unknown as Parameters<typeof executorRoutingConfigUpdate>[1]);
1186
+ }
1187
+
1188
+ export function consoleRoutingReset(ctx: ConsoleFacadeContext) {
1189
+ return executorRoutingConfigReset(configCtx(ctx));
1190
+ }
1191
+
1192
+ export function consoleRoutePreview(ctx: ConsoleFacadeContext, body: Record<string, unknown> = {}) {
1193
+ return executorRoutePreviewWithConfig(configCtx(ctx), {
1194
+ taskIntent: typeof body.taskIntent === 'string' ? body.taskIntent as never : typeof body.task_intent === 'string' ? body.task_intent as never : undefined,
1195
+ risk: typeof body.risk === 'string' ? body.risk as never : undefined,
1196
+ objective: typeof body.objective === 'string' ? body.objective : undefined,
1197
+ externalWrite: body.externalWrite === true || body.external_write === true,
1198
+ });
1199
+ }
1200
+
1201
+ export function consoleGoalLoopPolicyGet(ctx: ConsoleFacadeContext) {
1202
+ return goalLoopPolicyGet(configCtx(ctx));
1203
+ }
1204
+
1205
+ export function consoleGoalLoopPolicyUpdate(ctx: ConsoleFacadeContext, body: Record<string, unknown>) {
1206
+ return goalLoopPolicyUpdate(configCtx(ctx), body as unknown as Parameters<typeof goalLoopPolicyUpdate>[1]);
1207
+ }
1208
+
1209
+ export function getConsoleAccessPolicy(ctx: ConsoleFacadeContext) {
1210
+ const policy = readRepositoryAccessPolicy(ctx.controllerHome, ctx.repository.repoId);
1211
+ return {
1212
+ policy,
1213
+ descriptor: accessModeDescriptor(policy.mode),
1214
+ access: controllerAccessStateView(ctx),
1215
+ };
1216
+ }
1217
+
1218
+ export function setConsoleAccessPolicy(
1219
+ ctx: ConsoleFacadeContext,
1220
+ input: { mode: unknown; confirmAuthorization?: boolean; confirmationText?: string },
1221
+ ) {
1222
+ if (!isAccessMode(input.mode)) throw new Error('ACCESS_MODE_INVALID: mode must be request or full_access');
1223
+ if (input.confirmAuthorization !== true) {
1224
+ throw new Error('ACCESS_MODE_AUTHORIZATION_REQUIRED: changing repository access requires explicit confirmation');
1225
+ }
1226
+ persistControllerAccessMode(ctx.controllerHome, input.mode, ctx.repository.canonicalRoot);
1227
+ const policy = writeRepositoryAccessPolicy(ctx.controllerHome, ctx.repository.repoId, input.mode, 'user');
1228
+ return {
1229
+ policy,
1230
+ descriptor: accessModeDescriptor(policy.mode),
1231
+ access: controllerAccessStateView(ctx),
1232
+ };
1233
+ }
1234
+
1235
+ export function startConsoleWork(
1236
+ ctx: ConsoleFacadeContext,
1237
+ input: {
1238
+ objective: string;
1239
+ acceptanceCriteria?: string[];
1240
+ allowedPaths?: string[];
1241
+ forbiddenPaths?: string[];
1242
+ expectedFiles?: number;
1243
+ expectedChangedLines?: number;
1244
+ scopeClear?: boolean;
1245
+ requiresInvestigation?: boolean;
1246
+ requiresLongRunningChecks?: boolean;
1247
+ requiresWorker?: boolean;
1248
+ requiresApproval?: boolean;
1249
+ destructive?: boolean;
1250
+ accessMode?: AccessMode;
1251
+ approvalConfirmed?: boolean;
1252
+ forceMode?: 'direct_control' | 'goal_workloop' | 'handoff_only';
1253
+ checkIds?: string[];
1254
+ },
1255
+ ): FacadeResult {
1256
+ const checks = listControllerChecks(ctx.repository.canonicalRoot);
1257
+ return routeWorkStart(
1258
+ {
1259
+ workStore: store(ctx),
1260
+ handoffStore: store(ctx),
1261
+ repoId: ctx.repository.repoId,
1262
+ availableChecks: checks,
1263
+ },
1264
+ {
1265
+ objective: input.objective,
1266
+ acceptanceCriteria: input.acceptanceCriteria,
1267
+ allowedPaths: input.allowedPaths,
1268
+ forbiddenPaths: input.forbiddenPaths,
1269
+ checks: input.checkIds,
1270
+ constraints: input.accessMode ? { accessMode: input.accessMode } : undefined,
1271
+ modeInput: {
1272
+ objective: input.objective,
1273
+ expectedFiles: input.expectedFiles,
1274
+ expectedChangedLines: input.expectedChangedLines,
1275
+ scopeClear: input.scopeClear !== false,
1276
+ requiresInvestigation: input.requiresInvestigation === true,
1277
+ requiresLongRunningChecks: input.requiresLongRunningChecks === true,
1278
+ requiresWorker: input.requiresWorker === true,
1279
+ requiresApproval: input.requiresApproval === true,
1280
+ destructive: input.destructive === true,
1281
+ },
1282
+ requestedBy: 'user',
1283
+ approvalConfirmed: input.approvalConfirmed === true,
1284
+ forceMode: input.forceMode,
1285
+ },
1286
+ );
1287
+ }
1288
+
1289
+ export function continueConsoleWork(ctx: ConsoleFacadeContext, workId: string, note?: string): FacadeResult {
1290
+ return continueGoalWorkloop(
1291
+ {
1292
+ workStore: store(ctx),
1293
+ handoffStore: store(ctx),
1294
+ repoId: ctx.repository.repoId,
1295
+ availableChecks: listControllerChecks(ctx.repository.canonicalRoot),
1296
+ },
1297
+ { workId, note },
1298
+ );
1299
+ }
1300
+
1301
+ export function verifyConsoleWork(
1302
+ ctx: ConsoleFacadeContext,
1303
+ input: { workId: string; checkId?: string; simulate?: boolean; checkFailed?: boolean; infrastructureFailed?: boolean },
1304
+ ): FacadeResult {
1305
+ const checks = listControllerChecks(ctx.repository.canonicalRoot);
1306
+ const workloopCtx = {
1307
+ workStore: store(ctx),
1308
+ handoffStore: store(ctx),
1309
+ repoId: ctx.repository.repoId,
1310
+ availableChecks: checks,
1311
+ };
1312
+ const work = getWorkContract(store(ctx), input.workId);
1313
+ const requested = input.checkId || work?.checks[0] || checks[0]?.id || '';
1314
+ const classified = classifyVerificationOutcome({ checkId: requested, available: checks });
1315
+ if (classified.outcome === 'invalid_check_id') {
1316
+ return verifyGoalWorkloop(workloopCtx, { workId: input.workId, checkId: requested });
1317
+ }
1318
+ if (input.simulate) {
1319
+ return verifyGoalWorkloop(workloopCtx, {
1320
+ workId: input.workId,
1321
+ checkId: classified.normalizedCheckId ?? requested,
1322
+ checkFailed: input.checkFailed === true,
1323
+ infrastructureFailed: input.infrastructureFailed === true,
1324
+ });
1325
+ }
1326
+ try {
1327
+ const executed = runControllerCheck(ctx.repository.canonicalRoot, classified.normalizedCheckId!);
1328
+ return verifyGoalWorkloop(workloopCtx, {
1329
+ workId: input.workId,
1330
+ checkId: classified.normalizedCheckId!,
1331
+ infrastructureFailed: executed.timedOut === true,
1332
+ checkFailed: !executed.ok && !executed.timedOut,
1333
+ });
1334
+ } catch (error) {
1335
+ const message = error instanceof Error ? error.message : String(error);
1336
+ const facade = verifyGoalWorkloop(workloopCtx, {
1337
+ workId: input.workId,
1338
+ checkId: classified.normalizedCheckId ?? requested,
1339
+ infrastructureFailed: true,
1340
+ });
1341
+ return {
1342
+ ...facade,
1343
+ warnings: [...facade.warnings, `环境执行检查失败:${message.slice(0, 160)}`],
1344
+ };
1345
+ }
1346
+ }
1347
+
1348
+ export function finalizeConsoleWork(ctx: ConsoleFacadeContext, workId: string): FacadeResult {
1349
+ return finalizeGoalWorkloop(
1350
+ {
1351
+ workStore: store(ctx),
1352
+ handoffStore: store(ctx),
1353
+ repoId: ctx.repository.repoId,
1354
+ availableChecks: listControllerChecks(ctx.repository.canonicalRoot),
1355
+ },
1356
+ { workId },
1357
+ );
1358
+ }
1359
+
1360
+ export function stopConsoleWork(ctx: ConsoleFacadeContext, workId: string, reason?: string): FacadeResult {
1361
+ return stopGoalWorkloop(
1362
+ {
1363
+ workStore: store(ctx),
1364
+ handoffStore: store(ctx),
1365
+ repoId: ctx.repository.repoId,
1366
+ availableChecks: listControllerChecks(ctx.repository.canonicalRoot),
1367
+ },
1368
+ { workId, reason },
1369
+ );
1370
+ }
1371
+
1372
+ export function delegateConsoleWork(
1373
+ ctx: ConsoleFacadeContext,
1374
+ input: { workId?: string; target?: 'codex' | 'grok' | 'claude'; objective?: string; available?: boolean },
1375
+ ): FacadeResult {
1376
+ return delegateToCodexCerebellum(
1377
+ { repoId: ctx.repository.repoId },
1378
+ {
1379
+ workId: input.workId,
1380
+ target: input.target ?? 'codex',
1381
+ objective: input.objective || '请协助完成当前任务',
1382
+ available: input.available,
1383
+ codexAvailable: input.available,
1384
+ },
1385
+ );
1386
+ }
1387
+
1388
+ export function repairConsole(
1389
+ ctx: ConsoleFacadeContext,
1390
+ input: {
1391
+ operation?: 'diagnose' | 'repair' | 'verify' | 'handoff';
1392
+ dryRun?: boolean;
1393
+ approvalConfirmed?: boolean;
1394
+ destructive?: boolean;
1395
+ workId?: string;
1396
+ } = {},
1397
+ ): FacadeResult {
1398
+ let maintenanceStatus: {
1399
+ readyForExecution?: boolean;
1400
+ recommendedActions?: string[];
1401
+ candidates?: Array<{ kind?: string; reason?: string; suggestedAction?: string; safe?: boolean }>;
1402
+ warnings?: string[];
1403
+ } | undefined;
1404
+ try {
1405
+ const status = buildRuntimeMaintenanceStatus(ctx.repository, ctx.controllerHome, { maxCandidates: 20 });
1406
+ maintenanceStatus = {
1407
+ readyForExecution: status.readyForExecution,
1408
+ recommendedActions: status.recommendedActions,
1409
+ candidates: status.candidates.map((candidate) => ({
1410
+ kind: candidate.kind,
1411
+ reason: candidate.reason,
1412
+ suggestedAction: candidate.suggestedAction,
1413
+ safe: candidate.safe,
1414
+ })),
1415
+ warnings: status.warnings,
1416
+ };
1417
+ } catch {
1418
+ maintenanceStatus = {
1419
+ readyForExecution: false,
1420
+ warnings: ['维护状态读取失败,按环境问题处理,不记作任务验收失败。'],
1421
+ };
1422
+ }
1423
+ const daemon = readForgeRuntimeStatus(ctx.controllerHome);
1424
+ return runSelfHealingLoop(
1425
+ { repoId: ctx.repository.repoId, handoffStore: store(ctx) },
1426
+ {
1427
+ operation: input.operation ?? 'diagnose',
1428
+ dryRun: input.dryRun === undefined ? true : input.dryRun,
1429
+ approvalConfirmed: input.approvalConfirmed === true,
1430
+ destructive: input.destructive === true,
1431
+ workId: input.workId,
1432
+ maintenanceStatus,
1433
+ diagnostics: {
1434
+ controllerDaemonUnhealthy: daemon.status !== 'ready',
1435
+ },
1436
+ },
1437
+ );
1438
+ }
1439
+
1440
+ export function listConsoleHandoffs(ctx: ConsoleFacadeContext, status: 'pending' | 'active' | 'all' = 'pending'): HandoffCardViewModel[] {
1441
+ return listHandoffItems({ ...store(ctx), status, limit: 50 }).map(mapHandoffCard);
1442
+ }
1443
+
1444
+ export function getConsoleHandoff(ctx: ConsoleFacadeContext, handoffId: string): HandoffCardViewModel | undefined {
1445
+ const item = getHandoffItem(store(ctx), handoffId);
1446
+ return item ? mapHandoffCard(item) : undefined;
1447
+ }
1448
+
1449
+ export function ackConsoleHandoff(ctx: ConsoleFacadeContext, handoffId: string) {
1450
+ return mapHandoffCard(acknowledgeHandoffItem(store(ctx), handoffId));
1451
+ }
1452
+
1453
+ export function resolveConsoleHandoff(ctx: ConsoleFacadeContext, handoffId: string, decision: string, resolver = 'user') {
1454
+ return mapHandoffCard(resolveHandoffItem(store(ctx), handoffId, { decision, resolver }));
1455
+ }
1456
+
1457
+ export function approveConsoleHandoff(ctx: ConsoleFacadeContext, handoffId: string, resolver = 'user') {
1458
+ const item = getHandoffItem(store(ctx), handoffId);
1459
+ if (!item) throw new Error(`handoff not found: ${handoffId}`);
1460
+ const decision = describeHandoffDecision(item);
1461
+ if (decision.type !== 'approval') throw new Error('该事项不是审批,不能使用“批准并继续”。');
1462
+ if (!item.approvalAction || item.approvalAction.operation !== 'start') {
1463
+ throw new Error('该审批项没有保存可安全重放的任务创建动作,请仅记录决定并按人工提示处理。');
1464
+ }
1465
+ if (item.status !== 'pending' && item.status !== 'acknowledged') {
1466
+ throw new Error(`该审批已处理(${item.status})。`);
1467
+ }
1468
+
1469
+ const payload = item.approvalAction.payload;
1470
+ const strings = (value: unknown): string[] | undefined => Array.isArray(value) ? value.map(String) : undefined;
1471
+ const actionResult = startConsoleWork(ctx, {
1472
+ objective: typeof payload.objective === 'string' ? payload.objective : '',
1473
+ acceptanceCriteria: strings(payload.acceptanceCriteria),
1474
+ allowedPaths: strings(payload.allowedPaths),
1475
+ forbiddenPaths: strings(payload.forbiddenPaths),
1476
+ expectedFiles: typeof payload.expectedFiles === 'number' ? payload.expectedFiles : undefined,
1477
+ expectedChangedLines: typeof payload.expectedChangedLines === 'number' ? payload.expectedChangedLines : undefined,
1478
+ scopeClear: payload.scopeClear !== false,
1479
+ requiresInvestigation: payload.requiresInvestigation === true,
1480
+ requiresLongRunningChecks: payload.requiresLongRunningChecks === true,
1481
+ requiresWorker: payload.requiresWorker === true,
1482
+ requiresApproval: payload.requiresApproval === true,
1483
+ destructive: payload.destructive === true,
1484
+ accessMode: isAccessMode(payload.accessMode) ? payload.accessMode : undefined,
1485
+ approvalConfirmed: true,
1486
+ forceMode: 'goal_workloop',
1487
+ checkIds: strings(payload.checkIds),
1488
+ });
1489
+
1490
+ if (actionResult.status !== 'ok') {
1491
+ return {
1492
+ item: mapHandoffCard(item),
1493
+ actionResult,
1494
+ continued: false,
1495
+ summary: `批准已收到,但后续动作未成功:${actionResult.summary}`,
1496
+ };
1497
+ }
1498
+ const resolved = resolveHandoffItem(store(ctx), handoffId, {
1499
+ decision: `approved: ${item.approvalAction.label}`,
1500
+ resolver,
1501
+ });
1502
+ return {
1503
+ item: mapHandoffCard(resolved),
1504
+ actionResult,
1505
+ continued: true,
1506
+ summary: '已批准并创建任务,页面将进入任务详情并显示后续动作。',
1507
+ };
1508
+ }
1509
+
1510
+ export function dismissConsoleHandoff(ctx: ConsoleFacadeContext, handoffId: string, decision = 'dismissed', resolver = 'user') {
1511
+ return mapHandoffCard(dismissHandoffItem(store(ctx), handoffId, { decision, resolver }));
1512
+ }
1513
+
1514
+ export function getConsoleWork(ctx: ConsoleFacadeContext, workId: string): WorkSummaryViewModel | undefined {
1515
+ const work = getWorkContract(store(ctx), workId);
1516
+ return work
1517
+ ? mapWorkSummary(work, { controllerHome: ctx.controllerHome, repoId: ctx.repository.repoId })
1518
+ : undefined;
1519
+ }
1520
+
1521
+ export function listConsoleWork(ctx: ConsoleFacadeContext, status: 'active' | 'all' = 'active'): WorkSummaryViewModel[] {
1522
+ return listWorkContracts({ ...store(ctx), status, limit: 50 }).map((work) =>
1523
+ mapWorkSummary(work, { controllerHome: ctx.controllerHome, repoId: ctx.repository.repoId }));
1524
+ }
1525
+
1526
+ /** Normalize facade/tool results into GUI-friendly operation feedback. */
1527
+ export function toConsoleOperationFeedback(result: FacadeResult): {
1528
+ phase: string;
1529
+ statusLabel: string;
1530
+ summary: string;
1531
+ terminal: boolean;
1532
+ errorClass?: UserFacingErrorClass;
1533
+ error?: ConsoleErrorViewModel;
1534
+ suggestedNextActions: SuggestedActionViewModel[];
1535
+ changedFiles?: ChangedFilesSummaryViewModel;
1536
+ } {
1537
+ const data = (result.data ?? {}) as Record<string, unknown>;
1538
+ const status = result.status;
1539
+ const phase: UserFacingPhase = status === 'ok'
1540
+ ? 'succeeded'
1541
+ : status === 'blocked' || status === 'approval_required'
1542
+ ? 'needs_attention'
1543
+ : status === 'failed'
1544
+ ? 'failed'
1545
+ : status === 'not_found'
1546
+ ? 'failed'
1547
+ : 'running';
1548
+ const errorClass = status === 'ok'
1549
+ ? undefined
1550
+ : classifyUserFacingError({
1551
+ code: String(data.errorClass ?? data.code ?? ''),
1552
+ message: result.summary || String(data.errorMessage ?? ''),
1553
+ status,
1554
+ infrastructure: status === 'failed' && /环境|infrastructure|daemon|timeout/i.test(result.summary),
1555
+ acceptance: /验收|acceptance|valid_fail|未通过/i.test(result.summary),
1556
+ });
1557
+ const changed = Array.isArray(data.changedFiles)
1558
+ ? summarizeChangedFiles(data.changedFiles.map(String))
1559
+ : undefined;
1560
+ return {
1561
+ phase,
1562
+ statusLabel: statusLabelForPhase(phase),
1563
+ summary: result.summary || (status === 'ok' ? '操作已完成' : '操作未完成'),
1564
+ terminal: phase === 'succeeded' || phase === 'failed' || phase === 'needs_attention',
1565
+ errorClass,
1566
+ error: errorClass ? describeConsoleError(errorClass, result.summary) : undefined,
1567
+ suggestedNextActions: mapSuggested(result.suggestedNextActions),
1568
+ changedFiles: changed,
1569
+ };
1570
+ }
1571
+
1572
+ export function buildConsoleContext(ctx: ConsoleFacadeContext, workId?: string) {
1573
+ const checks = listControllerChecks(ctx.repository.canonicalRoot);
1574
+ const work = workId ? getWorkContract(store(ctx), workId) : undefined;
1575
+ return buildFacadeResult({
1576
+ summary: work ? `任务上下文:${work.objective.slice(0, 120)}` : '仓库上下文已就绪',
1577
+ data: {
1578
+ repositoryName: ctx.repository.displayName,
1579
+ checks: checks.map((check) => ({ id: check.id, description: check.description })),
1580
+ work: work ? summarizeWorkContract(work) : undefined,
1581
+ handoffs: listHandoffItems({ ...store(ctx), status: 'pending', limit: 10 }).map(summarizeHandoffItem),
1582
+ },
1583
+ suggestedNextActions: work
1584
+ ? mapSuggested(work.suggestedNextActions).map((action) => ({
1585
+ label: action.label,
1586
+ tool: (action.tool as 'rh_work') || 'rh_work',
1587
+ operation: action.operation || 'continue',
1588
+ payload: action.payload,
1589
+ risk: 'readonly' as const,
1590
+ }))
1591
+ : [{
1592
+ label: '开始一个任务',
1593
+ tool: 'rh_work' as const,
1594
+ operation: 'start',
1595
+ risk: 'readonly' as const,
1596
+ }],
1597
+ });
1598
+ }
1599
+
1600
+ export function normalizeRequestedChecks(ctx: ConsoleFacadeContext, requested: string[]) {
1601
+ return normalizeCheckIds(requested, listControllerChecks(ctx.repository.canonicalRoot));
1602
+ }
1603
+
1604
+ const ADVANCED_SCHEDULER_STALE_MS = 10_000;
1605
+
1606
+ function controllerObservation(ctx: ConsoleFacadeContext): ConnectivityComponentObservation {
1607
+ const daemon = readForgeRuntimeStatus(ctx.controllerHome);
1608
+ if (daemon.status === 'ready' && daemon.degraded !== true) {
1609
+ return createConnectivityObservation(
1610
+ 'controller_daemon',
1611
+ 'Forge Runtime',
1612
+ 'ready',
1613
+ 'Forge Runtime 处于 ready 状态。',
1614
+ daemon.pid ? [`pid=${daemon.pid}`] : [],
1615
+ );
1616
+ }
1617
+ if (daemon.status === 'ready' || daemon.status === 'starting') {
1618
+ return createConnectivityObservation(
1619
+ 'controller_daemon',
1620
+ 'Forge Runtime',
1621
+ 'degraded',
1622
+ daemon.error
1623
+ ? `Forge Runtime ${daemon.status},并带有降级信号:${daemon.error}`
1624
+ : `Forge Runtime 当前为 ${daemon.status}。`,
1625
+ [daemon.error ? `daemon=${daemon.error}` : `status=${daemon.status}`],
1626
+ );
1627
+ }
1628
+ return createConnectivityObservation(
1629
+ 'controller_daemon',
1630
+ 'Forge Runtime',
1631
+ 'failed',
1632
+ daemon.error
1633
+ ? `Forge Runtime 不可用:${daemon.error}`
1634
+ : `Forge Runtime 当前为 ${daemon.status}。`,
1635
+ [daemon.error ? `daemon=${daemon.error}` : `status=${daemon.status}`],
1636
+ );
1637
+ }
1638
+
1639
+ function schedulerObservation(ctx: ConsoleFacadeContext): ConnectivityComponentObservation {
1640
+ const snapshot = readSchedulerHealthSnapshot(ctx.controllerHome);
1641
+ const lastTick = snapshot.lastTickAt ? Date.parse(snapshot.lastTickAt) : Number.NaN;
1642
+ const ageMs = Number.isFinite(lastTick) ? Date.now() - lastTick : undefined;
1643
+ if (snapshot.loopStartedAt && typeof ageMs === 'number' && ageMs <= ADVANCED_SCHEDULER_STALE_MS) {
1644
+ return createConnectivityObservation(
1645
+ 'scheduler',
1646
+ 'Scheduler',
1647
+ 'ready',
1648
+ `最近一次 durable scheduler 心跳距今 ${Math.round(ageMs)}ms。`,
1649
+ [`lastTickAt=${snapshot.lastTickAt}`],
1650
+ );
1651
+ }
1652
+ if (snapshot.loopStartedAt && typeof ageMs === 'number') {
1653
+ return createConnectivityObservation(
1654
+ 'scheduler',
1655
+ 'Scheduler',
1656
+ 'degraded',
1657
+ `durable scheduler 心跳已陈旧(${Math.round(ageMs)}ms)。`,
1658
+ [`lastTickAt=${snapshot.lastTickAt}`],
1659
+ );
1660
+ }
1661
+ return createConnectivityObservation(
1662
+ 'scheduler',
1663
+ 'Scheduler',
1664
+ 'unknown',
1665
+ '尚未观察到 durable scheduler 心跳。',
1666
+ [],
1667
+ );
1668
+ }
1669
+
1670
+ function localMcpObservation(
1671
+ ctx: ConsoleFacadeContext,
1672
+ freshness: ConnectorFreshnessReport,
1673
+ ): ConnectivityComponentObservation {
1674
+ const runtime = loadMcpServiceRuntimeState(ctx.controllerHome, ctx.repository.canonicalRoot);
1675
+ if (
1676
+ freshness.status === 'local_mcp_updated'
1677
+ || freshness.status === 'chatgpt_snapshot_missing_facade'
1678
+ || freshness.status === 'unable_to_verify_chatgpt_snapshot'
1679
+ ) {
1680
+ return createConnectivityObservation(
1681
+ 'local_mcp',
1682
+ 'Local MCP',
1683
+ 'ready',
1684
+ freshness.summary || 'Local MCP /health 可达且工具面匹配。',
1685
+ runtime?.server?.lastHealthyAt ? [`lastHealthyAt=${runtime.server.lastHealthyAt}`] : [],
1686
+ );
1687
+ }
1688
+ if (freshness.status === 'local_mcp_missing_facade' || freshness.status === 'stale_fingerprint') {
1689
+ return createConnectivityObservation(
1690
+ 'local_mcp',
1691
+ 'Local MCP',
1692
+ 'degraded',
1693
+ freshness.summary,
1694
+ freshness.missingLocalTools.slice(0, 5).map((tool) => `missing_local_tool=${tool}`),
1695
+ );
1696
+ }
1697
+ if (runtime?.status === 'running' || runtime?.status === 'degraded') {
1698
+ return createConnectivityObservation(
1699
+ 'local_mcp',
1700
+ 'Local MCP',
1701
+ 'degraded',
1702
+ '存在 MCP runtime 状态,但当前未确认 live /health 为 healthy。',
1703
+ runtime.server.lastExit ? [`lastExit=${runtime.server.lastExit}`] : [],
1704
+ );
1705
+ }
1706
+ return createConnectivityObservation(
1707
+ 'local_mcp',
1708
+ 'Local MCP',
1709
+ 'failed',
1710
+ '未确认本地 MCP /health 可达。',
1711
+ [],
1712
+ );
1713
+ }
1714
+
1715
+ function publicMcpObservation(ctx: ConsoleFacadeContext): ConnectivityComponentObservation {
1716
+ const config = loadMcpServiceLocalConfig(ctx.controllerHome, ctx.repository.canonicalRoot);
1717
+ const runtime = loadMcpServiceRuntimeState(ctx.controllerHome, ctx.repository.canonicalRoot);
1718
+ const publicEndpoint = config?.chatgpt?.endpoint ?? runtime?.tunnel?.publicEndpoint;
1719
+ if (!publicEndpoint) {
1720
+ return createConnectivityObservation(
1721
+ 'public_mcp',
1722
+ 'Public MCP endpoint',
1723
+ 'unknown',
1724
+ '当前没有配置公网 MCP endpoint。',
1725
+ [],
1726
+ );
1727
+ }
1728
+ if (runtime?.tunnel?.healthy === true && runtime.tunnel.connectorNeedsReconnect !== true) {
1729
+ return createConnectivityObservation(
1730
+ 'public_mcp',
1731
+ 'Public MCP endpoint',
1732
+ 'ready',
1733
+ '公网 MCP endpoint 最近一次发现检查通过。',
1734
+ [`endpoint=${publicEndpoint}`],
1735
+ );
1736
+ }
1737
+ if (runtime?.tunnel?.connectorNeedsReconnect === true) {
1738
+ return createConnectivityObservation(
1739
+ 'public_mcp',
1740
+ 'Public MCP endpoint',
1741
+ 'degraded',
1742
+ '公网 MCP endpoint 发生变化,ChatGPT Connector 需要重连。',
1743
+ [`endpoint=${publicEndpoint}`],
1744
+ );
1745
+ }
1746
+ if (runtime?.tunnel?.healthy === false || runtime?.status === 'degraded') {
1747
+ return createConnectivityObservation(
1748
+ 'public_mcp',
1749
+ 'Public MCP endpoint',
1750
+ 'degraded',
1751
+ '公网 MCP endpoint 最近一次发现检查未通过或 tunnel 处于降级状态。',
1752
+ [`endpoint=${publicEndpoint}`],
1753
+ );
1754
+ }
1755
+ return createConnectivityObservation(
1756
+ 'public_mcp',
1757
+ 'Public MCP endpoint',
1758
+ 'unknown',
1759
+ '公网 MCP endpoint 已配置,但当前没有足够健康证据。',
1760
+ [`endpoint=${publicEndpoint}`],
1761
+ );
1762
+ }
1763
+
1764
+ function localBridgeObservation(): ConnectivityComponentObservation {
1765
+ return createConnectivityObservation(
1766
+ 'local_bridge',
1767
+ 'Local Bridge',
1768
+ 'ready',
1769
+ '高级诊断请求已经到达 Local Bridge HTTP 进程。',
1770
+ [],
1771
+ );
1772
+ }
1773
+
1774
+ function connectorSignals(ctx: ConsoleFacadeContext, freshness: ConnectorFreshnessReport): string[] {
1775
+ const runtime = loadMcpServiceRuntimeState(ctx.controllerHome, ctx.repository.canonicalRoot);
1776
+ const signals: string[] = [];
1777
+ if (freshness.status === 'chatgpt_snapshot_missing_facade' || freshness.status === 'unable_to_verify_chatgpt_snapshot') {
1778
+ signals.push(freshness.summary);
1779
+ }
1780
+ if (runtime?.tunnel?.connectorNeedsReconnect === true) {
1781
+ signals.push('Public MCP endpoint changed; reconnect the ChatGPT connector.');
1782
+ }
1783
+ return signals;
1784
+ }
1785
+
1786
+ async function buildConnectivityObservability(
1787
+ ctx: ConsoleFacadeContext,
1788
+ freshness: ConnectorFreshnessReport,
1789
+ ) {
1790
+ const jobs = listExecutionJobs(ctx.controllerHome, ctx.repository.repoId, 6);
1791
+ return recordConnectivityProbe({
1792
+ controllerHome: ctx.controllerHome,
1793
+ repoId: ctx.repository.repoId,
1794
+ observations: [
1795
+ localMcpObservation(ctx, freshness),
1796
+ publicMcpObservation(ctx),
1797
+ localBridgeObservation(),
1798
+ controllerObservation(ctx),
1799
+ schedulerObservation(ctx),
1800
+ ],
1801
+ connectorSignals: connectorSignals(ctx, freshness),
1802
+ jobTimings: deriveExecutionJobTimingSnapshots(ctx.controllerHome, ctx.repository.repoId, jobs),
1803
+ });
1804
+ }
1805
+
1806
+ /**
1807
+ * Interactive safe patch for console/GUI: always sync, returns readable digest.
1808
+ */
1809
+ export function applyConsoleSafePatch(
1810
+ ctx: ConsoleFacadeContext,
1811
+ input: {
1812
+ operations: unknown;
1813
+ purpose?: string;
1814
+ allowedPaths?: string[];
1815
+ sessionId?: string;
1816
+ },
1817
+ ): Record<string, unknown> {
1818
+ const applied = withControllerLock(
1819
+ ctx.controllerHome,
1820
+ { scope: 'repository', repoId: ctx.repository.repoId },
1821
+ 'console:safe_patch_apply',
1822
+ () => applySafePatch(ctx.repository, {
1823
+ sessionId: input.sessionId,
1824
+ purpose: input.purpose ?? 'Console interactive edit',
1825
+ operations: input.operations,
1826
+ allowedPaths: input.allowedPaths,
1827
+ refreshFingerprints: true,
1828
+ recoverStaleSession: true,
1829
+ }),
1830
+ 60_000,
1831
+ );
1832
+ const changedFiles = [...new Set((applied.appliedChunks ?? []).flatMap((chunk) => chunk.paths ?? []))];
1833
+ const ok = applied.status === 'applied';
1834
+ const firstFailure = applied.failures?.[0];
1835
+ const digest = buildSyncOperationDigest({
1836
+ ok,
1837
+ operation: 'safe_patch_apply',
1838
+ summary: ok
1839
+ ? `已保存修改(${changedFiles.length} 个文件)。`
1840
+ : `修改失败:${firstFailure?.message || '请检查路径或内容后重试'}`,
1841
+ changedFiles,
1842
+ errorClass: ok ? undefined : classifyUserFacingError({
1843
+ code: firstFailure?.code,
1844
+ message: firstFailure?.message,
1845
+ }),
1846
+ errorMessage: firstFailure?.message,
1847
+ });
1848
+ return {
1849
+ ...applied,
1850
+ digest,
1851
+ summary: digest.summary,
1852
+ phase: digest.phase,
1853
+ statusLabel: digest.statusLabel,
1854
+ terminal: true,
1855
+ applyMode: 'sync',
1856
+ changedFiles,
1857
+ errorClass: digest.errorClass,
1858
+ errorMessage: digest.errorMessage,
1859
+ suggestedNextActions: digest.suggestedNextActions,
1860
+ };
1861
+ }
1862
+
1863
+ /** Keep advanced/debug payload separate from primary console models. */
1864
+ export async function buildAdvancedDiagnosticsEnvelope(rawSnapshot: unknown, ctx: ConsoleFacadeContext) {
1865
+ const readiness = await buildSystemReadiness(ctx);
1866
+ const connector = await evaluateConsoleConnectorFreshness(ctx);
1867
+ const connectivity = await buildConnectivityObservability(ctx, connector);
1868
+ return {
1869
+ schemaVersion: 1,
1870
+ note: '高级诊断仅供排错。日常任务请使用控制台主流程。',
1871
+ readinessSummary: readiness,
1872
+ preferredTools: [...EXPECTED_FACADE_TOOLS],
1873
+ connectorFreshness: connector,
1874
+ connectivity,
1875
+ raw: rawSnapshot,
1876
+ };
1877
+ }