@moretea-labs/forge 1.6.0 → 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 (396) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +26 -0
  3. package/CLAUDE.md +2 -0
  4. package/README.md +2 -2
  5. package/README.zh-CN.md +1 -1
  6. package/SKILL.md +2 -2
  7. package/THIRD_PARTY_NOTICES.md +1 -1
  8. package/agents/openai.yaml +1 -1
  9. package/assets/hooks/hook-input.sh +11 -8
  10. package/assets/hooks/lib/session-state.sh +10 -7
  11. package/assets/hooks/lib/workflow-state.sh +13 -177
  12. package/assets/hooks/post-tool-observer.sh +8 -2
  13. package/assets/hooks/prompt-guard.sh +5 -81
  14. package/assets/hooks/session-start-context.sh +56 -1
  15. package/assets/partials/05-workflow.partial.md +1 -1
  16. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  17. package/assets/plugin-registry.v1.json +1 -1
  18. package/assets/reference-configs/document-generation.md +1 -1
  19. package/assets/reference-configs/harness-overview.md +1 -1
  20. package/assets/reference-configs/sprint-contracts.md +3 -3
  21. package/assets/skill-version.json +2 -2
  22. package/assets/templates/contract.template.md +2 -7
  23. package/assets/templates/helpers/capture-plan.sh +2 -2
  24. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  25. package/assets/templates/helpers/contract-worktree.sh +1 -21
  26. package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
  27. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  28. package/assets/templates/helpers/new-plan.sh +1 -1
  29. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  30. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  31. package/assets/templates/helpers/verify-contract.sh +1 -111
  32. package/assets/templates/helpers/verify-sprint.sh +3 -180
  33. package/assets/templates/plan.template.md +1 -1
  34. package/assets/templates/review.template.md +9 -34
  35. package/assets/workflow-contract.v1.json +10 -19
  36. package/docs/README.md +10 -10
  37. package/docs/architecture/CURRENT.md +170 -0
  38. package/docs/architecture/EVOLUTION.md +42 -0
  39. package/docs/architecture/current/README.md +4 -156
  40. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  41. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  42. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  43. package/docs/architecture/history/README.md +7 -0
  44. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  45. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  46. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  47. package/docs/architecture/history.md +3 -34
  48. package/docs/architecture/index.md +15 -98
  49. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  50. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  51. package/docs/architecture/versions/1.6.md +38 -0
  52. package/docs/architecture/versions/1.7.md +41 -0
  53. package/docs/forge-chatgpt-browser-engine.md +2 -2
  54. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  55. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  56. package/docs/forge-github-issue-launcher.md +1 -1
  57. package/docs/forge-runtime-self-healing-loop.md +1 -1
  58. package/docs/operations/controller-browser-plugin.md +26 -6
  59. package/docs/operations/controller-performance-and-502.md +124 -0
  60. package/docs/operations/controller-reliability-runbook.md +1 -1
  61. package/docs/operations/features.md +2 -2
  62. package/docs/operations/github-repository.md +1 -1
  63. package/docs/operations/homebrew.md +2 -2
  64. package/docs/operations/mcp-tool-exposure.md +5 -3
  65. package/docs/wiki/Architecture.md +6 -6
  66. package/docs/wiki/Implementation.md +2 -2
  67. package/docs/wiki/Integrations.md +2 -2
  68. package/docs/wiki/Work-Lifecycle.md +1 -1
  69. package/package.json +29 -10
  70. package/scripts/activate-source-baseline.ts +17 -13
  71. package/scripts/benchmark-background-check-overlap.ts +228 -0
  72. package/scripts/benchmark-mcp-host.sh +117 -0
  73. package/scripts/benchmark-mcp-transport.ts +170 -0
  74. package/scripts/benchmark-quality-harness.ts +146 -0
  75. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  76. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  77. package/scripts/benchmark-typescript-navigation.ts +186 -0
  78. package/scripts/benchmark-work-efficiency.ts +334 -0
  79. package/scripts/capture-plan.sh +3 -3
  80. package/scripts/check-agent-tooling.sh +8 -35
  81. package/scripts/check-architecture-sync.sh +17 -86
  82. package/scripts/check-mcp-compatibility.ts +14 -0
  83. package/scripts/check-runtime-architecture.mjs +107 -64
  84. package/scripts/check-stable-baseline.ts +92 -0
  85. package/scripts/check-task-workflow.sh +4 -4
  86. package/scripts/contract-worktree.sh +1 -21
  87. package/scripts/ensure-task-workflow.sh +22 -55
  88. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  89. package/scripts/install-standalone-recovery.ts +24 -0
  90. package/scripts/lib/project-init-lib.sh +15 -44
  91. package/scripts/migrate-project-template.sh +23 -1179
  92. package/scripts/new-plan.sh +1 -1
  93. package/scripts/plan-to-todo.sh +11 -36
  94. package/scripts/run-governed-gate.ts +2 -1
  95. package/scripts/ship-worktrees.sh +2 -29
  96. package/scripts/smoke-ios-physical-device.ts +4 -2
  97. package/scripts/smoke-runtime-recovery.ts +1 -35
  98. package/scripts/smoke-schedule-engine.ts +1 -20
  99. package/scripts/verify-contract.sh +1 -111
  100. package/scripts/verify-forge-runtime.sh +0 -1
  101. package/scripts/verify-sprint.sh +3 -180
  102. package/scripts/workflow-contract.ts +1 -5
  103. package/src/cli/commands/init-hook.ts +3 -3
  104. package/src/cli/commands/mcp.ts +9 -5
  105. package/src/cli/commands/plugin.ts +1 -1
  106. package/src/cli/commands/recovery.ts +25 -3
  107. package/src/cli/commands/runtime.ts +55 -3
  108. package/src/cli/commands/security.ts +12 -1
  109. package/src/cli/commands/setup-profile.ts +31 -21
  110. package/src/cli/controller/check-runner.ts +106 -3
  111. package/src/cli/controller/composite-result.ts +1 -1
  112. package/src/cli/controller/context/focus.ts +51 -0
  113. package/src/cli/controller/context/instruction-context.ts +114 -0
  114. package/src/cli/controller/context/known-paths.ts +76 -0
  115. package/src/cli/controller/context/query-planning.ts +110 -0
  116. package/src/cli/controller/context/rule-resolution.ts +532 -0
  117. package/src/cli/controller/context/source-materializer.ts +278 -0
  118. package/src/cli/controller/context/structural.ts +16 -0
  119. package/src/cli/controller/context/types.ts +286 -0
  120. package/src/cli/controller/context-pack.ts +785 -469
  121. package/src/cli/controller/launch-agents.ts +77 -2
  122. package/src/cli/controller/operational-plan.ts +2 -2
  123. package/src/cli/controller/work-mode.ts +6 -4
  124. package/src/cli/editing/edit-session.ts +60 -7
  125. package/src/cli/github/github.ts +133 -0
  126. package/src/cli/installer/managed-entries.ts +15 -1
  127. package/src/cli/local-bridge/console-automations.ts +54 -78
  128. package/src/cli/local-bridge/console-view-models.ts +1 -35
  129. package/src/cli/local-bridge/facade-api.ts +0 -217
  130. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  131. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
  132. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  133. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
  134. package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
  135. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  136. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
  137. package/src/cli/local-bridge/frontend/src/styles.css +1 -1
  138. package/src/cli/local-bridge/frontend/src/types.ts +5 -8
  139. package/src/cli/local-bridge/job-store.ts +1 -490
  140. package/src/cli/local-bridge/server.ts +0 -513
  141. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  142. package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
  143. package/src/cli/mcp/auth.ts +5 -5
  144. package/src/cli/mcp/instructions.ts +3 -2
  145. package/src/cli/mcp/legacy-tool-service.ts +0 -189
  146. package/src/cli/mcp/multi-repository.ts +16 -5
  147. package/src/cli/mcp/oauth.ts +175 -26
  148. package/src/cli/mcp/repository-tools.ts +247 -107
  149. package/src/cli/mcp/server.ts +537 -111
  150. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  151. package/src/cli/mcp/toolset-selection.ts +1 -1
  152. package/src/cli/mcp/toolset.ts +42 -5
  153. package/src/cli/mcp/transports/http.ts +101 -14
  154. package/src/cli/mcp/transports/session-registry.ts +43 -5
  155. package/src/cli/mcp/types.ts +3 -3
  156. package/src/cli/repositories/command-classifier.ts +101 -19
  157. package/src/cli/repositories/command-executor.ts +55 -521
  158. package/src/cli/repositories/command-process.ts +108 -0
  159. package/src/cli/repositories/command-scope.ts +99 -41
  160. package/src/cli/repositories/controller-home.ts +7 -8
  161. package/src/cli/repositories/git-command-executor.ts +2 -0
  162. package/src/cli/repositories/locks.ts +53 -16
  163. package/src/cli/repositories/repository-snapshot.ts +121 -0
  164. package/src/cli/repositories/selected-path-actions.ts +17 -9
  165. package/src/cli/repositories/structured-git.ts +61 -3
  166. package/src/cli/repository/inspector.ts +553 -63
  167. package/src/cli/repository/session-cache.ts +58 -17
  168. package/src/cli/v81-entry.ts +1 -4
  169. package/src/effects/fs-transaction.ts +85 -17
  170. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  171. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  172. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  173. package/src/runtime/context/lsp-client.ts +283 -0
  174. package/src/runtime/context/semantic-navigation.ts +275 -0
  175. package/src/runtime/context/swift-navigation.ts +565 -0
  176. package/src/runtime/context/typescript-navigation.ts +226 -0
  177. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  178. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +47 -8
  179. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  180. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  181. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  182. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  183. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  184. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  185. package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
  186. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +143 -8
  187. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
  188. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +85 -8
  189. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  190. package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
  191. package/src/runtime/control-plane/facade/controller-session-store.ts +132 -1
  192. package/src/runtime/control-plane/facade/goal-workloop-access.ts +2 -0
  193. package/src/runtime/control-plane/facade/goal-workloop.ts +686 -129
  194. package/src/runtime/control-plane/facade/index.ts +1 -0
  195. package/src/runtime/control-plane/facade/plan-contract-store.ts +185 -20
  196. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  197. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  198. package/src/runtime/control-plane/facade/types.ts +22 -5
  199. package/src/runtime/control-plane/facade/work-contract-store.ts +175 -73
  200. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  201. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  202. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  203. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  204. package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
  205. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  206. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  207. package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
  208. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  209. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  210. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  211. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  212. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  213. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  214. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  215. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  216. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  217. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  218. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
  219. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +355 -116
  220. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
  221. package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
  222. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  223. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  224. package/src/runtime/control-plane/release-retention.ts +62 -2
  225. package/src/runtime/control-plane/routing/route-policy.ts +7 -16
  226. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  227. package/src/runtime/diagnostics/mcp-timing.ts +13 -0
  228. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  229. package/src/runtime/diagnostics/performance.ts +24 -1
  230. package/src/runtime/evidence/event-ledger.ts +1 -1
  231. package/src/runtime/execution/jobs/store.ts +58 -4
  232. package/src/runtime/execution/jobs/types.ts +1 -1
  233. package/src/runtime/execution/managed-workspace.ts +203 -1
  234. package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
  235. package/src/runtime/execution/process-runtime/check-result.ts +53 -0
  236. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  237. package/src/runtime/execution/process-runtime/command-facade.ts +345 -40
  238. package/src/runtime/execution/process-runtime/gc.ts +38 -6
  239. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
  240. package/src/runtime/execution/process-runtime/persisted-check.ts +47 -4
  241. package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
  242. package/src/runtime/execution/process-runtime/runtime.ts +43 -3
  243. package/src/runtime/execution/process-runtime/store.ts +1 -0
  244. package/src/runtime/execution/process-runtime/types.ts +2 -0
  245. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  246. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  247. package/src/runtime/execution/workers/executor.ts +0 -48
  248. package/src/runtime/gateway/mcp/execution-tools.ts +1192 -114
  249. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  250. package/src/runtime/gateway/mcp/router.ts +184 -82
  251. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
  252. package/src/runtime/gateway/mcp/runtime-tools.ts +2224 -1589
  253. package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
  254. package/src/runtime/plugins/browser-adapter.ts +2901 -241
  255. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  256. package/src/runtime/plugins/browser-handoff.ts +92 -2
  257. package/src/runtime/plugins/browser-macos-bridge.ts +582 -49
  258. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  259. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  260. package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
  261. package/src/runtime/plugins/browser-runtime.ts +183 -0
  262. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  263. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  264. package/src/runtime/plugins/desktop-operator-registration.ts +8 -2
  265. package/src/runtime/plugins/external-adapter.ts +324 -3
  266. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  267. package/src/runtime/plugins/first-party-registry.ts +2 -0
  268. package/src/runtime/plugins/github-adapter.ts +125 -0
  269. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  270. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  271. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  272. package/src/runtime/plugins/ios-adapter.ts +106 -16
  273. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  274. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  275. package/src/runtime/plugins/lightweight-action.ts +153 -0
  276. package/src/runtime/plugins/local-system-adapter.ts +123 -6
  277. package/src/runtime/plugins/macos-capability-broker.ts +55 -9
  278. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  279. package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
  280. package/src/runtime/plugins/resend-adapter.ts +1 -1
  281. package/src/runtime/plugins/store.ts +269 -29
  282. package/src/runtime/plugins/types.ts +35 -2
  283. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  284. package/src/runtime/projections/git-status-sampler.ts +16 -16
  285. package/src/runtime/projections/materialized-view.ts +28 -10
  286. package/src/runtime/recovery/maintenance-executor.ts +393 -28
  287. package/src/runtime/root/mcp-transport.ts +180 -10
  288. package/src/runtime/root/package-connector-service.ts +186 -8
  289. package/src/runtime/root/package-runtime-release.ts +143 -17
  290. package/src/runtime/root/package-runtime-service.ts +430 -22
  291. package/src/runtime/root/release-manifest.ts +7 -2
  292. package/src/runtime/root/release-materialize.ts +19 -0
  293. package/src/runtime/root/release-store.ts +48 -11
  294. package/src/runtime/root/runtime.ts +23 -5
  295. package/src/runtime/root/service.ts +100 -29
  296. package/src/runtime/root/types.ts +2 -0
  297. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  298. package/src/runtime/shared/trusted-executable.ts +62 -0
  299. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  300. package/src/runtime/standalone-recovery/core.ts +700 -92
  301. package/src/runtime/standalone-recovery/entry.ts +99 -23
  302. package/src/runtime/standalone-recovery/installer.ts +19 -3
  303. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  304. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  305. package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
  306. package/src/runtime/workflow/schedules/engine.ts +287 -151
  307. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  308. package/src/runtime/workflow/schedules/settlement.ts +48 -4
  309. package/src/runtime/workflow/schedules/store.ts +131 -10
  310. package/src/runtime/workflow/schedules/types.ts +2 -4
  311. package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
  312. package/src/testing/test-governance.ts +8 -40
  313. package/assets/reference-configs/heartbeat-triage.md +0 -57
  314. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  315. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  316. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  317. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  318. package/docs/architecture/current/architecture-invariants.md +0 -331
  319. package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
  320. package/docs/architecture/current/codegraph-context-provider.md +0 -238
  321. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  322. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  323. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
  324. package/docs/architecture/current/entity-model.md +0 -612
  325. package/docs/architecture/current/external-plugin-governance.md +0 -396
  326. package/docs/architecture/current/failure-recovery.md +0 -447
  327. package/docs/architecture/current/governance.md +0 -213
  328. package/docs/architecture/current/human-interaction-plane.md +0 -87
  329. package/docs/architecture/current/implementation-status.md +0 -128
  330. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  331. package/docs/architecture/current/legacy-governance.md +0 -58
  332. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  333. package/docs/architecture/current/migration-roadmap.md +0 -105
  334. package/docs/architecture/current/multi-repository-execution.md +0 -371
  335. package/docs/architecture/current/operations-runbook.md +0 -45
  336. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  337. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  338. package/docs/architecture/current/repository-access-modes.md +0 -102
  339. package/docs/architecture/current/route-policy.md +0 -15
  340. package/docs/architecture/current/route-session-concurrency.md +0 -85
  341. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  342. package/docs/architecture/current/runtime-directory-map.md +0 -49
  343. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  344. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  345. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  346. package/docs/architecture/current/system-overview.md +0 -169
  347. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  348. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  349. package/docs/architecture/current/thin-harness-v1.md +0 -321
  350. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  351. package/docs/forge-autonomous-goal-loop.md +0 -9
  352. package/docs/forge-provider-configuration.md +0 -7
  353. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
  354. package/docs/operations/gmail-assistant-routines.md +0 -186
  355. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  356. package/docs/operations/provider-configuration.md +0 -31
  357. package/scripts/harness-trace-grade.sh +0 -150
  358. package/scripts/heartbeat-triage.sh +0 -361
  359. package/scripts/maintenance-triage.sh +0 -167
  360. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  361. package/src/cli/repositories/goal-registry.ts +0 -239
  362. package/src/runtime/assistant/action-proposals.ts +0 -279
  363. package/src/runtime/assistant/intent.ts +0 -395
  364. package/src/runtime/assistant/model-provider.ts +0 -460
  365. package/src/runtime/assistant/openapi.ts +0 -223
  366. package/src/runtime/assistant/policy.ts +0 -89
  367. package/src/runtime/assistant/readiness.ts +0 -258
  368. package/src/runtime/assistant/routine-runtime.ts +0 -477
  369. package/src/runtime/assistant/schedule-binding.ts +0 -171
  370. package/src/runtime/assistant/standing-grants.ts +0 -375
  371. package/src/runtime/assistant/store.ts +0 -243
  372. package/src/runtime/assistant/types.ts +0 -135
  373. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  374. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  375. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  376. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  377. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  378. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  379. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  380. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  381. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  382. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  383. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  384. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  385. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  386. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  387. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  388. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  389. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  390. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  391. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  392. package/src/runtime/workflow/findings/store.ts +0 -204
  393. package/src/runtime/workflow/findings/types.ts +0 -58
  394. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  395. package/src/runtime/workflow/portfolio/store.ts +0 -185
  396. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -1,477 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'fs';
