@moretea-labs/forge 1.6.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (469) hide show
  1. package/AGENTS.md +4 -0
  2. package/CHANGELOG.md +39 -0
  3. package/CLAUDE.md +4 -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/02-operating-mode.partial.md +2 -0
  17. package/assets/partials-agents/03-orchestration.partial.md +1 -1
  18. package/assets/plugin-registry.v1.json +3 -3
  19. package/assets/reference-configs/document-generation.md +1 -1
  20. package/assets/reference-configs/harness-overview.md +1 -1
  21. package/assets/reference-configs/sprint-contracts.md +3 -3
  22. package/assets/skill-version.json +2 -2
  23. package/assets/templates/contract.template.md +2 -7
  24. package/assets/templates/helpers/capture-plan.sh +2 -2
  25. package/assets/templates/helpers/check-architecture-sync.sh +17 -0
  26. package/assets/templates/helpers/check-task-workflow.sh +4 -4
  27. package/assets/templates/helpers/contract-worktree.sh +1 -21
  28. package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
  29. package/assets/templates/helpers/inspect-project-state.ts +8 -6
  30. package/assets/templates/helpers/migrate-project-template.sh +22 -45
  31. package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
  32. package/assets/templates/helpers/new-plan.sh +1 -1
  33. package/assets/templates/helpers/plan-to-todo.sh +12 -37
  34. package/assets/templates/helpers/ship-worktrees.sh +2 -29
  35. package/assets/templates/helpers/verify-contract.sh +1 -111
  36. package/assets/templates/helpers/verify-sprint.sh +3 -180
  37. package/assets/templates/helpers/workflow-contract.ts +5 -1
  38. package/assets/templates/plan.template.md +1 -1
  39. package/assets/templates/review.template.md +9 -34
  40. package/assets/workflow-contract.v1.json +10 -19
  41. package/docs/README.md +10 -10
  42. package/docs/architecture/CURRENT.md +178 -0
  43. package/docs/architecture/EVOLUTION.md +42 -0
  44. package/docs/architecture/current/README.md +4 -156
  45. package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
  46. package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
  47. package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
  48. package/docs/architecture/history/README.md +7 -0
  49. package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
  50. package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
  51. package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
  52. package/docs/architecture/history.md +3 -34
  53. package/docs/architecture/index.md +15 -98
  54. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
  55. package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
  56. package/docs/architecture/versions/1.6.md +38 -0
  57. package/docs/architecture/versions/1.7.md +41 -0
  58. package/docs/forge-chatgpt-browser-engine.md +2 -2
  59. package/docs/forge-chatgpt-mcp-setup.md +4 -28
  60. package/docs/forge-chatgpt-supervised-automation.md +3 -3
  61. package/docs/forge-github-issue-launcher.md +1 -1
  62. package/docs/forge-plugin-management.md +1 -1
  63. package/docs/forge-runtime-self-healing-loop.md +1 -1
  64. package/docs/operations/controller-browser-plugin.md +26 -6
  65. package/docs/operations/controller-performance-and-502.md +143 -0
  66. package/docs/operations/controller-reliability-runbook.md +1 -1
  67. package/docs/operations/features.md +2 -2
  68. package/docs/operations/github-repository.md +1 -1
  69. package/docs/operations/homebrew.md +2 -2
  70. package/docs/operations/mcp-tool-exposure.md +5 -3
  71. package/docs/wiki/Architecture.md +6 -6
  72. package/docs/wiki/Implementation.md +2 -2
  73. package/docs/wiki/Integrations.md +2 -2
  74. package/docs/wiki/Work-Lifecycle.md +1 -1
  75. package/package.json +33 -11
  76. package/scripts/activate-source-baseline.ts +17 -13
  77. package/scripts/architecture-event.ts +2 -820
  78. package/scripts/architecture-queue.sh +2 -645
  79. package/scripts/archive-architecture-request.sh +2 -242
  80. package/scripts/archive-workflow.sh +1 -291
  81. package/scripts/benchmark-background-check-overlap.ts +228 -0
  82. package/scripts/benchmark-mcp-host.sh +117 -0
  83. package/scripts/benchmark-mcp-transport.ts +170 -0
  84. package/scripts/benchmark-quality-harness.ts +146 -0
  85. package/scripts/benchmark-route-session-concurrency.ts +207 -6
  86. package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
  87. package/scripts/benchmark-typescript-navigation.ts +186 -0
  88. package/scripts/benchmark-work-efficiency.ts +334 -0
  89. package/scripts/capability-config.ts +2 -392
  90. package/scripts/capability-resolver.ts +2 -503
  91. package/scripts/capture-plan.sh +1 -332
  92. package/scripts/check-agent-tooling.sh +2 -1558
  93. package/scripts/check-architecture-sync.sh +2 -371
  94. package/scripts/check-brain-manifest.sh +2 -253
  95. package/scripts/check-context-files.sh +2 -47
  96. package/scripts/check-deploy-sql-order.sh +2 -115
  97. package/scripts/check-mcp-compatibility.ts +14 -0
  98. package/scripts/check-runtime-architecture.mjs +349 -79
  99. package/scripts/check-skill-version.ts +3 -191
  100. package/scripts/check-source-duplication.mjs +61 -0
  101. package/scripts/check-stable-baseline.ts +92 -0
  102. package/scripts/check-task-sync.sh +2 -63
  103. package/scripts/check-task-workflow.sh +2 -1000
  104. package/scripts/codex-handoff-resume.sh +2 -267
  105. package/scripts/context-contract-sync.sh +2 -514
  106. package/scripts/contract-run.ts +2 -463
  107. package/scripts/contract-worktree.sh +1 -722
  108. package/scripts/ensure-task-workflow.sh +1 -1225
  109. package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
  110. package/scripts/inspect-project-state.ts +4 -279
  111. package/scripts/install-standalone-recovery.ts +24 -0
  112. package/scripts/lib/project-init-lib.sh +22 -46
  113. package/scripts/lib/run-forge-helper.sh +19 -0
  114. package/scripts/lib/run-forge-helper.ts +26 -0
  115. package/scripts/migrate-project-template.sh +1 -1189
  116. package/scripts/migrate-workflow-docs.ts +4 -412
  117. package/scripts/new-plan.sh +1 -180
  118. package/scripts/new-spec.sh +1 -47
  119. package/scripts/new-sprint.sh +1 -46
  120. package/scripts/plan-to-todo.sh +1 -796
  121. package/scripts/prepare-codex-handoff.sh +2 -201
  122. package/scripts/prepare-handoff.sh +1 -78
  123. package/scripts/refresh-current-status.sh +2 -467
  124. package/scripts/run-governed-gate.ts +5 -1
  125. package/scripts/select-agent-context-blocks.sh +2 -64
  126. package/scripts/ship-worktrees.sh +1 -558
  127. package/scripts/smoke-ios-physical-device.ts +4 -2
  128. package/scripts/smoke-runtime-recovery.ts +1 -35
  129. package/scripts/smoke-schedule-engine.ts +1 -20
  130. package/scripts/sprint-backlog.sh +1 -834
  131. package/scripts/stage-runtime-release.ts +2 -1
  132. package/scripts/summarize-failures.sh +2 -123
  133. package/scripts/switch-plan.sh +1 -150
  134. package/scripts/sync-brain-docs.sh +2 -379
  135. package/scripts/sync-generated-authority.mjs +175 -0
  136. package/scripts/verify-contract.sh +2 -703
  137. package/scripts/verify-forge-runtime.sh +0 -1
  138. package/scripts/verify-sprint.sh +1 -677
  139. package/scripts/workflow-contract.ts +4 -205
  140. package/scripts/workstream-sync.sh +2 -261
  141. package/src/cli/agent-jobs/executor-health.ts +1 -1
  142. package/src/cli/commands/init-hook.ts +3 -3
  143. package/src/cli/commands/mcp.ts +9 -5
  144. package/src/cli/commands/plugin.ts +12 -3
  145. package/src/cli/commands/recovery.ts +25 -3
  146. package/src/cli/commands/runtime.ts +55 -3
  147. package/src/cli/commands/security.ts +12 -1
  148. package/src/cli/commands/setup-profile.ts +31 -21
  149. package/src/cli/controller/check-runner.ts +113 -5
  150. package/src/cli/controller/composite-result.ts +1 -1
  151. package/src/cli/controller/context/focus.ts +51 -0
  152. package/src/cli/controller/context/instruction-context.ts +114 -0
  153. package/src/cli/controller/context/known-paths.ts +76 -0
  154. package/src/cli/controller/context/query-planning.ts +110 -0
  155. package/src/cli/controller/context/rule-resolution.ts +532 -0
  156. package/src/cli/controller/context/source-materializer.ts +278 -0
  157. package/src/cli/controller/context/structural.ts +16 -0
  158. package/src/cli/controller/context/types.ts +286 -0
  159. package/src/cli/controller/context-pack.ts +828 -487
  160. package/src/cli/controller/launch-agents.ts +77 -2
  161. package/src/cli/controller/operational-plan.ts +2 -2
  162. package/src/cli/controller/work-mode.ts +6 -4
  163. package/src/cli/editing/edit-session.ts +60 -7
  164. package/src/cli/github/contracts.ts +16 -0
  165. package/src/cli/github/github.ts +135 -16
  166. package/src/cli/github/session.ts +2 -1
  167. package/src/cli/installer/managed-entries.ts +15 -1
  168. package/src/cli/local-bridge/console-automations.ts +54 -78
  169. package/src/cli/local-bridge/console-view-models.ts +1 -35
  170. package/src/cli/local-bridge/facade-api.ts +37 -264
  171. package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
  172. package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
  173. package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
  174. package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
  175. package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
  176. package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
  177. package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
  178. package/src/cli/local-bridge/frontend/src/styles.css +1 -1
  179. package/src/cli/local-bridge/frontend/src/types.ts +5 -8
  180. package/src/cli/local-bridge/job-store.ts +1 -490
  181. package/src/cli/local-bridge/server.ts +2 -515
  182. package/src/cli/local-bridge/ui-dist/app.css +1 -1
  183. package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
  184. package/src/cli/mcp/auth.ts +5 -5
  185. package/src/cli/mcp/instructions.ts +3 -2
  186. package/src/cli/mcp/legacy-tool-service.ts +10 -222
  187. package/src/cli/mcp/multi-repository.ts +16 -5
  188. package/src/cli/mcp/oauth.ts +175 -26
  189. package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
  190. package/src/cli/mcp/repository-tools.ts +282 -146
  191. package/src/cli/mcp/server.ts +586 -116
  192. package/src/cli/mcp/tool-capability-cache.ts +1 -1
  193. package/src/cli/mcp/tool-contract.ts +14 -0
  194. package/src/cli/mcp/toolset-selection.ts +1 -1
  195. package/src/cli/mcp/toolset.ts +44 -7
  196. package/src/cli/mcp/transports/http.ts +124 -14
  197. package/src/cli/mcp/transports/session-registry.ts +43 -5
  198. package/src/cli/mcp/types.ts +3 -3
  199. package/src/cli/repositories/command-classifier.ts +103 -21
  200. package/src/cli/repositories/command-executor.ts +78 -526
  201. package/src/cli/repositories/command-process.ts +108 -0
  202. package/src/cli/repositories/command-scope.ts +99 -41
  203. package/src/cli/repositories/controller-home.ts +7 -8
  204. package/src/cli/repositories/git-command-executor.ts +2 -0
  205. package/src/cli/repositories/locks.ts +53 -16
  206. package/src/cli/repositories/repository-snapshot.ts +124 -0
  207. package/src/cli/repositories/selected-path-actions.ts +17 -9
  208. package/src/cli/repositories/structured-git.ts +118 -6
  209. package/src/cli/repository/inspector.ts +563 -65
  210. package/src/cli/repository/session-cache.ts +58 -17
  211. package/src/cli/v81-entry.ts +1 -4
  212. package/src/effects/fs-transaction.ts +85 -17
  213. package/src/runtime/context/codegraph-read-provider.ts +233 -2
  214. package/src/runtime/context/codegraph-sidecar.cjs +74 -40
  215. package/src/runtime/context/generic-lsp-provider.ts +355 -0
  216. package/src/runtime/context/lsp-client.ts +283 -0
  217. package/src/runtime/context/semantic-navigation-contract.ts +61 -0
  218. package/src/runtime/context/semantic-navigation.ts +223 -0
  219. package/src/runtime/context/swift-navigation.ts +565 -0
  220. package/src/runtime/context/typescript-navigation.ts +226 -0
  221. package/src/runtime/control-plane/dispatch-priority.ts +28 -0
  222. package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
  223. package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
  224. package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
  225. package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
  226. package/src/runtime/control-plane/execution/session-store.ts +18 -0
  227. package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
  228. package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
  229. package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
  230. package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
  231. package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
  232. package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
  233. package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
  234. package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
  235. package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
  236. package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
  237. package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
  238. package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
  239. package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
  240. package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
  241. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
  242. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
  243. package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
  244. package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
  245. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
  246. package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
  247. package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
  248. package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
  249. package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
  250. package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
  251. package/src/runtime/control-plane/facade/index.ts +3 -0
  252. package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
  253. package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
  254. package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
  255. package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
  256. package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
  257. package/src/runtime/control-plane/facade/types.ts +90 -10
  258. package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
  259. package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
  260. package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
  261. package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
  262. package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
  263. package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
  264. package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
  265. package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
  266. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
  267. package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
  268. package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
  269. package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
  270. package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
  271. package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
  272. package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
  273. package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
  274. package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
  275. package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
  276. package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
  277. package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
  278. package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
  279. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
  280. package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
  281. package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
  282. package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
  283. package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
  284. package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
  285. package/src/runtime/control-plane/release-retention.ts +62 -2
  286. package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
  287. package/src/runtime/control-plane/routing/route-policy.ts +24 -21
  288. package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
  289. package/src/runtime/control-plane/runtime-status-client.ts +0 -2
  290. package/src/runtime/diagnostics/mcp-timing.ts +13 -0
  291. package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
  292. package/src/runtime/diagnostics/performance.ts +24 -1
  293. package/src/runtime/evidence/event-ledger.ts +1 -1
  294. package/src/runtime/evidence/process-check-execution.ts +18 -0
  295. package/src/runtime/execution/jobs/child-reference.ts +6 -2
  296. package/src/runtime/execution/jobs/store.ts +58 -4
  297. package/src/runtime/execution/jobs/types.ts +1 -1
  298. package/src/runtime/execution/managed-workspace.ts +203 -1
  299. package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
  300. package/src/runtime/execution/process-runtime/check-result.ts +88 -0
  301. package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
  302. package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
  303. package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
  304. package/src/runtime/execution/process-runtime/gc.ts +38 -6
  305. package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
  306. package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
  307. package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
  308. package/src/runtime/execution/process-runtime/runtime.ts +53 -3
  309. package/src/runtime/execution/process-runtime/store.ts +1 -0
  310. package/src/runtime/execution/process-runtime/types.ts +4 -12
  311. package/src/runtime/execution/thin-harness/async-process.ts +12 -2
  312. package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
  313. package/src/runtime/execution/work-activity.ts +21 -0
  314. package/src/runtime/execution/workers/executor.ts +18 -61
  315. package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
  316. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
  317. package/src/runtime/gateway/mcp/process-tools.ts +83 -37
  318. package/src/runtime/gateway/mcp/router.ts +186 -84
  319. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
  320. package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
  321. package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
  322. package/src/runtime/plugins/browser-adapter.ts +2662 -829
  323. package/src/runtime/plugins/browser-automation-service.ts +10 -0
  324. package/src/runtime/plugins/browser-handoff-host.ts +2 -25
  325. package/src/runtime/plugins/browser-handoff.ts +110 -5
  326. package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
  327. package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
  328. package/src/runtime/plugins/browser-native-session-service.ts +222 -0
  329. package/src/runtime/plugins/browser-node-bridge.ts +6 -34
  330. package/src/runtime/plugins/browser-provider-registry.ts +109 -0
  331. package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
  332. package/src/runtime/plugins/browser-runtime.ts +193 -0
  333. package/src/runtime/plugins/browser-session-authority.ts +332 -0
  334. package/src/runtime/plugins/browser-session-store.ts +121 -0
  335. package/src/runtime/plugins/browser-session-types.ts +98 -0
  336. package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
  337. package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
  338. package/src/runtime/plugins/external-adapter.ts +358 -3
  339. package/src/runtime/plugins/external-unix-socket.ts +7 -1
  340. package/src/runtime/plugins/first-party-registry.ts +2 -0
  341. package/src/runtime/plugins/github-adapter.ts +125 -0
  342. package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
  343. package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
  344. package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
  345. package/src/runtime/plugins/ios-adapter.ts +106 -16
  346. package/src/runtime/plugins/ios-agent-device.ts +234 -64
  347. package/src/runtime/plugins/ios-physical-device.ts +1143 -392
  348. package/src/runtime/plugins/lightweight-action.ts +153 -0
  349. package/src/runtime/plugins/local-system-adapter.ts +123 -6
  350. package/src/runtime/plugins/macos-capability-broker.ts +47 -9
  351. package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
  352. package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
  353. package/src/runtime/plugins/resend-adapter.ts +1 -1
  354. package/src/runtime/plugins/store.ts +330 -33
  355. package/src/runtime/plugins/types.ts +38 -3
  356. package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
  357. package/src/runtime/projections/git-status-sampler.ts +16 -16
  358. package/src/runtime/projections/materialized-view.ts +28 -10
  359. package/src/runtime/recovery/maintenance-executor.ts +426 -25
  360. package/src/runtime/root/mcp-transport.ts +180 -10
  361. package/src/runtime/root/package-connector-service.ts +230 -10
  362. package/src/runtime/root/package-runtime-release.ts +143 -17
  363. package/src/runtime/root/package-runtime-service.ts +461 -22
  364. package/src/runtime/root/release-manifest.ts +144 -18
  365. package/src/runtime/root/release-materialize.ts +125 -32
  366. package/src/runtime/root/release-store.ts +48 -11
  367. package/src/runtime/root/runtime.ts +23 -5
  368. package/src/runtime/root/service.ts +100 -29
  369. package/src/runtime/root/types.ts +12 -0
  370. package/src/runtime/safe-tooling/ios-development.ts +321 -63
  371. package/src/runtime/shared/trusted-executable.ts +62 -0
  372. package/src/runtime/shared/trusted-node-executable.ts +48 -0
  373. package/src/runtime/standalone-recovery/core.ts +1079 -240
  374. package/src/runtime/standalone-recovery/entry.ts +100 -24
  375. package/src/runtime/standalone-recovery/installer.ts +20 -4
  376. package/src/runtime/standalone-recovery/service-labels.ts +2 -0
  377. package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
  378. package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
  379. package/src/runtime/workflow/schedules/engine.ts +364 -202
  380. package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
  381. package/src/runtime/workflow/schedules/settlement.ts +64 -17
  382. package/src/runtime/workflow/schedules/store.ts +209 -19
  383. package/src/runtime/workflow/schedules/types.ts +2 -4
  384. package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
  385. package/src/testing/test-governance.ts +8 -40
  386. package/assets/reference-configs/heartbeat-triage.md +0 -57
  387. package/assets/templates/helpers/harness-trace-grade.sh +0 -150
  388. package/assets/templates/helpers/heartbeat-triage.sh +0 -361
  389. package/assets/templates/helpers/maintenance-triage.sh +0 -167
  390. package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
  391. package/docs/architecture/current/architecture-invariants.md +0 -331
  392. package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
  393. package/docs/architecture/current/codegraph-context-provider.md +0 -238
  394. package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
  395. package/docs/architecture/current/controller-repository-decoupling.md +0 -148
  396. package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
  397. package/docs/architecture/current/entity-model.md +0 -612
  398. package/docs/architecture/current/external-plugin-governance.md +0 -396
  399. package/docs/architecture/current/failure-recovery.md +0 -447
  400. package/docs/architecture/current/governance.md +0 -213
  401. package/docs/architecture/current/human-interaction-plane.md +0 -87
  402. package/docs/architecture/current/implementation-status.md +0 -128
  403. package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
  404. package/docs/architecture/current/legacy-governance.md +0 -58
  405. package/docs/architecture/current/local-workspace-target-grants.md +0 -70
  406. package/docs/architecture/current/migration-roadmap.md +0 -105
  407. package/docs/architecture/current/multi-repository-execution.md +0 -371
  408. package/docs/architecture/current/operations-runbook.md +0 -45
  409. package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
  410. package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
  411. package/docs/architecture/current/repository-access-modes.md +0 -102
  412. package/docs/architecture/current/route-policy.md +0 -15
  413. package/docs/architecture/current/route-session-concurrency.md +0 -85
  414. package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
  415. package/docs/architecture/current/runtime-directory-map.md +0 -49
  416. package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
  417. package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
  418. package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
  419. package/docs/architecture/current/system-overview.md +0 -169
  420. package/docs/architecture/current/target-requirements-traceability.md +0 -202
  421. package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
  422. package/docs/architecture/current/thin-harness-v1.md +0 -321
  423. package/docs/architecture/current/verification-and-release-gates.md +0 -391
  424. package/docs/forge-autonomous-goal-loop.md +0 -9
  425. package/docs/forge-provider-configuration.md +0 -7
  426. package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
  427. package/docs/operations/gmail-assistant-routines.md +0 -186
  428. package/docs/operations/personal-assistant-operator-guide.md +0 -61
  429. package/docs/operations/provider-configuration.md +0 -31
  430. package/scripts/harness-trace-grade.sh +0 -150
  431. package/scripts/heartbeat-triage.sh +0 -361
  432. package/scripts/maintenance-triage.sh +0 -167
  433. package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
  434. package/src/cli/repositories/goal-registry.ts +0 -239
  435. package/src/runtime/assistant/action-proposals.ts +0 -279
  436. package/src/runtime/assistant/intent.ts +0 -395
  437. package/src/runtime/assistant/model-provider.ts +0 -460
  438. package/src/runtime/assistant/openapi.ts +0 -223
  439. package/src/runtime/assistant/policy.ts +0 -89
  440. package/src/runtime/assistant/readiness.ts +0 -258
  441. package/src/runtime/assistant/routine-runtime.ts +0 -477
  442. package/src/runtime/assistant/schedule-binding.ts +0 -171
  443. package/src/runtime/assistant/standing-grants.ts +0 -375
  444. package/src/runtime/assistant/store.ts +0 -243
  445. package/src/runtime/assistant/types.ts +0 -135
  446. package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
  447. package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
  448. package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
  449. package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
  450. package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
  451. package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
  452. package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
  453. package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
  454. package/src/runtime/control-plane/goal-loop/index.ts +0 -13
  455. package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
  456. package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
  457. package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
  458. package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
  459. package/src/runtime/control-plane/goal-loop/types.ts +0 -378
  460. package/src/runtime/control-plane/startup-recovery.ts +0 -198
  461. package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
  462. package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
  463. package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
  464. package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
  465. package/src/runtime/workflow/findings/store.ts +0 -204
  466. package/src/runtime/workflow/findings/types.ts +0 -58
  467. package/src/runtime/workflow/portfolio/engine.ts +0 -20
  468. package/src/runtime/workflow/portfolio/store.ts +0 -185
  469. package/src/runtime/workflow/portfolio/types.ts +0 -29
