@tea-agent/loop-agent 0.43.0-next.9 → 0.43.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 (287) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +31 -2309
  3. package/README.md +7 -0
  4. package/dist/application/dag/args.js +8 -0
  5. package/dist/application/task-lifecycle/advance.js +38 -7
  6. package/dist/build-stamp.json +3 -3
  7. package/dist/cli/command-definitions.js +2 -0
  8. package/dist/cli/program.js +3 -1
  9. package/dist/cli/update/init-surface-notifier.js +2 -0
  10. package/dist/cli/update/policy.js +2 -0
  11. package/dist/commands/client-recovery.js +20 -55
  12. package/dist/commands/cursor-prompt.js +2 -0
  13. package/dist/commands/dag-approve.js +2 -0
  14. package/dist/commands/dag-final-verification.js +2 -0
  15. package/dist/commands/dag-init-hybrid.js +2 -0
  16. package/dist/commands/dag-reconcile-tasks.js +2 -0
  17. package/dist/commands/dag-reject.js +2 -0
  18. package/dist/commands/dag-report.js +2 -0
  19. package/dist/commands/dag-rerun.js +2 -0
  20. package/dist/commands/dag-resume.js +2 -0
  21. package/dist/commands/dag-workflow-compile.js +2 -0
  22. package/dist/commands/dag-workflow-plan.js +2 -0
  23. package/dist/commands/dag-workflow-validate.js +2 -0
  24. package/dist/commands/delegate.js +2 -0
  25. package/dist/commands/examples.js +2 -0
  26. package/dist/commands/import-prd.js +2 -0
  27. package/dist/commands/init-model-catalog.js +2 -0
  28. package/dist/commands/init.js +19 -13
  29. package/dist/commands/instructions.js +2 -0
  30. package/dist/commands/knowledge.js +2 -0
  31. package/dist/commands/loop-benchmark.js +2 -0
  32. package/dist/commands/pi-prompt.js +2 -0
  33. package/dist/commands/pi-reuse-benchmark.js +2 -0
  34. package/dist/commands/promote-run.js +2 -0
  35. package/dist/commands/stats.js +1 -1
  36. package/dist/commands/study-init.js +2 -0
  37. package/dist/commands/task-advance.js +34 -0
  38. package/dist/commands/task-source-prepare.js +2 -0
  39. package/dist/commands/worktree-create.js +2 -0
  40. package/dist/commands/worktree-remove.js +2 -0
  41. package/dist/executors/dag-pi-executor.js +1650 -198
  42. package/dist/executors/pi-executor.js +85 -3
  43. package/dist/executors/pi-sdk-executor.js +18 -0
  44. package/dist/executors/shell-executor.js +93 -7
  45. package/dist/executors/shell-verification.js +3 -0
  46. package/dist/executors/shell-write-guard.js +27 -32
  47. package/dist/governance/exec-plans.js +6 -3
  48. package/dist/infrastructure/console/app-data.js +6 -0
  49. package/dist/shared/artifacts-core.js +2 -0
  50. package/dist/shared/backend-dogfood-preflight.js +47 -0
  51. package/dist/shared/dag-failure-category.js +3 -0
  52. package/dist/shared/git-progress.js +2 -0
  53. package/dist/shared/one-shot-prompt-args.js +2 -0
  54. package/dist/shared/operator/capabilities.js +180 -0
  55. package/dist/shared/package-metadata.js +6 -4
  56. package/dist/shared/pi-context-pressure/extension.js +26 -16
  57. package/dist/shared/pi-context-pressure/sift-bridge.js +1 -1
  58. package/dist/shared/pi-provider-recovery.js +182 -0
  59. package/dist/shared/pi-retry-settings.js +40 -0
  60. package/dist/shared/reference-context.js +2 -0
  61. package/dist/task/config-types.js +29 -0
  62. package/dist/task/contract/project.js +9 -0
  63. package/dist/task/contract/recover.js +6 -0
  64. package/dist/task/contract/schema.js +17 -0
  65. package/dist/task/source-prepare/artifact-meta.js +9 -2
  66. package/dist/task/source-prepare/build-draft.js +60 -0
  67. package/dist/task/source-prepare/fragment-inventory.js +4 -1
  68. package/dist/task/source-prepare/parse-intent.js +36 -11
  69. package/dist/task/task-demand-routing.js +5 -2
  70. package/dist/worker/console/chat/browser-automation.js +523 -0
  71. package/dist/worker/console/chat/browser-policy.js +6 -36
  72. package/dist/worker/console/chat/browser-routes.js +188 -56
  73. package/dist/worker/console/chat/explore-tools.js +5 -2
  74. package/dist/worker/console/chat/pi-mode-loop-isolation.js +155 -0
  75. package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +44 -0
  76. package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +184 -0
  77. package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +202 -0
  78. package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +79 -0
  79. package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +94 -0
  80. package/dist/worker/console/chat/pi-runtime/custom-tools/scheduled-goal-tools.js +22 -0
  81. package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +38 -0
  82. package/dist/worker/console/chat/pi-runtime/inspection-types.js +28 -0
  83. package/dist/worker/console/chat/pi-runtime/inspection.js +439 -0
  84. package/dist/worker/console/chat/pi-runtime/progressive-tools.js +195 -0
  85. package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +153 -0
  86. package/dist/worker/console/chat/pi-runtime/sdk-session.js +5 -0
  87. package/dist/worker/console/chat/pi-runtime.js +208 -1138
  88. package/dist/worker/console/chat/routes.js +27 -7
  89. package/dist/worker/console/chat/scheduled-goal-booking.js +59 -0
  90. package/dist/worker/console/chat/scheduled-goal-delivery.js +27 -0
  91. package/dist/worker/console/chat/scheduled-goal-request.js +190 -0
  92. package/dist/worker/console/chat/sdd-data-alignment.js +1 -1
  93. package/dist/worker/console/chat/session-mode-view.js +5 -4
  94. package/dist/worker/console/chat/session-mode.js +18 -12
  95. package/dist/worker/console/chat/session-store.js +42 -9
  96. package/dist/worker/console/chat/tool-preview.js +115 -0
  97. package/dist/worker/console/chat/tools.js +10 -0
  98. package/dist/worker/console/chat/turn-order.js +13 -0
  99. package/dist/worker/console/chat/turn-process.js +32 -30
  100. package/dist/worker/console/console-update-and-init.js +1 -19
  101. package/dist/worker/console/console-update-runtime.js +5 -20
  102. package/dist/worker/console/operator-actions.js +63 -1
  103. package/dist/worker/console/prd-intake-bridge.js +54 -1
  104. package/dist/worker/console/routes.js +8 -0
  105. package/dist/worker/console/scheduled-goal-host.js +98 -0
  106. package/dist/worker/console/scheduled-goal-operation-adapter.js +127 -0
  107. package/dist/worker/console/server.js +24 -1
  108. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-D_fNbWc_.js → abnfDiagram-N423BO3Z-8-j6y-sd.js} +1 -1
  109. package/dist/worker/console/static/assets/{arc-_HuqLW6m.js → arc-eoQiMvuk.js} +1 -1
  110. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-Djy2qY1m.js → architectureDiagram-T3A2C74G-C4A3uMcI.js} +1 -1
  111. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-bB-1TSyY.js → blockDiagram-VBNYF7ZC-D4zD2F-Q.js} +1 -1
  112. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-C5Hmghrz.js → c4Diagram-5PPSVZJV-j1RkJziL.js} +1 -1
  113. package/dist/worker/console/static/assets/channel-ChE7y-cx.js +1 -0
  114. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-Duyl39oy.js → chunk-2GRJ4B5K-YBHmhik1.js} +1 -1
  115. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-x6mdnSyf.js → chunk-2Q5K7J3B-COfWyo9P.js} +1 -1
  116. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-WlBuFj7W.js → chunk-5RXB4S5H-I99OUkHY.js} +1 -1
  117. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CTF429HX.js → chunk-5VM5RSS4-XKxoJNJ4.js} +1 -1
  118. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Dwd6r-SF.js → chunk-6Q2QTUOP-DLT_cYx1.js} +1 -1
  119. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-C-Br82D1.js → chunk-GF5L2VYU-CxcKZ9mV.js} +1 -1
  120. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CuGp0DeE.js → chunk-JWPE2WC7-V1EqXdjY.js} +1 -1
  121. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CJhFX6Lh.js → chunk-KBJHAD2P-DebTtdFp.js} +1 -1
  122. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DpGOa_jT.js → chunk-RYQCIY6F-B-ivNRDf.js} +1 -1
  123. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DXJaTynd.js → chunk-XXDRQBXY-DC_Ds11b.js} +1 -1
  124. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +1 -0
  125. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +1 -0
  126. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CRBZWjE4.js → cose-bilkent-JH36ORCC-sWEqKwIb.js} +1 -1
  127. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BdXcxtlb.js → cynefin-VYW2F7L2-BXa_dcu4.js} +1 -1
  128. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-07UKsOts.js → cynefinDiagram-MW4NZA55-C-Mle84F.js} +1 -1
  129. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-D38p9__S.js → dagre-VZM6K2ZE-CXPDBITe.js} +1 -1
  130. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-nSiBNNjH.js → diagram-7IWD3JNH-CC-WJQfa.js} +1 -1
  131. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BFOTot5D.js → diagram-B4RE2ZJO-CDAV5Vs4.js} +1 -1
  132. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DypKaZ6k.js → diagram-LBJQPF4R-CmFiAcNz.js} +1 -1
  133. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-B6yoOQeW.js → diagram-Q27KOJAE-DPBZHuyn.js} +1 -1
  134. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BHYS6viX.js → diagram-UB23O5K3-jUlm_Ds3.js} +1 -1
  135. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-D2VD5Snj.js → ebnfDiagram-BXEA7PRR-DWhQ3mfY.js} +1 -1
  136. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-E98mWxN1.js → erDiagram-JOGREHBK-Tr2gMqet.js} +1 -1
  137. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CT1zxhqP.js → flowDiagram-UKHOOZJN-DJjVQHPA.js} +1 -1
  138. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-6GcngNcy.js → ganttDiagram-PKOTCBZU-D74dQ4u0.js} +1 -1
  139. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DCL6NvKe.js → gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js} +1 -1
  140. package/dist/worker/console/static/assets/index-BWkIfcrK.css +1 -0
  141. package/dist/worker/console/static/assets/index-Cdkvw_H6.js +469 -0
  142. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-C0627e-9.js → infoDiagram-6WML65LV-ILCbxJyb.js} +1 -1
  143. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-C_u3P6Ik.js → ishikawaDiagram-WSZJBQD7-DeuWBQ89.js} +1 -1
  144. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DPDPv9HL.js → journeyDiagram-NVQOT4AX-CF5ih8Fk.js} +1 -1
  145. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-D3OHdWiO.js → kanban-definition-27J2QSJJ-C7yOSuRO.js} +1 -1
  146. package/dist/worker/console/static/assets/{linear-DI88zD7n.js → linear-BDZ9riWi.js} +1 -1
  147. package/dist/worker/console/static/assets/{mermaid.core-p2l8IrWY.js → mermaid.core-7pKqYtpZ.js} +5 -5
  148. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-B1bAX-oS.js → mindmap-definition-FAOFIHXS-LeJDybSU.js} +1 -1
  149. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DwOnWxdD.js → pegDiagram-VL7TDLO6-BJvT3pMD.js} +1 -1
  150. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-xRkTXnJC.js → pieDiagram-7S7Q4E2Y-_rGqpMan.js} +1 -1
  151. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-_9UC5Zio.js → quadrantDiagram-CIZ2JOQS-DPcSv5aA.js} +1 -1
  152. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-CgEUirWJ.js → railroadDiagram-AXF67PYL-Drxx4hkJ.js} +1 -1
  153. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-Czbpwiyz.js → requirementDiagram-LRYGKXZP-BCTUdU4z.js} +1 -1
  154. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-_j7_KiqY.js → sankeyDiagram-W5VNT64P-B6wzbZmB.js} +1 -1
  155. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BNJa6Hyi.js → sequenceDiagram-SI44F4Z6-BGt8d3QQ.js} +1 -1
  156. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-DedpKtOc.js → sizeCapture-X5ZJPWSS-7nlhJKo7.js} +1 -1
  157. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BBvthr7c.js → stateDiagram-OKZ733FA-Cv2stqMA.js} +1 -1
  158. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +1 -0
  159. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-Ds7umgEM.js → swimlanes-SLNWSIFB-qDAo4Yc1.js} +2 -2
  160. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +8 -0
  161. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CKa-p1nF.js → timeline-definition-Z64GVDOM-CKbDNKTr.js} +1 -1
  162. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-LoUNR56o.js → vennDiagram-T6HMQDX7-DI-9EHic.js} +1 -1
  163. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-Bqbl6ym7.js → wardleyDiagram-T6FBY63Y-BrzzRDpX.js} +1 -1
  164. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-B8dUg4lf.js → xychartDiagram-ELKLHX3M-BclH5hGh.js} +1 -1
  165. package/dist/worker/console/static/index.html +2 -2
  166. package/dist/worker/console/static-src/operator-chat/chat-scroll-position.js +24 -0
  167. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +41 -11
  168. package/dist/worker/console/static-src/operator-chat/compaction-message.js +3 -17
  169. package/dist/worker/console/static-src/operator-chat/composer-session-ledger.js +192 -0
  170. package/dist/worker/console/static-src/operator-chat/pending-user-message.js +9 -1
  171. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +15 -5
  172. package/dist/worker/console/static-src/operator-chat/timeline-merge.js +29 -0
  173. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +92 -38
  174. package/dist/worker/console/static-src/operator-chat/useChatStream.js +145 -70
  175. package/dist/worker/console/static-src/operator-chat/useChatThread.js +38 -55
  176. package/dist/worker/console/static-src/operator-chat/useComposer.js +182 -69
  177. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +2 -0
  178. package/dist/worker/console/static-src/prd-file-import.js +3 -1
  179. package/dist/worker/console/workspace-context.js +22 -0
  180. package/dist/worker/console/workspace-initialization.js +209 -0
  181. package/dist/worker/delivery/git-transaction.js +2 -0
  182. package/dist/worker/feature/fullstack-validate.js +2 -0
  183. package/dist/worker/loop-agent/loop-agent-client.js +8 -2
  184. package/dist/worker/materialize/harness-task-materializer.js +4 -1
  185. package/dist/worker/metrics/projector.js +1 -1
  186. package/dist/worker/observability/event-store.js +4 -1
  187. package/dist/worker/observability/read-model.js +4 -1
  188. package/dist/worker/observe/node-transparency.js +23 -1
  189. package/dist/worker/observe/routes.js +4 -0
  190. package/dist/worker/observe/static/operator-chrome.js +3 -1
  191. package/dist/worker/observe/static/styles.css +7 -4
  192. package/dist/worker/observe/static/views/dag-inspector.js +5 -19
  193. package/dist/worker/outcomes/declared-artifacts.js +2 -0
  194. package/dist/worker/preflight.js +3 -0
  195. package/dist/worker/scheduler/scheduled-goal-dispatch.js +117 -0
  196. package/dist/worker/scheduler/scheduled-goal-evidence.js +167 -0
  197. package/dist/worker/scheduler/scheduled-goal-recovery.js +62 -0
  198. package/dist/worker/scheduler/scheduled-goal-store.js +699 -0
  199. package/dist/worker/scheduler/scheduled-goal-supervisor.js +132 -0
  200. package/dist/worker/scheduler/scheduled-goal-time.js +102 -0
  201. package/dist/worker/scheduler/scheduled-goal-types.js +95 -0
  202. package/dist/worker/task-spec/validate.js +4 -1
  203. package/dist/workflows/dag/backend-test-markdown-workflow.js +6 -24
  204. package/dist/workflows/dag/backend-test-plan-protocol.js +82 -5
  205. package/dist/workflows/dag/dag-retry-schema.js +11 -0
  206. package/dist/workflows/dag/decision-envelope.js +2 -0
  207. package/dist/workflows/dag/dynamic-runtime/shared.js +2 -2
  208. package/dist/workflows/dag/frontend-closeout.js +3 -1
  209. package/dist/workflows/dag/frontend-committed-facts.js +461 -0
  210. package/dist/workflows/dag/frontend-durable-tools.js +15 -3
  211. package/dist/workflows/dag/frontend-implementation-contract.js +369 -12
  212. package/dist/workflows/dag/frontend-plan-canary.js +53 -0
  213. package/dist/workflows/dag/frontend-plan-decision-contract.js +803 -0
  214. package/dist/workflows/dag/frontend-plan-render.js +0 -2
  215. package/dist/workflows/dag/frontend-provider-capability-matrix.js +8 -61
  216. package/dist/workflows/dag/frontend-recovery-run.js +57 -0
  217. package/dist/workflows/dag/frontend-repair.js +55 -76
  218. package/dist/workflows/dag/frontend-review-context.js +42 -68
  219. package/dist/workflows/dag/frontend-review-scopes.js +2 -2
  220. package/dist/workflows/dag/frontend-risk.js +92 -10
  221. package/dist/workflows/dag/frontend-session-budget.js +117 -3
  222. package/dist/workflows/dag/frontend-shape.js +11 -55
  223. package/dist/workflows/dag/frontend-test-execution-evidence.js +35 -10
  224. package/dist/workflows/dag/frontend-typed-event-store.js +32 -25
  225. package/dist/workflows/dag/frontend-verification-trace.js +33 -54
  226. package/dist/workflows/dag/frontend-writer-admission.js +3 -47
  227. package/dist/workflows/dag/frontend-writer-status.js +0 -23
  228. package/dist/workflows/dag/init-hybrid.js +47 -19
  229. package/dist/workflows/dag/interrupt-request.js +2 -0
  230. package/dist/workflows/dag/lifecycle.js +11 -2
  231. package/dist/workflows/dag/node-execution.js +50 -13
  232. package/dist/workflows/dag/reconcile-run.js +2 -0
  233. package/dist/workflows/dag/repair-artifact.js +3 -1
  234. package/dist/workflows/dag/report.js +2 -0
  235. package/dist/workflows/dag/rerun-plan.js +10 -0
  236. package/dist/workflows/dag/rerun-task.js +79 -1
  237. package/dist/workflows/dag/retry-policy.js +18 -0
  238. package/dist/workflows/dag/scheduler.js +2 -4
  239. package/dist/workflows/dag/types.js +44 -6
  240. package/dist/workflows/dag/validate.js +4 -0
  241. package/docs/README.md +1 -0
  242. package/docs/architecture/runtime-boundaries.md +3 -3
  243. package/docs/governance/README.md +1 -0
  244. package/docs/init-surface.manifest.json +1 -0
  245. package/docs/operations/README.md +2 -0
  246. package/docs/templates/README.md +1 -1
  247. package/docs/templates/agent-dag.schema.json +2 -2
  248. package/docs/templates/backend-test-dag.json +14 -10
  249. package/docs/templates/frontend-implementation-contract.schema.json +0 -7
  250. package/package.json +9 -3
  251. package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
  252. package/skills/frontend-contract/references/contract-protocol.md +3 -1
  253. package/skills/frontend-plan/SKILL.md +8 -6
  254. package/skills/frontend-plan/references/decision-contract.md +3 -3
  255. package/skills/frontend-review/SKILL.md +18 -28
  256. package/skills/frontend-review/references/review-findings.md +8 -3
  257. package/dist/commands/new-task.js +0 -5
  258. package/dist/commands/status.js +0 -57
  259. package/dist/commands/task-contract.js +0 -269
  260. package/dist/shared/prompts.js +0 -26
  261. package/dist/task/config.js +0 -2
  262. package/dist/task/contract/validate-draft.js +0 -106
  263. package/dist/task/goal.js +0 -3
  264. package/dist/task/index.js +0 -12
  265. package/dist/task/lifecycle.js +0 -1
  266. package/dist/task/operator/capabilities.js +0 -6
  267. package/dist/task/operator/envelope.js +0 -2
  268. package/dist/task/operator/index.js +0 -5
  269. package/dist/task/operator/registry.js +0 -2
  270. package/dist/task/operator/types.js +0 -1
  271. package/dist/task/paths.js +0 -1
  272. package/dist/task/source-state.js +0 -1
  273. package/dist/task/subagent-guidance.js +0 -1
  274. package/dist/worker/console/chat/sift-bridge.js +0 -1
  275. package/dist/worker/console/index.js +0 -20
  276. package/dist/worker/console/static/assets/channel-C0A6UEIa.js +0 -1
  277. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DzguNppy.js +0 -1
  278. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DzguNppy.js +0 -1
  279. package/dist/worker/console/static/assets/index-24jp3aBh.js +0 -468
  280. package/dist/worker/console/static/assets/index-CY6y6I8M.css +0 -1
  281. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BABVQnfM.js +0 -1
  282. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D0qsLaxR.js +0 -8
  283. package/dist/worker/scheduler/index.js +0 -25
  284. package/dist/workflows/dag/facts.js +0 -4
  285. package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -975
  286. package/dist/workflows/dag/index.js +0 -6
  287. package/dist/workflows/dynamic/index.js +0 -7