2
- import { dirname, join } from 'path';
3
- import type { RepositoryRecord } from '../../cli/repositories/types';
4
- import type { ExecutionJobOrigin } from '../execution/jobs/types';
5
- import { executeAssistantPluginAction } from '../plugins/store';
6
- import { isAssistantPluginError } from '../plugins/errors';
7
- import {
8
- createAssistantActionProposals,
9
- listAssistantActionProposals,
10
- type AssistantActionProposal,
11
- type AssistantActionProposalInput,
12
- } from './action-proposals';
13
- import { addAssistantInboxItem, getAssistantRoutine, touchAssistantRoutineRun } from './store';
14
- import { analyzeAssistantMessages, type AssistantModelAnalysis } from './model-provider';
15
- import { applyAssistantStandingGrants, type StandingGrantExecutionResult } from './standing-grants';
16
-
17
- export type AssistantRoutineRunStatus = 'collecting' | 'completed' | 'failed' | 'auth_required';
18
-
19
- export interface AssistantRoutineRun {
20
- schemaVersion: 1;
21
- runId: string;
22
- routineId: string;
23
- requestId: string;
24
- occurrenceId?: string;
25
- status: AssistantRoutineRunStatus;
26
- windowStart: string;
27
- windowEnd: string;
28
- collectedItems: number;
29
- processedItems: number;
30
- proposedActions: number;
31
- autoSubmittedActions?: number;
32
- analysis?: {
33
- usedModel: boolean;
34
- provider: AssistantModelAnalysis['provider'];
35
- model?: string;
36
- promptVersion: string;
37
- fallbackReason?: string;
38
- warnings: string[];
39
- };
40
- summary?: string;
41
- error?: string;
42
- createdAt: string;
43
- completedAt?: string;
44
- }
45
-
46
- interface GmailRoutineCursor {
47
- schemaVersion: 1;
48
- routineId: string;
49
- lastSuccessfulAt?: string;
50
- historyId?: string;
51
- continuation?: { mode: 'history' | 'query'; pageToken: string };
52
- processedMessageIds: string[];
53
- updatedAt: string;
54
- }
55
-
56
- export interface GmailMessageSummary {
57
- id: string;
58
- threadId?: string;
59
- from: string;
60
- subject: string;
61
- date?: string;
62
- snippet: string;
63
- bodyPreview?: string;
64
- labelIds: string[];
65
- }
66
-
67
- function now(): string { return new Date().toISOString(); }
68
- function assistantRoot(repoRoot: string): string { return join(repoRoot, '.forge', 'assistant'); }
69
- function runsPath(repoRoot: string): string { return join(assistantRoot(repoRoot), 'routine-runs.json'); }
70
- function cursorsPath(repoRoot: string): string { return join(assistantRoot(repoRoot), 'gmail-cursors.json'); }
71
-
72
- function readJson<T>(path: string, fallback: T): T {
73
- if (!existsSync(path)) return fallback;
74
- try { return JSON.parse(readFileSync(path, 'utf-8')) as T; } catch { return fallback; }
75
- }
76
-
77
- function writeJson(path: string, value: unknown): void {
78
- mkdirSync(dirname(path), { recursive: true });
79
- const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
80
- writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`, 'utf-8');
81
- renameSync(tmp, path);
82
- }
83
-
84
- function saveRun(repoRoot: string, run: AssistantRoutineRun): AssistantRoutineRun {
85
- const current = readJson<{ schemaVersion: 1; updatedAt: string; runs: AssistantRoutineRun[] }>(runsPath(repoRoot), {
86
- schemaVersion: 1,
87
- updatedAt: now(),
88
- runs: [],
89
- });
90
- current.runs = [run, ...current.runs.filter((entry) => entry.runId !== run.runId)].slice(0, 500);
91
- current.updatedAt = now();
92
- writeJson(runsPath(repoRoot), current);
93
- return run;
94
- }
95
-
96
- function readCursor(repoRoot: string, routineId: string): GmailRoutineCursor {
97
- const current = readJson<{ schemaVersion: 1; updatedAt: string; cursors: GmailRoutineCursor[] }>(cursorsPath(repoRoot), {
98
- schemaVersion: 1,
99
- updatedAt: now(),
100
- cursors: [],
101
- });
102
- return current.cursors.find((entry) => entry.routineId === routineId) ?? {
103
- schemaVersion: 1,
104
- routineId,
105
- processedMessageIds: [],
106
- updatedAt: now(),
107
- };
108
- }
109
-
110
- function saveCursor(repoRoot: string, cursor: GmailRoutineCursor): void {
111
- const current = readJson<{ schemaVersion: 1; updatedAt: string; cursors: GmailRoutineCursor[] }>(cursorsPath(repoRoot), {
112
- schemaVersion: 1,
113
- updatedAt: now(),
114
- cursors: [],
115
- });
116
- current.cursors = [{ ...cursor, updatedAt: now() }, ...current.cursors.filter((entry) => entry.routineId !== cursor.routineId)].slice(0, 200);
117
- current.updatedAt = now();
118
- writeJson(cursorsPath(repoRoot), current);
119
- }
120
-
121
- function recordValue(value: unknown): Record<string, unknown> {
122
- return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
123
- }
124
-
125
- function stringValue(value: unknown): string | undefined {
126
- return typeof value === 'string' && value.trim() ? value.trim() : undefined;
127
- }
128
-
129
- function stringArray(value: unknown): string[] {
130
- return Array.isArray(value) ? value.map(String).filter(Boolean) : [];
131
- }
132
-
133
- function decodeBase64Url(value: string | undefined): string | undefined {
134
- if (!value) return undefined;
135
- try {
136
- const normalized = value.replace(/-/g, '+').replace(/_/g, '/');
137
- return Buffer.from(normalized, 'base64').toString('utf-8');
138
- } catch {
139
- return undefined;
140
- }
141
- }
142
-
143
- function bodyText(payload: Record<string, unknown>, depth = 0): string | undefined {
144
- if (depth > 8) return undefined;
145
- const body = recordValue(payload.body);
146
- const direct = decodeBase64Url(stringValue(body.data));
147
- if (direct) return direct;
148
- const parts = Array.isArray(payload.parts) ? payload.parts : [];
149
- const preferred = parts
150
- .map((entry) => recordValue(entry))
151
- .sort((left, right) => String(left.mimeType) === 'text/plain' ? -1 : String(right.mimeType) === 'text/plain' ? 1 : 0);
152
- for (const part of preferred) {
153
- const decoded = bodyText(part, depth + 1);
154
- if (decoded) return decoded;
155
- }
156
- return undefined;
157
- }
158
-
159
- function summarizeMessage(raw: Record<string, unknown>): GmailMessageSummary {
160
- const payload = recordValue(raw.payload);
161
- const headers = Array.isArray(payload.headers) ? payload.headers.map(recordValue) : [];
162
- const header = (name: string): string | undefined => stringValue(headers.find((entry) => String(entry.name).toLowerCase() === name.toLowerCase())?.value);
163
- const decoded = bodyText(payload)?.replace(/\s+/g, ' ').trim();
164
- return {
165
- id: String(raw.id ?? ''),
166
- threadId: stringValue(raw.threadId),
167
- from: header('From') ?? 'unknown sender',
168
- subject: header('Subject') ?? '(no subject)',
169
- date: header('Date'),
170
- snippet: String(raw.snippet ?? '').replace(/\s+/g, ' ').trim(),
171
- bodyPreview: decoded?.slice(0, 1_500),
172
- labelIds: stringArray(raw.labelIds),
173
- };
174
- }
175
-
176
- function senderAddress(value: string): string | undefined {
177
- return value.match(/<([^>]+@[^>]+)>/)?.[1] ?? value.match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i)?.[0];
178
- }
179
-
180
- function protectedMessageSummary(message: GmailMessageSummary): boolean {
181
- return /(security|authentication|password|login|billing|invoice|incident|production|quota|permission|dependabot|gift[ -]?card|安全|登录|账单|故障|告警|权限|礼品卡|禮品卡)/i
182
- .test(`${message.subject} ${message.snippet}`);
183
- }
184
-
185
- function promotionalMessageSummary(message: GmailMessageSummary): boolean {
186
- if (message.labelIds.includes('CATEGORY_PROMOTIONS')) return true;
187
- return /(newsletter|digest|marketing|unsubscribe|推广|营销|周报)/i
188
- .test(`${message.subject} ${message.snippet} ${message.bodyPreview ?? ''}`);
189
- }
190
-
191
- export function buildDeterministicAssistantProposals(messages: GmailMessageSummary[]): AssistantActionProposalInput[] {
192
- const proposals: AssistantActionProposalInput[] = [];
193
- for (const message of messages) {
194
- const text = `${message.subject} ${message.snippet} ${message.bodyPreview ?? ''}`.toLowerCase();
195
- const protectedMessage = protectedMessageSummary(message);
196
- const promotionsCategory = message.labelIds.includes('CATEGORY_PROMOTIONS');
197
- if (/(please reply|reply requested|请回复|需要回复|your response)/i.test(text)) {
198
- const to = senderAddress(message.from);
199
- proposals.push({
200
- pluginId: 'gmail', actionId: 'create_draft', evidenceMessageIds: [message.id],
201
- reason: `Prepare a reviewable reply draft for “${message.subject}”.`, confidence: 0.75,
202
- executable: false,
203
- context: { sender: to ?? message.from, subject: message.subject, protected: protectedMessage },
204
- arguments: to ? { to: [to], subject: message.subject.startsWith('Re:') ? message.subject : `Re: ${message.subject}`, body_text: '[Draft response pending review]' } : {},
205
- });
206
- }
207
- if (/(action required|todo|deadline|due date|需要处理|截止|待办)/i.test(text)) {
208
- proposals.push({
209
- pluginId: 'google_tasks', actionId: 'create_task', evidenceMessageIds: [message.id],
210
- reason: `Create a task from “${message.subject}”.`, confidence: 0.8,
211
- context: { sender: message.from, subject: message.subject, protected: protectedMessage },
212
- arguments: { title: message.subject, notes: `${message.from}\n${message.snippet}`.slice(0, 2_000) },
213
- });
214
- }
215
- if (promotionalMessageSummary(message) && !protectedMessage) {
216
- proposals.push({
217
- pluginId: 'gmail', actionId: 'archive_message', evidenceMessageIds: [message.id],
218
- reason: promotionsCategory
219
- ? `Archive Gmail Promotions candidate: “${message.subject}”.`
220
- : `Archive marketing/newsletter candidate: “${message.subject}”.`,
221
- confidence: promotionsCategory ? 0.98 : 0.7,
222
- context: { sender: message.from, subject: message.subject, protected: false },
223
- arguments: { message_id: message.id },
224
- });
225
- }
226
- }
227
- return proposals.slice(0, 50);
228
- }
229
-
230
- export function renderAssistantRoutineReport(
231
- messages: GmailMessageSummary[],
232
- proposals: AssistantActionProposal[],
233
- windowStart: string,
234
- windowEnd: string,
235
- analysis: AssistantModelAnalysis,
236
- standingGrantResults: StandingGrantExecutionResult[],
237
- ): string {
238
- const important = messages.filter((message) => /(security|alert|billing|invoice|jira|github|production|incident|安全|账单|故障|告警)/i.test(`${message.subject} ${message.snippet}`));
239
- const promotional = messages.filter(promotionalMessageSummary);
240
- const archiveProposals = proposals.filter((proposal) => proposal.pluginId === 'gmail' && proposal.actionId === 'archive_message');
241
- const proposalById = new Map(proposals.map((proposal) => [proposal.proposalId, proposal]));
242
- const submitted = standingGrantResults.filter((entry) => entry.status === 'submitted');
243
- const autoArchiveSubmitted = submitted.filter((entry) => {
244
- const proposal = proposalById.get(entry.proposalId);
245
- return proposal?.pluginId === 'gmail' && proposal.actionId === 'archive_message';
246
- });
247
- const lines = [
248
- `窗口:${windowStart} — ${windowEnd}`,
249
- `读取邮件:${messages.length} 封;重要候选:${important.length} 封;推广候选:${promotional.length} 封;归档建议:${archiveProposals.length} 项;自动归档:${autoArchiveSubmitted.length} 项;自动提交总计:${submitted.length} 项。`,
250
- `分析方式:${analysis.usedModel ? `${analysis.provider}${analysis.model ? ` (${analysis.model})` : ''}` : 'deterministic rules'}.`,
251
- ...(analysis.summary ? ['', '模型摘要:', analysis.summary] : []),
252
- '',
253
- '重要邮件候选:',
254
- ...(important.length > 0
255
- ? important.slice(0, 15).map((message) => `- ${message.subject} — ${message.from}`)
256
- : ['- 未发现明确的重要邮件候选。']),
257
- '',
258
- '最近邮件:',
259
- ...messages.slice(0, 20).map((message) => `- ${message.subject} — ${message.from}${message.snippet ? `:${message.snippet.slice(0, 160)}` : ''}`),
260
- '',
261
- '行动建议(仅提议,不会自动发送或删除):',
262
- ...(proposals.length > 0 ? proposals.slice(0, 20).map((proposal) => `- ${proposal.reason}`) : ['- 暂无。']),
263
- '',
264
- '自动执行结果:',
265
- ...(standingGrantResults.length > 0
266
- ? standingGrantResults.slice(0, 20).map((entry) => `- ${entry.status}: ${entry.proposalId}${entry.reason ? ` — ${entry.reason}` : ''}`)
267
- : ['- 没有匹配 Standing Grant。']),
268
- ...(analysis.warnings.length > 0 ? ['', '分析警告:', ...analysis.warnings.map((warning) => `- ${warning}`)] : []),
269
- ];
270
- return lines.join('\n');
271
- }
272
-
273
- async function gmailAction(
274
- controllerHome: string,
275
- repository: RepositoryRecord,
276
- origin: ExecutionJobOrigin,
277
- requestId: string,
278
- actionId: string,
279
- args: Record<string, unknown>,
280
- ): Promise<Record<string, unknown>> {
281
- const executed = await executeAssistantPluginAction({
282
- controllerHome,
283
- repoId: repository.repoId,
284
- repoRoot: repository.canonicalRoot,
285
- pluginId: 'gmail',
286
- actionId,
287
- requestId,
288
- args,
289
- origin,
290
- });
291
- return recordValue(executed.result);
292
- }
293
-
294
- export async function executeAssistantRoutineRuntime(input: {
295
- controllerHome: string;
296
- repository: RepositoryRecord;
297
- routineId: string;
298
- requestId: string;
299
- origin: ExecutionJobOrigin;
300
- occurrenceId?: string;
301
- }): Promise<{
302
- run: AssistantRoutineRun;
303
- messages: GmailMessageSummary[];
304
- proposals: AssistantActionProposal[];
305
- analysis?: AssistantModelAnalysis;
306
- standingGrantResults?: StandingGrantExecutionResult[];
307
- }> {
308
- const routine = getAssistantRoutine(input.repository.canonicalRoot, input.routineId);
309
- if (routine.status !== 'enabled') throw new Error(`ASSISTANT_ROUTINE_NOT_ENABLED: ${routine.routineId}`);
310
- const cursor = readCursor(input.repository.canonicalRoot, routine.routineId);
311
- const windowEnd = now();
312
- const fallbackStart = new Date(Date.now() - 24 * 60 * 60_000).toISOString();
313
- const windowStart = cursor.lastSuccessfulAt ?? fallbackStart;
314
- const runId = input.occurrenceId ? `routine-run-${input.occurrenceId}` : `routine-run-${routine.routineId}-${Date.now()}`;
315
- let run = saveRun(input.repository.canonicalRoot, {
316
- schemaVersion: 1,
317
- runId,
318
- routineId: routine.routineId,
319
- requestId: input.requestId,
320
- occurrenceId: input.occurrenceId,
321
- status: 'collecting',
322
- windowStart,
323
- windowEnd,
324
- collectedItems: 0,
325
- processedItems: 0,
326
- proposedActions: 0,
327
- createdAt: now(),
328
- });
329
- try {
330
- if (!routine.dataSources.includes('gmail')) {
331
- throw new Error('ASSISTANT_ROUTINE_GMAIL_REQUIRED: this runtime currently finalizes Gmail-backed routines');
332
- }
333
- const profile = await gmailAction(input.controllerHome, input.repository, input.origin, `${input.requestId}:profile`, 'get_profile', {});
334
- const known = new Set(cursor.processedMessageIds);
335
- const messageIds: string[] = [];
336
- const startingContinuation = cursor.continuation;
337
- let nextContinuation: GmailRoutineCursor['continuation'];
338
- let historyFallback = !cursor.historyId;
339
- let historyInvalid = false;
340
- if (cursor.historyId) {
341
- try {
342
- let historyPageToken = startingContinuation?.mode === 'history' ? startingContinuation.pageToken : undefined;
343
- for (let page = 0; page < 5 && messageIds.length < 100; page += 1) {
344
- const history = await gmailAction(input.controllerHome, input.repository, input.origin, `${input.requestId}:history:${page}`, 'list_history', {
345
- start_history_id: cursor.historyId, max_results: 100, label_id: 'INBOX', history_type: 'messageAdded',
346
- ...(historyPageToken ? { page_token: historyPageToken } : {}),
347
- });
348
- for (const entry of Array.isArray(history.history) ? history.history.map(recordValue) : []) {
349
- for (const added of Array.isArray(entry.messagesAdded) ? entry.messagesAdded.map(recordValue) : []) {
350
- const id = stringValue(recordValue(added.message).id);
351
- if (id && !known.has(id) && !messageIds.includes(id)) messageIds.push(id);
352
- }
353
- }
354
- historyPageToken = stringValue(history.nextPageToken);
355
- if (!historyPageToken) break;
356
- }
357
- if (historyPageToken) nextContinuation = { mode: 'history', pageToken: historyPageToken };
358
- } catch (error) {
359
- const status = isAssistantPluginError(error) ? Number(error.details?.status) : undefined;
360
- if (status !== 404) throw error;
361
- historyFallback = true;
362
- historyInvalid = true;
363
- nextContinuation = undefined;
364
- }
365
- }
366
- if (historyFallback) {
367
- const overlapStart = Math.max(0, Date.parse(windowStart) - 5 * 60_000);
368
- const query = `in:inbox -in:spam -in:trash after:${Math.floor(overlapStart / 1000)}`;
369
- let pageToken = startingContinuation?.mode === 'query' ? startingContinuation.pageToken : undefined;
370
- for (let page = 0; page < 5 && messageIds.length < 100; page += 1) {
371
- const listed = await gmailAction(input.controllerHome, input.repository, input.origin, `${input.requestId}:list:${page}`, 'list_messages', {
372
- query, max_results: 25, ...(pageToken ? { page_token: pageToken } : {}),
373
- });
374
- for (const message of Array.isArray(listed.messages) ? listed.messages.map(recordValue) : []) {
375
- const id = stringValue(message.id);
376
- if (id && !known.has(id) && !messageIds.includes(id)) messageIds.push(id);
377
- }
378
- pageToken = stringValue(listed.nextPageToken);
379
- if (!pageToken) break;
380
- }
381
- nextContinuation = pageToken ? { mode: 'query', pageToken } : undefined;
382
- }
383
- const messages: GmailMessageSummary[] = [];
384
- for (const messageId of messageIds.slice(0, 50)) {
385
- const fetched = await gmailAction(input.controllerHome, input.repository, input.origin, `${input.requestId}:message:${messageId}`, 'get_message', {
386
- message_id: messageId,
387
- format: 'full',
388
- });
389
- const raw = recordValue(fetched.message && typeof fetched.message === 'object' ? fetched.message : fetched);
390
- messages.push(summarizeMessage(raw));
391
- }
392
- const modelAnalysis = await analyzeAssistantMessages({ messages, routineGoal: routine.naturalLanguageGoal });
393
- const proposalInputs = modelAnalysis.usedModel ? modelAnalysis.proposals : buildDeterministicAssistantProposals(messages);
394
- const proposals = createAssistantActionProposals(input.controllerHome, input.repository, { routineId: routine.routineId, runId, proposals: proposalInputs });
395
- const standingGrantApplication = await applyAssistantStandingGrants(input.controllerHome, input.repository, { routineId: routine.routineId, runId, proposals });
396
- const persistedProposals = listAssistantActionProposals(input.controllerHome, input.repository, { limit: 500 }).proposals
397
- .filter((proposal) => proposal.runId === runId);
398
- const analysis: AssistantModelAnalysis = {
399
- ...modelAnalysis,
400
- warnings: [...modelAnalysis.warnings, ...standingGrantApplication.warnings],
401
- };
402
- const hydrationTruncated = messageIds.length > messages.length;
403
- const paginationIncomplete = Boolean(nextContinuation);
404
- const truncated = hydrationTruncated || paginationIncomplete;
405
- const savedContinuation = truncated
406
- ? hydrationTruncated ? startingContinuation : nextContinuation
407
- : undefined;
408
- const report = renderAssistantRoutineReport(messages, persistedProposals, windowStart, windowEnd, analysis, standingGrantApplication.results);
409
- run = saveRun(input.repository.canonicalRoot, {
410
- ...run,
411
- status: 'completed',
412
- collectedItems: messageIds.length,
413
- processedItems: messages.length,
414
- proposedActions: persistedProposals.length,
415
- autoSubmittedActions: standingGrantApplication.results.filter((entry) => entry.status === 'submitted').length,
416
- analysis: {
417
- usedModel: analysis.usedModel,
418
- provider: analysis.provider,
419
- model: analysis.model,
420
- promptVersion: analysis.promptVersion,
421
- fallbackReason: analysis.fallbackReason,
422
- warnings: analysis.warnings,
423
- },
424
- summary: report,
425
- completedAt: now(),
426
- });
427
- touchAssistantRoutineRun(input.repository.canonicalRoot, routine.routineId);
428
- addAssistantInboxItem(input.repository.canonicalRoot, {
429
- kind: 'routine_result',
430
- title: `Routine 已完成:${routine.name}`,
431
- summary: `读取 ${messages.length} 封新邮件,生成 ${persistedProposals.length} 项行动建议,自动提交 ${standingGrantApplication.results.filter((entry) => entry.status === 'submitted').length} 项。`,
432
- body: report,
433
- source: 'routine',
434
- relatedRoutineId: routine.routineId,
435
- relatedRequestId: input.requestId,
436
- jobIds: standingGrantApplication.results.flatMap((entry) => entry.executionJobId ? [entry.executionJobId] : []),
437
- recommendations: [
438
- ...(truncated ? ['本次达到正文读取上限,下一次将从相同时间窗口继续处理剩余邮件。'] : []),
439
- '发送邮件和移入垃圾箱仍需单独明确确认。',
440
- '可基于行动建议创建草稿、任务或归档审批。',
441
- ...(analysis.fallbackReason ? ['模型分析不可用,本次已安全回退到规则引擎。'] : []),
442
- ],
443
- data: { run, messages, proposals: persistedProposals, truncated, analysis, standingGrantResults: standingGrantApplication.results },
444
- });
445
- saveCursor(input.repository.canonicalRoot, {
446
- schemaVersion: 1,
447
- routineId: routine.routineId,
448
- lastSuccessfulAt: truncated ? windowStart : windowEnd,
449
- historyId: truncated ? historyInvalid ? undefined : cursor.historyId : stringValue(profile.historyId) ?? cursor.historyId,
450
- continuation: savedContinuation,
451
- processedMessageIds: [...messages.map((message) => message.id), ...cursor.processedMessageIds].slice(0, 1_000),
452
- updatedAt: now(),
453
- });
454
- return { run, messages, proposals: persistedProposals, analysis, standingGrantResults: standingGrantApplication.results };
455
- } catch (error) {
456
- const message = error instanceof Error ? error.message : String(error);
457
- const authRequired = /PLUGIN_AUTH|UNAUTHENTICATED|invalid credentials/i.test(message);
458
- run = saveRun(input.repository.canonicalRoot, {
459
- ...run,
460
- status: authRequired ? 'auth_required' : 'failed',
461
- error: message,
462
- completedAt: now(),
463
- });
464
- addAssistantInboxItem(input.repository.canonicalRoot, {
465
- kind: 'system_note',
466
- title: authRequired ? `Routine 需要重新授权:${routine.name}` : `Routine 执行失败:${routine.name}`,
467
- summary: message,
468
- source: 'routine',
469
- relatedRoutineId: routine.routineId,
470
- relatedRequestId: input.requestId,
471
- jobIds: [],
472
- recommendations: authRequired ? ['重新完成 Google Workspace 授权后再恢复 Routine。'] : ['检查 Routine Run 和 Execution Job 证据后重试。'],
473
- data: { run },
474
- });
475
- throw error;
476
- }
477
- }
@@ -1,171 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'fs';
2
- import { dirname, join } from 'path';
3
- import type { RepositoryRecord } from '../../cli/repositories/types';
4
- import { createSchedule, getSchedule, saveSchedule } from '../workflow/schedules/store';
5
- import type { ScheduleTrigger } from '../workflow/schedules/types';
6
- import { getAssistantRoutine, updateAssistantRoutineStatus } from './store';
7
- import type { AssistantRoutine, AssistantRoutineStatus } from './types';
8
-
9
- export interface AssistantRoutineScheduleBinding {
10
- schemaVersion: 1;
11
- routineId: string;
12
- scheduleId: string;
13
- repoId: string;
14
- timezone: string;
15
- normalizedSchedule: string;
16
- createdAt: string;
17
- updatedAt: string;
18
- }
19
-
20
- interface BindingStore {
21
- schemaVersion: 1;
22
- updatedAt: string;
23
- bindings: AssistantRoutineScheduleBinding[];
24
- }
25
-
26
- function now(): string { return new Date().toISOString(); }
27
- function bindingPath(repoRoot: string): string { return join(repoRoot, '.forge', 'assistant', 'routine-schedules.json'); }
28
-
29
- function readBindings(repoRoot: string): BindingStore {
30
- const path = bindingPath(repoRoot);
31
- if (!existsSync(path)) return { schemaVersion: 1, updatedAt: now(), bindings: [] };
32
- try {
33
- const parsed = JSON.parse(readFileSync(path, 'utf-8')) as Partial<BindingStore>;
34
- return {
35
- schemaVersion: 1,
36
- updatedAt: typeof parsed.updatedAt === 'string' ? parsed.updatedAt : now(),
37
- bindings: Array.isArray(parsed.bindings)
38
- ? parsed.bindings.filter((entry): entry is AssistantRoutineScheduleBinding => Boolean(
39
- entry && typeof entry === 'object'
40
- && typeof entry.routineId === 'string'
41
- && typeof entry.scheduleId === 'string'
42
- && typeof entry.repoId === 'string',
43
- ))
44
- : [],
45
- };
46
- } catch {
47
- return { schemaVersion: 1, updatedAt: now(), bindings: [] };
48
- }
49
- }
50
-
51
- function writeBindings(repoRoot: string, store: BindingStore): void {
52
- const path = bindingPath(repoRoot);
53
- mkdirSync(dirname(path), { recursive: true });
54
- const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
55
- writeFileSync(tmp, `${JSON.stringify({ ...store, schemaVersion: 1, updatedAt: now() }, null, 2)}\n`, 'utf-8');
56
- renameSync(tmp, path);
57
- }
58
-
59
- const WEEKDAYS: Array<[RegExp, number]> = [
60
- [/(?:周|星期)(?:日|天)|sunday/i, 0],
61
- [/(?:周|星期)一|monday/i, 1],
62
- [/(?:周|星期)二|tuesday/i, 2],
63
- [/(?:周|星期)三|wednesday/i, 3],
64
- [/(?:周|星期)四|thursday/i, 4],
65
- [/(?:周|星期)五|friday/i, 5],
66
- [/(?:周|星期)六|saturday/i, 6],
67
- ];
68
-
69
- export function parseAssistantScheduleText(
70
- scheduleText: string,
71
- timezone = 'UTC',
72
- ): { trigger: ScheduleTrigger; normalized: string; timezone: string } {
73
- const text = scheduleText.trim();
74
- const time = text.match(/(?:^|\D)([01]?\d|2[0-3])(?:\s*[:点时]\s*([0-5]?\d))?/);
75
- const hour = time ? Number(time[1]) : 9;
76
- const minute = time?.[2] ? Number(time[2]) : 0;
77
- const weekday = WEEKDAYS.find(([pattern]) => pattern.test(text))?.[1];
78
- const cronExpression = `${minute} ${hour} * * ${weekday ?? '*'}`;
79
- const normalized = `${weekday === undefined ? 'daily' : `weekly:${weekday}`} ${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')} ${timezone}`;
80
- return {
81
- trigger: {
82
- type: 'cron',
83
- cronExpression,
84
- timezone,
85
- catchUpMinutes: 12 * 60,
86
- },
87
- normalized,
88
- timezone,
89
- };
90
- }
91
-
92
- export function getAssistantRoutineScheduleBinding(
93
- repoRoot: string,
94
- routineId: string,
95
- ): AssistantRoutineScheduleBinding | undefined {
96
- return readBindings(repoRoot).bindings.find((entry) => entry.routineId === routineId);
97
- }
98
-
99
- export function bindAssistantRoutineSchedule(
100
- controllerHome: string,
101
- repository: RepositoryRecord,
102
- routine: AssistantRoutine,
103
- ): AssistantRoutineScheduleBinding {
104
- const existing = getAssistantRoutineScheduleBinding(repository.canonicalRoot, routine.routineId);
105
- if (existing) return existing;
106
- const parsed = parseAssistantScheduleText(routine.scheduleText, routine.timezone || 'UTC');
107
- const schedule = createSchedule(controllerHome, {
108
- requestId: `assistant-routine:${repository.repoId}:${routine.routineId}`,
109
- repoId: repository.repoId,
110
- name: `Assistant Routine: ${routine.name}`,
111
- enabled: routine.status === 'enabled',
112
- trigger: parsed.trigger,
113
- policy: {
114
- maxActiveOccurrences: 1,
115
- maxFailures: 5,
116
- cooldownMinutes: 1,
117
- dailyBudgetMinutes: 30,
118
- shadowMode: false,
119
- backoffBaseMinutes: 5,
120
- backoffMaxMinutes: 12 * 60,
121
- },
122
- action: {
123
- operation: 'assistant_routine_execute',
124
- target: 'runtime',
125
- arguments: { routineId: routine.routineId },
126
- priority: 'P3',
127
- resourceClaims: [{ resourceKey: `assistant-routine:${repository.repoId}:${routine.routineId}`, mode: 'exclusive' }],
128
- },
129
- stopConditions: [],
130
- });
131
- const at = now();
132
- const binding: AssistantRoutineScheduleBinding = {
133
- schemaVersion: 1,
134
- routineId: routine.routineId,
135
- scheduleId: schedule.scheduleId,
136
- repoId: repository.repoId,
137
- timezone: parsed.timezone,
138
- normalizedSchedule: parsed.normalized,
139
- createdAt: at,
140
- updatedAt: at,
141
- };
142
- const store = readBindings(repository.canonicalRoot);
143
- store.bindings.unshift(binding);
144
- writeBindings(repository.canonicalRoot, store);
145
- return binding;
146
- }
147
-
148
- export function updateAssistantRoutineLifecycle(
149
- controllerHome: string,
150
- repository: RepositoryRecord,
151
- routineId: string,
152
- status: AssistantRoutineStatus,
153
- ): { routine: AssistantRoutine; binding?: AssistantRoutineScheduleBinding } {
154
- const routine = updateAssistantRoutineStatus(repository.canonicalRoot, routineId, status);
155
- let binding = getAssistantRoutineScheduleBinding(repository.canonicalRoot, routineId);
156
- if (!binding && status === 'enabled') binding = bindAssistantRoutineSchedule(controllerHome, repository, routine);
157
- if (binding) {
158
- const schedule = getSchedule(controllerHome, repository.repoId, binding.scheduleId);
159
- const enabled = status === 'enabled';
160
- saveSchedule(controllerHome, {
161
- ...schedule,
162
- enabled,
163
- pausedReason: enabled ? undefined : `Assistant Routine ${status}: ${routineId}`,
164
- });
165
- binding = { ...binding, updatedAt: now() };
166
- const store = readBindings(repository.canonicalRoot);
167
- store.bindings = [binding, ...store.bindings.filter((entry) => entry.routineId !== routineId)];
168
- writeBindings(repository.canonicalRoot, store);
169
- }
170
- return { routine, binding };
171
- }