@@ -0,0 +1,515 @@
1
+ import type {
2
+ AssistantPluginActionDescriptor,
3
+ AssistantPluginActionExecutionInput,
4
+ AssistantPluginCapability,
5
+ AssistantPluginHealth,
6
+ AssistantPluginManifest,
7
+ AssistantPluginPermissionScope,
8
+ } from './types';
9
+ import { AssistantPluginError } from './errors';
10
+ import { executeBrowserPluginAction } from './browser-adapter';
11
+
12
+ const PLUGIN_ID = 'xiaohongshu';
13
+ const RECIPE_VERSION = 7;
14
+ const CREATOR_BASE_URL = 'https://creator.xiaohongshu.com/publish/publish?source=official';
15
+ const CREATOR_ARTICLE_URL = `${CREATOR_BASE_URL}&target=article`;
16
+ const CREATOR_NOTE_MANAGER_URL = 'https://creator.xiaohongshu.com/new/note-manager';
17
+ const IMAGE_TAB_TEXT = '上传图文';
18
+ const ARTICLE_NEW_TEXT = '新的创作';
19
+ const ARTICLE_LAYOUT_TEXT = '一键排版';
20
+ const ARTICLE_NEXT_TEXT = '下一步';
21
+ const IMAGE_FILE_SELECTOR = 'input[type=file]';
22
+ const IMAGE_TITLE_SELECTOR = 'input[placeholder*="标题"]';
23
+ const IMAGE_BODY_SELECTOR = '[contenteditable="true"][role="textbox"]';
24
+ const ARTICLE_TITLE_SELECTOR = 'textarea:nth-of-type(1)';
25
+ const ARTICLE_BODY_SELECTOR = '[contenteditable="true"]';
26
+ const PUBLISH_SELECTOR = 'xhs-publish-btn';
27
+ const PUBLISH_EVENT = 'publish';
28
+
29
+ const LOGIN_URL_MARKERS = ['/login', 'passport.xiaohongshu.com', 'login.xiaohongshu.com'];
30
+ const LOGIN_TEXT_MARKERS = ['手机号登录', '扫码登录', '验证码登录'];
31
+
32
+ type PublishMode = 'image_note' | 'generated_image_note' | 'long_text';
33
+ type NormalizedPublishMode = 'image_note' | 'long_text';
34
+ type RecipeStep = {
35
+ id: string;
36
+ actionId: string;
37
+ args: Record<string, unknown>;
38
+ expectation?: string;
39
+ };
40
+
41
+ interface XiaohongshuHooks {
42
+ executeBrowserAction?: typeof executeBrowserPluginAction;
43
+ now?: () => string;
44
+ }
45
+
46
+ let hooks: XiaohongshuHooks = {};
47
+
48
+ export function setXiaohongshuPluginHooksForTest(next: XiaohongshuHooks): void {
49
+ hooks = next;
50
+ }
51
+
52
+ export function resetXiaohongshuPluginHooksForTest(): void {
53
+ hooks = {};
54
+ }
55
+
56
+ function now(): string {
57
+ return hooks.now?.() ?? new Date().toISOString();
58
+ }
59
+
60
+ function browserExecutor(): typeof executeBrowserPluginAction {
61
+ return hooks.executeBrowserAction ?? executeBrowserPluginAction;
62
+ }
63
+
64
+ function stringValue(value: unknown): string | undefined {
65
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
66
+ }
67
+
68
+ function requiredString(value: unknown, name: string): string {
69
+ const normalized = stringValue(value);
70
+ if (!normalized) throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', `${name} is required.`, { retryable: false });
71
+ return normalized;
72
+ }
73
+
74
+ function stringList(value: unknown): string[] {
75
+ if (value === undefined) return [];
76
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== 'string' || !entry.trim())) {
77
+ throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'image_paths must be a string array.', { retryable: false });
78
+ }
79
+ return value.map((entry) => String(entry).trim());
80
+ }
81
+
82
+ function publishMode(value: unknown): PublishMode {
83
+ if (value === 'image_note' || value === 'generated_image_note' || value === 'long_text') return value;
84
+ throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'mode must be image_note, generated_image_note, or long_text.', { retryable: false });
85
+ }
86
+
87
+ function normalizedMode(mode: PublishMode): NormalizedPublishMode {
88
+ return mode === 'generated_image_note' ? 'image_note' : mode;
89
+ }
90
+
91
+ function validProfileUrl(value: string): boolean {
92
+ try {
93
+ const parsed = new URL(value);
94
+ return parsed.protocol === 'https:' && parsed.hostname === 'www.xiaohongshu.com' && parsed.pathname.startsWith('/user/profile/');
95
+ } catch {
96
+ return false;
97
+ }
98
+ }
99
+
100
+ export function isXiaohongshuAuthRequired(url: string, text: string): boolean {
101
+ const normalizedUrl = url.toLowerCase();
102
+ return LOGIN_URL_MARKERS.some((marker) => normalizedUrl.includes(marker.toLowerCase()))
103
+ || LOGIN_TEXT_MARKERS.some((marker) => text.includes(marker));
104
+ }
105
+
106
+ export function classifyXiaohongshuPublishState(input: {
107
+ phase: 'preflight' | 'creator_receipt' | 'profile_verify';
108
+ url: string;
109
+ text: string;
110
+ expectedTitle?: string;
111
+ }): 'AUTH_REQUIRED' | 'READY' | 'PUBLISHED_RECEIPT' | 'PROFILE_VERIFIED' | 'VERIFY_PENDING' | 'PAGE_SCHEMA_CHANGED' {
112
+ if (isXiaohongshuAuthRequired(input.url, input.text)) return 'AUTH_REQUIRED';
113
+ if (input.phase === 'creator_receipt') {
114
+ const normalizedUrl = input.url.toLowerCase();
115
+ return normalizedUrl.includes('published=true')
116
+ || normalizedUrl.includes('/publish/success')
117
+ || normalizedUrl.includes('/publish/editsuccess')
118
+ ? 'PUBLISHED_RECEIPT'
119
+ : 'VERIFY_PENDING';
120
+ }
121
+ if (input.phase === 'profile_verify') {
122
+ return input.expectedTitle && input.text.includes(input.expectedTitle) ? 'PROFILE_VERIFIED' : 'VERIFY_PENDING';
123
+ }
124
+ return input.text.includes('创作服务平台') || input.text.includes('发布笔记') ? 'READY' : 'PAGE_SCHEMA_CHANGED';
125
+ }
126
+
127
+ function parseRecipeArgs(args: Record<string, unknown>) {
128
+ const mode = publishMode(args.mode);
129
+ const sessionId = requiredString(args.session_id, 'session_id');
130
+ const profileUrl = requiredString(args.profile_url, 'profile_url');
131
+ if (!validProfileUrl(profileUrl)) {
132
+ throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'profile_url must be an https://www.xiaohongshu.com/user/profile/... URL.', { retryable: false });
133
+ }
134
+ const title = requiredString(args.title, 'title');
135
+ const body = requiredString(args.body, 'body');
136
+ const imagePaths = stringList(args.image_paths);
137
+ const summary = stringValue(args.summary);
138
+ const templateText = stringValue(args.template_text);
139
+ if (normalizedMode(mode) === 'image_note' && mode !== 'generated_image_note' && (imagePaths.length < 1 || imagePaths.length > 18)) {
140
+ throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'image_note requires 1-18 image_paths.', { retryable: false });
141
+ }
142
+ if (mode === 'generated_image_note' && imagePaths.length > 18) {
143
+ throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'generated_image_note accepts at most 18 generated image_paths.', { retryable: false });
144
+ }
145
+ return { mode, normalizedMode: normalizedMode(mode), sessionId, profileUrl, title, body, imagePaths, summary, templateText };
146
+ }
147
+
148
+ export function buildXiaohongshuPublishRecipe(args: Record<string, unknown>): Record<string, unknown> {
149
+ const parsed = parseRecipeArgs(args);
150
+ const generationRequired = parsed.mode === 'generated_image_note' && parsed.imagePaths.length === 0;
151
+ if (generationRequired) {
152
+ return {
153
+ schemaVersion: 1,
154
+ recipeVersion: RECIPE_VERSION,
155
+ provider: 'xiaohongshu-web',
156
+ requestedMode: parsed.mode,
157
+ normalizedMode: parsed.normalizedMode,
158
+ generationRequired: true,
159
+ sessionId: parsed.sessionId,
160
+ profileUrl: parsed.profileUrl,
161
+ generationHandoff: {
162
+ status: 'required',
163
+ requiredInput: 'image_paths',
164
+ minImages: 1,
165
+ maxImages: 18,
166
+ resumeAction: 'publish_note',
167
+ resumeMode: 'generated_image_note',
168
+ next: 'Generate one or more image files, then call publish_note again with image_paths. The publishing path will normalize to image_note.',
169
+ },
170
+ steps: [],
171
+ verification: [],
172
+ };
173
+ }
174
+
175
+ const creatorUrl = parsed.normalizedMode === 'long_text' ? CREATOR_ARTICLE_URL : CREATOR_BASE_URL;
176
+ const steps: RecipeStep[] = [
177
+ {
178
+ id: 'preflight.navigate_creator',
179
+ actionId: 'navigate',
180
+ args: { session_id: parsed.sessionId, url: creatorUrl, wait_until: 'domcontentloaded', timeout_ms: 60_000 },
181
+ expectation: 'Creator page remains in the persisted browser profile; raw cookies/tokens are not copied into recipe state.',
182
+ },
183
+ parsed.normalizedMode === 'image_note'
184
+ ? {
185
+ id: 'image.select_mode',
186
+ actionId: 'click_text',
187
+ args: { session_id: parsed.sessionId, text: IMAGE_TAB_TEXT, post_action_wait_ms: 750 },
188
+ expectation: 'Creator may default to 上传视频; switch semantically to the exact visible 上传图文 tab before waiting for the image input.',
189
+ }
190
+ : {
191
+ id: 'preflight.read_auth_state',
192
+ actionId: 'get_text',
193
+ args: { session_id: parsed.sessionId, max_chars: 8_000 },
194
+ expectation: 'Classify with phase=preflight; stop as AUTH_REQUIRED before editing if login markers appear.',
195
+ },
196
+ ];
197
+
198
+ if (parsed.normalizedMode === 'image_note') {
199
+ steps.push(
200
+ { id: 'image.wait_file_input', actionId: 'wait_for_selector', args: { session_id: parsed.sessionId, selector: IMAGE_FILE_SELECTOR, state: 'attached', timeout_ms: 30_000 }, expectation: 'Wait for the image-upload input only after the semantic 上传图文 mode switch.' },
201
+ { id: 'image.attach_files', actionId: 'attach_local_file', args: { session_id: parsed.sessionId, selector: IMAGE_FILE_SELECTOR, file_paths: parsed.imagePaths, post_action_wait_ms: 1_200 }, expectation: 'One input event/change event after all files are selected.' },
202
+ { id: 'image.wait_editor', actionId: 'wait_for_selector', args: { session_id: parsed.sessionId, selector: IMAGE_TITLE_SELECTOR, state: 'visible', timeout_ms: 30_000 } },
203
+ { id: 'image.fill_title', actionId: 'fill', args: { session_id: parsed.sessionId, selector: IMAGE_TITLE_SELECTOR, text: parsed.title } },
204
+ { id: 'image.fill_body', actionId: 'fill', args: { session_id: parsed.sessionId, selector: IMAGE_BODY_SELECTOR, text: parsed.body } },
205
+ );
206
+ } else {
207
+ steps.push(
208
+ { id: 'article.new', actionId: 'click_text', args: { session_id: parsed.sessionId, text: ARTICLE_NEW_TEXT, post_action_wait_ms: 900 }, expectation: 'Exact visible 新的创作 button.' },
209
+ { id: 'article.wait_editor', actionId: 'wait_for_selector', args: { session_id: parsed.sessionId, selector: ARTICLE_TITLE_SELECTOR, state: 'visible', timeout_ms: 30_000 } },
210
+ { id: 'article.fill_title', actionId: 'fill', args: { session_id: parsed.sessionId, selector: ARTICLE_TITLE_SELECTOR, text: parsed.title } },
211
+ { id: 'article.fill_body', actionId: 'fill', args: { session_id: parsed.sessionId, selector: ARTICLE_BODY_SELECTOR, text: parsed.body } },
212
+ { id: 'article.layout', actionId: 'click_text', args: { session_id: parsed.sessionId, text: ARTICLE_LAYOUT_TEXT, post_action_wait_ms: 1_500 } },
213
+ );
214
+ if (parsed.templateText) {
215
+ steps.push({ id: 'article.select_template', actionId: 'click_text', args: { session_id: parsed.sessionId, text: parsed.templateText, post_action_wait_ms: 500 } });
216
+ }
217
+ steps.push(
218
+ { id: 'article.next', actionId: 'click_text', args: { session_id: parsed.sessionId, text: ARTICLE_NEXT_TEXT, post_action_wait_ms: 1_200 } },
219
+ { id: 'article.wait_publish', actionId: 'wait_for_selector', args: { session_id: parsed.sessionId, selector: PUBLISH_SELECTOR, state: 'visible', timeout_ms: 30_000 } },
220
+ );
221
+ if (parsed.summary) {
222
+ steps.push({ id: 'article.fill_summary', actionId: 'fill', args: { session_id: parsed.sessionId, selector: ARTICLE_BODY_SELECTOR, text: parsed.summary } });
223
+ }
224
+ }
225
+
226
+ steps.push(
227
+ { id: 'publish.semantic_submit', actionId: 'dispatch_event', args: { session_id: parsed.sessionId, selector: PUBLISH_SELECTOR, event: PUBLISH_EVENT, post_action_wait_ms: 1_500 }, expectation: 'Current XHS closed-shadow publish control emits publish; recipe version must be updated if this semantic event changes.' },
228
+ { id: 'verify.creator_receipt', actionId: 'get_text', args: { session_id: parsed.sessionId, max_chars: 4_000 }, expectation: 'Creator URL must expose a recognized publish success receipt before title verification.' },
229
+ { id: 'verify.creator_note_manager', actionId: 'navigate', args: { session_id: parsed.sessionId, url: CREATOR_NOTE_MANAGER_URL, wait_until: 'domcontentloaded', timeout_ms: 60_000 } },
230
+ { id: 'verify.creator_title', actionId: 'get_text', args: { session_id: parsed.sessionId, max_chars: 12_000 }, expectation: `Creator note manager text must contain exact title even when the note is still under review: ${parsed.title}` },
231
+ );
232
+
233
+ return {
234
+ schemaVersion: 1,
235
+ recipeVersion: RECIPE_VERSION,
236
+ provider: 'xiaohongshu-web',
237
+ requestedMode: parsed.mode,
238
+ normalizedMode: parsed.normalizedMode,
239
+ generationRequired: false,
240
+ sessionId: parsed.sessionId,
241
+ profileUrl: parsed.profileUrl,
242
+ authPolicy: {
243
+ loginUrlMarkers: LOGIN_URL_MARKERS,
244
+ loginTextMarkers: LOGIN_TEXT_MARKERS,
245
+ credentialPersistence: 'browser_profile_only',
246
+ onAuthRequired: 'stop_before_edit_and_resume_after_user_login',
247
+ },
248
+ selectors: {
249
+ imageFile: IMAGE_FILE_SELECTOR,
250
+ imageTitle: IMAGE_TITLE_SELECTOR,
251
+ imageBody: IMAGE_BODY_SELECTOR,
252
+ articleTitle: ARTICLE_TITLE_SELECTOR,
253
+ articleBody: ARTICLE_BODY_SELECTOR,
254
+ publish: PUBLISH_SELECTOR,
255
+ publishEvent: PUBLISH_EVENT,
256
+ },
257
+ steps,
258
+ verification: ['creator_publish_success_receipt', 'creator_note_manager_contains_exact_title'],
259
+ };
260
+ }
261
+
262
+ function resultText(result: Record<string, unknown>): string {
263
+ return typeof result.text === 'string' ? result.text : '';
264
+ }
265
+
266
+ function resultUrl(result: Record<string, unknown>): string {
267
+ if (typeof result.url === 'string') return result.url;
268
+ const session = result.session;
269
+ if (session && typeof session === 'object' && typeof (session as Record<string, unknown>).url === 'string') return String((session as Record<string, unknown>).url);
270
+ const navigation = result.navigation;
271
+ if (navigation && typeof navigation === 'object' && typeof (navigation as Record<string, unknown>).url === 'string') return String((navigation as Record<string, unknown>).url);
272
+ return '';
273
+ }
274
+
275
+ function schemaDriftMessage(message: string): boolean {
276
+ return /not found|does not allow multiple|Timeout waiting for selector|visible exact text not found|file input/i.test(message);
277
+ }
278
+
279
+ export async function executeXiaohongshuPluginAction(input: AssistantPluginActionExecutionInput): Promise<Record<string, unknown>> {
280
+ if (input.actionId === 'get_publish_recipe') return buildXiaohongshuPublishRecipe(input.args);
281
+ if (input.actionId === 'classify_publish_state') {
282
+ const phase = input.args.phase;
283
+ if (phase !== 'preflight' && phase !== 'creator_receipt' && phase !== 'profile_verify') {
284
+ throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'phase must be preflight, creator_receipt, or profile_verify.', { retryable: false });
285
+ }
286
+ const url = requiredString(input.args.url, 'url');
287
+ const text = typeof input.args.text === 'string' ? input.args.text : '';
288
+ const expectedTitle = stringValue(input.args.expected_title);
289
+ return { state: classifyXiaohongshuPublishState({ phase, url, text, expectedTitle }), recipeVersion: RECIPE_VERSION };
290
+ }
291
+ if (input.actionId !== 'publish_note') {
292
+ throw new AssistantPluginError('PLUGIN_ACTION_NOT_SUPPORTED', `xiaohongshu/${input.actionId} is not supported.`, { retryable: false });
293
+ }
294
+
295
+ const parsed = parseRecipeArgs(input.args);
296
+ if (parsed.mode === 'generated_image_note' && parsed.imagePaths.length === 0) {
297
+ return {
298
+ status: 'generation_required',
299
+ recipeVersion: RECIPE_VERSION,
300
+ resumeMode: 'generated_image_note',
301
+ next: 'Generate one or more image files, then call publish_note again with image_paths. The publishing path will normalize to image_note.',
302
+ };
303
+ }
304
+
305
+ const recipe = buildXiaohongshuPublishRecipe(input.args);
306
+ const steps = recipe.steps as RecipeStep[];
307
+ const execute = browserExecutor();
308
+ const receipts: Array<{ stepId: string; actionId: string; url?: string }> = [];
309
+ let currentStep = 'preflight';
310
+ let creatorReceipt: Record<string, unknown> | undefined;
311
+ let profileReceipt: Record<string, unknown> | undefined;
312
+
313
+ const runStep = async (step: RecipeStep, index: number): Promise<Record<string, unknown>> => {
314
+ currentStep = step.id;
315
+ const result = await execute({
316
+ ...input,
317
+ pluginId: 'browser',
318
+ actionId: step.actionId,
319
+ requestId: `${input.requestId}:xhs:${RECIPE_VERSION}:${index}:${step.actionId}`,
320
+ args: step.args,
321
+ });
322
+ receipts.push({ stepId: step.id, actionId: step.actionId, ...(resultUrl(result) ? { url: resultUrl(result) } : {}) });
323
+ return result;
324
+ };
325
+
326
+ try {
327
+ const preflightNavigate = await runStep(steps[0], 0);
328
+ const navigateUrl = resultUrl(preflightNavigate);
329
+ if (isXiaohongshuAuthRequired(navigateUrl, '')) {
330
+ return {
331
+ status: 'auth_required',
332
+ recipeVersion: RECIPE_VERSION,
333
+ checkpoint: 'preflight.navigate_creator',
334
+ sessionId: parsed.sessionId,
335
+ next: 'Complete only the necessary Xiaohongshu login/verification in the existing browser profile, then rerun publish_note with the same inputs.',
336
+ receipts,
337
+ };
338
+ }
339
+
340
+ const preflight = await runStep(steps[1], 1);
341
+ const preflightUrl = resultUrl(preflight) || navigateUrl;
342
+ const preflightState = parsed.normalizedMode === 'image_note'
343
+ ? 'READY'
344
+ : classifyXiaohongshuPublishState({ phase: 'preflight', url: preflightUrl, text: resultText(preflight) });
345
+ if (preflightState === 'AUTH_REQUIRED') {
346
+ return {
347
+ status: 'auth_required',
348
+ recipeVersion: RECIPE_VERSION,
349
+ checkpoint: steps[1].id,
350
+ sessionId: parsed.sessionId,
351
+ next: 'Complete only the necessary Xiaohongshu login/verification in the existing browser profile, then rerun publish_note with the same inputs.',
352
+ receipts,
353
+ };
354
+ }
355
+ if (preflightState !== 'READY') {
356
+ return { status: 'page_schema_changed', recipeVersion: RECIPE_VERSION, checkpoint: currentStep, receipts };
357
+ }
358
+
359
+ for (let index = 2; index < steps.length; index += 1) {
360
+ const step = steps[index];
361
+ const result = await runStep(step, index);
362
+ if (step.id === 'verify.creator_receipt') {
363
+ creatorReceipt = result;
364
+ const state = classifyXiaohongshuPublishState({ phase: 'creator_receipt', url: resultUrl(result), text: resultText(result) });
365
+ if (state === 'AUTH_REQUIRED') {
366
+ return { status: 'auth_required', recipeVersion: RECIPE_VERSION, checkpoint: step.id, receipts, next: 'Restore Xiaohongshu login in the existing browser profile, then verify the draft/publication state before retrying.' };
367
+ }
368
+ if (state !== 'PUBLISHED_RECEIPT') {
369
+ return { status: 'publish_unverified', recipeVersion: RECIPE_VERSION, checkpoint: step.id, creatorUrl: resultUrl(result), receipts };
370
+ }
371
+ }
372
+ if (step.id === 'verify.creator_title') profileReceipt = result;
373
+ }
374
+
375
+ const profileState = profileReceipt
376
+ ? classifyXiaohongshuPublishState({ phase: 'profile_verify', url: resultUrl(profileReceipt), text: resultText(profileReceipt), expectedTitle: parsed.title })
377
+ : 'VERIFY_PENDING';
378
+ if (profileState !== 'PROFILE_VERIFIED') {
379
+ return {
380
+ status: 'verification_pending',
381
+ recipeVersion: RECIPE_VERSION,
382
+ creatorReceipt: creatorReceipt ? { url: resultUrl(creatorReceipt) } : undefined,
383
+ verificationUrl: CREATOR_NOTE_MANAGER_URL,
384
+ expectedTitle: parsed.title,
385
+ receipts,
386
+ };
387
+ }
388
+ return {
389
+ status: 'published',
390
+ recipeVersion: RECIPE_VERSION,
391
+ mode: parsed.mode,
392
+ normalizedMode: parsed.normalizedMode,
393
+ title: parsed.title,
394
+ creatorReceipt: { url: resultUrl(creatorReceipt ?? {}) },
395
+ creatorVerification: { url: resultUrl(profileReceipt ?? {}), titleFound: true },
396
+ receipts,
397
+ publishedAt: now(),
398
+ };
399
+ } catch (error) {
400
+ const message = error instanceof Error ? error.message : String(error);
401
+ if (schemaDriftMessage(message)) {
402
+ return {
403
+ status: 'page_schema_changed',
404
+ recipeVersion: RECIPE_VERSION,
405
+ checkpoint: currentStep,
406
+ message,
407
+ receipts,
408
+ next: 'Re-discover only the failed semantic anchor, update the Xiaohongshu recipe version, then resume with the same content inputs.',
409
+ };
410
+ }
411
+ throw error;
412
+ }
413
+ }
414
+
415
+ function health(): AssistantPluginHealth {
416
+ return {
417
+ state: 'ready',
418
+ checkedAt: now(),
419
+ ready: true,
420
+ probed: true,
421
+ errors: [],
422
+ warnings: ['Xiaohongshu authentication is owned by the Browser profile; login validity is checked at publish preflight and no raw token/cookie is stored by this recipe.'],
423
+ details: { recipeVersion: RECIPE_VERSION, browserSessionRequired: true },
424
+ };
425
+ }
426
+
427
+ function permissions(): AssistantPluginPermissionScope[] {
428
+ return [
429
+ { scope: 'xiaohongshu.recipe', mode: 'read', description: 'Resolve and classify the versioned Xiaohongshu publishing recipe.', granted: true, required: true },
430
+ { scope: 'xiaohongshu.publish', mode: 'write', description: 'Execute the bounded Xiaohongshu publishing recipe through the existing Browser plugin.', granted: true, required: true },
431
+ ];
432
+ }
433
+
434
+ function capabilities(): AssistantPluginCapability[] {
435
+ return [{
436
+ capabilityId: 'xiaohongshu-publish',
437
+ title: 'Xiaohongshu Publishing Recipe',
438
+ description: 'Versioned Browser-backed publishing flow with auth fencing, image/long-text routing, generated-image handoff, semantic publish activation, and dual verification.',
439
+ scopes: ['xiaohongshu.recipe', 'xiaohongshu.publish'],
440
+ actions: ['get_publish_recipe', 'classify_publish_state', 'publish_note'],
441
+ }];
442
+ }
443
+
444
+ function actions(): AssistantPluginActionDescriptor[] {
445
+ const recipeSchema = {
446
+ type: 'object',
447
+ properties: {
448
+ mode: { type: 'string', enum: ['image_note', 'generated_image_note', 'long_text'] },
449
+ session_id: { type: 'string' },
450
+ profile_url: { type: 'string' },
451
+ title: { type: 'string' },
452
+ body: { type: 'string' },
453
+ image_paths: { type: 'array', minItems: 1, maxItems: 18, items: { type: 'string' } },
454
+ summary: { type: 'string' },
455
+ template_text: { type: 'string' },
456
+ },
457
+ required: ['mode', 'session_id', 'profile_url', 'title', 'body'],
458
+ additionalProperties: false,
459
+ };
460
+ return [
461
+ {
462
+ actionId: 'get_publish_recipe', title: 'Resolve publish recipe', description: 'Return the current deterministic Xiaohongshu Browser action sequence without executing it.',
463
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true,
464
+ scopes: ['xiaohongshu.recipe'], resourceClaims: [], argumentsSchema: recipeSchema,
465
+ },
466
+ {
467
+ actionId: 'classify_publish_state', title: 'Classify publish state', description: 'Classify observed Xiaohongshu URL/text as ready, auth-required, published receipt, profile verified, pending, or schema drift.',
468
+ readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true,
469
+ scopes: ['xiaohongshu.recipe'], resourceClaims: [],
470
+ argumentsSchema: {
471
+ type: 'object',
472
+ properties: {
473
+ phase: { type: 'string', enum: ['preflight', 'creator_receipt', 'profile_verify'] },
474
+ url: { type: 'string' }, text: { type: 'string' }, expected_title: { type: 'string' },
475
+ },
476
+ required: ['phase', 'url', 'text'], additionalProperties: false,
477
+ },
478
+ },
479
+ {
480
+ actionId: 'publish_note', title: 'Publish Xiaohongshu note', description: 'Execute the versioned Xiaohongshu publish recipe through the existing persisted Browser session. Stops on login expiry or page-schema drift and only reports published after a Creator success receipt plus exact-title verification in Creator note manager.',
481
+ readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 180_000, cancellable: true, idempotent: false,
482
+ scopes: ['xiaohongshu.publish'], resourceClaims: [{ resource: 'remote', mode: 'exclusive' }, { resource: 'repo-state', mode: 'write' }], argumentsSchema: recipeSchema,
483
+ },
484
+ ];
485
+ }
486
+
487
+ export function buildXiaohongshuPluginManifest(previousRevision = 0, previousUpdatedAt?: string): AssistantPluginManifest {
488
+ return {
489
+ schemaVersion: 1,
490
+ manifestVersion: 1,
491
+ revision: Math.max(1, previousRevision || 1),
492
+ pluginId: PLUGIN_ID,
493
+ provider: 'xiaohongshu-web-recipe',
494
+ displayName: 'Xiaohongshu Publishing',
495
+ pluginVersion: '1.0.0',
496
+ authority: {
497
+ strategy: 'derived',
498
+ duplicateStateAllowed: false,
499
+ sourceOfTruth: ['source:src/runtime/plugins/xiaohongshu-publish.ts', 'browser-profile:authentication-and-session'],
500
+ },
501
+ enabled: true,
502
+ lifecycle: { state: 'enabled', reason: 'Versioned Xiaohongshu publishing recipe is available; live authentication is fenced at execution time.' },
503
+ health: health(),
504
+ permissions: permissions(),
505
+ capabilities: capabilities(),
506
+ actions: actions(),
507
+ updatedAt: previousUpdatedAt ?? now(),
508
+ };
509
+ }
510
+
511
+ export const xiaohongshuPluginAdapter = {
512
+ pluginId: PLUGIN_ID,
513
+ buildManifest: buildXiaohongshuPluginManifest,
514
+ executeAction: executeXiaohongshuPluginAction,
515
+ };
@@ -1,9 +1,6 @@
1
1
  import { join } from 'path';
