@tea-agent/loop-agent 0.41.1 → 0.42.0-next.10

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 (302) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +397 -5
  3. package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
  4. package/dist/adapters/context-transfer/pi-session.js +61 -0
  5. package/dist/application/dag/generate-task-dag.js +75 -19
  6. package/dist/application/dag/run-dag.js +41 -0
  7. package/dist/application/task-lifecycle/advance.js +24 -5
  8. package/dist/application/task-lifecycle/observe.js +171 -17
  9. package/dist/application/task-lifecycle/plan-transitions.js +42 -7
  10. package/dist/application/task-lifecycle/recommendations.js +13 -2
  11. package/dist/build-stamp.json +3 -3
  12. package/dist/cli/command-definitions.js +19 -0
  13. package/dist/cli/program.js +29 -2
  14. package/dist/commands/client-recovery.js +3 -0
  15. package/dist/commands/dag-artifact.js +284 -0
  16. package/dist/commands/dag-context.js +184 -0
  17. package/dist/commands/dag-follow-up.js +138 -0
  18. package/dist/commands/dag-rerun-task.js +2 -0
  19. package/dist/commands/dag-rerun.js +206 -1
  20. package/dist/commands/init.js +27 -1
  21. package/dist/commands/task-advance.js +19 -0
  22. package/dist/executors/dag-pi-executor.js +4013 -86
  23. package/dist/executors/pi-executor.js +15 -4
  24. package/dist/executors/pi-read-budget-policy.js +239 -0
  25. package/dist/executors/pi-sdk-executor.js +3 -0
  26. package/dist/executors/shell-executor.js +978 -188
  27. package/dist/executors/shell-write-guard.js +7 -0
  28. package/dist/{worker → infrastructure}/console/app-data.js +4 -0
  29. package/dist/infrastructure/console/artifact-revision-store.js +430 -0
  30. package/dist/infrastructure/console/context-export-store.js +160 -0
  31. package/dist/infrastructure/console/dir-lock.js +132 -0
  32. package/dist/infrastructure/console/operation-store.js +417 -0
  33. package/dist/infrastructure/harness/artifact-store.js +10 -1
  34. package/dist/infrastructure/harness/atomic-write.js +12 -2
  35. package/dist/shared/context-transfer/artifact-revision.js +172 -0
  36. package/dist/shared/context-transfer.js +418 -0
  37. package/dist/shared/dag-failure-category.js +12 -0
  38. package/dist/shared/openspec-spec.js +70 -4
  39. package/dist/shared/operator/capabilities.js +21 -0
  40. package/dist/shared/operator/safe-run-summary.js +1 -0
  41. package/dist/shared/path-safety.js +93 -0
  42. package/dist/shared/preview.js +28 -4
  43. package/dist/task/config-types.js +107 -6
  44. package/dist/task/contract/adopt.js +4 -0
  45. package/dist/task/contract/import-revision.js +4 -0
  46. package/dist/task/contract/project.js +3 -0
  47. package/dist/task/contract/schema.js +2 -1
  48. package/dist/task/frontend-project-capability.js +203 -20
  49. package/dist/task/runtime.js +5 -2
  50. package/dist/task/source-prepare/build-draft.js +3 -3
  51. package/dist/task/source-prepare/fragment-inventory.js +64 -25
  52. package/dist/task/source-prepare/prepare.js +126 -21
  53. package/dist/task/source-prepare/semantic-intake.js +6 -2
  54. package/dist/task/source-references.js +22 -1
  55. package/dist/task/task-demand-routing.js +3 -0
  56. package/dist/worker/console/chat/chat-event-store.js +2 -2
  57. package/dist/worker/console/chat/model-resolver.js +7 -4
  58. package/dist/worker/console/chat/pi-runtime.js +86 -5
  59. package/dist/worker/console/chat/resource-preferences-store.js +1 -1
  60. package/dist/worker/console/chat/routes.js +136 -53
  61. package/dist/worker/console/chat/sdd-data-alignment.js +222 -0
  62. package/dist/worker/console/chat/session-catalog.js +2 -0
  63. package/dist/worker/console/chat/session-store.js +43 -1
  64. package/dist/worker/console/chat/shortcuts.js +18 -2
  65. package/dist/worker/console/chat/user-questions.js +1 -1
  66. package/dist/worker/console/console-update-runtime.js +1 -1
  67. package/dist/worker/console/context-transfer-diagnostics.js +198 -0
  68. package/dist/worker/console/dag-confirmation.js +1 -1
  69. package/dist/worker/console/dag-execution-receipt.js +1 -1
  70. package/dist/worker/console/doctor.js +1 -1
  71. package/dist/worker/console/draft-store.js +1 -1
  72. package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
  73. package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
  74. package/dist/worker/console/human-gate-token.js +1 -1
  75. package/dist/worker/console/index.js +6 -3
  76. package/dist/worker/console/interview/assessment.js +1 -1
  77. package/dist/worker/console/interview/session.js +1 -1
  78. package/dist/worker/console/operation-runner.js +45 -4
  79. package/dist/worker/console/operation-sse.js +1 -1
  80. package/dist/worker/console/operation-wait.js +1 -1
  81. package/dist/worker/console/operator-actions.js +273 -10
  82. package/dist/worker/console/operator-surface-health.js +1 -1
  83. package/dist/worker/console/operator-user-error.js +169 -0
  84. package/dist/worker/console/pi-plugins.js +57 -0
  85. package/dist/worker/console/pi-readiness.js +3 -2
  86. package/dist/worker/console/routes.js +637 -8
  87. package/dist/worker/console/security.js +35 -0
  88. package/dist/worker/console/server.js +29 -4
  89. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-D7oU983t.js → abnfDiagram-N423BO3Z-CcS17TBr.js} +1 -1
  90. package/dist/worker/console/static/assets/{arc-BrKSRdFS.js → arc-COptKq2S.js} +1 -1
  91. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CiXV7L56.js → architectureDiagram-T3A2C74G-h4LKMHjP.js} +1 -1
  92. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-D_Hs_gIG.js → blockDiagram-VBNYF7ZC-COA1MOH0.js} +1 -1
  93. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DbM-Lqqh.js → c4Diagram-5PPSVZJV-DJUf0QPm.js} +1 -1
  94. package/dist/worker/console/static/assets/channel-DdBCaOJ6.js +1 -0
  95. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D1M8uqJw.js → chunk-2GRJ4B5K-mtWfKrUX.js} +1 -1
  96. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-4D3RFHT6.js → chunk-2Q5K7J3B-B8pXsxDQ.js} +1 -1
  97. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CdwpY_n9.js → chunk-5RXB4S5H-ipKzByl1.js} +1 -1
  98. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BLeLZ9PG.js → chunk-5VM5RSS4-DYi3Ald_.js} +1 -1
  99. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-CeRNEMvc.js → chunk-6Q2QTUOP-DQtGYoty.js} +1 -1
  100. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CjkHP8Vy.js → chunk-GF5L2VYU-BU0qS2YV.js} +1 -1
  101. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-B54ZUQY-.js → chunk-JWPE2WC7-DVH9dIGE.js} +1 -1
  102. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-4bKaMVmx.js → chunk-KBJHAD2P-Dt60SmgM.js} +1 -1
  103. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-Cyjfznu_.js → chunk-RYQCIY6F-BoaUGuEY.js} +1 -1
  104. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CDN-zGFV.js → chunk-XXDRQBXY-_WHFDTp2.js} +1 -1
  105. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DZFra1GO.js +1 -0
  106. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DZFra1GO.js +1 -0
  107. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-HxJmz411.js → cose-bilkent-JH36ORCC-BxkbTIRd.js} +1 -1
  108. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DtSt5K2w.js → cynefin-VYW2F7L2-Bf2UVnoG.js} +1 -1
  109. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-VtkSpRmG.js → cynefinDiagram-MW4NZA55-Bo23q1J_.js} +1 -1
  110. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bj4DT4du.js → dagre-VZM6K2ZE-Dd_UU49i.js} +1 -1
  111. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Bpuk6BmP.js → diagram-7IWD3JNH-ebUa1a9y.js} +1 -1
  112. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BHYGv_yO.js → diagram-B4RE2ZJO-BucthU8r.js} +1 -1
  113. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DEx3WwnT.js → diagram-LBJQPF4R-BgqNpAkm.js} +1 -1
  114. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Cj0dS5e8.js → diagram-Q27KOJAE-DC4q5NGa.js} +1 -1
  115. package/dist/worker/console/static/assets/{diagram-UB23O5K3-B97h8l21.js → diagram-UB23O5K3-CBqCaMeb.js} +1 -1
  116. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DMSFAhBd.js → ebnfDiagram-BXEA7PRR-CGtnbQ3-.js} +1 -1
  117. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DhVOCbt6.js → erDiagram-JOGREHBK-CG3LUao5.js} +1 -1
  118. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-GlakITWL.js → flowDiagram-UKHOOZJN-D2ZVgoFS.js} +1 -1
  119. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-VxPpu9eQ.js → ganttDiagram-PKOTCBZU-DoYFZiKt.js} +1 -1
  120. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bs_e5DRT.js → gitGraphDiagram-DS77QQ5N-CKoP1s6j.js} +1 -1
  121. package/dist/worker/console/static/assets/index-CAZ2fC_X.css +1 -0
  122. package/dist/worker/console/static/assets/index-vbTcFnFs.js +449 -0
  123. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BXYuU4Uw.js → infoDiagram-6WML65LV-Duofv8p2.js} +1 -1
  124. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-C34erCbU.js → ishikawaDiagram-WSZJBQD7-D2nlCkA1.js} +1 -1
  125. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DZCAiOU3.js → journeyDiagram-NVQOT4AX-Dd4IHdus.js} +1 -1
  126. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-C9gaTdUm.js → kanban-definition-27J2QSJJ-Bi20AOdt.js} +1 -1
  127. package/dist/worker/console/static/assets/{linear-CvI0Z17U.js → linear-D59YJ9kB.js} +1 -1
  128. package/dist/worker/console/static/assets/{mermaid.core-DMMSIx7j.js → mermaid.core-Bl13LpNa.js} +5 -5
  129. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BKKP0EO6.js → mindmap-definition-FAOFIHXS-C6DLP5eY.js} +1 -1
  130. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-C3LiFbl5.js → pegDiagram-VL7TDLO6-BjO4cy64.js} +1 -1
  131. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-Bb3Om2Dz.js → pieDiagram-7S7Q4E2Y-D7i2qZTG.js} +1 -1
  132. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DNthMjzc.js → quadrantDiagram-CIZ2JOQS-Dx8o_h0y.js} +1 -1
  133. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-miFsr4Tv.js → railroadDiagram-AXF67PYL-B55orI_t.js} +1 -1
  134. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-yXUncmn6.js → requirementDiagram-LRYGKXZP-zWaehp6f.js} +1 -1
  135. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-DLeWKLak.js → sankeyDiagram-W5VNT64P-CcA-pjvD.js} +1 -1
  136. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CI3he1R3.js → sequenceDiagram-SI44F4Z6-BOFbzNHI.js} +1 -1
  137. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B7jfDkS5.js → sizeCapture-X5ZJPWSS-BjAejah1.js} +1 -1
  138. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BmrZod7J.js → stateDiagram-OKZ733FA-DXYwxJwZ.js} +1 -1
  139. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Clg3V9t1.js +1 -0
  140. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CDOlHVSY.js → swimlanes-SLNWSIFB-OYiOch8n.js} +2 -2
  141. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DX1dxAAW.js +8 -0
  142. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cz-XX1gy.js → timeline-definition-Z64GVDOM-cZfH3nmU.js} +1 -1
  143. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-caV4cI8C.js → vennDiagram-T6HMQDX7-BDE7b3E1.js} +1 -1
  144. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-dImPlnIO.js → wardleyDiagram-T6FBY63Y-kyyGy9WJ.js} +1 -1
  145. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CtRYVJcn.js → xychartDiagram-ELKLHX3M-CJj6VTog.js} +1 -1
  146. package/dist/worker/console/static/index.html +7 -2
  147. package/dist/worker/console/static-src/active-run-badge.js +17 -0
  148. package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
  149. package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
  150. package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
  151. package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +10 -0
  152. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +258 -23
  153. package/dist/worker/console/static-src/operator-chat/useChatThread.js +112 -4
  154. package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -4
  155. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +29 -6
  156. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +8 -2
  157. package/dist/worker/console/static-src/shell/console-update-reload.js +52 -0
  158. package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
  159. package/dist/worker/console/workspace-context.js +142 -2
  160. package/dist/worker/console/workspace-registry.js +2 -2
  161. package/dist/worker/continuation/worker-continuation.js +278 -0
  162. package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
  163. package/dist/worker/observe/health.js +1 -1
  164. package/dist/worker/observe/node-transparency.js +572 -0
  165. package/dist/worker/observe/routes.js +55 -1
  166. package/dist/worker/observe/static/api.js +69 -5
  167. package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
  168. package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
  169. package/dist/worker/observe/static/dag-helpers.js +4 -2
  170. package/dist/worker/observe/static/dag-node-purpose.js +5 -0
  171. package/dist/worker/observe/static/inspect-workspace.js +23 -0
  172. package/dist/worker/observe/static/kpi.js +2 -2
  173. package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
  174. package/dist/worker/observe/static/operator-chrome.js +37 -23
  175. package/dist/worker/observe/static/relations.js +2 -2
  176. package/dist/worker/observe/static/router.d.ts +12 -1
  177. package/dist/worker/observe/static/router.js +48 -6
  178. package/dist/worker/observe/static/run-processing.js +4 -2
  179. package/dist/worker/observe/static/shell-chrome.js +2 -2
  180. package/dist/worker/observe/static/state.js +8 -1
  181. package/dist/worker/observe/static/styles.css +1092 -86
  182. package/dist/worker/observe/static/views/dag-inspector.js +1228 -64
  183. package/dist/worker/observe/static/views/dag.js +10 -2
  184. package/dist/worker/observe/static/views/dags.js +3 -1
  185. package/dist/worker/observe/static/views/dashboard.js +14 -6
  186. package/dist/worker/observe/static/views/pool.js +7 -2
  187. package/dist/worker/observe/static/views/run.js +12 -3
  188. package/dist/worker/observe/static/views/session-timeline.js +142 -18
  189. package/dist/worker/observe/static/views/task.js +7 -2
  190. package/dist/worker/outcomes/adapters.js +19 -0
  191. package/dist/worker/outcomes/types.js +1 -0
  192. package/dist/worker/pool/attempt-lease.js +97 -66
  193. package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
  194. package/dist/worker/pool/reconcile.js +46 -1
  195. package/dist/worker/pool/run-store.js +2 -0
  196. package/dist/worker/pool/state-projection.js +2 -0
  197. package/dist/worker/task-spec/workflow-routing.js +8 -3
  198. package/dist/workflows/dag/artifact-bindings.js +149 -0
  199. package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
  200. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +215 -28
  201. package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
  202. package/dist/workflows/dag/backend-test-plan-protocol.js +106 -0
  203. package/dist/workflows/dag/backend-test-scenario-param.js +172 -25
  204. package/dist/workflows/dag/backend-test-scenario-partitions.js +62 -1
  205. package/dist/workflows/dag/backend-test-writer-completeness.js +104 -12
  206. package/dist/workflows/dag/budget-enforcement.js +10 -0
  207. package/dist/workflows/dag/context-receipt.js +305 -0
  208. package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
  209. package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
  210. package/dist/workflows/dag/context-transfer/renderers.js +93 -0
  211. package/dist/workflows/dag/contract-validator-registrations.js +1 -2
  212. package/dist/workflows/dag/dag-retry-schema.js +138 -0
  213. package/dist/workflows/dag/frontend-closeout.js +221 -0
  214. package/dist/workflows/dag/frontend-design-policy.js +400 -0
  215. package/dist/workflows/dag/frontend-human-decision.js +182 -0
  216. package/dist/workflows/dag/frontend-implementation-contract.js +1237 -192
  217. package/dist/workflows/dag/frontend-plan-render.js +2 -1
  218. package/dist/workflows/dag/frontend-prewrite-gate.js +256 -350
  219. package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
  220. package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
  221. package/dist/workflows/dag/frontend-recovery-controller.js +226 -0
  222. package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
  223. package/dist/workflows/dag/frontend-recovery-plan.js +21 -10
  224. package/dist/workflows/dag/frontend-recovery-run.js +166 -34
  225. package/dist/workflows/dag/frontend-repair.js +1 -432
  226. package/dist/workflows/dag/frontend-review-context.js +261 -15
  227. package/dist/workflows/dag/frontend-review-findings.js +270 -0
  228. package/dist/workflows/dag/frontend-shadow-dual-write.js +941 -0
  229. package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
  230. package/dist/workflows/dag/frontend-shape-facts.js +419 -0
  231. package/dist/workflows/dag/frontend-shape.js +435 -0
  232. package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
  233. package/dist/workflows/dag/frontend-split-application-service.js +203 -0
  234. package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
  235. package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
  236. package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
  237. package/dist/workflows/dag/frontend-verification-trace.js +252 -25
  238. package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
  239. package/dist/workflows/dag/frontend-writer-admission.js +319 -0
  240. package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
  241. package/dist/workflows/dag/frontend-writer-status.js +256 -0
  242. package/dist/workflows/dag/init-hybrid.js +1174 -562
  243. package/dist/workflows/dag/interrupt-request.js +7 -0
  244. package/dist/workflows/dag/node-execution.js +940 -31
  245. package/dist/workflows/dag/path-safety.js +1 -0
  246. package/dist/workflows/dag/prompt.js +99 -7
  247. package/dist/workflows/dag/recovery-lease.js +170 -0
  248. package/dist/workflows/dag/report.js +37 -1
  249. package/dist/workflows/dag/rerun-feedback.js +315 -1
  250. package/dist/workflows/dag/rerun-plan.js +568 -3
  251. package/dist/workflows/dag/rerun-run.js +642 -18
  252. package/dist/workflows/dag/rerun-task.js +251 -13
  253. package/dist/workflows/dag/retry-policy.js +219 -104
  254. package/dist/workflows/dag/runner.js +596 -121
  255. package/dist/workflows/dag/scheduler.js +133 -20
  256. package/dist/workflows/dag/skill-snapshot.js +17 -0
  257. package/dist/workflows/dag/types.js +426 -22
  258. package/dist/workflows/dag/validate.js +28 -12
  259. package/docs/architecture/runtime-boundaries.md +6 -6
  260. package/docs/architecture/worker-and-feature.md +1 -1
  261. package/docs/examples/README.md +5 -0
  262. package/docs/init-surface.manifest.json +30 -12
  263. package/docs/skills/vetted-skill-registry.md +4 -2
  264. package/docs/templates/README.md +2 -0
  265. package/docs/templates/agent-dag-report.schema.json +8 -2
  266. package/docs/templates/agent-dag.schema.json +33 -1
  267. package/docs/templates/backend-test-dag.json +23 -20
  268. package/docs/templates/frontend-implementation-contract.schema.json +4 -1
  269. package/docs/templates/frontend-implementation-dag.json +89 -0
  270. package/docs/templates/product-line/task.yaml +1 -1
  271. package/docs/templates/spec-registry.schema.json +45 -0
  272. package/harness.json +1 -1
  273. package/package.json +6 -3
  274. package/skills/frontend-bounded-implement/SKILL.md +15 -14
  275. package/skills/frontend-bounded-implement/references/code-standards.md +19 -0
  276. package/skills/frontend-contract/SKILL.md +23 -0
  277. package/skills/frontend-contract/references/contract-protocol.md +34 -0
  278. package/skills/frontend-design-review/SKILL.md +22 -41
  279. package/skills/frontend-plan/SKILL.md +26 -0
  280. package/skills/frontend-plan/references/decision-contract.md +37 -0
  281. package/skills/frontend-plan/references/design-decisions.md +17 -0
  282. package/skills/frontend-review/SKILL.md +20 -15
  283. package/skills/frontend-review/references/review-findings.md +6 -7
  284. package/skills/frontend-scout/SKILL.md +25 -0
  285. package/skills/frontend-scout/references/design-evidence.md +16 -0
  286. package/skills/frontend-scout/references/scout-evidence.md +23 -0
  287. package/skills/frontend-verification/SKILL.md +1 -1
  288. package/skills/loop-agent/references/command-reference.md +7 -0
  289. package/skills/loop-agent/references/hybrid-dag.md +2 -2
  290. package/dist/worker/console/operation-store.js +0 -169
  291. package/dist/worker/console/static/assets/channel-D1xajGpe.js +0 -1
  292. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BmuGTfRr.js +0 -1
  293. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BmuGTfRr.js +0 -1
  294. package/dist/worker/console/static/assets/index-BLzcAykx.js +0 -451
  295. package/dist/worker/console/static/assets/index-Cs9JUSPs.css +0 -1
  296. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-D9ykKEWP.js +0 -1
  297. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BvPOXALT.js +0 -8
  298. package/skills/frontend-implementation/SKILL.md +0 -52
  299. package/skills/frontend-implementation/references/code-standards.md +0 -33
  300. package/skills/frontend-implementation/references/design-spec.md +0 -56
  301. package/skills/frontend-implementation/references/node-contracts.md +0 -31
  302. /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
