@moretea-labs/forge 1.5.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +46 -0
  3. package/CLAUDE.md +2 -0
  4. package/DESIGN.md +7 -2
  5. package/README.md +4 -4
  6. package/README.zh-CN.md +2 -2
  7. package/SKILL.md +2 -2
  8. package/THIRD_PARTY_NOTICES.md +1 -1
  9. package/agents/openai.yaml +1 -1
  10. package/assets/hooks/hook-input.sh +11 -8
  11. package/assets/hooks/lib/session-state.sh +10 -7
  12. package/assets/hooks/lib/workflow-state.sh +13 -177
  13. package/assets/hooks/post-tool-observer.sh +8 -2
  14. package/assets/hooks/pre-edit-guard.sh +8 -39
  15. package/assets/hooks/prompt-guard.sh +7 -83
  16. package/assets/hooks/session-start-context.sh +56 -1
  17. package/assets/partials/05-workflow.partial.md +1 -1
  18. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  19. package/assets/plugin-registry.v1.json +54 -3
  20. package/assets/reference-configs/document-generation.md +1 -1
  21. package/assets/reference-configs/harness-overview.md +1 -1
  22. package/assets/reference-configs/hook-operations.md +1 -1
  23. package/assets/reference-configs/sprint-contracts.md +3 -3
  24. package/assets/skill-version.json +2 -2
  25. package/assets/templates/contract.template.md +2 -7
  26. package/assets/templates/helpers/capture-plan.sh +2 -2
  27. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  28. package/assets/templates/helpers/contract-worktree.sh +1 -21
  29. package/assets/templates/helpers/ensure-task-workflow.sh +13 -38
  30. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  31. package/assets/templates/helpers/new-plan.sh +1 -1
  32. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  33. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  34. package/assets/templates/helpers/sprint-backlog.sh +6 -6
  35. package/assets/templates/helpers/verify-contract.sh +1 -111
  36. package/assets/templates/helpers/verify-sprint.sh +3 -180
  37. package/assets/templates/plan.template.md +1 -1
  38. package/assets/templates/review.template.md +9 -34
  39. package/assets/workflow-contract.v1.json +10 -19
  40. package/docs/README.md +10 -10
  41. package/docs/architecture/CURRENT.md +170 -0
  42. package/docs/architecture/EVOLUTION.md +42 -0
  43. package/docs/architecture/current/README.md +4 -156
  44. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  45. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  46. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  47. package/docs/architecture/history/README.md +7 -0
  48. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  49. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  50. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  51. package/docs/architecture/history.md +3 -34
  52. package/docs/architecture/index.md +15 -98
  53. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  54. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  55. package/docs/architecture/versions/1.6.md +38 -0
  56. package/docs/architecture/versions/1.7.md +41 -0
  57. package/docs/forge-chatgpt-browser-engine.md +2 -2
  58. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  59. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  60. package/docs/forge-github-issue-launcher.md +1 -1
  61. package/docs/forge-runtime-self-healing-loop.md +1 -1
  62. package/docs/operations/controller-browser-plugin.md +38 -22
  63. package/docs/operations/controller-performance-and-502.md +124 -0
  64. package/docs/operations/controller-reliability-runbook.md +1 -1
  65. package/docs/operations/features.md +3 -3
  66. package/docs/operations/github-repository.md +1 -1
  67. package/docs/operations/homebrew.md +2 -2
  68. package/docs/operations/mcp-tool-exposure.md +5 -3
  69. package/docs/tutorials/01-install-and-start.md +1 -1
  70. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  71. package/docs/wiki/Architecture.md +6 -6
  72. package/docs/wiki/Implementation.md +2 -2
  73. package/docs/wiki/Installation.md +1 -1
  74. package/docs/wiki/Integrations.md +2 -2
  75. package/docs/wiki/Quick-Start.md +1 -1
  76. package/docs/wiki/Work-Lifecycle.md +1 -1
  77. package/package.json +29 -10
  78. package/scripts/activate-source-baseline.ts +17 -13
  79. package/scripts/benchmark-background-check-overlap.ts +228 -0
  80. package/scripts/benchmark-mcp-host.sh +117 -0
  81. package/scripts/benchmark-mcp-transport.ts +170 -0
  82. package/scripts/benchmark-quality-harness.ts +146 -0
  83. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  84. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  85. package/scripts/benchmark-typescript-navigation.ts +186 -0
  86. package/scripts/benchmark-work-efficiency.ts +334 -0
  87. package/scripts/capture-plan.sh +3 -3
  88. package/scripts/check-agent-tooling.sh +8 -35
  89. package/scripts/check-architecture-sync.sh +17 -86
  90. package/scripts/check-mcp-compatibility.ts +14 -0
  91. package/scripts/check-public-docs.sh +14 -1
  92. package/scripts/check-runtime-architecture.mjs +107 -64
  93. package/scripts/check-stable-baseline.ts +92 -0
  94. package/scripts/check-task-workflow.sh +4 -4
  95. package/scripts/contract-worktree.sh +1 -21
  96. package/scripts/ensure-task-workflow.sh +22 -55
  97. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  98. package/scripts/install-standalone-recovery.ts +24 -0
  99. package/scripts/lib/project-init-lib.sh +15 -44
  100. package/scripts/migrate-chatgpt-automation-schedules.ts +64 -0
  101. package/scripts/migrate-project-template.sh +23 -1179
  102. package/scripts/new-plan.sh +1 -1
  103. package/scripts/plan-to-todo.sh +11 -36
  104. package/scripts/run-governed-gate.ts +2 -1
  105. package/scripts/ship-worktrees.sh +2 -29
  106. package/scripts/smoke-ios-physical-device.ts +4 -2
  107. package/scripts/smoke-runtime-recovery.ts +1 -35
  108. package/scripts/smoke-schedule-engine.ts +1 -20
  109. package/scripts/sprint-backlog.sh +1 -1
  110. package/scripts/verify-contract.sh +1 -111
  111. package/scripts/verify-forge-runtime.sh +0 -1
  112. package/scripts/verify-sprint.sh +3 -180
  113. package/scripts/workflow-contract.ts +1 -5
  114. package/src/cli/commands/chatgpt.ts +31 -2
  115. package/src/cli/commands/init-hook.ts +3 -3
  116. package/src/cli/commands/mcp.ts +9 -5
  117. package/src/cli/commands/plugin.ts +37 -10
  118. package/src/cli/commands/recovery.ts +25 -3
  119. package/src/cli/commands/runtime.ts +55 -3
  120. package/src/cli/commands/security.ts +12 -1
  121. package/src/cli/commands/setup-profile.ts +31 -21
  122. package/src/cli/controller/check-runner.ts +106 -3
  123. package/src/cli/controller/composite-result.ts +1 -1
  124. package/src/cli/controller/context/focus.ts +51 -0
  125. package/src/cli/controller/context/instruction-context.ts +114 -0
  126. package/src/cli/controller/context/known-paths.ts +76 -0
  127. package/src/cli/controller/context/query-planning.ts +110 -0
  128. package/src/cli/controller/context/rule-resolution.ts +532 -0
  129. package/src/cli/controller/context/source-materializer.ts +278 -0
  130. package/src/cli/controller/context/structural.ts +16 -0
  131. package/src/cli/controller/context/types.ts +286 -0
  132. package/src/cli/controller/context-pack.ts +898 -430
  133. package/src/cli/controller/launch-agents.ts +77 -2
  134. package/src/cli/controller/operational-plan.ts +2 -2
  135. package/src/cli/controller/work-mode.ts +6 -4
  136. package/src/cli/editing/edit-session.ts +60 -7
  137. package/src/cli/github/github.ts +133 -0
  138. package/src/cli/hook/prompt-guard-decision.ts +17 -18
  139. package/src/cli/hook/prompt-intents.ts +1 -1
  140. package/src/cli/installer/managed-entries.ts +15 -1
  141. package/src/cli/local-bridge/console-automations.ts +225 -67
  142. package/src/cli/local-bridge/console-view-models.ts +3 -35
  143. package/src/cli/local-bridge/facade-api.ts +2 -217
  144. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  145. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +153 -3
  146. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  147. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +183 -3
  148. package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +1 -1
  149. package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +4 -3
  150. package/src/cli/local-bridge/frontend/src/lib/api.ts +18 -11
  151. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  152. package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +1 -1
  153. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +4 -3
  154. package/src/cli/local-bridge/frontend/src/styles.css +27 -11
  155. package/src/cli/local-bridge/frontend/src/types.ts +8 -7
  156. package/src/cli/local-bridge/job-store.ts +1 -490
  157. package/src/cli/local-bridge/server.ts +29 -532
  158. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  159. package/src/cli/local-bridge/ui-dist/app.js +5101 -4967
  160. package/src/cli/mcp/auth.ts +5 -5
  161. package/src/cli/mcp/instructions.ts +3 -2
  162. package/src/cli/mcp/legacy-tool-service.ts +0 -189
  163. package/src/cli/mcp/multi-repository.ts +16 -5
  164. package/src/cli/mcp/oauth.ts +175 -26
  165. package/src/cli/mcp/paths.ts +8 -2
  166. package/src/cli/mcp/repository-tools.ts +356 -101
  167. package/src/cli/mcp/server.ts +625 -54
  168. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  169. package/src/cli/mcp/toolset-selection.ts +1 -1
  170. package/src/cli/mcp/toolset.ts +42 -5
  171. package/src/cli/mcp/transports/http.ts +125 -15
  172. package/src/cli/mcp/transports/session-registry.ts +67 -5
  173. package/src/cli/mcp/types.ts +3 -3
  174. package/src/cli/repositories/command-classifier.ts +209 -14
  175. package/src/cli/repositories/command-executor.ts +55 -521
  176. package/src/cli/repositories/command-process.ts +108 -0
  177. package/src/cli/repositories/command-scope.ts +99 -41
  178. package/src/cli/repositories/controller-home.ts +7 -8
  179. package/src/cli/repositories/git-command-executor.ts +2 -0
  180. package/src/cli/repositories/locks.ts +53 -16
  181. package/src/cli/repositories/repository-snapshot.ts +121 -0
  182. package/src/cli/repositories/safe-patch.ts +6 -4
  183. package/src/cli/repositories/selected-path-actions.ts +17 -9
  184. package/src/cli/repositories/structured-git.ts +110 -10
  185. package/src/cli/repository/inspector.ts +565 -62
  186. package/src/cli/repository/session-cache.ts +58 -17
  187. package/src/cli/v81-entry.ts +1 -4
  188. package/src/effects/fs-transaction.ts +85 -17
  189. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  190. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  191. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  192. package/src/runtime/context/lsp-client.ts +283 -0
  193. package/src/runtime/context/semantic-navigation.ts +275 -0
  194. package/src/runtime/context/swift-navigation.ts +565 -0
  195. package/src/runtime/context/typescript-navigation.ts +226 -0
  196. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  197. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +196 -2
  198. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  199. package/src/runtime/control-plane/execution/execution-identity.ts +65 -14
  200. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  201. package/src/runtime/control-plane/execution/validation.ts +15 -2
  202. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  203. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  204. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  205. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  206. package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
  207. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +175 -8
  208. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -21
  209. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +87 -9
  210. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  211. package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
  212. package/src/runtime/control-plane/facade/controller-session-store.ts +144 -14
  213. package/src/runtime/control-plane/facade/goal-workloop-access.ts +9 -2
  214. package/src/runtime/control-plane/facade/goal-workloop.ts +885 -105
  215. package/src/runtime/control-plane/facade/index.ts +1 -0
  216. package/src/runtime/control-plane/facade/plan-contract-store.ts +211 -26
  217. package/src/runtime/control-plane/facade/requirement-board.ts +24 -8
  218. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  219. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  220. package/src/runtime/control-plane/facade/types.ts +55 -11
  221. package/src/runtime/control-plane/facade/work-contract-store.ts +204 -73
  222. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  223. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  224. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  225. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  226. package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
  227. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  228. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  229. package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
  230. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  231. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  232. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  233. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  234. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  235. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  236. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  237. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  238. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  239. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  240. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
  241. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +642 -51
  242. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
  243. package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
  244. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  245. package/src/runtime/control-plane/persistence/requirement-store.ts +15 -11
  246. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  247. package/src/runtime/control-plane/release-retention.ts +62 -2
  248. package/src/runtime/control-plane/routing/route-policy.ts +7 -16
  249. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  250. package/src/runtime/diagnostics/mcp-timing.ts +19 -0
  251. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  252. package/src/runtime/diagnostics/performance.ts +24 -1
  253. package/src/runtime/evidence/event-ledger.ts +1 -1
  254. package/src/runtime/execution/jobs/store.ts +58 -4
  255. package/src/runtime/execution/jobs/types.ts +1 -1
  256. package/src/runtime/execution/managed-workspace.ts +203 -1
  257. package/src/runtime/execution/process-runtime/check-facade.ts +52 -14
  258. package/src/runtime/execution/process-runtime/check-result.ts +63 -2
  259. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  260. package/src/runtime/execution/process-runtime/command-facade.ts +346 -40
  261. package/src/runtime/execution/process-runtime/gc.ts +98 -12
  262. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
  263. package/src/runtime/execution/process-runtime/persisted-check.ts +57 -6
  264. package/src/runtime/execution/process-runtime/resource-claims.ts +97 -8
  265. package/src/runtime/execution/process-runtime/runtime.ts +59 -4
  266. package/src/runtime/execution/process-runtime/store.ts +7 -0
  267. package/src/runtime/execution/process-runtime/types.ts +2 -0
  268. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  269. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  270. package/src/runtime/execution/workers/executor.ts +0 -48
  271. package/src/runtime/gateway/mcp/execution-tools.ts +1219 -126
  272. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  273. package/src/runtime/gateway/mcp/router.ts +185 -83
  274. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
  275. package/src/runtime/gateway/mcp/runtime-tools.ts +2555 -1616
  276. package/src/runtime/model-clients/deepseek-adapter.ts +0 -32
  277. package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
  278. package/src/runtime/plugins/browser-adapter.ts +2925 -318
  279. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  280. package/src/runtime/plugins/browser-handoff-host.ts +6 -31
  281. package/src/runtime/plugins/browser-handoff.ts +92 -5
  282. package/src/runtime/plugins/browser-macos-bridge.ts +586 -45
  283. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  284. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  285. package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
  286. package/src/runtime/plugins/browser-runtime.ts +183 -0
  287. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  288. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  289. package/src/runtime/plugins/desktop-operator-registration.ts +15 -12
  290. package/src/runtime/plugins/external-adapter.ts +324 -3
  291. package/src/runtime/plugins/external-registration.ts +84 -5
  292. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  293. package/src/runtime/plugins/first-party-registry.ts +4 -2
  294. package/src/runtime/plugins/github-adapter.ts +125 -0
  295. package/src/runtime/plugins/gmail-adapter.ts +28 -2
  296. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  297. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  298. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  299. package/src/runtime/plugins/ios-adapter.ts +106 -16
  300. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  301. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  302. package/src/runtime/plugins/lightweight-action.ts +153 -0
  303. package/src/runtime/plugins/local-system-adapter.ts +125 -8
  304. package/src/runtime/plugins/macos-capability-broker.ts +55 -9
  305. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  306. package/src/runtime/plugins/plugin-management-adapter.ts +181 -0
  307. package/src/runtime/plugins/resend-adapter.ts +1 -1
  308. package/src/runtime/plugins/store.ts +366 -24
  309. package/src/runtime/plugins/types.ts +39 -2
  310. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  311. package/src/runtime/projections/git-status-sampler.ts +16 -16
  312. package/src/runtime/projections/materialized-view.ts +28 -10
  313. package/src/runtime/recovery/actions.ts +0 -10
  314. package/src/runtime/recovery/classifier.ts +0 -9
  315. package/src/runtime/recovery/maintenance-executor.ts +415 -18
  316. package/src/runtime/recovery/probe.ts +0 -1
  317. package/src/runtime/recovery/types.ts +0 -1
  318. package/src/runtime/root/mcp-transport.ts +180 -10
  319. package/src/runtime/root/package-connector-service.ts +186 -8
  320. package/src/runtime/root/package-runtime-release.ts +143 -17
  321. package/src/runtime/root/package-runtime-service.ts +430 -22
  322. package/src/runtime/root/release-manifest.ts +7 -2
  323. package/src/runtime/root/release-materialize.ts +162 -19
  324. package/src/runtime/root/release-store.ts +48 -11
  325. package/src/runtime/root/runtime.ts +23 -5
  326. package/src/runtime/root/service.ts +100 -27
  327. package/src/runtime/root/types.ts +2 -0
  328. package/src/runtime/safe-tooling/index.ts +0 -1
  329. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  330. package/src/runtime/safe-tooling/types.ts +0 -30
  331. package/src/runtime/shared/trusted-executable.ts +62 -0
  332. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  333. package/src/runtime/standalone-recovery/core.ts +702 -94
  334. package/src/runtime/standalone-recovery/entry.ts +99 -23
  335. package/src/runtime/standalone-recovery/installer.ts +19 -3
  336. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  337. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  338. package/src/runtime/workflow/schedules/browser-probe.ts +258 -0
  339. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +40 -0
  340. package/src/runtime/workflow/schedules/engine.ts +509 -80
  341. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  342. package/src/runtime/workflow/schedules/settlement.ts +48 -4
  343. package/src/runtime/workflow/schedules/store.ts +131 -10
  344. package/src/runtime/workflow/schedules/types.ts +6 -3
  345. package/src/runtime/workflow/schedules/work-continuation.ts +120 -34
  346. package/src/testing/test-governance.ts +8 -40
  347. package/assets/reference-configs/heartbeat-triage.md +0 -57
  348. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  349. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  350. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  351. package/bin/forge-desktop-helper.mjs +0 -204
  352. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  353. package/docs/architecture/current/architecture-invariants.md +0 -331
  354. package/docs/architecture/current/automation-and-schedule-engine.md +0 -453
  355. package/docs/architecture/current/codegraph-context-provider.md +0 -226
  356. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  357. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  358. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -354
  359. package/docs/architecture/current/entity-model.md +0 -592
  360. package/docs/architecture/current/external-plugin-governance.md +0 -396
  361. package/docs/architecture/current/failure-recovery.md +0 -447
  362. package/docs/architecture/current/governance.md +0 -213
  363. package/docs/architecture/current/human-interaction-plane.md +0 -87
  364. package/docs/architecture/current/implementation-status.md +0 -128
  365. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  366. package/docs/architecture/current/legacy-governance.md +0 -58
  367. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  368. package/docs/architecture/current/migration-roadmap.md +0 -105
  369. package/docs/architecture/current/multi-repository-execution.md +0 -371
  370. package/docs/architecture/current/operations-runbook.md +0 -45
  371. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  372. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  373. package/docs/architecture/current/repository-access-modes.md +0 -102
  374. package/docs/architecture/current/route-policy.md +0 -15
  375. package/docs/architecture/current/route-session-concurrency.md +0 -85
  376. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  377. package/docs/architecture/current/runtime-directory-map.md +0 -49
  378. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  379. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  380. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  381. package/docs/architecture/current/system-overview.md +0 -144
  382. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  383. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  384. package/docs/architecture/current/thin-harness-v1.md +0 -311
  385. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  386. package/docs/forge-autonomous-goal-loop.md +0 -9
  387. package/docs/forge-provider-configuration.md +0 -7
  388. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -83
  389. package/docs/operations/controller-desktop-plugin.md +0 -112
  390. package/docs/operations/gmail-assistant-routines.md +0 -186
  391. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  392. package/docs/operations/provider-configuration.md +0 -31
  393. package/scripts/harness-trace-grade.sh +0 -150
  394. package/scripts/heartbeat-triage.sh +0 -361
  395. package/scripts/maintenance-triage.sh +0 -167
  396. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  397. package/src/cli/repositories/goal-registry.ts +0 -239
  398. package/src/runtime/assistant/action-proposals.ts +0 -279
  399. package/src/runtime/assistant/intent.ts +0 -395
  400. package/src/runtime/assistant/model-provider.ts +0 -460
  401. package/src/runtime/assistant/openapi.ts +0 -223
  402. package/src/runtime/assistant/policy.ts +0 -89
  403. package/src/runtime/assistant/readiness.ts +0 -258
  404. package/src/runtime/assistant/routine-runtime.ts +0 -477
  405. package/src/runtime/assistant/schedule-binding.ts +0 -171
  406. package/src/runtime/assistant/standing-grants.ts +0 -375
  407. package/src/runtime/assistant/store.ts +0 -243
  408. package/src/runtime/assistant/types.ts +0 -135
  409. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  410. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  411. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  412. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  413. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  414. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  415. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  416. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  417. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  418. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  419. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  420. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  421. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  422. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  423. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  424. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  425. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  426. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  427. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  428. package/src/runtime/plugins/desktop-adapter.ts +0 -374
  429. package/src/runtime/safe-tooling/browser-safe.ts +0 -133
  430. package/src/runtime/workflow/findings/store.ts +0 -204
  431. package/src/runtime/workflow/findings/types.ts +0 -58
  432. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  433. package/src/runtime/workflow/portfolio/store.ts +0 -185
  434. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -1,3 +1,183 @@