@@ -148,6 +148,115 @@ export function getToolPreview(args, options) {
148
148
  return "";
149
149
  return s.length > maxLen ? `${s.slice(0, maxLen)}…` : s;
150
150
  }
151
+ /** DSH-style human-readable ask_user_question row (rowTitle「提问」). */
152
+ export function isAskUserQuestionTool(toolName) {
153
+ return (toolName ?? "").trim().toLowerCase() === "ask_user_question";
154
+ }
155
+ function normalizeOptionLabel(value) {
156
+ const record = asRecord(value);
157
+ if (!record)
158
+ return undefined;
159
+ const label = typeof record.label === "string" ? record.label.trim() : "";
160
+ return label || undefined;
161
+ }
162
+ function normalizeAskQuestion(value) {
163
+ const record = asRecord(value);
164
+ if (!record)
165
+ return undefined;
166
+ const id = typeof record.id === "string" ? record.id.trim() : "";
167
+ const question = typeof record.question === "string" ? record.question.trim() : "";
168
+ if (!id || !question)
169
+ return undefined;
170
+ const options = Array.isArray(record.options)
171
+ ? record.options
172
+ .map((item) => {
173
+ const label = normalizeOptionLabel(item);
174
+ if (!label)
175
+ return undefined;
176
+ const description = typeof item === "object" && item !== null &&
177
+ "description" in item && typeof item.description === "string"
178
+ ? item.description
179
+ : undefined;
180
+ return { label, ...(description && description.trim() ? { description: description.trim() } : {}) };
181
+ })
182
+ .filter((item) => Boolean(item))
183
+ : undefined;
184
+ return {
185
+ id,
186
+ question,
187
+ ...(typeof record.header === "string" && record.header.trim()
188
+ ? { header: record.header.trim() }
189
+ : {}),
190
+ ...(typeof record.detail === "string" && record.detail.trim()
191
+ ? { detail: record.detail.trim() }
192
+ : {}),
193
+ ...(options && options.length > 0 ? { options } : {}),
194
+ ...(record.multi_select === true || record.multiSelect === true
195
+ ? { multiSelect: true }
196
+ : {}),
197
+ };
198
+ }
199
+ /** Project ask_user_question args (questions array) into readable views. */
200
+ export function parseAskUserQuestions(args) {
201
+ const input = parseArgs(args);
202
+ if (!input)
203
+ return [];
204
+ const raw = Array.isArray(input)
205
+ ? input
206
+ : Array.isArray(input.questions)
207
+ ? input.questions
208
+ : [];
209
+ return raw
210
+ .map(normalizeAskQuestion)
211
+ .filter((item) => Boolean(item));
212
+ }
213
+ /** Project ask_user_question ToolResult into answer views keyed by question id. */
214
+ export function parseAskUserAnswers(result) {
215
+ if (result === undefined || result === null)
216
+ return [];
217
+ const text = extractToolResultText(result);
218
+ if (!text)
219
+ return [];
220
+ let parsed;
221
+ try {
222
+ parsed = JSON.parse(text);
223
+ }
224
+ catch {
225
+ return [];
226
+ }
227
+ const record = asRecord(parsed);
228
+ const raw = record && Array.isArray(record.answers) ? record.answers : undefined;
229
+ if (!raw)
230
+ return [];
231
+ const out = [];
232
+ for (const entry of raw) {
233
+ const rec = entry && typeof entry === "object" && !Array.isArray(entry)
234
+ ? entry
235
+ : undefined;
236
+ if (!rec || typeof rec.id !== "string")
237
+ continue;
238
+ const selected = Array.isArray(rec.selected)
239
+ ? rec.selected.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean)
240
+ : [];
241
+ const custom = typeof rec.custom === "string" ? rec.custom.trim() : "";
242
+ out.push({ id: rec.id, selected, ...(custom ? { custom } : {}) });
243
+ }
244
+ return out;
245
+ }
246
+ /** "{answered}/{total} 已回答" summary; total from args questions, answered from result answers. */
247
+ export function getAskUserQuestionSummary(args, result) {
248
+ const questions = parseAskUserQuestions(args);
249
+ if (questions.length === 0)
250
+ return null;
251
+ const answers = parseAskUserAnswers(result);
252
+ let answered = 0;
253
+ for (const question of questions) {
254
+ const answer = answers.find((item) => item.id === question.id);
255
+ if (answer && (answer.selected.length > 0 || answer.custom))
256
+ answered += 1;
257
+ }
258
+ return { answered, total: questions.length };
259
+ }
151
260
  /** Stable DSH-style tool row identity; unknown tools keep their real name. */