2
2
  import { repositoryControllerRoot } from '../../cli/repositories/controller-home';
3
- import {
4
- repositoryCheckoutLifecycle,
5
- selectRepositoryCheckout,
6
- } from '../../cli/repositories/registry';
3
+ import { selectRepositoryCheckout } from '../../cli/repositories/registry';
7
4
  import {
8
5
  repositoryGitStatus,
9
6
  type RepositoryGitStatusSnapshot,
@@ -66,18 +63,21 @@ export function sampleRepositoryGitStatusForRepositories(
66
63
  let sampled = 0;
67
64
  const failed: Array<{ repoId: string; checkoutId: string; message: string }> = [];
68
65
  for (const repository of repositories) {
69
- for (const checkout of repository.checkouts) {
70
- if (repositoryCheckoutLifecycle(checkout) !== 'active') continue;
71
- try {
72
- writeRepositoryGitStatusSample(controllerHome, selectRepositoryCheckout(repository, checkout.checkoutId));
73
- sampled += 1;
74
- } catch (error) {
75
- failed.push({
76
- repoId: repository.repoId,
77
- checkoutId: checkout.checkoutId,
78
- message: error instanceof Error ? error.message : String(error),
79
- });
80
- }
66
+ try {
67
+ // Scheduler projections consume only the currently selected checkout.
68
+ // Historical/parallel checkouts can be refreshed explicitly when selected;
69
+ // sampling all of them turns stale registry history into Runtime hot-path work.
70
+ writeRepositoryGitStatusSample(
71
+ controllerHome,
72
+ selectRepositoryCheckout(repository, repository.activeCheckoutId),
73
+ );
74
+ sampled += 1;
75
+ } catch (error) {
76
+ failed.push({
77
+ repoId: repository.repoId,
78
+ checkoutId: repository.activeCheckoutId,
79
+ message: error instanceof Error ? error.message : String(error),
80
+ });
81
81
  }
82
82
  }
83
83
  return { sampled, failed };
@@ -1,7 +1,7 @@
1
1
  import { createHash } from 'crypto';
2
2
  import { join } from 'path';
3
3
  import { listActiveExecutionJobs, listExecutionJobs } from '../execution/jobs/store';
4
- import type { ExecutionJob } from '../execution/jobs/types';
4
+ import { TERMINAL_JOB_STATUSES, type ExecutionJob } from '../execution/jobs/types';
5
5
  import { listActiveLeases } from '../resources/leases/store';
6
6
  import { readJsonFile, writeJsonAtomic } from '../shared/json-files';
7
7
  import { repositoryControllerRoot } from '../../cli/repositories/controller-home';
@@ -27,6 +27,7 @@ import type { ProjectionObservation, ProjectionSourceReconciliation } from '../h
27
27
  import { RUNTIME_HEALTH_THRESHOLDS } from '../health/evaluator';
28
28
  import { isProcessAlive } from '../shared/process-tree';
29
29
  import type { TaskLedgerProjection } from '../../cli/controller/task-ledger';
30
+ import { collectWorkLifecycleAttention } from '../control-plane/execution/work-lifecycle-audit';
30
31
 
31
32
  export interface ProjectionMetadata {
32
33
  contentRevision: number;
@@ -125,6 +126,15 @@ function attentionSummary(job: ExecutionJob): RepositoryRuntimeProjection['atten
125
126
  return { jobId: job.jobId, status: job.status, message: job.error?.message };
126
127
  }
127
128
 
129
+ function executionAttentionIsCurrent(job: ExecutionJob, activeJobIds: ReadonlySet<string>): boolean {
130
+ // Status is the durable lifecycle authority. Legacy/migrated terminal records may
131
+ // lack finishedAt, but that omission must not resurrect terminal attention as a
132
+ // current release blocker. If a future attention status is non-terminal, retain
133
+ // the legacy timestamp/index fallback for that genuinely active state.
134
+ if (TERMINAL_JOB_STATUSES.has(job.status)) return false;
135
+ return !job.finishedAt || activeJobIds.has(job.jobId);
136
+ }
137
+
128
138
  function projectionVisibleLeases(leases: ReturnType<typeof listActiveLeases>) {
129
139
  // Ephemeral leases protect fast-path Process execution from conflicting writes,
130
140
  // but by contract they do not represent Scheduler/Worker durable activity.
@@ -155,9 +165,11 @@ function projectionWithExecutionIndexOverlay(
155
165
  const attentionJobs = recentJobs?.filter((job) => ATTENTION_JOB_STATUSES.has(job.status));
156
166
  const attention = attentionJobs?.map(attentionSummary) ?? base.attention;
157
167
  const currentAttention = attentionJobs
158
- ?.filter((job) => !job.finishedAt || activeJobIds.has(job.jobId))
168
+ ?.filter((job) => executionAttentionIsCurrent(job, activeJobIds))
159
169
  .map(attentionSummary)
160
170
  ?? base.currentAttention;
171
+ const repository = listRepositories(controllerHome, { includeRemoved: true }).find((entry) => entry.repoId === repoId);
172
+ const lifecycleAttention = repository ? collectWorkLifecycleAttention(controllerHome, repository) : [];
161
173
 
162
174
  return {
163
175
  ...base,
@@ -172,8 +184,8 @@ function projectionWithExecutionIndexOverlay(
172
184
  releaseFrozen: projectionLeases
173
185
  ? projectionLeases.some((lease) => lease.resourceKey.startsWith('release:'))
174
186
  : base.releaseFrozen,
175
- currentAttention,
176
- attention,
187
+ currentAttention: [...currentAttention, ...lifecycleAttention].slice(0, 100),
188
+ attention: [...attention, ...lifecycleAttention].slice(0, 100),
177
189
  };
178
190
  }
179
191
 
@@ -196,8 +208,10 @@ function buildRepositoryProjection(
196
208
  .filter((job) => ATTENTION_JOB_STATUSES.has(job.status));
197
209
  // Terminal attention records remain in history for diagnosis and audit, but only
198
210
  // active/unresolved records should influence "current readiness" decisions.
199
- const currentAttentionJobs = attentionJobs.filter((job) => !job.finishedAt || activeJobIds.has(job.jobId));
211
+ // Terminal status wins over a missing legacy finishedAt timestamp.
212
+ const currentAttentionJobs = attentionJobs.filter((job) => executionAttentionIsCurrent(job, activeJobIds));
200
213
  const repository = listRepositories(controllerHome).find((entry) => entry.repoId === repoId);
214
+ const lifecycleAttention = repository ? collectWorkLifecycleAttention(controllerHome, repository) : [];
201
215
  const plugins = repository ? listAssistantPluginManifests(controllerHome, repository, {
202
216
  preferStored: true,
203
217
  }) : [];
@@ -225,10 +239,14 @@ function buildRepositoryProjection(
225
239
  queueDepth: activeJobs.filter((job) => job.status !== 'running' && job.status !== 'dispatched').length,
226
240
  runningWorkers: activeJobs.filter((job) => job.status === 'running').length,
227
241
  activeLeases: leases.length,
228
- currentAttention: currentAttentionJobs
229
- .map((job) => ({ jobId: job.jobId, status: job.status, message: job.error?.message })),
230
- attention: attentionJobs
231
- .map((job) => ({ jobId: job.jobId, status: job.status, message: job.error?.message })),
242
+ currentAttention: [
243
+ ...currentAttentionJobs.map((job) => ({ jobId: job.jobId, status: job.status, message: job.error?.message })),
244
+ ...lifecycleAttention,
245
+ ].slice(0, 100),
246
+ attention: [
247
+ ...attentionJobs.map((job) => ({ jobId: job.jobId, status: job.status, message: job.error?.message })),
248
+ ...lifecycleAttention,
249
+ ].slice(0, 100),
232
250
  plugins: {
233
251
  total: plugins.length,
234
252
  enabled: plugins.filter((plugin) => plugin.enabled).length,
@@ -620,7 +638,7 @@ export function readRepositoryProjectionSnapshot(
620
638
 
621
639
  if (!stale && persisted) {
622
640
  dirtyProjectionReadCache.delete(dirtyProjectionReadCacheKey(controllerHome, repoId));
623
- return { projection: persisted, stale: false, persisted: true };
641
+ return { projection: projectionWithExecutionIndexOverlay(controllerHome, repoId, persisted), stale: false, persisted: true };
624
642
  }
625
643
 
626
644
  // A hot read must remain read-only and must not rebuild the full projection in