1
- import type{ConsoleData,Dict}from'../../types';import{CommandBar}from'../../shell/CommandBar';import{StatusText}from'../../components/StatusText';import{SectionHeader}from'../../components/SectionHeader';import{compact}from'../../lib/format';
2
- function attentionKey(h:Dict){return`${String(h.title??'')}:${String(h.reason??'')}`}
3
- export function OverviewView({data,busy,onRefresh}:{data:ConsoleData;busy:boolean;onRefresh:()=>void}){const cc=data.commandCenter,a=data.automations.summary,w=data.work,p=cc.pluginSummary??{},repos=cc.repositories??[],readiness=cc.readiness??{};const unique=[...(cc.handoffs??[])].filter((h,i,all)=>all.findIndex(x=>attentionKey(x)===attentionKey(h))===i);const workAttention=w.waitingForUserCount??0;const attention=[...unique.slice(0,3),...(workAttention?[{title:'Persistent work needs attention',reason:`${workAttention} 项长期目标需要确认`,statusLabel:'Review',tone:'warning'}]:[]),...((p.needsAttention??0)?[{title:'Capabilities need attention',reason:`${p.needsAttention} 项能力需要配置或检查`,statusLabel:'Inspect',tone:'warning'}]:[])].slice(0,4)as Dict[];return <><CommandBar eyebrow="FORGE" title="Overview" description="长期配置与系统可用性。执行结果和通知继续由 ChatGPT 承担。" refreshedAt={data.generatedAt} busy={busy} onRefresh={onRefresh}/><div className="metric-strip"><a href="#/automations"><span>Automations</span><strong>{a.enabled}</strong><small>{a.paused?`${a.paused} paused`:'enabled'}</small></a><a href="#/capabilities"><span>Capabilities</span><strong>{p.ready??0}/{p.total??(cc.plugins??[]).length}</strong><small>ready</small></a><a href="#/repositories"><span>Repositories</span><strong>{repos.length}</strong><small>registered</small></a><a href="#/work"><span>Persistent work</span><strong>{w.activeRequirementCount??0}</strong><small>{w.waitingForUserCount??0} waiting</small></a></div><section className="page-section"><SectionHeader title="Needs attention" meta={attention.length?`${attention.length} actionable groups`:'All clear'}/>{attention.length?<div className="attention-list">{attention.map((h,i)=><div className="attention-row" key={`${String(h.title)}-${i}`}><span className="attention-index">{String(i+1).padStart(2,'0')}</span><div><strong>{String(h.title??'需要处理')}</strong><p>{compact(String(h.reason??'请在 ChatGPT 中确认'),120)}</p></div><StatusText label={String(h.statusLabel??'Review')} tone={String(h.tone??'warning')}/></div>)}</div>:<div className="quiet-empty"><StatusText label={String(readiness.label??readiness.headline??'Ready')} tone={String(readiness.state??'success')}/><p>当前没有需要你在控制台处理的配置问题。</p></div>}</section><section className="page-section"><SectionHeader title="Configured work" meta="只显示持久化目标,不展示执行流水"/><div className="compact-work-list">{(w.requirements??[]).filter(x=>x.state!=='done'&&x.state!=='cancelled').slice(0,5).map(x=><a href="#/work" key={x.requirementId}><div><strong>{x.title}</strong><p>{compact(x.outcome,100)}</p></div><StatusText label={x.state==='waiting_for_user'?'Waiting':x.state==='planned'?'Planned':'Active'} tone={x.state}/></a>)}</div></section></>}
1
+ import type { ConsoleData, Dict, WorkPortfolioItem } from '../../types';
2
+ import { CommandBar } from '../../shell/CommandBar';
3
+ import { StatusText } from '../../components/StatusText';
4
+ import { SectionHeader } from '../../components/SectionHeader';
5
+ import { compact } from '../../lib/format';
6
+
7
+ type AttentionItem = {
8
+ key: string;
9
+ source: string;
10
+ title: string;
11
+ summary: string;
12
+ statusLabel: string;
13
+ tone: string;
14
+ href: string;
15
+ };
16
+
17
+ type WorkspaceRow = {
18
+ key: string;
19
+ label: string;
20
+ summary: string;
21
+ href: string;
22
+ statusLabel?: string;
23
+ tone?: string;
24
+ };
25
+
26
+ function workStatus(item: WorkPortfolioItem): string {
27
+ return item.advanced?.status ?? '';
28
+ }
29
+
30
+ function isAttentionWork(item: WorkPortfolioItem): boolean {
31
+ return ['blocked', 'failed'].includes(workStatus(item));
32
+ }
33
+
34
+ function attentionKey(item: Dict): string {
35
+ return `${String(item.title ?? '')}:${String(item.reason ?? '')}`;
36
+ }
37
+
38
+ function repositoryNeedsAttention(repository: NonNullable<ConsoleData['commandCenter']['repositories']>[number]): boolean {
39
+ const state = `${repository.readinessLabel ?? ''} ${repository.statusLabel ?? ''}`;
40
+ return /error|failed|blocked|unavailable|degraded|warning|attention/i.test(state);
41
+ }
42
+
43
+ export function OverviewView({ data, busy, onRefresh }: { data: ConsoleData; busy: boolean; onRefresh: () => void }) {
44
+ const cc = data.commandCenter;
45
+ const portfolio = data.workPortfolio;
46
+ const automations = data.automations.summary;
47
+ const plugins = cc.pluginSummary ?? {};
48
+ const repositories = cc.repositories ?? [];
49
+ const readiness = cc.readiness ?? {};
50
+ const capabilityTotal = plugins.total ?? (cc.plugins ?? []).length;
51
+ const repositoryAttention = repositories.filter(repositoryNeedsAttention).length;
52
+ const readinessState = String(readiness.state ?? readiness.status ?? 'ready');
53
+ const runtimeNeedsAttention = /error|failed|blocked|unavailable|degraded|warning|attention/i.test(readinessState);
54
+ const runtimeLabel = String(readiness.label ?? readiness.headline ?? (runtimeNeedsAttention ? 'Needs attention' : 'Ready'));
55
+
56
+ const attentionWork = portfolio.items.filter(isAttentionWork).slice(0, 4);
57
+ const uniqueHandoffs = [...(cc.handoffs ?? [])].filter((handoff, index, all) => all.findIndex((candidate) => attentionKey(candidate) === attentionKey(handoff)) === index);
58
+
59
+ const attention: AttentionItem[] = [
60
+ ...(runtimeNeedsAttention ? [{
61
+ key: 'runtime',
62
+ source: 'System',
63
+ title: runtimeLabel,
64
+ summary: compact(String(readiness.explanation ?? readiness.summary ?? 'Inspect Runtime status.'), 112),
65
+ statusLabel: 'Inspect',
66
+ tone: readinessState,
67
+ href: '#/system',
68
+ }] : []),
69
+ ...attentionWork.map((item) => ({
70
+ key: `work:${item.id}`,
71
+ source: `Work · ${item.repositoryName}`,
72
+ title: item.title,
73
+ summary: compact(item.latestSummary || item.nextAction || item.objective, 112),
74
+ statusLabel: item.statusLabel,
75
+ tone: item.tone ?? 'warning',
76
+ href: '#/work',
77
+ })),
78
+ ...uniqueHandoffs.slice(0, 2).map((handoff, index) => ({
79
+ key: `handoff:${index}:${attentionKey(handoff)}`,
80
+ source: 'Decision',
81
+ title: String(handoff.title ?? 'Needs review'),
82
+ summary: compact(String(handoff.reason ?? handoff.summary ?? 'Review in ChatGPT.'), 112),
83
+ statusLabel: String(handoff.statusLabel ?? 'Review'),
84
+ tone: String(handoff.tone ?? 'warning'),
85
+ href: '#/work',
86
+ })),
87
+ ...((automations.needsAttention ?? 0) > 0 ? [{
88
+ key: 'automations',
89
+ source: 'Automations',
90
+ title: `${automations.needsAttention} automation${automations.needsAttention === 1 ? '' : 's'} need attention`,
91
+ summary: 'Inspect configured schedules.',
92
+ statusLabel: 'Inspect',
93
+ tone: 'warning',
94
+ href: '#/automations',
95
+ }] : []),
96
+ ...((plugins.needsAttention ?? 0) > 0 ? [{
97
+ key: 'capabilities',
98
+ source: 'Capabilities',
99
+ title: `${plugins.needsAttention} ${plugins.needsAttention === 1 ? 'capability' : 'capabilities'} need attention`,
100
+ summary: 'Inspect configured capability readiness.',
101
+ statusLabel: 'Inspect',
102
+ tone: 'warning',
103
+ href: '#/capabilities',
104
+ }] : []),
105
+ ...(repositoryAttention > 0 ? [{
106
+ key: 'repositories',
107
+ source: 'Repositories',
108
+ title: `${repositoryAttention} ${repositoryAttention === 1 ? 'repository' : 'repositories'} need attention`,
109
+ summary: 'Inspect repository registration and readiness.',
110
+ statusLabel: 'Inspect',
111
+ tone: 'warning',
112
+ href: '#/repositories',
113
+ }] : []),
114
+ ].filter((item, index, all) => all.findIndex((candidate) => candidate.key === item.key) === index).slice(0, 6);
115
+
116
+ const workSummary = portfolio.summary.needsAttention
117
+ ? `${portfolio.summary.open} open · ${portfolio.summary.needsAttention} need attention`
118
+ : `${portfolio.summary.open} open · no attention needed`;
119
+ const automationSummary = automations.needsAttention
120
+ ? `${automations.enabled} 个运行中 · ${automations.needsAttention} 个需要处理`
121
+ : automations.paused
122
+ ? `${automations.enabled} 个运行中 · ${automations.paused} 个已暂停`
123
+ : (automations.completed ?? 0) > 0
124
+ ? `${automations.enabled} 个运行中 · ${automations.completed} 个已完成`
125
+ : `${automations.enabled} 个运行中 · 状态正常`;
126
+ const capabilitySummary = (plugins.needsAttention ?? 0) > 0
127
+ ? `${plugins.ready ?? 0} / ${capabilityTotal} ready · ${plugins.needsAttention} need attention`
128
+ : `${plugins.ready ?? 0} / ${capabilityTotal} ready`;
129
+ const repositorySummary = repositoryAttention
130
+ ? `${repositories.length} registered · ${repositoryAttention} need attention`
131
+ : `${repositories.length} registered`;
132
+
133
+ const workspaces: WorkspaceRow[] = [
134
+ { key: 'work', label: 'Work', summary: workSummary, href: '#/work', statusLabel: portfolio.summary.needsAttention ? 'Attention' : undefined, tone: portfolio.summary.needsAttention ? 'warning' : undefined },
135
+ { key: 'automations', label: 'Automations', summary: automationSummary, href: '#/automations', statusLabel: automations.needsAttention ? 'Attention' : undefined, tone: automations.needsAttention ? 'warning' : undefined },
136
+ { key: 'capabilities', label: 'Capabilities', summary: capabilitySummary, href: '#/capabilities', statusLabel: (plugins.needsAttention ?? 0) > 0 ? 'Attention' : undefined, tone: (plugins.needsAttention ?? 0) > 0 ? 'warning' : undefined },
137
+ { key: 'repositories', label: 'Repositories', summary: repositorySummary, href: '#/repositories', statusLabel: repositoryAttention ? 'Attention' : undefined, tone: repositoryAttention ? 'warning' : undefined },
138
+ { key: 'system', label: 'System', summary: runtimeNeedsAttention ? runtimeLabel : 'Runtime ready', href: '#/system', statusLabel: runtimeNeedsAttention ? 'Attention' : 'Ready', tone: runtimeNeedsAttention ? readinessState : 'success' },
139
+ ];
140
+
141
+ return <>
142
+ <CommandBar
143
+ eyebrow="FORGE CONTROL PLANE"
144
+ title="Overview"
145
+ description="需要处理的事项,以及 Forge 各工作区当前状态。"
146
+ refreshedAt={data.generatedAt}
147
+ busy={busy}
148
+ onRefresh={onRefresh}
149
+ />
150
+
151
+ <div className="overview-home">
152
+ <section className="page-section overview-attention-section">
153
+ <SectionHeader title="Needs attention" meta={attention.length ? `${attention.length} items` : 'All clear'} />
154
+ {attention.length ? <div className="overview-attention-list">
155
+ {attention.map((item) => <a className="overview-attention-row" href={item.href} key={item.key}>
156
+ <div className="overview-attention-source">{item.source}</div>
157
+ <div className="overview-attention-copy">
158
+ <strong>{compact(item.title, 82)}</strong>
159
+ <p>{item.summary}</p>
160
+ </div>
161
+ <StatusText label={item.statusLabel} tone={item.tone} />
162
+ <span className="overview-row-arrow" aria-hidden="true">→</span>
163
+ </a>)}
164
+ </div> : <div className="overview-clear-state">
165
+ <StatusText label="No action needed" tone="success" />
166
+ <span>Forge is operating normally.</span>
167
+ </div>}
168
+ </section>
169
+
170
+ <section className="page-section overview-workspace-section">
171
+ <SectionHeader title="Workspace" meta="Current state" />
172
+ <div className="overview-workspace-list">
173
+ {workspaces.map((workspace) => <a className="overview-workspace-row" href={workspace.href} key={workspace.key}>
174
+ <strong>{workspace.label}</strong>
175
+ <span>{workspace.summary}</span>
176
+ {workspace.statusLabel && <StatusText label={workspace.statusLabel} tone={workspace.tone} />}
177
+ <span className="overview-row-arrow" aria-hidden="true">→</span>
178
+ </a>)}
179
+ </div>
180
+ </section>
181
+ </div>
182
+ </>;
183
+ }
@@ -1,2 +1,2 @@
1
1
  import{useMemo,useState}from'react';import type{ConsoleData}from'../../types';import{CommandBar}from'../../shell/CommandBar';import{SearchField}from'../../components/SearchField';import{DetailPane,DefinitionList}from'../../components/DetailPane';import{StatusText}from'../../components/StatusText';import{Button}from'../../components/Button';import{compact,pretty}from'../../lib/format';