152
261
  export function getToolPresentation(toolName) {
153
262
  const raw = toolName?.trim() || "Tool";
@@ -191,6 +300,9 @@ export function getToolPresentation(toolName) {
191
300
  if (name === "agent") {
192
301
  return { kind: "search", label: "Explore" };
193
302
  }
303
+ if (isAskUserQuestionTool(toolName)) {
304
+ return { kind: "tool", label: "提问" };
305
+ }
194
306
  return { kind: "tool", label: raw };
195
307
  }
196
308
  /** A tool call that failed on its own. A call interrupted by Stop carries
@@ -213,6 +325,9 @@ export function getToolSectionLabels(toolName) {
213
325
  if (name === "mcp" || name.startsWith("mcp_")) {
214
326
  return { argsLabel: "参数", resultLabel: "结果" };
215
327
  }
328
+ if (isAskUserQuestionTool(toolName)) {
329
+ return { argsLabel: "提问", resultLabel: "您的回答" };
330
+ }
216
331
  return { argsLabel: "入参", resultLabel: "结果" };
217
332
  }
218
333
  /**
@@ -25,6 +25,16 @@ export const OPERATOR_CHAT_BUILTIN_CUSTOM_TOOLS = Object.freeze([
25
25
  OPERATOR_CHAT_AGENT_TOOL_NAME,
26
26
  "terminal",
27
27
  "scm",
28
+ "browser_navigate",
29
+ "browser_search",
30
+ "browser_snapshot",
31
+ "browser_act",
32
+ "browser_tabs",
33
+ "browser_scroll",
34
+ "browser_press",
35
+ "browser_dialog",
36
+ "browser_wait",
37
+ "browser_screenshot",
28
38
  ]);
29
39
  /**
30
40
  * Actions the model MAY invoke. Derived from the registry but filtered by the
@@ -0,0 +1,13 @@
1
+ export function compareChatOrder(a, b) {
2
+ if (a.eventSeq !== undefined &&
3
+ b.eventSeq !== undefined &&
4
+ a.eventSeq !== b.eventSeq) {
5
+ return a.eventSeq - b.eventSeq;
6
+ }
7
+ if (a.createdAt !== b.createdAt)
8
+ return a.createdAt - b.createdAt;
9
+ // Same timestamp with mismatched sequence presence: keep the original
10
+ // insertion order (stable sort). Deciding here would re-order live SSE
11
+ // frames that happen to share a millisecond with a tool row.
12
+ return 0;
13
+ }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Turn grouping for Operator Chat inline process UX (pi-web ProcessDetails mind).
3
3
  */
4
+ import { compareChatOrder } from "./turn-order.js";
4
5
  import { getDisplayableAssistantBlocks, joinAssistantText, parseAssistantContent, splitFinalAssistantBlocks, } from "./assistant-content.js";
5
6
  import { dagProgressActivityForTool, semanticActivityForTool, } from "./semantic-activity.js";
6
7
  function emptyGroup(key) {
@@ -264,13 +265,14 @@ function fillProcessDetails(group) {
264
265
  items,
265
266
  });
266
267
  }