@@ -46,6 +46,37 @@ export function planTransitions(input) {
46
46
  actions.push({ type: "recover-contract" });
47
47
  expectedTransitions.push("contract-recovered");
48
48
  }
49
+ const startupStatus = snapshot.source?.sourceFidelityStartup?.status;
50
+ const startupRepairDetected = startupStatus === "missing" ||
51
+ startupStatus === "invalid" ||
52
+ startupStatus === "coverage-incomplete";
53
+ if (startupRepairDetected && input.startupRepairAttempted) {
54
+ actions.push({ type: "stop", reason: "blocker" });
55
+ return {
56
+ actions,
57
+ gate: null,
58
+ blockers,
59
+ next: {
60
+ kind: "advance",
61
+ command: `loop-agent task advance ${snapshot.taskId} --json`,
62
+ },
63
+ expectedTransitions,
64
+ };
65
+ }
66
+ const startupRepairRequired = startupRepairDetected;
67
+ if (startupStatus === "human-decision-blocked") {
68
+ actions.push({ type: "stop", reason: "blocker" });
69
+ return {
70
+ actions,
71
+ gate: null,
72
+ blockers,
73
+ next: {
74
+ kind: "external-action",
75
+ description: "Requirement ledger requires an existing human source decision; preserve sourceResolutions, resolve the blocker, then advance again.",
76
+ },
77
+ expectedTransitions,
78
+ };
79
+ }
49
80
  const sourceReady = snapshot.source?.requirementReady === true;