2
- export function RepositoriesView({data,busy,onRefresh,onRegister,onRemove}:{data:ConsoleData;busy:boolean;onRefresh:()=>void;onRegister:(path:string,name?:string)=>Promise<void>;onRemove:(id:string)=>Promise<void>}){const all=data.commandCenter.repositories??[];const[q,setQ]=useState('');const[selectedId,setSelectedId]=useState<string>();const[path,setPath]=useState('');const[name,setName]=useState('');const list=useMemo(()=>all.filter(r=>`${r.name} ${r.path} ${r.branchLabel}`.toLowerCase().includes(q.toLowerCase())),[all,q]);const selected=list.find(x=>x.id===selectedId)??list[0];return <><CommandBar eyebrow="CONTROLLER REGISTRY" title="Repositories" description="管理 Forge 已注册仓库;不存在隐含的全局“当前仓库”。" refreshedAt={data.generatedAt} busy={busy} onRefresh={onRefresh}/><div className="repository-tools"><SearchField value={q} onChange={setQ} placeholder="Search repositories…"/><form onSubmit={e=>{e.preventDefault();if(path.trim())void onRegister(path.trim(),name.trim()||undefined).then(()=>{setPath('');setName('')})}}><input value={path} onChange={e=>setPath(e.target.value)} placeholder="/absolute/path"/><input value={name} onChange={e=>setName(e.target.value)} placeholder="Display name (optional)"/><Button disabled={busy||!path.trim()}>Add</Button></form></div><div className="split-workspace"><div className="scan-list">{list.map(r=><button key={r.id} className={`scan-row ${selected?.id===r.id?'selected':''}`} onClick={()=>setSelectedId(r.id)}><div className="scan-main"><strong>{r.name}</strong><p>{compact(r.path,100)}</p></div><div className="scan-meta"><StatusText label={r.readinessLabel??r.statusLabel??'Registered'} tone={r.readinessLabel??r.statusLabel}/><span>{r.branchLabel??'—'}</span></div></button>)}</div><DetailPane title={selected?.name} subtitle={selected?.path}>{selected&&<><DefinitionList items={[["Repository id",<code>{selected.id}</code>],["Branch",selected.branchLabel??'—'],["Working tree",selected.dirtyLabel??'—'],["Readiness",<StatusText label={selected.readinessLabel??selected.statusLabel??'Registered'} tone={selected.readinessLabel??selected.statusLabel}/>]]}/><details className="advanced"><summary>Advanced registry metadata</summary><pre>{pretty(selected.advanced)}</pre></details><div className="detail-button-row"><Button className="danger-text" disabled={busy} onClick={()=>void onRemove(selected.id)}>Remove registry entry</Button></div></>}</DetailPane></div></>}
2
+ export function RepositoriesView({data,busy,onRefresh,onRegister,onRemove}:{data:ConsoleData;busy:boolean;onRefresh:()=>void;onRegister:(path:string,name?:string)=>Promise<void>;onRemove:(id:string)=>Promise<void>}){const all=data.commandCenter.repositories??[];const[q,setQ]=useState('');const[selectedId,setSelectedId]=useState<string>();const[path,setPath]=useState('');const[name,setName]=useState('');const[adding,setAdding]=useState(false);const list=useMemo(()=>all.filter(r=>`${r.name} ${r.path} ${r.branchLabel}`.toLowerCase().includes(q.toLowerCase())),[all,q]);const selected=list.find(x=>x.id===selectedId)??list[0];return <><CommandBar eyebrow="CONTROLLER REGISTRY" title="Repositories" description="查看和管理 Forge 的持久化仓库边界;临时目录不需要出现在这里。" refreshedAt={data.generatedAt} busy={busy} onRefresh={onRefresh} actions={<Button onClick={()=>setAdding(v=>!v)} aria-expanded={adding}>{adding?'Cancel':'Add repository'}</Button>}/><div className="repository-tools"><SearchField value={q} onChange={setQ} placeholder="Search repositories…"/><span className="repository-count">{list.length===all.length?`${all.length} registered`:`${list.length} of ${all.length}`}</span></div>{adding&&<form className="repository-add-panel" onSubmit={e=>{e.preventDefault();if(path.trim())void onRegister(path.trim(),name.trim()||undefined).then(()=>{setPath('');setName('');setAdding(false)})}}><div className="repository-add-fields"><label><span>Local path</span><input autoFocus value={path} onChange={e=>setPath(e.target.value)} placeholder="/absolute/path"/></label><label><span>Display name</span><input value={name} onChange={e=>setName(e.target.value)} placeholder="Optional"/></label><Button type="submit" disabled={busy||!path.trim()}>Register</Button></div><p>只为需要持久化 Work、缓存、并发隔离或发布治理的仓库建立注册项。</p></form>}<div className="split-workspace repository-workspace"><div className="scan-list">{list.map(r=><button key={r.id} className={`scan-row ${selected?.id===r.id?'selected':''}`} onClick={()=>setSelectedId(r.id)}><div className="scan-main"><strong>{r.name}</strong><p>{compact(r.path,100)}</p></div><div className="scan-meta"><StatusText label={r.readinessLabel??r.statusLabel??'Registered'} tone={r.readinessLabel??r.statusLabel}/><span>{r.branchLabel??'—'}</span></div></button>)}</div><DetailPane title={selected?.name} subtitle={selected?.path}>{selected&&<><DefinitionList items={[["Repository id",<code>{selected.id}</code>],["Branch",selected.branchLabel??'—'],["Working tree",selected.dirtyLabel??'—'],["Readiness",<StatusText label={selected.readinessLabel??selected.statusLabel??'Registered'} tone={selected.readinessLabel??selected.statusLabel}/>]]}/><details className="advanced"><summary>Advanced registry metadata</summary><pre>{pretty(selected.advanced)}</pre></details><div className="detail-button-row"><Button className="danger-text" disabled={busy} onClick={()=>void onRemove(selected.id)}>Remove registry entry</Button></div></>}</DetailPane></div></>}
@@ -1,3 +1,4 @@
1
- import{useMemo,useState}from'react';import type{ConsoleData,TrackedWorkView}from'../../types';import{CommandBar}from'../../shell/CommandBar';import{Segmented}from'../../components/Segmented';import{DetailPane,DefinitionList}from'../../components/DetailPane';import{StatusText}from'../../components/StatusText';import{formatDate,compact}from'../../lib/format';
2
- type Filter='active'|'waiting'|'completed'|'all';function match(w:TrackedWorkView,f:Filter){if(f==='all')return true;if(f==='waiting')return w.state==='waiting_for_user';if(f==='completed')return w.state==='done'||w.state==='cancelled';return w.state==='active'||w.state==='planned'}
3
- export function WorkView({data,busy,onRefresh}:{data:ConsoleData;busy:boolean;onRefresh:()=>void}){const all=data.work.requirements??[];const[filter,setFilter]=useState<Filter>('active');const list=useMemo(()=>all.filter(w=>match(w,filter)),[all,filter]);const[selectedId,setSelectedId]=useState<string>();const selected=list.find(x=>x.requirementId===selectedId)??list[0];return <><CommandBar eyebrow="PERSISTENT GOALS" title="Work" description="粗粒度查看长期目标;不展示 Agent 步骤、Run 流水或推测进度。" refreshedAt={data.generatedAt} busy={busy} onRefresh={onRefresh}/><div className="toolbar"><Segmented value={filter} onChange={setFilter} items={[{id:'active',label:'Open',count:all.filter(x=>match(x,'active')).length},{id:'waiting',label:'Waiting',count:all.filter(x=>match(x,'waiting')).length},{id:'completed',label:'Completed',count:all.filter(x=>match(x,'completed')).length},{id:'all',label:'All',count:all.length}]}/></div><div className="split-workspace"><div className="scan-list">{list.map(w=><button key={w.requirementId} className={`scan-row ${selected?.requirementId===w.requirementId?'selected':''}`} onClick={()=>setSelectedId(w.requirementId)}><div className="scan-main"><strong>{w.title}</strong><p>{compact(w.outcome||w.blocker,96)}</p></div><div className="scan-meta"><StatusText label={w.state==='waiting_for_user'?'Waiting':w.state==='done'?'Completed':w.state==='planned'?'Planned':'Active'} tone={w.state}/><time>{formatDate(w.updatedAt)}</time></div></button>)}{!list.length&&<div className="quiet-empty">这个筛选条件下没有持久化目标。</div>}</div><DetailPane title={selected?.title} subtitle={selected?.outcome} empty="选择一个长期目标查看完整上下文">{selected&&<><DefinitionList items={[["State",<StatusText label={selected.state} tone={selected.state}/>],["Updated",formatDate(selected.updatedAt)],["Requirement",<code>{selected.requirementId}</code>]]}/>{selected.blocker&&<div className="detail-callout warning"><strong>Blocker</strong><p>{selected.blocker}</p></div>}{selected.requiredUserDecision&&<div className="detail-callout danger"><strong>Decision required</strong><p>{selected.requiredUserDecision}</p></div>}<p className="detail-note">执行步骤、检查证据和 Run 细节不在这里展开;继续工作请回到 ChatGPT。</p></>}</DetailPane></div></>}
1
+ import{useMemo,useState}from'react';import type{ConsoleData,WorkPortfolioItem}from'../../types';import{CommandBar}from'../../shell/CommandBar';import{Segmented}from'../../components/Segmented';import{DetailPane,DefinitionList}from'../../components/DetailPane';import{StatusText}from'../../components/StatusText';import{formatDate,compact}from'../../lib/format';
2
+ type Filter='open'|'attention'|'completed'|'all';
3
+ function statusOf(w:WorkPortfolioItem){return w.advanced?.status??''}function match(w:WorkPortfolioItem,f:Filter){const s=statusOf(w);if(f==='all')return true;if(f==='attention')return s==='blocked'||s==='failed';if(f==='completed')return s==='completed'||s==='cancelled';return s==='open'||s==='running'||s==='ready'}
4
+ export function WorkView({data,busy,onRefresh}:{data:ConsoleData;busy:boolean;onRefresh:()=>void}){const portfolio=data.workPortfolio;const all=portfolio.items??[];const[filter,setFilter]=useState<Filter>('open');const[repoId,setRepoId]=useState('all');const[selectedId,setSelectedId]=useState<string>();const list=useMemo(()=>all.filter(w=>match(w,filter)&&(repoId==='all'||w.repoId===repoId)),[all,filter,repoId]);const selected=list.find(x=>x.id===selectedId)??list[0];return <><CommandBar eyebrow="EXECUTION WORK" title="Work" description="查看所有已注册仓库的持久 Work;仓库是归属维度,默认聚合展示。" refreshedAt={portfolio.generatedAt} busy={busy} onRefresh={onRefresh}/><div className="toolbar work-toolbar"><Segmented value={filter} onChange={setFilter} items={[{id:'open',label:'Open',count:all.filter(x=>match(x,'open')).length},{id:'attention',label:'Needs attention',count:all.filter(x=>match(x,'attention')).length},{id:'completed',label:'Completed',count:all.filter(x=>match(x,'completed')).length},{id:'all',label:'All',count:all.length}]}/><label className="repository-filter"><span>Repository</span><select value={repoId} onChange={e=>{setRepoId(e.target.value);setSelectedId(undefined)}}><option value="all">All repositories</option>{portfolio.repositories.map(r=><option key={r.repoId} value={r.repoId}>{r.repositoryName}</option>)}</select></label></div><div className="split-workspace"><div className="scan-list">{list.map(w=><button key={w.id} className={`scan-row work-row ${selected?.id===w.id?'selected':''}`} onClick={()=>setSelectedId(w.id)}><div className="scan-main"><span className="row-eyebrow">{w.repositoryName}</span><strong>{w.title}</strong><p>{compact(w.latestSummary||w.objective,108)}</p></div><div className="scan-meta"><StatusText label={w.statusLabel} tone={w.tone}/><time>{formatDate(w.updatedAt)}</time></div></button>)}{!list.length&&<div className="quiet-empty">这个筛选条件下没有 Work。</div>}</div><DetailPane title={selected?.title} subtitle={selected?.objective} empty="选择一个 Work 查看完整上下文">{selected&&<><DefinitionList items={[["Repository",selected.repositoryName],["Status",<StatusText label={selected.statusLabel} tone={selected.tone}/>],["Updated",formatDate(selected.updatedAt)],["Work id",<code>{selected.id}</code>]]}/>{selected.latestSummary&&<div className="detail-callout"><strong>Latest</strong><p>{selected.latestSummary}</p></div>}<p className="detail-note">这里聚合所有仓库的持久 Work。具体执行、检查和继续操作仍由 ChatGPT 主控。</p></>}</DetailPane></div></>}
@@ -1,23 +1,30 @@
1
- import type { AutomationSettingsView, AutomationsResponse, CommandCenterView, Dict, WorkResponse } from '../types';
1
+ import type { AutomationsResponse, CommandCenterView, Dict, WorkPortfolioResponse, WorkResponse } from '../types';
2
2
  export class ApiError extends Error { constructor(message:string, readonly status:number, readonly payload:unknown){super(message);} }