267
- const fullySequenced = nodes.every((node) => node.eventSeq !== undefined);
268
+ // Unified order (DSH rule): server seq is the single authoritative key,
269
+ // createdAt falls back, insertion order breaks remaining ties. No
270
+ // "fullySequenced" switch — a partially-sequenced group must not
271
+ // silently re-sort its peers by wall clock when most nodes carry seq.
268
272
  nodes.sort((a, b) => {
269
- if (fullySequenced && a.eventSeq !== b.eventSeq) {
270
- return a.eventSeq - b.eventSeq;
271
- }
272
- if (a.createdAt !== b.createdAt)
273
- return a.createdAt - b.createdAt;
273
+ const byOrder = compareChatOrder(a, b);
274
+ if (byOrder !== 0)
275
+ return byOrder;
274
276
  return a.insertionOrder - b.insertionOrder;
275
277
  });
276
278
  const processItems = nodes.flatMap((node) => node.items);
@@ -301,20 +303,7 @@ function fillProcessDetails(group) {
301
303
  export function buildTurnProcessGroups(input) {
302
304
  const visible = input.messages
303
305
  .filter((m) => m.role === "user" || m.role === "assistant")
304
- .sort((a, b) => {
305
- if (a.eventSeq !== undefined &&
306
- b.eventSeq !== undefined &&
307
- a.eventSeq !== b.eventSeq) {
308
- return a.eventSeq - b.eventSeq;
309
- }
310
- if (a.createdAt !== b.createdAt)
311
- return a.createdAt - b.createdAt;
312
- if (a.eventSeq !== undefined && b.eventSeq === undefined)
313
- return 1;
314
- if (a.eventSeq === undefined && b.eventSeq !== undefined)
315
- return -1;
316
- return 0;
317
- });
306
+ .sort(compareChatOrder);
318
307
  const groups = [];
319
308
  let current = null;
320
309
  const startGroup = (key) => {
@@ -326,6 +315,8 @@ export function buildTurnProcessGroups(input) {
326
315
  if (message.role === "user") {
327
316
  current = startGroup(`user-${message.id}`);
328
317
  current.user = message;
318
+ if (message.turnId)
319
+ current.turnId = message.turnId;
329
320
  continue;
330
321
  }
331
322
  if (message.kind === "compaction") {
@@ -333,12 +324,16 @@ export function buildTurnProcessGroups(input) {
333
324
  // prevents it from replacing the previous turn's assistant answer.
334
325
  current = startGroup(`compaction-${message.id}`);
335
326
  current.assistants.push(message);
327
+ if (message.turnId)
328
+ current.turnId = message.turnId;
336
329
  current = null;
337
330
  continue;
338
331
  }
339
332
  if (!current)
340
333
  current = startGroup(`assistant-${message.id}`);
341
334
  current.assistants.push(message);
335
+ if (current.turnId === undefined && message.turnId)
336
+ current.turnId = message.turnId;
342
337
  }
343
338
  const assigned = new Set();
344
339
  const assistantToGroup = new Map();
@@ -360,10 +355,10 @@ export function buildTurnProcessGroups(input) {
360
355
  if (assigned.has(tool.id))
361
356
  continue;
362
357
  if (tool.turnId) {
363
- const byTurn = groups.find((group) => group.user?.turnId === tool.turnId ||
358
+ const byTurn = groups.find((group) => group.turnId === tool.turnId ||
359
+ group.user?.turnId === tool.turnId ||
364
360
  group.assistants.some((assistant) => assistant.turnId === tool.turnId) ||
365
361
  group.tools.some((t) => t.turnId === tool.turnId) ||
366
- group.key === `turn-${tool.turnId}` ||
367
362
  (group.user && `restored-${group.user.id}` === tool.turnId));
368
363
  if (byTurn) {
369
364
  byTurn.tools.push(tool);
@@ -379,13 +374,25 @@ export function buildTurnProcessGroups(input) {
379
374
  }
380
375
  }
381
376
  }
377
+ // Time-window fallback for legacy rows with neither assistantMessageId nor
378
+ // turnId. The window ends at the NEXT group's first anchor (user OR first
379
+ // assistant) — using POSITIVE_INFINITY would swallow every later group's
380
+ // tools when the very next group has no user message (e.g. compaction or a
381
+ // stand-alone assistant row), dumping one turn's bash cards into another.
382
382
  for (const tool of input.toolCalls) {
383
383
  if (assigned.has(tool.id))
384
384
  continue;
385
385
  const windowGroup = groups.find((group, index) => {
386
386
  const start = group.user?.createdAt ?? group.assistants[0]?.createdAt ?? 0;
387
- const next = groups[index + 1];
388
- const end = next?.user?.createdAt ?? Number.POSITIVE_INFINITY;
387
+ let end = Number.POSITIVE_INFINITY;
388
+ for (let i = index + 1; i < groups.length; i += 1) {
389
+ const next = groups[i];
390
+ const nextAnchor = next?.user?.createdAt ?? next?.assistants[0]?.createdAt;
391
+ if (nextAnchor !== undefined) {
392
+ end = nextAnchor;
393
+ break;
394
+ }
395
+ }
389
396
  return tool.createdAt >= start && tool.createdAt < end;
390
397
  });
391
398
  // Time window only. Never dump unmatched tools onto the latest group:
@@ -396,12 +403,7 @@ export function buildTurnProcessGroups(input) {
396
403
  }
397
404
  }
398
405
  for (const group of groups) {
399
- group.tools.sort((a, b) => {
400
- if (a.eventSeq !== undefined && b.eventSeq !== undefined) {
401
- return a.eventSeq - b.eventSeq;
402
- }
403
- return a.createdAt - b.createdAt;
404
- });
406
+ group.tools.sort(compareChatOrder);
405
407
  group.live =
406
408
  input.streaming &&
407
409
  input.activeAssistantId !== null &&
@@ -67,29 +67,11 @@ export async function performConsoleUpdateAndInit(deps, channel = "latest") {
67
67
  return failed("INSTALL_VERIFICATION_FAILED", verified.reason, maintenance);
68
68
  }
69
69
  }
70
- const initResult = await deps.applySafeInit();
71
- maintenance.initialization = initResult.ok
72
- ? { status: "clean", pendingPaths: initResult.pendingPaths }
73
- : {
74
- status: "needs-reconcile",
75
- pendingPaths: initResult.pendingPaths,
76
- message: initResult.message,
77
- };
78
70
  if (!packageChanged) {
79
- return {
80
- state: initResult.ok ? "succeeded" : "needs-reconcile",
81
- maintenance,
82
- ...(initResult.ok
83
- ? {}
84
- : {
85
- errorCode: "INIT_NEEDS_RECONCILE",
86
- errorMessage: initResult.message,
87
- }),
88
- };
71
+ return { state: "succeeded", maintenance };
89
72
  }
90
73
  const handoff = await deps.requestHandoff({
91
74
  targetVersion: target.version,
92
- initResult,
93
75
  });
94
76
  if (!handoff.ok) {
95
77
  maintenance.handoff = {
@@ -28,14 +28,13 @@ export function createConsoleUpdateExecutor(input) {
28
28
  return performConsoleUpdateAndInit({
29
29
  currentVersion,
30
30
  preflight: async () => {
31
- const [activity, operations] = await Promise.all([
32
- readInitRuntimeActivity(input.repoRoot),
33
- input.operations.list(),
34
- ]);
35
- if (activity.workerProjectionError ||
31
+ const scopes = await input.workspaceActivity?.() ?? [input];
32
+ const activities = await Promise.all(scopes.map(scope => readInitRuntimeActivity(scope.repoRoot)));
33
+ const operations = (await Promise.all(scopes.map(scope => scope.operations.list()))).flat();
34
+ if (activities.some(activity => activity.workerProjectionError ||
36
35
  activity.activeDagRunIds.length > 0 ||
37
36
  activity.activeWorkerTasks > 0 ||
38
- activity.activeWorkerBatches > 0) {
37
+ activity.activeWorkerBatches > 0)) {
39
38
  return {
40
39
  ok: false,
41
40
  code: "ACTIVE_RUNTIME",
@@ -64,20 +63,6 @@ export function createConsoleUpdateExecutor(input) {
64
63
  canAutoInstall: () => npm.canAutoInstall(),
65
64
  installExact: (version, selectedChannel) => npm.installExact(version, selectedChannel),
66
65
  verifyInstalled: (version) => npm.verifyInstalled(version),
67
- applySafeInit: async () => {
68
- const result = await input.client.runExternal("loop-agent", ["init", "update", "--apply-safe", "--repo-root", input.repoRoot], {
69
- cwd: input.repoRoot,
70
- artifactName: "console-update-safe-init",
71
- timeoutMs: 120_000,
72
- });
73
- return result.ok
74
- ? { ok: true, pendingPaths: [] }
75
- : {
76
- ok: false,
77
- message: result.stderr || result.stdout || "safe initialization failed",
78
- pendingPaths: [],
79
- };
80
- },
81
66
  requestHandoff: async ({ targetVersion }) => input.requestHandoff(targetVersion),
82
67
  }, channel);
83
68
  };
@@ -1,3 +1,6 @@
1
+ import { prepareScheduledTask } from "./chat/scheduled-goal-request.js";
2
+ import { ScheduledGoalStore } from "../scheduler/scheduled-goal-store.js";
3
+ import { prepareScheduledGoalBooking, confirmScheduledGoalBooking, getScheduledGoalBooking } from "./chat/scheduled-goal-booking.js";
1
4
  import { access, readFile } from "node:fs/promises";
2
5
  import { buildOperatorCapabilitiesDocument, OFFICIAL_TAIL_ACTION_IDS, } from "../../shared/operator/capabilities.js";
3
6
  import { DAG_RERUN_PROMPT_OVERRIDE_ENV, isDagRerunPromptOverrideEnabled, MAX_DAG_RERUN_PROMPT_OVERRIDE_CHARS, } from "../../shared/dag-prompt-override.js";
@@ -772,7 +775,7 @@ async function acceptConsoleUpdateAndInit(ctx, clientRequestId, channel) {
772
775
  at: new Date().toISOString(),
773
776
  kind: "state",
774
777
  state: "running",
775
- message: `checking runtime, npm ${channel}, and safe initialization`,
778
+ message: `checking runtime and npm ${channel}`,
776
779
  });
777
780
  try {
778
781
  const result = await ctx.consoleUpdateExecutor(channel);
@@ -849,6 +852,18 @@ export async function dispatchOperatorAction(ctx, req) {
849
852
  };
850
853
  }
851
854
  const p = paramsOf(req);
855
+ if (action === "workspaceInitialize") {
856
+ if (req.source !== "human-ui")
857
+ return { kind: "error", status: 403, body: operatorFailed({ command: action, outcome: "blocked", code: "HUMAN_UI_REQUIRED", message: "工作区初始化需要从界面显式发起" }) };
858
+ if (!req.clientRequestId?.trim() || Object.keys(p).some(key => key !== "model") || (p.model !== undefined && (typeof p.model !== "string" || p.model.length > 300)))
859
+ return invalid(action, "只接受 clientRequestId 和可选模型引用");
860
+ if (!ctx.workspaceInitialization)
861
+ return invalid(action, "工作区初始化服务不可用");
862
+ const created = await ctx.workspaceInitialization.start(req.clientRequestId, p.model);
863
+ if (created.kind === "conflict")
864
+ return { kind: "error", status: 409, body: operatorFailed({ command: action, outcome: "conflict", code: "REQUEST_ID_REUSE_CONFLICT", message: created.message }) };
865
+ return { kind: "accepted", status: 202, body: { operationId: created.operation.operationId, state: created.operation.state, clientRequestId: created.operation.clientRequestId, action } };
866
+ }
852
867
  if (action === "consoleUpdateAndInit") {
853
868
  if (req.source !== "human-ui") {
854
869
  return {
@@ -874,6 +889,52 @@ export async function dispatchOperatorAction(ctx, req) {
874
889
  }
875
890
  return acceptConsoleUpdateAndInit(ctx, req.clientRequestId, channel);
876
891
  }
892
+ if (["prepareScheduledTask", "prepareScheduledGoal", "confirmScheduledGoal", "scheduledGoalGet", "scheduledGoalList", "cancelScheduledGoal"].includes(action)) {
893
+ try {
894
+ const goals = new ScheduledGoalStore(ctx.repoRoot);
895
+ const { ChatSessionStore } = await import("./chat/session-store.js");
896
+ const deps = { goals, client: ctx.client, sessions: new ChatSessionStore(ctx.appData) };
897
+ let result;
898
+ if (action === "prepareScheduledTask") {
899
+ if (!ctx.chatSessionRef || Object.keys(p).some((key) => key !== "request"))
900
+ return invalid(action, "requires a host-bound Chat session and request only");
901
+ result = await prepareScheduledTask(p.request, { repoRoot: ctx.repoRoot, preparationRoot: nodePath.join(ctx.appData.staged, "scheduled-tasks"),
902
+ sessionId: ctx.chatSessionRef, client: ctx.client, sessions: deps.sessions });
903
+ }
904
+ else if (action === "prepareScheduledGoal") {
905
+ if (Object.keys(p).some((key) => key !== "booking"))
906
+ return invalid(action, "only booking may be provided");
907
+ result = await prepareScheduledGoalBooking(p.booking, deps);
908
+ }
909
+ else if (action === "confirmScheduledGoal") {
910
+ result = await confirmScheduledGoalBooking(p, deps, { source: req.source, requestId: req.clientRequestId });
911
+ }
912
+ else if (action === "cancelScheduledGoal") {
913
+ if (req.source !== "human-ui" || !req.clientRequestId?.trim())
914
+ return invalid(action, "human cancellation required");
915
+ if (Object.keys(p).some((key) => !["goalId", "stateVersion", "reason"].includes(key)) ||
916
+ typeof p.goalId !== "string" || !Number.isSafeInteger(p.stateVersion) || typeof p.reason !== "string")
917
+ return invalid(action, "goalId, stateVersion and reason required");
918
+ await goals.cancel(p.goalId, p.stateVersion, req.clientRequestId, p.reason);
919
+ result = await getScheduledGoalBooking(p.goalId, goals);
920
+ }
921
+ else if (action === "scheduledGoalGet") {
922
+ if (Object.keys(p).some((key) => key !== "goalId") || typeof p.goalId !== "string")
923
+ return invalid(action, "goalId required");
924
+ result = await getScheduledGoalBooking(p.goalId, goals);
925
+ }
926
+ else {
927
+ if (Object.keys(p).some((key) => key !== "chatSessionRef") || (p.chatSessionRef !== undefined && typeof p.chatSessionRef !== "string"))
928
+ return invalid(action, "only optional chatSessionRef may be provided");
929
+ const records = (await goals.list()).filter((goal) => !p.chatSessionRef || goal.frozen.chatSessionRef === p.chatSessionRef);
930
+ result = await Promise.all(records.map((goal) => getScheduledGoalBooking(goal.goalId, goals)));
931
+ }
932
+ return { kind: "sync", status: 200, body: operatorSucceeded(action, result) };
933
+ }
934
+ catch (error) {
935
+ return invalid(action, error instanceof Error ? error.message : String(error));
936
+ }
937
+ }
877
938
  switch (action) {
878
939
  case "operatorCapabilities": {
879
940
  const doc = buildOperatorCapabilitiesDocument();
@@ -1065,6 +1126,7 @@ export async function dispatchOperatorAction(ctx, req) {
1065
1126
  const args = ["dag", "doctor"];
1066
1127
  if (runId)
1067
1128
  args.push("--run-id", runId);
1129
+ args.push("--json");
1068
1130
  const body = await runReadCli(ctx, args, "dag doctor");
1069
1131
  return { kind: "sync", status: body.ok ? 200 : 400, body };
1070
1132
  }
@@ -119,6 +119,33 @@ export function parseEngineeringBoundaryFromParams(p) {
119
119
  };
120
120
  const allowedPaths = asList(p.allowedPaths ?? p.allowedPath);
121
121
  const forbiddenPaths = asList(p.forbiddenPaths ?? p.forbiddenPath);
122
+ const capabilityBoundaryRaw = p.capabilityBoundary;
123
+ const capabilityBoundary = capabilityBoundaryRaw && typeof capabilityBoundaryRaw === "object" && !Array.isArray(capabilityBoundaryRaw)
124
+ ? {
125
+ allowed: Array.isArray(capabilityBoundaryRaw.allowed)
126
+ ? capabilityBoundaryRaw.allowed.filter((x) => typeof x === "string" && x.trim().length > 0)
127
+ : undefined,
128
+ forbidden: Array.isArray(capabilityBoundaryRaw.forbidden)
129
+ ? capabilityBoundaryRaw.forbidden.filter((x) => typeof x === "string" && x.trim().length > 0)
130
+ : undefined,
131
+ }
132
+ : undefined;
133
+ const interactionIds = asList(p.interactionIds ?? p.interactionId);
134
+ const requirementOwnership = Array.isArray(p.requirementOwnership)
135
+ ? p.requirementOwnership
136
+ .map((entry) => ({
137
+ requirementIds: Array.isArray(entry.requirementIds)
138
+ ? entry.requirementIds.filter((id) => typeof id === "string" && id.trim().length > 0)
139
+ : [],
140
+ implementationFiles: Array.isArray(entry.implementationFiles)
141
+ ? entry.implementationFiles.filter((f) => typeof f === "string" && f.trim().length > 0)
142
+ : undefined,
143
+ verificationFiles: Array.isArray(entry.verificationFiles)
144
+ ? entry.verificationFiles.filter((f) => typeof f === "string" && f.trim().length > 0)
145
+ : undefined,
146
+ }))
147
+ .filter((entry) => entry.requirementIds.length > 0)
148
+ : undefined;
122
149
  const verifyRaw = p.verifyCommands ?? p.verify;
123
150
  const verifyCommands = [];
124
151
  if (Array.isArray(verifyRaw)) {
@@ -175,6 +202,11 @@ export function parseEngineeringBoundaryFromParams(p) {
175
202
  ...(allowedPaths.length > 0 ? { allowedPaths } : {}),
176
203
  ...(forbiddenPaths.length > 0 ? { forbiddenPaths } : {}),
177
204
  ...(verifyCommands.length > 0 ? { verifyCommands } : {}),
205
+ ...(requirementOwnership?.length ? { requirementOwnership } : {}),
206
+ ...(capabilityBoundary && ((capabilityBoundary.allowed?.length ?? 0) + (capabilityBoundary.forbidden?.length ?? 0)) > 0
207
+ ? { capabilityBoundary }
208
+ : {}),
209
+ ...(interactionIds.length > 0 ? { interactionIds } : {}),
178
210
  };
179
211
  }
180
212
  export function appendEngineeringCliArgs(args, boundary) {
@@ -187,17 +219,38 @@ export function appendEngineeringCliArgs(args, boundary) {
187
219
  for (const cmd of boundary.verifyCommands ?? []) {
188
220
  args.push("--verify", `${cmd.label}:${cmd.command}`);
189
221
  }
222
+ if (boundary.requirementOwnership?.length) {
223
+ args.push("--requirement-ownership", JSON.stringify(boundary.requirementOwnership));
224
+ }
225
+ if (boundary.capabilityBoundary) {
226
+ args.push("--capability-boundary", JSON.stringify(boundary.capabilityBoundary));
227
+ }
228
+ for (const id of boundary.interactionIds ?? []) {
229
+ args.push("--interaction-ids", id);
230
+ }
190
231
  }
191
232
  export function applyEngineeringBoundaryToDraft(draft, boundary) {
192
233
  const allowedPaths = filterPlaceholderPaths(boundary.allowedPaths);
193
234
  const forbiddenPaths = filterPlaceholderPaths(boundary.forbiddenPaths);
194
235
  if (allowedPaths.length === 0 &&
195
236
  forbiddenPaths.length === 0 &&
196
- !(boundary.verifyCommands?.length)) {
237
+ !(boundary.verifyCommands?.length) &&
238
+ !(boundary.requirementOwnership?.length) &&
239
+ !boundary.capabilityBoundary &&
240
+ !(boundary.interactionIds?.length)) {
197
241
  return draft;
198
242
  }
199
243
  return {
200
244
  ...draft,
245
+ ...(boundary.requirementOwnership?.length
246
+ ? { requirementOwnership: boundary.requirementOwnership }
247
+ : {}),
248
+ ...(boundary.capabilityBoundary
249
+ ? { capabilityBoundary: boundary.capabilityBoundary }
250
+ : {}),
251
+ ...(boundary.interactionIds?.length
252
+ ? { interactionIds: boundary.interactionIds }
253
+ : {}),
201
254
  constraints: {
202
255
  invariants: draft.constraints?.invariants ?? [],
203
256
  allowedPaths: allowedPaths.length > 0
@@ -1130,6 +1130,14 @@ export async function handleConsoleOperatorRequest(req, res, ctx) {
1130
1130
  !pathname.startsWith("/api/session/")) {
1131
1131
  return false;
1132
1132
  }
1133
+ if (method === "GET" && pathname === "/api/operator/v1/workspace-initialization") {
1134
+ const service = ctx.operator?.actionContext.workspaceInitialization;
1135
+ if (!service)
1136
+ sendJson(res, 503, { ok: false, error: { code: "UNAVAILABLE", message: "工作区配置服务不可用" } });
1137
+ else
1138
+ sendJson(res, 200, { ok: true, initialization: await service.check(url.searchParams.get("refresh") === "1") });
1139
+ return true;
1140
+ }
1133
1141
  if (method === "GET" && pathname === "/api/operator/v1/readiness") {
1134
1142
  await handleReadiness(req, res, ctx);
1135
1143
  return true;
@@ -0,0 +1,98 @@
1
+ import { dispatchScheduledGoalOperation } from "./scheduled-goal-operation-adapter.js";
2
+ import { assessScheduledGoal, continueScheduledGoal } from "../scheduler/scheduled-goal-supervisor.js";
3
+ import { evaluateScheduledGoalWake } from "../scheduler/scheduled-goal-time.js";
4
+ /** Background operation ownership, independent of a browser or an interactive Chat turn.
5
+ * Every wake rebuilds decisions from the ledger; timers carry no authorization.
6
+ * No stale takeover: reserved work is observed, never re-issued.
7
+ */
8
+ export class ScheduledGoalHost {
9
+ deps;
10
+ timer;
11
+ active;
12
+ stopped = true;
13
+ constructor(deps) {
14
+ this.deps = deps;
15
+ if (deps.intervalMs !== undefined && (!Number.isFinite(deps.intervalMs) || deps.intervalMs < 100))
16
+ throw new Error("invalid scheduled goal wake interval");
17
+ }
18
+ start() {
19
+ if (!this.stopped)
20
+ return;
21
+ this.stopped = false;
22
+ this.wake();
23
+ }
24
+ /** Nonblocking. The host retains the operation promise until it settles. */
25
+ wake() {
26
+ if (this.stopped || this.active)
27
+ return;
28
+ if (this.timer) {
29
+ clearTimeout(this.timer);
30
+ this.timer = undefined;
31
+ }
32
+ this.active = this.tick().catch((error) => this.deps.onError?.(undefined, error)).finally(() => {
33
+ this.active = undefined;
34
+ if (!this.stopped) {
35
+ this.timer = setTimeout(() => { this.timer = undefined; this.wake(); }, this.deps.intervalMs ?? 5000);
36
+ this.timer.unref?.();
37
+ }
38
+ });
39
+ }
40
+ /** Stop future admissions. Existing children keep their operation ownership. */
41
+ stop() {
42
+ this.stopped = true;
43
+ if (this.timer)
44
+ clearTimeout(this.timer);
45
+ this.timer = undefined;
46
+ }
47
+ async waitForIdle() { await this.active; }
48
+ async tick() {
49
+ for (const snapshot of await this.deps.goals.list()) {
50
+ if (this.stopped)
51
+ return;
52
+ try {
53
+ await this.advance(snapshot.goalId);
54
+ }
55
+ catch (error) {
56
+ this.deps.onError?.(snapshot.goalId, error);
57
+ }
58
+ }
59
+ }
60
+ async advance(goalId) {
61
+ const { goals } = this.deps;
62
+ let goal = await goals.get(goalId);
63
+ if (!goal.approval)
64
+ return;
65
+ const intent = goal.intents.at(-1);
66
+ if (intent && ["dispatching", "associated", "unknown"].includes(intent.state)) {
67
+ await dispatchScheduledGoalOperation(goalId, this.deps);
68
+ goal = await goals.get(goalId);
69
+ }
70
+ if (this.stopped)
71
+ return;
72
+ if (["cancelled", "blocked", "budget_exhausted", "unknown"].includes(goal.state)) {
73
+ await this.deps.deliver?.(goal);
74
+ return;
75
+ }
76
+ goal = await goals.refreshAcceptance(goalId);
77
+ goal = await goals.settleIdleGoal(goalId, this.deps.now?.());
78
+ if (["accepted", "cancelled", "blocked", "budget_exhausted", "unknown"].includes(goal.state)) {
79
+ await this.deps.deliver?.(goal);
80
+ return;
81
+ }
82
+ if (goal.assessmentAttempt || goal.supervisions.some((entry) => ["requested", "preparing", "unknown"].includes(entry.state)))
83
+ return;
84
+ const current = goal.intents.at(-1);
85
+ if (!current || current.state === "prepared") {
86
+ if (evaluateScheduledGoalWake(goal.frozen.time, this.deps.now?.() ?? new Date()).kind === "due")
87
+ await dispatchScheduledGoalOperation(goalId, this.deps);
88
+ return;
89
+ }
90
+ if (current.state !== "terminal")
91
+ return;
92
+ if (goal.acceptanceState === "failed") {
93
+ await continueScheduledGoal(goalId, goals, this.deps.client);
94
+ return;
95
+ }
96
+ await assessScheduledGoal(goalId, goals, { now: this.deps.now });
97
+ }
98
+ }