50
81
  const contractManaged = snapshot.contract?.effectiveStatus === "managed" ||
51
82
  Boolean(snapshot.contract?.revision && snapshot.contract.revision > 0);
@@ -53,7 +84,9 @@ export function planTransitions(input) {
53
84
  // Prefer prepare once PRDs are on disk; re-import only when caller still
54
85
  // signals hasPrdInput (advance.ts latches this after one import per call).
55
86
  const shouldImportPrd = input.hasPrdInput;
56
- if (input.forcePrepareContract || input.refreshManagedContract) {
87
+ if (input.forcePrepareContract ||
88
+ input.refreshManagedContract ||
89
+ startupRepairRequired) {
57
90
  if (shouldImportPrd) {
58
91
  actions.push({ type: "import-prd" });
59
92
  expectedTransitions.push("prd-imported");
@@ -124,7 +157,8 @@ export function planTransitions(input) {
124
157
  const strictValidated = snapshot.dagDraft?.strictValidated === true;
125
158
  const dagRefreshRequired = Boolean(input.forceDagRefresh ||
126
159
  input.refreshManagedContract ||
127
- input.forcePrepareContract);
160
+ input.forcePrepareContract ||
161
+ startupRepairRequired);
128
162
  if (!dagExists || dagRefreshRequired) {
129
163
  actions.push({ type: "generate-dag" });
130
164
  expectedTransitions.push("dag-generated");
@@ -145,10 +179,11 @@ export function planTransitions(input) {
145
179
  snapshot.lifecycleState === "evidence-closed" ||
146
180
  snapshot.lifecycleState === "needs-attention" ||
147
181
  snapshot.lifecycleState === "awaiting-decision";
148
- const approvedForCurrent = postRunState || (input.approveGate && gate !== null);
182
+ const approveCurrentGate = input.approveGate && !dagRefreshRequired;
183
+ const approvedForCurrent = postRunState || (approveCurrentGate && gate !== null);
149
184
  if (!approvedForCurrent && (strictValidated || !dagExists || dagRefreshRequired)) {
150
185
  // Gate opens after generate+validate in the advance loop; plan includes open.
151
- if (!input.approveGate) {
186
+ if (!approveCurrentGate) {
152
187
  actions.push({ type: "open-write-set-gate" });
153
188
  expectedTransitions.push("write-set-gate-opened");
154
189
  if (input.dryRun) {
@@ -177,14 +212,14 @@ export function planTransitions(input) {
177
212
  (snapshot.lifecycleState === "run-failed" && !failedRecoverable) ||
178
213
  snapshot.lifecycleState === "evidence-closed";
179
214
  if (!alreadyTerminalRun) {
180
- if (input.approveGate && !postRunState) {
215
+ if (approveCurrentGate && !postRunState) {
181
216
  expectedTransitions.push("write-set-approved");
182
217
  actions.push({ type: "start-or-monitor-run" });
183
218
  expectedTransitions.push("dag-run-started", "dag-run-monitored");
184
219
  }
185
220
  else if (snapshot.lifecycleState === "running" ||
186
221
  snapshot.activeRun ||
187
- (input.approveGate && Boolean(snapshot.activeRun))) {
222
+ (approveCurrentGate && Boolean(snapshot.activeRun))) {
188
223
  actions.push({ type: "start-or-monitor-run" });
189
224
  expectedTransitions.push("dag-run-monitored");
190
225
  }
@@ -218,7 +253,7 @@ export function planTransitions(input) {
218
253
  actions,
219
254
  gate,
220
255
  blockers,
221
- next: deriveNext(snapshot, gate, input.approveGate),
256
+ next: deriveNext(snapshot, gate, approveCurrentGate),
222
257
  expectedTransitions,
223
258
  };
224
259
  }
@@ -42,6 +42,7 @@ export async function buildTaskLifecycleRecommendations(input) {
42
42
  const allowedPaths = [];
43
43
  const verifyCommands = [];
44
44
  // 1) managed task config
45
+ let hasDeclaredManagedVerify = false;
45
46
  try {
46
47
  const config = await loadTaskConfig(input.repoRoot, input.taskId);
47
48
  for (const glob of config.allowedPaths ?? []) {
@@ -53,7 +54,9 @@ export async function buildTaskLifecycleRecommendations(input) {
53
54
  source: "managed-task-config",
54
55
  });
55
56
  }
56
- for (const cmd of config.verifyCommands ?? []) {
57
+ const managedVerify = (config.verifyCommands ?? []).filter((cmd) => Boolean(cmd.command?.trim()));
58
+ hasDeclaredManagedVerify = managedVerify.length > 0;
59
+ for (const cmd of managedVerify) {
57
60
  const label = cmd.label?.trim() || "verify";
58
61
  const command = cmd.command?.trim();
59
62
  if (!command)
@@ -70,14 +73,22 @@ export async function buildTaskLifecycleRecommendations(input) {
70
73
  // task may not exist yet
71
74
  }
72
75
  // 2) package.json scripts (only if not already covered by managed config)
76
+ // The full-suite `test` script is recommended only when the task declares
77
+ // NO managed verifyCommands at all. Once a task configures a targeted
78
+ // verify set (e.g. per-change typecheck + a scoped vitest run), the full
79
+ // `npm run test` must never be auto-added: it validates the whole repo,
80
+ // costs far more than the change under review, and can fail on unrelated
81
+ // pre-existing breakage, turning a targeted verify green into a hard fail.
73
82
  const scripts = await readPackageScripts(input.repoRoot);
74
83
  const hasVerifyLabel = (label) => verifyCommands.some((entry) => entry.label === label);
75
84
  const scriptCandidates = [
76
85
  { label: "typecheck", script: "typecheck" },
77
- { label: "test", script: "test" },
78
86
  { label: "check-repo", script: "check:repo" },
79
87
  { label: "check-repo", script: "check-repo" },
80
88
  ];
89
+ if (!hasDeclaredManagedVerify) {
90
+ scriptCandidates.push({ label: "test", script: "test" });
91
+ }
81
92
  for (const candidate of scriptCandidates) {
82
93
  const scriptBody = scripts[candidate.script];
83
94
  if (!scriptBody)
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.41.1",
4
- "gitSha": "d5dec271d8ae9c579c423d9ea7b98fb0270baa5a",
5
- "builtAt": "2026-09-02T04:40:44.741Z"
3
+ "version": "0.42.0-next.10",
4
+ "gitSha": "610477f8b0bb4b8e4972bb540abdf5ad438c236a",
5
+ "builtAt": "2026-09-03T14:48:16.726Z"
6
6
  }
@@ -31,17 +31,20 @@ import { runDagWorkflowValidate } from "../commands/dag-workflow-validate.js";
31
31
  import { runDagWorkflowCompile } from "../commands/dag-workflow-compile.js";
32
32
  import { runDagWorkflowPlan } from "../commands/dag-workflow-plan.js";
33
33
  import { runDagApprove } from "../commands/dag-approve.js";
34
+ import { runDagArtifact } from "../commands/dag-artifact.js";
34
35
  import { runDagReject } from "../commands/dag-reject.js";
35
36
  import { runDagReconcileTasks } from "../commands/dag-reconcile-tasks.js";
36
37
  import { runDagReconcileRun } from "../commands/dag-reconcile-run.js";
37
38
  import { runDagRequestInterrupt } from "../commands/dag-request-interrupt.js";
38
39
  import { runDagRerunTask } from "../commands/dag-rerun-task.js";
40
+ import { runDagFollowUp } from "../commands/dag-follow-up.js";
39
41
  import { runDagRerun } from "../commands/dag-rerun.js";
40
42
  import { runDagResume } from "../commands/dag-resume.js";
41
43
  import { runDagDecisionInspect, runDagDecisionValidate, } from "../workflows/dag/decision-envelope.js";
42
44
  import { runDagDoctor, runDagStatus } from "../workflows/dag/lifecycle.js";
43
45
  import { runDagCloseoutDraft, runDagReport } from "../commands/dag-report.js";
44
46
  import { runDagFinalVerification } from "../commands/dag-final-verification.js";
47
+ import { runDagContext } from "../commands/dag-context.js";
45
48
  import { runKnowledge } from "../commands/knowledge.js";
46
49
  import { runLoopBenchmark } from "../commands/loop-benchmark.js";
47
50
  import { runPiReuseBenchmark } from "../commands/pi-reuse-benchmark.js";
@@ -135,8 +138,11 @@ const DAG_SUBCOMMANDS = [
135
138
  "request-interrupt",
136
139
  "rerun",
137
140
  "rerun-task",
141
+ "follow-up",
138
142
  "reconcile-tasks",
139
143
  "final-verification",
144
+ "context",
145
+ "artifact",
140
146
  "decision",
141
147
  ];
142
148
  const TASK_SUBCOMMANDS = ["advance", "status", "source"];
@@ -581,6 +587,11 @@ export const COMMAND_DEFINITIONS = [
581
587
  await runDagRerunTask(repoRoot, rerunTaskArgs);
582
588
  return;
583
589
  }
590
+ if (subcommand === "follow-up") {
591
+ const followUpArgs = rest.filter((arg) => Boolean(arg));
592
+ await runDagFollowUp(repoRoot, followUpArgs);
593
+ return;
594
+ }
584
595
  if (subcommand === "reconcile-tasks") {
585
596
  const reconcileArgs = rest.filter((arg) => Boolean(arg));
586
597
  await runDagReconcileTasks(repoRoot, reconcileArgs);
@@ -591,6 +602,14 @@ export const COMMAND_DEFINITIONS = [
591
602
  await runDagFinalVerification(repoRoot, finalVerificationArgs);
592
603
  return;
593
604
  }
605
+ if (subcommand === "context") {
606
+ await runDagContext(repoRoot, rest.filter((arg) => Boolean(arg)));
607
+ return;
608
+ }
609
+ if (subcommand === "artifact") {
610
+ await runDagArtifact(repoRoot, rest.filter((arg) => Boolean(arg)));
611
+ return;
612
+ }
594
613
  if (subcommand === "decision") {
595
614
  const decisionSubcommand = rest[0];
596
615
  const decisionArgs = rest
@@ -7,12 +7,15 @@ import { runCoverageAudit } from "../commands/coverage-audit.js";
7
7
  import { runCoverageReport } from "../commands/coverage-report.js";
8
8
  import { parseCursorPromptArgs, printCursorPromptUsage, runCursorPrompt, } from "../commands/cursor-prompt.js";
9
9
  import { runDagApprove } from "../commands/dag-approve.js";
10
+ import { runDagArtifact } from "../commands/dag-artifact.js";
10
11
  import { runDagFinalVerification } from "../commands/dag-final-verification.js";
12
+ import { runDagContext } from "../commands/dag-context.js";
11
13
  import { runDagInitHybrid } from "../commands/dag-init-hybrid.js";
12
14
  import { runDagReconcileTasks } from "../commands/dag-reconcile-tasks.js";
13
15
  import { runDagReconcileRun } from "../commands/dag-reconcile-run.js";
14
16
  import { runDagRequestInterrupt } from "../commands/dag-request-interrupt.js";
15
17
  import { runDagRerunTask } from "../commands/dag-rerun-task.js";
18
+ import { runDagFollowUp } from "../commands/dag-follow-up.js";
16
19
  import { runDagRerun } from "../commands/dag-rerun.js";
17
20
  import { runDagReject } from "../commands/dag-reject.js";
18
21
  import { runDagCloseoutDraft, runDagReport } from "../commands/dag-report.js";
@@ -357,12 +360,21 @@ async function runDagAction(repoRoot, subcommand, rest) {
357
360
  case "rerun-task":
358
361
  await runDagRerunTask(repoRoot, args);
359
362
  return;
363
+ case "follow-up":
364
+ await runDagFollowUp(repoRoot, args);
365
+ return;
360
366
  case "reconcile-tasks":
361
367
  await runDagReconcileTasks(repoRoot, args);
362
368
  return;
363
369
  case "final-verification":
364
370
  await runDagFinalVerification(repoRoot, args);
365
371
  return;
372
+ case "context":
373
+ await runDagContext(repoRoot, args);
374
+ return;
375
+ case "artifact":
376
+ await runDagArtifact(repoRoot, args);
377
+ return;
366
378
  case "decision": {
367
379
  const [decisionSubcommand, ...decisionArgs] = args;
368
380
  if (decisionSubcommand === "inspect") {
@@ -376,7 +388,7 @@ async function runDagAction(repoRoot, subcommand, rest) {
376
388
  throw new Error("usage: dag decision <inspect|validate> --run-id <id> [--node-id <node-id>]");
377
389
  }
378
390
  default:
379
- throw new Error("usage: dag <init-hybrid|execute|validate|workflow-plan|workflow-validate|workflow-compile|approve|reject|resume|status|doctor|report|closeout-draft|reconcile-run|request-interrupt|rerun|rerun-task|reconcile-tasks|final-verification|decision> ...");
391
+ throw new Error("usage: dag <init-hybrid|execute|validate|workflow-plan|workflow-validate|workflow-compile|approve|reject|resume|status|doctor|report|closeout-draft|reconcile-run|request-interrupt|rerun|rerun-task|reconcile-tasks|final-verification|context|artifact|decision> ...");
380
392
  }
381
393
  }
382
394
  function compactArgs(args) {
@@ -428,7 +440,7 @@ const RAW_SUBCOMMAND_USAGE = {
428
440
  "dag approve": 'usage: dag approve --run-id <id> --option <A|B|...> [--notes "..."]',
429
441
  "dag reject": 'usage: dag reject --run-id <id> --reason "..."',
430
442
  "dag rerun": "usage: dag rerun --run-id <id> --from-node <node-id> --plan [--json] | dag rerun --run-id <id> --from-node <node-id> --plan-hash <hash> --request-id <key> --reason <text> [--json]",
431
- "dag rerun-task": "usage: dag rerun-task --run-id <id> --reason <text> --request-id <key> [--profile auto|minimal|standard|reviewed|supervised] [--task-id <id>] [--json]",
443
+ "dag rerun-task": "usage: dag rerun-task --run-id <id> --reason <text> --request-id <key> [--profile auto|minimal|standard|reviewed|supervised] [--task-id <id>] [--frontend-recovery --recovery-operation-id <id>] [--json]",
432
444
  "dag reconcile-run": "usage: dag reconcile-run --run-id <id> [--action supersede|abandon] [--reason <text>]",
433
445
  "dag doctor": "usage: dag doctor [--run-id <id>]\nInspect health issues before approve/reject/resume; no --json (output is already structured JSON).",
434
446
  "dag report": "usage: dag report [--run-id <id>] [--lifecycle active|paused|completed|all] [--json|--markdown] [--failed-only] [--latest] [--paused-latest]\nRead-only run report; --json gives structured facts (canonical: `dag report --run-id <id> --lifecycle completed --json`).",
@@ -545,9 +557,24 @@ function addDagCommand(program, defaultRepoRoot) {
545
557
  "request-interrupt",
546
558
  "rerun",
547
559
  "rerun-task",
560
+ "follow-up",
548
561
  "reconcile-tasks",
549
562
  "final-verification",
563
+ "context",
564
+ "artifact",
550
565
  ], defaultRepoRoot);
566
+ const context = dag.commands.find((command) => command.name() === "context");
567
+ if (!context)
568
+ throw new Error("dag context command registration failed");
569
+ context
570
+ .description("Inspect or export a captured effective node context")
571
+ .addHelpText("after", "\nActions:\n show --run-id <id> --node-id <id> --attempt <n> [--json]\n export --run-id <id> --node-id <id> --attempt <n> --output <zip> [--target <generic|pi-session|pi-handoff|readable>] [--redaction <redacted|raw>] [--acknowledge-sensitive-data] [--json]");
572
+ const artifact = dag.commands.find((command) => command.name() === "artifact");
573
+ if (!artifact)
574
+ throw new Error("dag artifact command registration failed");
575
+ artifact
576
+ .description("Inspect artifact consumers or manage staged artifact revisions")
577
+ .addHelpText("after", "\nActions:\n consumers --run-id <id> --node-id <producer> --artifact <path> [--json]\n revision stage --run-id <id> --node-id <producer> --artifact <path> --file <replacement> --request-id <id> [--json]\n revision discard --revision-id <id> --request-id <id> [--json]");
551
578
  const validate = createRawArgsCommand("validate", "Validate an Agent DAG spec", defaultRepoRoot);
552
579
  configureDagValidate(validate);
553
580
  replaceActionWithOptionAwareDispatch(validate, defaultRepoRoot);
@@ -1328,12 +1328,15 @@ export async function runClientRecovery(input) {
1328
1328
  }
1329
1329
  const plugin = await installProjectOpenCodePlugin({
1330
1330
  repoRoot: input.repoRoot,
1331
+ onlyIfMissing: true,
1331
1332
  });
1332
1333
  const overflowPlugin = await installProjectOpenCodeOverflowCompactPlugin({
1333
1334
  repoRoot: input.repoRoot,
1335
+ onlyIfMissing: true,
1334
1336
  });
1335
1337
  const piExtension = await installProjectPiContextOverflowExtension({
1336
1338
  repoRoot: input.repoRoot,
1339
+ onlyIfMissing: true,
1337
1340
  });
1338
1341
  const pi = await applyProjectPiSettingsMerge({ repoRoot: input.repoRoot });
1339
1342
  if (mode !== "user") {
@@ -0,0 +1,284 @@
1
+ import { createHash } from "node:crypto";
2
+ import { lstat, readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { readRevisionParentArtifact } from "../workflows/dag/context-transfer/operator-actions.js";
5
+ import { readFileBoundarySafe } from "../shared/path-safety.js";
6
+ import { openConsoleAppData } from "../infrastructure/console/app-data.js";
7
+ import { artifactRevisionUploadPath, createArtifactRevision, discardArtifactRevision, acquireArtifactRevisionLease, releaseArtifactRevisionLease, } from "../infrastructure/console/artifact-revision-store.js";
8
+ import { OperationStore } from "../infrastructure/console/operation-store.js";
9
+ import { buildArtifactBindingGraph } from "../workflows/dag/artifact-bindings.js";
10
+ import { resolveArtifactRevisionSchema } from "../workflows/dag/artifact-revision-schema-registry.js";
11
+ import { locateDagRun, readDagRunSpec } from "../workflows/dag/lifecycle.js";
12
+ function parseArtifactArgs(args) {
13
+ const [first, second] = args;
14
+ const action = first === "consumers"
15
+ ? "consumers"
16
+ : first === "revision" && second === "stage"
17
+ ? "stage"
18
+ : first === "revision" && second === "discard"
19
+ ? "discard"
20
+ : undefined;
21
+ if (!action) {
22
+ throw new Error("usage: dag artifact consumers --run-id <run> --node-id <producer> --artifact <path> [--json] | dag artifact revision <stage|discard> ...");
23
+ }
24
+ let runId;
25
+ let nodeId;
26
+ let artifactPath;
27
+ let artifactId;
28
+ let file;
29
+ let candidateUpload;
30
+ let revisionId;
31
+ let requestId;
32
+ let json = false;
33
+ for (let index = action === "consumers" ? 1 : 2; index < args.length; index += 1) {
34
+ const arg = args[index];
35
+ const next = () => args[++index];
36
+ if (arg === "--run-id")
37
+ runId = next();
38
+ else if (arg.startsWith("--run-id="))
39
+ runId = arg.slice(9);
40
+ else if (arg === "--node-id")
41
+ nodeId = next();
42
+ else if (arg.startsWith("--node-id="))
43
+ nodeId = arg.slice(10);
44
+ else if (arg === "--artifact")
45
+ artifactPath = next();
46
+ else if (arg.startsWith("--artifact="))
47
+ artifactPath = arg.slice(11);
48
+ else if (arg === "--artifact-id")
49
+ artifactId = next();
50
+ else if (arg.startsWith("--artifact-id="))
51
+ artifactId = arg.slice(14);
52
+ else if (arg === "--file")
53
+ file = next();
54
+ else if (arg.startsWith("--file="))
55
+ file = arg.slice(7);
56
+ else if (arg === "--candidate-upload")
57
+ candidateUpload = next();
58
+ else if (arg.startsWith("--candidate-upload="))
59
+ candidateUpload = arg.slice(19);
60
+ else if (arg === "--revision-id")
61
+ revisionId = next();
62
+ else if (arg.startsWith("--revision-id="))
63
+ revisionId = arg.slice(14);
64
+ else if (arg === "--request-id")
65
+ requestId = next();
66
+ else if (arg.startsWith("--request-id="))
67
+ requestId = arg.slice(13);
68
+ else if (arg === "--json")
69
+ json = true;
70
+ else
71
+ throw new Error(`unknown dag artifact flag: ${arg}`);
72
+ }
73
+ return { action, runId, nodeId, artifactPath, artifactId, file, candidateUpload, revisionId, requestId, json };
74
+ }
75
+ function sha256(bytes) {
76
+ return createHash("sha256").update(bytes).digest("hex");
77
+ }
78
+ function print(value, json, fallback) {
79
+ process.stdout.write(json ? `${JSON.stringify(value, null, 2)}\n` : `${fallback}\n`);
80
+ }
81
+ export async function runDagArtifactCore(repoRoot, args, cliOptions) {
82
+ const input = parseArtifactArgs(args);
83
+ if (input.action === "discard") {
84
+ if (!input.revisionId || !input.requestId) {
85
+ throw new Error("dag artifact revision discard requires --revision-id and --request-id");
86
+ }
87
+ const appData = openConsoleAppData({ repoRoot, ...(cliOptions?.appDataRoot ? { appDataRoot: cliOptions.appDataRoot } : {}) });
88
+ const operations = new OperationStore(appData);
89
+ const created = await operations.create({
90
+ clientRequestId: input.requestId,
91
+ action: "cliArtifactRevisionDiscard",
92
+ actionParams: { revisionId: input.revisionId },
93
+ });
94
+ if (created.kind === "conflict")
95
+ throw new Error(created.code);
96
+ if (created.kind === "idempotent" && created.operation.state === "succeeded") {
97
+ return asRecord(created.operation.result);
98
+ }
99
+ const leaseHolder = `cli-discard:${input.requestId}`;
100
+ const revision = await acquireArtifactRevisionLease({
101
+ appData,
102
+ revisionId: input.revisionId,
103
+ holder: leaseHolder,
104
+ })
105
+ .then(() => discardArtifactRevision({
106
+ appData,
107
+ revisionId: input.revisionId,
108
+ holder: leaseHolder,
109
+ }))
110
+ .finally(() => releaseArtifactRevisionLease({
111
+ appData,
112
+ revisionId: input.revisionId,
113
+ holder: leaseHolder,
114
+ }).catch(() => { }));
115
+ const result = { ok: true, revisionId: revision.revisionId, state: revision.state };
116
+ await operations.update(created.operation.operationId, {
117
+ state: "succeeded",
118
+ startedAt: created.operation.createdAt,
119
+ finishedAt: new Date().toISOString(),
120
+ result,
121
+ });
122
+ return result;
123
+ }
124
+ if (!input.runId || !input.nodeId || (!input.artifactPath && !input.artifactId)) {
125
+ throw new Error("dag artifact requires --run-id, --node-id, and --artifact <relative-path> or --artifact-id <id>");
126
+ }
127
+ const located = await locateDagRun(repoRoot, input.runId);
128
+ if (!located)
129
+ throw new Error(`dag run not found: ${input.runId}`);
130
+ const graph = buildArtifactBindingGraph(await readDagRunSpec(located.runDir));
131
+ const declaration = graph.artifacts.find((item) => item.producerNodeId === input.nodeId &&
132
+ (input.artifactId
133
+ ? item.artifactId === input.artifactId
134
+ : item.path === input.artifactPath));
135
+ if (!declaration)
136
+ throw new Error("ARTIFACT_BINDING_UNAVAILABLE");
137
+ if (input.action === "consumers") {
138
+ return {
139
+ schemaVersion: 1,
140
+ runId: input.runId,
141
+ nodeId: input.nodeId,
142
+ artifactId: declaration.artifactId,
143
+ artifactPath: declaration.path,
144
+ directConsumerNodeIds: declaration.directConsumerNodeIds,
145
+ graphSha256: graph.artifactGraphSha256,
146
+ };
147
+ }
148
+ if (!input.requestId || (!input.file && !input.candidateUpload)) {
149
+ throw new Error("dag artifact revision stage requires --file or --candidate-upload, and --request-id");
150
+ }
151
+ const appData = openConsoleAppData({
152
+ repoRoot,
153
+ ...(cliOptions?.appDataRoot ? { appDataRoot: cliOptions.appDataRoot } : {}),
154
+ });
155
+ // The candidate upload lives in app-data and is cross-process mutable;
156
+ // resolve + read it TOCTOU-safe (O_NOFOLLOW fd read) instead of
157
+ // lstat-then-readFile, so a swapped symlink can never feed an unintended
158
+ // file into the stage. Both the path resolution (assertSafeAppDataPath
159
+ // rejects symlinks/hardlinks) and the fd read map to the stable
160
+ // ARTIFACT_REVISION_PATH_INVALID code.
161
+ let candidateBytes;
162
+ if (input.candidateUpload) {
163
+ try {
164
+ if (!/^[a-f0-9]{64}$/.test(input.candidateUpload)) {
165
+ throw new Error("invalid --candidate-upload hash");
166
+ }
167
+ const candidatePath = artifactRevisionUploadPath(appData, input.candidateUpload);
168
+ candidateBytes = await readFileBoundarySafe({
169
+ boundaryRoot: path.dirname(candidatePath),
170
+ targetPath: candidatePath,
171
+ label: "candidate upload",
172
+ });
173
+ }
174
+ catch (error) {
175
+ const message = error instanceof Error ? error.message : String(error);
176
+ if (/symlink|hardlink|single-link|regular|boundary|escap|app-data/i.test(message)) {
177
+ throw new Error("ARTIFACT_REVISION_PATH_INVALID");
178
+ }
179
+ throw error;
180
+ }
181
+ }
182
+ else {
183
+ const candidatePath = path.resolve(repoRoot, input.file);
184
+ const stat = await lstat(candidatePath);
185
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1) {
186
+ throw new Error("candidate must be an owned regular file");
187
+ }
188
+ candidateBytes = await readFile(candidatePath);
189
+ }
190
+ if (candidateBytes.byteLength > 10 * 1024 * 1024)
191
+ throw new Error("candidate exceeds 10 MiB");
192
+ const operations = new OperationStore(appData);
193
+ const actionParams = {
194
+ runId: input.runId,
195
+ nodeId: input.nodeId,
196
+ artifactPath: declaration.path,
197
+ candidateSha256: sha256(candidateBytes),
198
+ candidateBytes: candidateBytes.byteLength,
199
+ };
200
+ const created = await operations.create({
201
+ clientRequestId: input.requestId,
202
+ action: "cliArtifactRevisionStage",
203
+ actionParams,
204
+ });
205
+ if (created.kind === "conflict")
206
+ throw new Error(created.code);
207
+ if (created.kind === "idempotent" && created.operation.state === "succeeded") {
208
+ return asRecord(created.operation.result);
209
+ }
210
+ const parent = await readRevisionParentArtifact({
211
+ cwd: repoRoot,
212
+ runId: input.runId,
213
+ producerNodeId: input.nodeId,
214
+ artifactId: declaration.artifactId,
215
+ });
216
+ const revision = await createArtifactRevision({
217
+ appData,
218
+ parentRunId: input.runId,
219
+ producerNodeId: input.nodeId,
220
+ declaration: parent.declaration,
221
+ parentSha256: parent.parentSha256,
222
+ parentBytes: parent.parentBytes,
223
+ candidateBytes,
224
+ mediaType: declaration.mediaType,
225
+ artifactSchemaResolver: resolveArtifactRevisionSchema,
226
+ });
227
+ const projected = {
228
+ revisionId: revision.revisionId,
229
+ state: revision.state,
230
+ parent: revision.parent,
231
+ candidate: {
232
+ sha256: revision.candidate.sha256,
233
+ bytes: revision.candidate.bytes,
234
+ mediaType: revision.candidate.mediaType,
235
+ uploadedAt: revision.candidate.uploadedAt,
236
+ },
237
+ validation: revision.validation,
238
+ diff: {
239
+ kind: revision.diff.kind,
240
+ summary: revision.diff.summary,
241
+ truncated: revision.diff.truncated,
242
+ },
243
+ expiresAt: revision.expiresAt,
244
+ };
245
+ const result = { ok: true, revision: projected };
246
+ await operations.update(created.operation.operationId, {
247
+ state: "succeeded",
248
+ startedAt: created.operation.createdAt,
249
+ finishedAt: new Date().toISOString(),
250
+ result,
251
+ });
252
+ return result;
253
+ }
254
+ function asRecord(value) {
255
+ return value && typeof value === "object" ? value : { ok: true };
256
+ }
257
+ export async function runDagArtifact(repoRoot, args) {
258
+ const input = parseArtifactArgs(args);
259
+ try {
260
+ const result = await runDagArtifactCore(repoRoot, args);
261
+ if (input.action === "discard") {
262
+ print(result, input.json, `Discarded ${String(result.revisionId ?? "")}`);
263
+ }
264
+ else if (input.action === "consumers") {
265
+ const consumers = result;
266
+ print(result, input.json, `${consumers.artifactPath}: ${(consumers.directConsumerNodeIds ?? []).join(", ") || "no consumers"}`);
267
+ }
268
+ else {
269
+ print(result, input.json, `Staged revision ${String(result.revisionId ?? "")}`);
270
+ }
271
+ }
272
+ catch (error) {
273
+ if (!input.json)
274
+ throw error;
275
+ const message = error instanceof Error ? error.message : String(error);
276
+ const candidateCode = message.split(":", 1)[0]?.trim() ?? "";
277
+ const code = /^[A-Z][A-Z0-9_]+$/.test(candidateCode)
278
+ ? candidateCode
279
+ : "INTERNAL_ERROR";
280
+ process.stdout.write(`${JSON.stringify({ schemaVersion: 1, ok: false, error: { code, message } }, null, 2)}\n`);
281
+ process.stderr.write(`${message}\n`);
282
+ process.exitCode = 1;
283
+ }
284
+ }