3
- export async function requestJson<T>(path:string, init:RequestInit={}):Promise<T>{
4
- const headers=new Headers(init.headers); if(init.body&&!headers.has('content-type'))headers.set('content-type','application/json');
5
- const response=await fetch(path,{...init,headers,credentials:'same-origin'}); let payload:unknown={}; try{payload=await response.json();}catch{}
6
- if(!response.ok){const r=payload&&typeof payload==='object'?payload as Dict:{}; const message=typeof r.error==='string'?r.error:typeof r.message==='string'?r.message:`Request failed (${response.status})`; throw new ApiError(message,response.status,payload);}
7
- return payload as T;
3
+ const CONSOLE_REQUEST_TIMEOUT_MS=15_000;
4
+ export async function requestJson<T>(path:string,init:RequestInit={}):Promise<T>{
5
+ const headers=new Headers(init.headers);if(init.body&&!headers.has('content-type'))headers.set('content-type','application/json');
6
+ const controller=new AbortController();let timedOut=false;const callerSignal=init.signal;const abortFromCaller=()=>controller.abort();
7
+ if(callerSignal){if(callerSignal.aborted)abortFromCaller();else callerSignal.addEventListener('abort',abortFromCaller,{once:true});}
8
+ const timer=setTimeout(()=>{timedOut=true;controller.abort();},CONSOLE_REQUEST_TIMEOUT_MS);
9
+ try{
10
+ const response=await fetch(path,{...init,headers,signal:controller.signal,credentials:'same-origin'});let payload:unknown={};try{payload=await response.json();}catch{}
11
+ if(!response.ok){const r=payload&&typeof payload==='object'?payload as Dict:{};const message=typeof r.error==='string'?r.error:typeof r.message==='string'?r.message:`Request failed (${response.status})`;throw new ApiError(message,response.status,payload);}
12
+ return payload as T;
13
+ }catch(error){
14
+ if(timedOut)throw new Error('Forge Runtime 暂时没有响应。请在连接恢复后重试。');
15
+ throw error;
16
+ }finally{
17
+ clearTimeout(timer);callerSignal?.removeEventListener('abort',abortFromCaller);
18
+ }
8
19
  }
9
20
  export const api={
10
21
  commandCenter:()=>requestJson<CommandCenterView>('/api/console/command-center'),
11
22
  work:()=>requestJson<WorkResponse>('/api/console/requirements'),
23
+ workPortfolio:()=>requestJson<WorkPortfolioResponse>('/api/console/work-portfolio'),
12
24
  automations:()=>requestJson<AutomationsResponse>('/api/console/automations'),
13
- automationSettings:()=>requestJson<AutomationSettingsView>('/api/console/automation-settings'),
14
25
  connector:()=>requestJson<Dict>('/api/console/connector/status'),
15
26
  advanced:()=>requestJson<Dict>('/api/console/advanced'),
16
27
  automationAction:(source:string,repoId:string,id:string,action:string)=>requestJson<Dict>(`/api/console/automations/${encodeURIComponent(source)}/${encodeURIComponent(repoId)}/${encodeURIComponent(id)}/${encodeURIComponent(action)}`,{method:'POST',body:'{}'}),
17
- providerAction:(id:string,action:'enable'|'disable')=>requestJson<Dict>(`/api/console/providers/${encodeURIComponent(id)}/${action}`,{method:'POST',body:'{}'}),
18
- providerHealth:(id:string)=>requestJson<Dict>('/api/console/providers/health',{method:'POST',body:JSON.stringify({providerId:id})}),
19
- localToolAction:(id:string,action:'enable'|'disable')=>requestJson<Dict>(`/api/console/local-tools/${encodeURIComponent(id)}/${action}`,{method:'POST',body:'{}'}),
20
- localToolHealth:(id:string)=>requestJson<Dict>('/api/console/local-tools/health',{method:'POST',body:JSON.stringify({toolId:id})}),
21
28
  registerRepository:(path:string,displayName?:string)=>requestJson<Dict>('/api/repositories/register',{method:'POST',body:JSON.stringify({path,displayName})}),
22
29
  removeRepository:(id:string)=>requestJson<Dict>(`/api/repositories/${encodeURIComponent(id)}/remove`,{method:'POST',body:'{}'}),
23
30
  };
@@ -1,7 +1,7 @@
1
1
  import type { RouteId } from '../types';
2
2
  export const routes:{id:RouteId;label:string;group:'daily'|'manage'|'system'}[]=[
3
3
  {id:'overview',label:'Overview',group:'daily'}, {id:'automations',label:'Automations',group:'daily'}, {id:'work',label:'Work',group:'daily'},
4
- {id:'capabilities',label:'Capabilities',group:'manage'}, {id:'repositories',label:'Repositories',group:'manage'}, {id:'settings',label:'Settings',group:'manage'},
4
+ {id:'capabilities',label:'Capabilities',group:'manage'}, {id:'repositories',label:'Repositories',group:'manage'},
5
5
  {id:'system',label:'System',group:'system'},
6
6
  ];
7
7
  export function routeFromHash():RouteId{const id=location.hash.replace(/^#\/?/,'').split('/')[0] as RouteId;return routes.some(r=>r.id===id)?id:'overview';}
@@ -1,2 +1,2 @@
1
1
  import type{ReactNode}from'react';import{RefreshIcon}from'../design/icons';import{formatDate}from'../lib/format';
2
- export function CommandBar({eyebrow,title,description,refreshedAt,busy,onRefresh,actions}:{eyebrow?:string;title:string;description:string;refreshedAt?:string;busy?:boolean;onRefresh:()=>void;actions?:ReactNode}){return <header className="command-bar"><div className="command-title">{eyebrow&&<div className="eyebrow">{eyebrow}</div>}<h1>{title}</h1><p>{description}</p></div><div className="command-actions"><span className="refreshed">Updated {formatDate(refreshedAt)}</span><button className="icon-button" onClick={onRefresh} disabled={busy} title="Refresh"><RefreshIcon/></button>{actions}<a className="button ghost-link" href="https://chatgpt.com" target="_blank" rel="noreferrer">ChatGPT ↗</a></div></header>}
2
+ export function CommandBar({eyebrow,title,description,refreshedAt,busy,onRefresh,actions}:{eyebrow?:string;title:string;description:string;refreshedAt?:string;busy?:boolean;onRefresh:()=>void;actions?:ReactNode}){return <header className="command-bar"><div className="command-title">{eyebrow&&<div className="eyebrow">{eyebrow}</div>}<h1>{title}</h1><p>{description}</p></div><div className="command-actions"><div className="command-meta"><span>Last synced</span><strong>{formatDate(refreshedAt)}</strong></div><button className="icon-button" onClick={onRefresh} disabled={busy} title="Refresh"><RefreshIcon/></button>{actions}<a className="button ghost-link" href="https://chatgpt.com" target="_blank" rel="noreferrer">Open ChatGPT ↗</a></div></header>}
@@ -1,3 +1,4 @@
1
- import type{ComponentType,SVGProps}from'react';import type{RouteId}from'../types';import{routes}from'../lib/routes';import{AutomationIcon,CapabilityIcon,OverviewIcon,RepoIcon,SettingsIcon,SystemIcon,WorkIcon}from'../design/icons';
2
- const icons:Record<RouteId,ComponentType<SVGProps<SVGSVGElement>>>={overview:OverviewIcon,automations:AutomationIcon,work:WorkIcon,capabilities:CapabilityIcon,repositories:RepoIcon,settings:SettingsIcon,system:SystemIcon};
3
- export function Sidebar({route}:{route:RouteId}){let last='';return <aside className="sidebar"><div className="brand"><span className="brand-mark">F</span><div><strong>Forge</strong><small>Utility Console</small></div></div><nav>{routes.map(item=>{const Icon=icons[item.id];const sep=Boolean(last&&last!==item.group);last=item.group;return <div key={item.id} className={sep?'nav-group-start':''}><a href={`#/${item.id}`} className={route===item.id?'active':''}><Icon/><span>{item.label}</span></a></div>})}</nav><div className="sidebar-foot"><span className="pulse-dot"/>ChatGPT is the primary workspace</div></aside>}
1
+ import type{ComponentType,SVGProps}from'react';import type{RouteId}from'../types';import{routes}from'../lib/routes';import{AutomationIcon,CapabilityIcon,OverviewIcon,RepoIcon,SystemIcon,WorkIcon}from'../design/icons';
2
+ const icons:Record<RouteId,ComponentType<SVGProps<SVGSVGElement>>>={overview:OverviewIcon,automations:AutomationIcon,work:WorkIcon,capabilities:CapabilityIcon,repositories:RepoIcon,system:SystemIcon};
3
+ const groupLabels={daily:'Workspace',manage:'Configure',system:'System'} as const;
4
+ export function Sidebar({route}:{route:RouteId}){let last='';return <aside className="sidebar"><div className="brand"><span className="brand-mark">F</span><div><strong>Forge</strong><small>Utility Console</small></div></div><nav>{routes.map(item=>{const Icon=icons[item.id];const first=item.group!==last;last=item.group;return <div key={item.id} className={first?'nav-group-start':'nav-item'}>{first&&<div className="nav-group-label">{groupLabels[item.group]}</div>}<a href={`#/${item.id}`} className={route===item.id?'active':''}><Icon/><span>{item.label}</span></a></div>})}</nav><div className="sidebar-foot"><span className="pulse-dot"/><span><strong>Runtime connected</strong><small>ChatGPT remains the primary workspace</small></span></div></aside>}
@@ -1,12 +1,28 @@
1
1
  *{box-sizing:border-box}html,body,#app{min-height:100%;margin:0}body{font-family:var(--font-sans);font-size:13px;color:var(--ink);background:var(--canvas);-webkit-font-smoothing:antialiased}button,input{font:inherit}button,a{transition:background-color .14s,border-color .14s,color .14s,opacity .14s}a{color:inherit;text-decoration:none}code,pre{font-family:var(--font-mono)}button:focus-visible,a:focus-visible,input:focus-visible,summary:focus-visible{outline:2px solid var(--focus);outline-offset:2px}.app-shell{min-height:100vh;display:grid;grid-template-columns:var(--rail-width) minmax(0,1fr)}
2
- .sidebar{position:sticky;top:0;height:100vh;background:var(--rail);color:#B8B8BF;padding:18px 12px 14px;display:flex;flex-direction:column}.brand{height:50px;display:flex;align-items:center;gap:10px;padding:0 8px;color:var(--inverse)}.brand-mark{display:grid;place-items:center;width:26px;height:26px;border-radius:7px;background:var(--accent);color:white;font-weight:750}.brand strong{display:block;font-size:14px}.brand small{display:block;color:#77777F;font-size:10px;margin-top:1px}.sidebar nav{margin-top:18px;display:grid;gap:2px}.sidebar nav>div.nav-group-start{margin-top:13px;padding-top:13px;border-top:1px solid #2A2A2E}.sidebar nav a{display:flex;height:34px;align-items:center;gap:10px;padding:0 9px;border-radius:6px;color:#9C9CA4}.sidebar nav a:hover{background:var(--rail-hover);color:#DCDCE0}.sidebar nav a.active{background:var(--rail-selected);color:var(--inverse);box-shadow:inset 2px 0 var(--accent)}.sidebar nav svg{width:16px;height:16px}.sidebar-foot{margin-top:auto;border-top:1px solid #2A2A2E;padding:13px 8px 0;color:#75757D;font-size:10px;display:flex;align-items:center;gap:7px}.pulse-dot{width:6px;height:6px;border-radius:50%;background:#5CC58A}
3
- .workspace{width:100%;max-width:calc(var(--content-max) + 80px);padding:0 40px 64px;min-width:0}.command-bar{min-height:126px;display:flex;justify-content:space-between;gap:28px;align-items:flex-end;padding:28px 0 20px;border-bottom:1px solid var(--border)}.command-title{min-width:0}.eyebrow,.row-eyebrow{font-size:10px;letter-spacing:.1em;color:var(--faint);font-weight:700}.command-title h1{font-size:24px;line-height:1.2;letter-spacing:-.02em;margin:4px 0 5px;font-weight:650}.command-title p{margin:0;color:var(--muted);max-width:700px;line-height:1.45}.command-actions{display:flex;align-items:center;gap:8px;white-space:nowrap}.refreshed{color:var(--faint);font-size:11px;margin-right:2px}
4
- .button,.icon-button,.text-button{border:1px solid var(--border-strong);background:var(--surface);color:var(--ink);border-radius:6px;min-height:32px;padding:0 10px;cursor:pointer}.button:hover,.icon-button:hover{background:var(--surface-subtle)}.button:disabled,.icon-button:disabled{opacity:.45;cursor:default}.ghost-link{display:inline-flex;align-items:center}.icon-button{width:32px;padding:0;display:grid;place-items:center}.icon-button svg{width:15px;height:15px}.text-button{border:0;background:transparent;padding:0;color:var(--accent);min-height:auto}.text-button:hover{text-decoration:underline}.global-error{margin:12px 0 -4px;border:1px solid #E7BFBF;background:var(--danger-soft);border-radius:8px;padding:10px 12px;display:flex;gap:10px;align-items:center}.global-error span{color:var(--muted);flex:1}.global-error button{border:0;background:transparent;font-size:18px;cursor:pointer}
5
- .metric-strip{margin-top:24px;background:var(--surface);border:1px solid var(--border);border-radius:8px;display:grid;grid-template-columns:repeat(4,1fr);overflow:hidden}.metric-strip a{padding:16px 18px;border-right:1px solid var(--border);min-width:0}.metric-strip a:last-child{border-right:0}.metric-strip a:hover{background:#FAFAF9}.metric-strip span{display:block;color:var(--muted);font-size:11px}.metric-strip strong{display:inline-block;font-size:22px;letter-spacing:-.02em;margin:5px 6px 0 0}.metric-strip small{color:var(--faint)}.page-section{margin-top:30px}.section-header{height:32px;display:flex;align-items:flex-start;justify-content:space-between;border-bottom:1px solid var(--border)}.section-header>div:first-child{display:flex;align-items:baseline;gap:10px}.section-header h2{font-size:15px;margin:0;font-weight:650}.section-header span{font-size:11px;color:var(--faint)}
6
- .attention-list,.compact-work-list{background:var(--surface);border-bottom:1px solid var(--border)}.attention-row{display:grid;grid-template-columns:38px minmax(0,1fr) auto;align-items:center;gap:10px;min-height:62px;padding:9px 4px;border-bottom:1px solid var(--border)}.attention-row:last-child{border-bottom:0}.attention-index{font-family:var(--font-mono);font-size:10px;color:var(--faint)}.attention-row strong,.compact-work-list strong{font-size:13px}.attention-row p,.compact-work-list p{margin:3px 0 0;color:var(--muted);font-size:12px}.compact-work-list a>div{min-width:0}.compact-work-list p{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.compact-work-list a{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:16px;align-items:center;min-height:54px;padding:8px 4px;border-bottom:1px solid var(--border)}.compact-work-list a:last-child{border-bottom:0}.compact-work-list a:hover strong{color:var(--accent)}.quiet-empty{padding:28px 4px;color:var(--muted)}.quiet-empty p{margin:6px 0 0}.status-text{display:inline-flex;align-items:center;gap:7px;white-space:nowrap;font-size:11px;color:var(--muted)}.status-dot{width:7px;height:7px;border-radius:50%;background:var(--faint)}.status-dot.success{background:var(--success)}.status-dot.warning{background:var(--warning)}.status-dot.danger{background:var(--danger)}.status-dot.info{background:var(--info)}
7
- .toolbar{min-height:58px;display:flex;align-items:center;border-bottom:1px solid var(--border)}.segmented{display:inline-flex;padding:2px;background:var(--surface-strong);border-radius:7px;gap:1px}.segmented button{border:0;background:transparent;color:var(--muted);border-radius:5px;height:28px;padding:0 9px;cursor:pointer;font-size:11px}.segmented button.selected{background:var(--surface);color:var(--ink);box-shadow:0 1px 2px rgba(0,0,0,.05)}.segmented button span{margin-left:5px;color:var(--faint);font-size:10px}.split-workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(300px,.85fr);min-height:520px;border-bottom:1px solid var(--border)}.automation-layout{grid-template-columns:minmax(0,2.2fr) minmax(300px,.9fr)}.scan-list{min-width:0}.scan-row{width:100%;min-height:68px;border:0;border-bottom:1px solid var(--border);background:transparent;text-align:left;padding:11px 12px 11px 4px;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:18px;align-items:center;cursor:pointer;color:var(--ink)}.scan-row.static{cursor:default}.scan-row:hover{background:#FAFAF8}.scan-row.selected{background:var(--accent-soft);box-shadow:inset 2px 0 var(--accent)}.scan-main{min-width:0}.scan-main strong{display:block;font-size:13px;font-weight:620;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.scan-main p{margin:4px 0 0;color:var(--muted);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row-eyebrow{display:block;margin-bottom:3px}.scan-meta{text-align:right;display:grid;justify-items:end;gap:5px;color:var(--faint);font-size:10px}
8
- .detail-pane{border-left:1px solid var(--border);background:rgba(255,255,255,.55);min-width:0}.detail-head{padding:18px 18px 15px;border-bottom:1px solid var(--border);display:flex;gap:12px;justify-content:space-between}.detail-head h2{font-size:16px;margin:4px 0 5px;line-height:1.3}.detail-head p{margin:0;color:var(--muted);font-size:12px;line-height:1.5}.detail-body{padding:18px}.detail-empty{display:grid;place-items:center;min-height:260px;color:var(--faint);font-size:12px}.definition-list{margin:0}.definition-list>div{display:grid;grid-template-columns:100px minmax(0,1fr);gap:10px;padding:8px 0;border-bottom:1px solid var(--border)}.definition-list dt{color:var(--faint);font-size:11px}.definition-list dd{margin:0;min-width:0;overflow-wrap:anywhere}.definition-list code{font-size:10px;color:var(--muted)}.detail-callout{margin-top:16px;border-left:2px solid var(--warning);padding:4px 0 4px 12px}.detail-callout.danger{border-color:var(--danger)}.detail-callout p{margin:5px 0 0;color:var(--muted);line-height:1.5}.detail-note{margin-top:18px;color:var(--faint);font-size:11px;line-height:1.55}.detail-button-row{display:flex;gap:7px;margin-top:18px;flex-wrap:wrap}.danger-text{color:var(--danger)}
9
- .table-wrap{min-width:0;overflow:auto}.data-table{width:100%;border-collapse:collapse;font-size:12px}.data-table th{text-align:left;height:38px;color:var(--faint);font-size:10px;font-weight:650;letter-spacing:.03em;border-bottom:1px solid var(--border);padding:0 10px}.data-table td{height:62px;border-bottom:1px solid var(--border);padding:9px 10px;vertical-align:middle}.data-table tbody tr{cursor:pointer}.data-table tbody tr:hover{background:#FAFAF8}.data-table tbody tr.selected{background:var(--accent-soft)}.data-table td:first-child{max-width:280px}.data-table td strong{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.data-table td small{display:block;color:var(--faint);margin-top:3px}.capability-lines{display:flex;flex-wrap:wrap;gap:6px;margin-top:16px}.capability-lines span{font-size:10px;color:var(--muted);border:1px solid var(--border);border-radius:5px;padding:4px 6px;background:var(--surface)}.advanced{margin-top:18px;border-top:1px solid var(--border);padding-top:12px}.advanced summary{cursor:pointer;color:var(--muted);font-size:11px}.advanced pre{white-space:pre-wrap;word-break:break-word;background:var(--surface-subtle);border-radius:6px;padding:10px;font-size:10px;max-height:420px;overflow:auto}
10
- .repository-tools{min-height:64px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:14px;justify-content:space-between}.repository-tools form{display:flex;gap:6px}.repository-tools input,.search-field input{height:32px;border:1px solid var(--border-strong);border-radius:6px;background:var(--surface);padding:0 9px;color:var(--ink);min-width:170px}.search-field{display:flex;align-items:center;position:relative}.search-field svg{position:absolute;left:9px;width:14px;color:var(--faint)}.search-field input{padding-left:30px;width:230px}.settings-column{max-width:900px;padding-top:12px}.settings-column section{margin-top:26px}.settings-list{border-top:1px solid var(--border)}.settings-row{display:grid;grid-template-columns:minmax(0,1fr) 130px auto;gap:20px;align-items:center;min-height:68px;border-bottom:1px solid var(--border);padding:9px 0}.settings-row strong{font-size:13px}.settings-row p{margin:3px 0 0;color:var(--muted);font-size:12px}.settings-actions{display:flex;gap:6px}.system-layout{max-width:900px;padding-top:26px}.system-summary{border-bottom:1px solid var(--border);padding-bottom:20px}.system-posture{display:flex;justify-content:space-between;align-items:flex-start;gap:20px;margin-bottom:12px}.system-posture h2{font-size:18px;margin:4px 0}.system-posture p{margin:0;color:var(--muted)}.system-layout>section+section{margin-top:24px}.boot-state{min-height:100vh;display:grid;place-content:center;justify-items:center;gap:10px;background:var(--canvas);color:var(--muted)}.boot-state .brand-mark{width:34px;height:34px}.boot-state p{max-width:460px;text-align:center}.single-list{max-width:900px}
11
- @media(max-width:1080px){.workspace{padding-left:28px;padding-right:28px}.command-bar{align-items:flex-start;flex-direction:column}.command-actions{width:100%;justify-content:flex-start}.metric-strip{grid-template-columns:repeat(2,1fr)}.metric-strip a:nth-child(2){border-right:0}.metric-strip a:nth-child(-n+2){border-bottom:1px solid var(--border)}.split-workspace,.automation-layout{grid-template-columns:1fr}.detail-pane{border-left:0;border-top:1px solid var(--border)}.data-table{min-width:760px}}
12
- @media(max-width:760px){.app-shell{display:block}.sidebar{position:sticky;z-index:10;top:0;width:100%;height:auto;padding:8px 12px;flex-direction:row;align-items:center;overflow:auto}.brand{height:38px;min-width:120px}.brand small,.sidebar-foot{display:none}.sidebar nav{display:flex;margin:0 0 0 8px;gap:2px}.sidebar nav>div.nav-group-start{margin:0;padding:0;border:0}.sidebar nav a{white-space:nowrap;height:32px}.sidebar nav svg{display:none}.workspace{padding:0 18px 44px}.command-actions{flex-wrap:wrap}.refreshed{width:100%}.metric-strip{grid-template-columns:1fr 1fr}.repository-tools{align-items:flex-start;flex-direction:column;padding:12px 0}.repository-tools form{width:100%;flex-wrap:wrap}.repository-tools input{flex:1}.settings-row{grid-template-columns:1fr auto}.settings-row>.status-text{grid-column:2}.settings-actions{grid-column:1/-1}.definition-list>div{grid-template-columns:88px 1fr}}
2
+ .sidebar{position:sticky;top:0;height:100vh;background:linear-gradient(180deg,#1A1A1D 0%,#171719 100%);color:#B8B8BF;padding:20px 14px 16px;display:flex;flex-direction:column;border-right:1px solid #252529}.brand{height:48px;display:flex;align-items:center;gap:11px;padding:0 8px;color:var(--inverse)}.brand-mark{display:grid;place-items:center;width:28px;height:28px;border-radius:9px;background:linear-gradient(145deg,#746CF0,#554AE0);box-shadow:0 6px 18px rgba(94,84,232,.28);color:white;font-weight:780}.brand strong{display:block;font-size:14px;letter-spacing:-.01em}.brand small{display:block;color:#77777F;font-size:10px;margin-top:2px}.sidebar nav{margin-top:20px;display:grid;gap:2px}.sidebar nav>div.nav-group-start{margin-top:15px}.sidebar nav>div.nav-group-start:first-child{margin-top:0}.nav-group-label{height:24px;display:flex;align-items:center;padding:0 10px;color:#626269;font-size:9px;font-weight:750;letter-spacing:.12em;text-transform:uppercase}.sidebar nav a{display:flex;height:36px;align-items:center;gap:10px;padding:0 10px;border-radius:8px;color:#9999A1;position:relative}.sidebar nav a:hover{background:rgba(255,255,255,.045);color:#E0E0E4}.sidebar nav a.active{background:rgba(255,255,255,.075);color:var(--inverse)}.sidebar nav a.active:before{content:"";position:absolute;left:-4px;top:9px;bottom:9px;width:3px;border-radius:3px;background:var(--accent)}.sidebar nav svg{width:16px;height:16px}.sidebar-foot{margin-top:auto;border-top:1px solid #28282C;padding:14px 8px 0;color:#77777F;font-size:10px;display:flex;align-items:flex-start;gap:8px}.sidebar-foot>span:last-child{display:grid;gap:2px}.sidebar-foot strong{font-size:10px;font-weight:620;color:#A7A7AE}.sidebar-foot small{font-size:9px;color:#696970}.pulse-dot{width:7px;height:7px;margin-top:3px;border-radius:50%;background:#5CC58A;box-shadow:0 0 0 3px rgba(92,197,138,.1)}
3
+ .workspace{width:100%;max-width:calc(var(--content-max) + 88px);padding:0 44px 72px;min-width:0}.command-bar{min-height:112px;display:flex;justify-content:space-between;gap:32px;align-items:center;padding:24px 0 20px}.command-title{min-width:0}.eyebrow,.row-eyebrow{font-size:9px;letter-spacing:.12em;color:var(--faint);font-weight:760}.command-title h1{font-size:24px;line-height:1.2;letter-spacing:-.02em;margin:4px 0 6px;font-weight:650}.command-title p{margin:0;color:var(--muted);max-width:720px;line-height:1.5}.command-actions{display:flex;align-items:center;gap:8px;white-space:nowrap}.command-meta{display:grid;justify-items:end;line-height:1.2;margin-right:3px}.command-meta span{font-size:9px;text-transform:uppercase;letter-spacing:.08em;color:var(--faint)}.command-meta strong{font-size:10px;color:var(--muted);font-weight:580;margin-top:3px}.refreshed{display:none}
4
+ .button,.icon-button,.text-button{border:1px solid var(--border-strong);background:var(--surface);color:var(--ink);border-radius:8px;min-height:34px;padding:0 11px;cursor:pointer}.button:hover,.icon-button:hover{background:var(--surface-subtle);border-color:#C5C5C0}.button:disabled,.icon-button:disabled{opacity:.45;cursor:default}.ghost-link{display:inline-flex;align-items:center}.icon-button{width:34px;padding:0;display:grid;place-items:center}.icon-button svg{width:15px;height:15px}.text-button{border:0;background:transparent;padding:0;color:var(--accent);min-height:auto}.text-button:hover{text-decoration:underline}.global-error{margin:12px 0 -4px;border:1px solid #E7BFBF;background:var(--danger-soft);border-radius:8px;padding:10px 12px;display:flex;gap:10px;align-items:center}.global-error span{color:var(--muted);flex:1}.global-error button{border:0;background:transparent;font-size:18px;cursor:pointer}
5
+ .overview-hero{margin-top:10px;min-height:190px;display:grid;grid-template-columns:minmax(0,1.7fr) minmax(300px,.8fr);gap:26px;align-items:stretch;padding:28px 30px;border-radius:var(--r-lg);background:linear-gradient(135deg,var(--accent-soft),#FCFCFB 60%,#F1F1EE);box-shadow:inset 0 0 0 1px rgba(94,84,232,.09)}.overview-posture{align-self:center;max-width:760px}.overview-posture-title{display:flex;align-items:center;gap:14px;margin-top:7px}.overview-posture h2{font-size:25px;line-height:1.18;letter-spacing:-.03em;margin:0;font-weight:670}.overview-posture p{margin:10px 0 0;color:var(--muted);font-size:13px;line-height:1.6}.overview-actions{display:grid;grid-template-columns:1fr 1fr;gap:10px;align-self:stretch}.overview-action{min-width:0;border-radius:11px;background:rgba(255,255,255,.68);padding:17px;display:flex;flex-direction:column;justify-content:flex-end;box-shadow:inset 0 0 0 1px rgba(34,34,39,.06)}a.overview-action:hover{background:#FFF;box-shadow:inset 0 0 0 1px rgba(34,34,39,.08);transform:translateY(-1px)}.overview-action.primary{background:var(--ink);color:white;box-shadow:none}.overview-action.primary:hover{background:#2B2B30}.overview-action.calm{background:rgba(255,255,255,.5)}.overview-action span{font-size:10px;color:var(--faint)}.overview-action.primary span,.overview-action.primary small{color:#B9B9C0}.overview-action strong{font-size:28px;line-height:1;margin-top:10px;letter-spacing:-.04em}.overview-action small{font-size:10px;color:var(--muted);margin-top:8px}.signal-strip{margin-top:18px;display:grid;grid-template-columns:repeat(4,1fr);gap:4px;padding:0 4px}.signal-strip a{padding:14px 16px;border-radius:9px;min-width:0;position:relative}.signal-strip a:not(:last-child):after{content:"";position:absolute;right:-2px;top:17px;bottom:17px;width:1px;background:var(--border)}.signal-strip a:hover{background:rgba(255,255,255,.72)}.signal-strip span{display:block;color:var(--muted);font-size:10px}.signal-strip strong{display:block;font-size:19px;letter-spacing:-.02em;margin-top:5px;text-transform:capitalize}.signal-strip strong i{font-style:normal;font-size:12px;color:var(--faint);font-weight:550}.signal-strip small{display:block;color:var(--faint);font-size:10px;margin-top:3px}.page-section{margin-top:34px}.section-header{height:32px;display:flex;align-items:flex-start;justify-content:space-between;border-bottom:1px solid var(--border)}.section-header>div:first-child{display:flex;align-items:baseline;gap:10px}.section-header h2{font-size:15px;margin:0;font-weight:650}.section-header span{font-size:11px;color:var(--faint)}
6
+ .attention-list,.compact-work-list{border-bottom:1px solid var(--border)}.attention-section .attention-list{margin-top:4px}.attention-row{display:grid;grid-template-columns:42px minmax(0,1fr) auto;align-items:center;gap:12px;min-height:66px;padding:10px 8px;border-bottom:1px solid var(--border);border-radius:8px}.attention-row:hover{background:rgba(255,255,255,.7)}.attention-row:last-child{border-bottom:0}.attention-index{font-family:var(--font-mono);font-size:10px;color:var(--faint)}.attention-row strong,.compact-work-list strong{font-size:13px}.attention-row p,.compact-work-list p{margin:3px 0 0;color:var(--muted);font-size:12px}.compact-work-list a>div{min-width:0}.compact-work-list p{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.compact-work-list a{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:16px;align-items:center;min-height:54px;padding:8px 4px;border-bottom:1px solid var(--border)}.compact-work-list a:last-child{border-bottom:0}.compact-work-list a:hover strong{color:var(--accent)}.quiet-empty{padding:30px 8px;color:var(--muted)}.success-empty{padding:24px 8px;border-radius:10px;background:linear-gradient(90deg,var(--success-soft),transparent 72%)}.quiet-empty p{margin:6px 0 0}.status-text{display:inline-flex;align-items:center;gap:7px;white-space:nowrap;font-size:11px;color:var(--muted)}.status-dot{width:7px;height:7px;border-radius:50%;background:var(--faint)}.status-dot.success{background:var(--success)}.status-dot.warning{background:var(--warning)}.status-dot.danger{background:var(--danger)}.status-dot.info{background:var(--info)}
7
+ .toolbar{min-height:58px;display:flex;align-items:center;border-bottom:1px solid var(--border);position:sticky;top:0;z-index:4;background:rgba(245,245,243,.92);backdrop-filter:blur(12px)}.work-toolbar{justify-content:space-between;gap:18px}.repository-filter{display:flex;align-items:center;gap:8px;color:var(--faint);font-size:10px}.repository-filter select{height:30px;max-width:220px;border:1px solid var(--border-strong);border-radius:var(--r-sm);background:var(--surface);color:var(--ink);padding:0 28px 0 9px;font:inherit}.work-row .row-eyebrow{color:var(--accent);letter-spacing:.06em;text-transform:none}.segmented{display:inline-flex;padding:2px;background:var(--surface-strong);border-radius:7px;gap:1px}.segmented button{border:0;background:transparent;color:var(--muted);border-radius:5px;height:28px;padding:0 9px;cursor:pointer;font-size:11px}.segmented button.selected{background:var(--surface);color:var(--ink);box-shadow:0 1px 2px rgba(0,0,0,.05)}.segmented button span{margin-left:5px;color:var(--faint);font-size:10px}.split-workspace{display:grid;grid-template-columns:minmax(0,1.65fr) minmax(310px,.85fr);gap:22px;min-height:520px;padding-top:16px}.automation-layout{grid-template-columns:minmax(0,2.1fr) minmax(320px,.9fr)}.scan-list{min-width:0}.scan-row{width:100%;min-height:68px;border:0;border-bottom:1px solid var(--border);border-radius:9px;background:transparent;text-align:left;padding:11px 12px;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:18px;align-items:center;cursor:pointer;color:var(--ink)}.scan-row.static{cursor:default}.scan-row:hover{background:rgba(255,255,255,.72)}.scan-row.selected{background:var(--accent-soft);box-shadow:inset 0 0 0 1px rgba(94,84,232,.12)}.scan-main{min-width:0}.scan-main strong{display:block;font-size:13px;font-weight:620;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.scan-main p{margin:4px 0 0;color:var(--muted);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.row-eyebrow{display:block;margin-bottom:3px}.scan-meta{text-align:right;display:grid;justify-items:end;gap:5px;color:var(--faint);font-size:10px}
8
+ .detail-pane{border:1px solid var(--border);border-radius:12px;background:var(--surface);min-width:0;align-self:start;position:sticky;top:18px;overflow:hidden}.detail-head{padding:19px 20px 16px;border-bottom:1px solid var(--border);display:flex;gap:12px;justify-content:space-between}.detail-head h2{font-size:16px;margin:4px 0 5px;line-height:1.3}.detail-head p{margin:0;color:var(--muted);font-size:12px;line-height:1.5}.detail-body{padding:18px}.detail-empty{display:grid;place-items:center;min-height:260px;color:var(--faint);font-size:12px}.definition-list{margin:0}.definition-list>div{display:grid;grid-template-columns:100px minmax(0,1fr);gap:10px;padding:8px 0;border-bottom:1px solid var(--border)}.definition-list dt{color:var(--faint);font-size:11px}.definition-list dd{margin:0;min-width:0;overflow-wrap:anywhere}.definition-list code{font-size:10px;color:var(--muted)}.detail-callout{margin-top:16px;border-left:2px solid var(--warning);padding:4px 0 4px 12px}.detail-callout.danger{border-color:var(--danger)}.detail-callout.success{border-color:var(--success)}.detail-callout p{margin:5px 0 0;color:var(--muted);line-height:1.5}.detail-note{margin-top:18px;color:var(--faint);font-size:11px;line-height:1.55}.detail-button-row{display:flex;gap:7px;margin-top:18px;flex-wrap:wrap}.danger-text{color:var(--danger)}
9
+ .table-wrap{min-width:0;overflow:auto}.data-table{width:100%;border-collapse:collapse;font-size:12px}.data-table th{text-align:left;height:38px;color:var(--faint);font-size:10px;font-weight:650;letter-spacing:.03em;border-bottom:1px solid var(--border);padding:0 10px;position:sticky;top:58px;background:rgba(245,245,243,.96);backdrop-filter:blur(10px);z-index:2}.data-table td{height:62px;border-bottom:1px solid var(--border);padding:9px 10px;vertical-align:middle}.data-table tbody tr{cursor:pointer}.data-table tbody tr:hover{background:rgba(255,255,255,.72)}.data-table tbody tr.selected{background:var(--accent-soft);box-shadow:inset 3px 0 var(--accent)}.data-table td:first-child{max-width:280px}.data-table td strong{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.data-table td small{display:block;color:var(--faint);margin-top:3px}.capability-lines{display:flex;flex-wrap:wrap;gap:6px;margin-top:16px}.capability-lines span{font-size:10px;color:var(--muted);border:1px solid var(--border);border-radius:5px;padding:4px 6px;background:var(--surface)}.advanced{margin-top:18px;border-top:1px solid var(--border);padding-top:12px}.advanced summary{cursor:pointer;color:var(--muted);font-size:11px}.advanced pre{white-space:pre-wrap;word-break:break-word;background:var(--surface-subtle);border-radius:6px;padding:10px;font-size:10px;max-height:420px;overflow:auto}
10
+ .repository-tools{min-height:58px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:14px;justify-content:space-between}.repository-count{color:var(--faint);font-size:10px}.repository-tools input,.search-field input,.repository-add-panel input{height:34px;border:1px solid var(--border-strong);border-radius:8px;background:var(--surface);padding:0 10px;color:var(--ink);min-width:170px}.search-field{display:flex;align-items:center;position:relative}.search-field svg{position:absolute;left:10px;width:14px;color:var(--faint)}.search-field input{padding-left:31px;width:250px}.repository-add-panel{margin:14px 0 2px;padding:18px 20px;border-radius:11px;background:var(--surface);border:1px solid var(--border)}.repository-add-fields{display:grid;grid-template-columns:minmax(260px,1.4fr) minmax(180px,.8fr) auto;gap:10px;align-items:end}.repository-add-fields label{display:grid;gap:6px}.repository-add-fields label>span{font-size:10px;color:var(--faint);font-weight:650}.repository-add-fields input{width:100%}.repository-add-panel p{margin:11px 0 0;color:var(--faint);font-size:10px}.repository-workspace{padding-top:14px}.settings-column{max-width:900px;padding-top:12px}.settings-column section{margin-top:26px}.settings-list{border-top:1px solid var(--border)}.settings-row{display:grid;grid-template-columns:minmax(0,1fr) 130px auto;gap:20px;align-items:center;min-height:68px;border-bottom:1px solid var(--border);padding:9px 0}.settings-row strong{font-size:13px}.settings-row p{margin:3px 0 0;color:var(--muted);font-size:12px}.settings-actions{display:flex;gap:6px}.system-layout{max-width:900px;padding-top:26px}.system-summary{border-bottom:1px solid var(--border);padding-bottom:20px}.system-posture{display:flex;justify-content:space-between;align-items:flex-start;gap:20px;margin-bottom:12px}.system-posture h2{font-size:18px;margin:4px 0}.system-posture p{margin:0;color:var(--muted)}.system-layout>section+section{margin-top:24px}.boot-state{min-height:100vh;display:grid;place-content:center;justify-items:center;gap:10px;background:var(--canvas);color:var(--muted)}.boot-state .brand-mark{width:34px;height:34px}.boot-state p{max-width:460px;text-align:center}.single-list{max-width:900px}
11
+ @media(max-width:1080px){.workspace{padding-left:28px;padding-right:28px}.command-bar{align-items:flex-start;flex-direction:column}.command-actions{width:100%;justify-content:flex-start}.command-meta{justify-items:start}.overview-hero{grid-template-columns:1fr}.overview-actions{min-height:130px}.signal-strip{grid-template-columns:repeat(2,1fr)}.signal-strip a:nth-child(2):after{display:none}.split-workspace,.automation-layout{grid-template-columns:1fr}.detail-pane{position:static;border:1px solid var(--border);margin-bottom:20px}.data-table{min-width:760px}}
12
+ @media(max-width:760px){.app-shell{display:block}.sidebar{position:sticky;z-index:10;top:0;width:100%;height:auto;padding:8px 12px;flex-direction:row;align-items:center;overflow:auto}.brand{height:38px;min-width:120px}.brand small,.sidebar-foot,.nav-group-label{display:none}.sidebar nav{display:flex;margin:0 0 0 8px;gap:2px}.sidebar nav>div.nav-group-start{margin:0}.sidebar nav a{white-space:nowrap;height:32px}.sidebar nav svg{display:none}.workspace{padding:0 18px 44px}.command-actions{flex-wrap:wrap}.command-meta{width:100%;justify-items:start}.overview-hero{padding:22px 20px}.overview-actions{grid-template-columns:1fr}.signal-strip{grid-template-columns:1fr 1fr}.signal-strip a:after{display:none}.repository-tools{align-items:center;padding:10px 0}.repository-add-fields{grid-template-columns:1fr}.repository-add-panel{padding:16px}.repository-tools input{flex:1}.settings-row{grid-template-columns:1fr auto}.settings-row>.status-text{grid-column:2}.settings-actions{grid-column:1/-1}.definition-list>div{grid-template-columns:88px 1fr}}
13
+
14
+ .overview-posture{margin-top:8px;max-width:none;min-height:144px;display:grid;grid-template-columns:minmax(0,1fr) 180px;gap:32px;align-items:center;padding:24px 4px;border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
15
+ .overview-posture-copy{max-width:760px}.overview-posture h2{font-size:20px;line-height:1.25;letter-spacing:-.02em;font-weight:650}.overview-posture p{margin-top:9px;line-height:1.55}
16
+ .posture-attention{min-height:88px;border-left:1px solid var(--border);padding:8px 0 8px 22px;display:flex;flex-direction:column;justify-content:center}.posture-attention.has-attention{border-left-color:var(--warning)}.posture-attention:hover strong{color:var(--accent)}.posture-attention span,.posture-attention small{font-size:10px;color:var(--faint)}.posture-attention strong{font-size:25px;line-height:1;margin-top:7px;letter-spacing:-.03em}.posture-attention small{margin-top:7px;color:var(--muted)}
17
+
18
+ .signal-strip{margin-top:14px;gap:0;padding:0 4px}.signal-strip a{border-radius:0;padding:14px 16px}.signal-strip a:not(:last-child):after{right:0;top:16px;bottom:16px}.signal-strip a:hover{background:transparent}.signal-strip a:hover strong{color:var(--accent)}
19
+ .detail-pane{border-radius:var(--r-md);background:var(--surface);top:76px;box-shadow:none}.repository-workspace .detail-pane{top:18px}.repository-add-panel{border-radius:var(--r-md);background:var(--surface-subtle);box-shadow:none}
20
+ @media(max-width:1080px){.overview-posture{grid-template-columns:1fr 160px}.detail-pane{top:auto}}
21
+ @media(max-width:760px){.overview-posture{grid-template-columns:1fr;padding:20px 4px}.posture-attention{border-left:0;border-top:1px solid var(--border);padding:16px 0 0;min-height:auto}}
22
+
23
+ /* Overview V2: portfolio-first workspace; hierarchy comes from rows, whitespace and hairlines. */
24
+ .overview-home{width:min(100%,920px);padding-top:2px}.overview-home .page-section{margin-top:0}.overview-home .page-section+.page-section{margin-top:42px}.overview-attention-list,.overview-workspace-list{border-bottom:1px solid var(--border)}.overview-attention-row{min-height:66px;display:grid;grid-template-columns:150px minmax(0,1fr) auto 16px;gap:18px;align-items:center;padding:12px 4px 12px 0;border-top:1px solid var(--border)}.overview-attention-row:hover .overview-attention-copy strong,.overview-workspace-row:hover>strong{color:var(--accent)}.overview-attention-source{color:var(--faint);font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.045em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.overview-attention-copy{min-width:0}.overview-attention-copy strong{display:block;font-size:13px;font-weight:650;line-height:1.35;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.overview-attention-copy p{margin:4px 0 0;color:var(--muted);font-size:11px;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.overview-attention-row>.status-text{white-space:nowrap}.overview-row-arrow{color:var(--faint);font-size:13px;text-align:right}.overview-clear-state{min-height:52px;display:flex;align-items:center;gap:12px;border-top:1px solid var(--border);border-bottom:1px solid var(--border);color:var(--muted);font-size:12px}.overview-workspace-row{min-height:50px;display:grid;grid-template-columns:150px minmax(0,1fr) auto 16px;gap:18px;align-items:center;padding:0 4px 0 0;border-top:1px solid var(--border)}.overview-workspace-row>strong{font-size:13px;font-weight:650}.overview-workspace-row>span:nth-child(2){color:var(--muted);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.overview-workspace-row>.status-text{white-space:nowrap}
25
+ @media(max-width:760px){.overview-home{width:100%}.overview-home .page-section+.page-section{margin-top:32px}.overview-attention-row{grid-template-columns:minmax(0,1fr) auto 14px;gap:10px;padding:12px 0}.overview-attention-source{grid-column:1/-1;margin-bottom:-4px}.overview-attention-copy p{white-space:normal}.overview-workspace-row{grid-template-columns:112px minmax(0,1fr) 14px;gap:10px}.overview-workspace-row>.status-text{display:none}}
26
+
27
+ /* Automation configuration + execution history */
28
+ .automation-toolbar{justify-content:flex-start}.automation-table th:nth-child(1){width:30%}.automation-table th:nth-child(2){width:17%}.automation-table th:nth-child(3){width:23%}.automation-table th:nth-child(4){width:13%}.automation-table th:nth-child(5){width:17%}.automation-detail-status{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:16px;padding:2px 0 16px;margin-bottom:4px;border-bottom:1px solid var(--border)}.automation-detail-status>div:first-child{display:grid;gap:7px}.automation-result{text-align:right;display:grid;gap:3px}.automation-result strong{font-size:14px;font-weight:650}.automation-result small{color:var(--faint);font-size:10px}.automation-section-head{display:flex;justify-content:space-between;align-items:end;margin:22px 0 8px;padding-top:16px;border-top:1px solid var(--border)}.automation-section-head h3{font-size:13px;margin:3px 0 0;font-weight:650}.automation-section-head>span{font-size:10px;color:var(--faint)}.automation-history{display:grid}.automation-history-row{display:grid;grid-template-columns:12px minmax(0,1fr);gap:9px;padding:10px 0;border-bottom:1px solid var(--border)}.automation-history-row:last-child{border-bottom:0}.automation-history-mark{width:7px;height:7px;border-radius:50%;margin-top:6px;background:var(--faint)}.automation-history-mark.green{background:#268a5c}.automation-history-mark.amber{background:#b27820}.automation-history-mark.red{background:#b74c4c}.automation-history-mark.blue{background:#557bd8}.automation-history-copy{min-width:0}.automation-history-top{display:flex;justify-content:space-between;gap:12px;align-items:center}.automation-history-top time{color:var(--faint);font-size:10px;white-space:nowrap}.automation-history-copy p{margin:5px 0 0;color:var(--muted);font-size:11px;line-height:1.45}.automation-history-copy small{display:block;margin-top:4px;color:var(--faint);font-size:10px}.automation-history-empty{padding:18px 0;color:var(--faint);font-size:11px}.automation-advanced{margin-top:14px}@media(max-width:720px){.automation-detail-status{grid-template-columns:1fr}.automation-result{text-align:left}.automation-table{min-width:680px}}
@@ -1,14 +1,15 @@
1
- export type RouteId = 'overview' | 'automations' | 'work' | 'capabilities' | 'repositories' | 'settings' | 'system';
1
+ export type RouteId = 'overview' | 'automations' | 'work' | 'capabilities' | 'repositories' | 'system';
2
2
  export type Dict = Record<string, unknown>;
3
3
  export interface RepositoryView { id:string; name:string; path?:string; branchLabel?:string; dirtyLabel?:string; readinessLabel?:string; statusLabel?:string; current?:boolean; advanced?:Dict }
4
4
  export interface PluginActionView { id:string; title?:string; description?:string; risk?:string; riskLabel?:string; readOnly?:boolean; canPreview?:boolean; confirmationLabel?:string }
5
5
  export interface PluginView { id:string; name:string; description?:string; status?:string; statusLabel?:string; tone?:string; nextStep?:string; provider?:string; healthLabel?:string; lifecycleLabel?:string; capabilityLabels?:string[]; actions?:PluginActionView[]; warnings?:string[]; advanced?:Dict }
6
- export interface ProviderView { providerId:string; displayName:string; status?:string; statusLabel?:string; kindLabel?:string; summary?:string; explanation?:string; directDispatch?:boolean; handoffOnly?:boolean; enabled?:boolean; priority?:number; capabilities?:string[]; lastErrorSummary?:string }
7
- export interface LocalToolView { toolId:string; displayName:string; status?:string; summary?:string; enabled?:boolean; executablePath?:string; version?:string; usedByWorkflows?:string[] }
8
- export interface AutomationSettingsView { schemaVersion?:number; overview?:Dict; providers?:ProviderView[]; localTools?:LocalToolView[]; credentials?:Dict[]; routing?:{orders?:Record<string,string[]>}; warnings?:string[] }
9
6
  export interface CommandCenterView { readiness?:Dict; repositories?:RepositoryView[]; handoffs?:Dict[]; plugins?:PluginView[]; pluginSummary?:{total?:number;ready?:number;needsAttention?:number}; warnings?:string[] }
10
- export interface AutomationView { id:string; source:'schedule'|'routine'; repoId:string; repositoryName:string; name:string; summary?:string; status:'enabled'|'paused'|'attention'|'disabled'; schedule:string; timezone?:string; delivery?:string; lastRunAt?:string; lastResult?:string; nextRunHint?:string; pausedReason?:string; actions:Array<'run'|'pause'|'resume'> }
11
- export interface AutomationsResponse { schemaVersion:1; generatedAt:string; summary:{total:number;enabled:number;paused:number;needsAttention:number}; automations:AutomationView[] }
7
+ export type AutomationMode='browser_watch'|'browser_keepalive'|'continuation'|'schedule';
8
+ export interface AutomationHistoryView { id:string; at:string; result:string; tone:'green'|'amber'|'red'|'blue'|'gray'; reason?:string; trigger?:string }
9
+ export interface AutomationView { id:string; source:'schedule'; repoId:string; repositoryName:string; name:string; summary?:string; status:'enabled'|'paused'|'attention'|'completed'|'disabled'; schedule:string; timezone?:string; delivery?:string; mode:AutomationMode; modeLabel:string; live?:boolean; targetLabel?:string; boundWorkId?:string; boundWorkObjective?:string; observationStatus?:'baseline'|'unchanged'|'changed'|'keepalive'|'auth_required'; observationAt?:string; failureCount?:number; policySummary?:string; agentModel?:string; reasoningLevel?:string; tabPolicy?:string; lastRunAt?:string; lastResult?:string; nextRunHint?:string; pausedReason?:string; attentionMessage?:string; history:AutomationHistoryView[]; actions:Array<'run'|'pause'|'resume'> }
10
+ export interface AutomationsResponse { schemaVersion:1; generatedAt:string; summary:{total:number;enabled:number;paused:number;completed:number;needsAttention:number}; automations:AutomationView[] }
12
11
  export interface TrackedWorkView { requirementId:string; title:string; outcome?:string; state:'planned'|'active'|'waiting_for_user'|'done'|'cancelled'; needsAttention?:boolean; blocker?:string; requiredUserDecision?:string; updatedAt?:string }
13
12
  export interface WorkResponse { schemaVersion?:number; requirementCount?:number; activeRequirementCount?:number; waitingForUserCount?:number; needsAttentionCount?:number; requirements?:TrackedWorkView[]; requirementTruncatedCount?:number }
14
- export interface ConsoleData { commandCenter:CommandCenterView; work:WorkResponse; automations:AutomationsResponse; automationSettings:AutomationSettingsView; connector?:Dict; generatedAt:string }
13
+ export interface WorkPortfolioItem { id:string; repoId:string; repositoryName:string; updatedAt:string; title:string; objective:string; modeLabel:string; statusLabel:string; tone?:string; phase:string; phaseLabel:string; nextAction:string; latestSummary:string; latestVerification?:Dict; changedFiles?:Dict; advanced?:{workId:string;status:string;checkIds:string[];handoffRefs:string[]} }
14
+ export interface WorkPortfolioResponse { schemaVersion:1; generatedAt:string; summary:{total:number;open:number;needsAttention:number;completed:number}; repositories:Array<{repoId:string;repositoryName:string}>; items:WorkPortfolioItem[] }
15
+ export interface ConsoleData { commandCenter:CommandCenterView; work:WorkResponse; workPortfolio:WorkPortfolioResponse; automations:AutomationsResponse; connector?:Dict; generatedAt:string }