@tea-agent/loop-agent 0.41.1-beta.0 → 0.42.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/CHANGELOG.md +61 -207
  2. package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
  3. package/dist/adapters/context-transfer/pi-session.js +61 -0
  4. package/dist/application/dag/generate-task-dag.js +19 -75
  5. package/dist/application/task-lifecycle/advance.js +5 -24
  6. package/dist/application/task-lifecycle/observe.js +17 -171
  7. package/dist/application/task-lifecycle/plan-transitions.js +7 -42
  8. package/dist/application/task-lifecycle/recommendations.js +2 -13
  9. package/dist/build-stamp.json +3 -3
  10. package/dist/cli/command-definitions.js +12 -7
  11. package/dist/cli/program.js +23 -6
  12. package/dist/commands/client-recovery.js +0 -3
  13. package/dist/commands/dag-artifact.js +284 -0
  14. package/dist/commands/dag-context.js +184 -0
  15. package/dist/commands/dag-rerun.js +206 -1
  16. package/dist/commands/init.js +1 -27
  17. package/dist/commands/task-advance.js +0 -19
  18. package/dist/executors/dag-pi-executor.js +266 -3792
  19. package/dist/executors/pi-executor.js +4 -15
  20. package/dist/executors/pi-sdk-executor.js +3 -0
  21. package/dist/executors/shell-executor.js +217 -945
  22. package/dist/executors/shell-write-guard.js +0 -7
  23. package/dist/{worker → infrastructure}/console/app-data.js +4 -0
  24. package/dist/infrastructure/console/artifact-revision-store.js +430 -0
  25. package/dist/infrastructure/console/context-export-store.js +160 -0
  26. package/dist/infrastructure/console/dir-lock.js +132 -0
  27. package/dist/infrastructure/console/operation-store.js +197 -0
  28. package/dist/infrastructure/harness/artifact-store.js +10 -1
  29. package/dist/infrastructure/harness/atomic-write.js +12 -2
  30. package/dist/shared/context-transfer/artifact-revision.js +172 -0
  31. package/dist/shared/context-transfer.js +418 -0
  32. package/dist/shared/dag-failure-category.js +0 -12
  33. package/dist/shared/openspec-spec.js +4 -70
  34. package/dist/shared/operator/capabilities.js +14 -7
  35. package/dist/shared/operator/safe-run-summary.js +1 -0
  36. package/dist/shared/path-safety.js +93 -0
  37. package/dist/shared/preview.js +28 -4
  38. package/dist/task/config-types.js +9 -35
  39. package/dist/task/contract/adopt.js +0 -4
  40. package/dist/task/contract/import-revision.js +0 -4
  41. package/dist/task/contract/project.js +0 -3
  42. package/dist/task/contract/schema.js +1 -2
  43. package/dist/task/frontend-project-capability.js +20 -203
  44. package/dist/task/runtime.js +2 -5
  45. package/dist/task/source-prepare/build-draft.js +3 -3
  46. package/dist/task/source-prepare/fragment-inventory.js +9 -15
  47. package/dist/task/source-prepare/prepare.js +1 -89
  48. package/dist/task/source-prepare/semantic-intake.js +2 -6
  49. package/dist/task/source-references.js +1 -22
  50. package/dist/task/task-demand-routing.js +3 -0
  51. package/dist/worker/console/chat/chat-event-store.js +2 -2
  52. package/dist/worker/console/chat/pi-runtime.js +3 -2
  53. package/dist/worker/console/chat/resource-preferences-store.js +1 -1
  54. package/dist/worker/console/chat/routes.js +13 -4
  55. package/dist/worker/console/chat/semantic-activity.js +12 -11
  56. package/dist/worker/console/chat/session-stats.js +96 -0
  57. package/dist/worker/console/chat/session-store.js +1 -1
  58. package/dist/worker/console/chat/turn-process.js +28 -4
  59. package/dist/worker/console/chat/user-questions.js +1 -1
  60. package/dist/worker/console/console-update-runtime.js +1 -1
  61. package/dist/worker/console/context-transfer-diagnostics.js +198 -0
  62. package/dist/worker/console/dag-confirmation.js +1 -1
  63. package/dist/worker/console/dag-execution-receipt.js +1 -1
  64. package/dist/worker/console/doctor.js +1 -1
  65. package/dist/worker/console/draft-store.js +1 -1
  66. package/dist/worker/console/human-gate-token.js +1 -1
  67. package/dist/worker/console/index.js +3 -6
  68. package/dist/worker/console/interview/assessment.js +1 -1
  69. package/dist/worker/console/interview/session.js +1 -1
  70. package/dist/worker/console/operation-runner.js +45 -4
  71. package/dist/worker/console/operation-sse.js +1 -1
  72. package/dist/worker/console/operation-wait.js +1 -1
  73. package/dist/worker/console/operator-actions.js +107 -168
  74. package/dist/worker/console/operator-surface-health.js +1 -1
  75. package/dist/worker/console/operator-user-error.js +169 -0
  76. package/dist/worker/console/pi-plugins.js +60 -1
  77. package/dist/worker/console/routes.js +637 -8
  78. package/dist/worker/console/security.js +35 -0
  79. package/dist/worker/console/server.js +6 -6
  80. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Bho4we4c.js → abnfDiagram-N423BO3Z-DZZ8m3PO.js} +1 -1
  81. package/dist/worker/console/static/assets/{arc-TBDkTeK0.js → arc-D6PvaVd-.js} +1 -1
  82. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BF_fZrn2.js → architectureDiagram-T3A2C74G-B_OTOiI8.js} +1 -1
  83. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CCnPgUe2.js → blockDiagram-VBNYF7ZC-Bv6rqHBg.js} +1 -1
  84. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BCYZJLAT.js → c4Diagram-5PPSVZJV-B8eHr0oz.js} +1 -1
  85. package/dist/worker/console/static/assets/channel-BU5gOilw.js +1 -0
  86. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D8K6adhU.js → chunk-2GRJ4B5K-DYwR0im2.js} +1 -1
  87. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DkT1Q78b.js → chunk-2Q5K7J3B-D2WPGqXt.js} +1 -1
  88. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DuhvpIr7.js → chunk-5RXB4S5H-CQISJ_I7.js} +1 -1
  89. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BnK6dC49.js → chunk-5VM5RSS4-C0o2Du1e.js} +1 -1
  90. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DPz__z7r.js → chunk-6Q2QTUOP-4f8kr-U7.js} +1 -1
  91. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-0kkppM2j.js → chunk-GF5L2VYU-D_OWvXzX.js} +1 -1
  92. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BB41iK5t.js → chunk-JWPE2WC7-CasdPz5X.js} +1 -1
  93. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CiVrxKHU.js → chunk-KBJHAD2P-Cj8lRrla.js} +1 -1
  94. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-C2h9Xje1.js → chunk-RYQCIY6F-CUvD1FSd.js} +1 -1
  95. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-3iOSHdbM.js → chunk-XXDRQBXY-CqLw_eqB.js} +1 -1
  96. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DIzKJGHr.js +1 -0
  97. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DIzKJGHr.js +1 -0
  98. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BN3A7tkk.js → cose-bilkent-JH36ORCC-ku-WqJPx.js} +1 -1
  99. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CTuBy0Or.js → cynefin-VYW2F7L2-Bq_PYDhl.js} +1 -1
  100. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-wb6tUlMN.js → cynefinDiagram-MW4NZA55-BiGZV641.js} +1 -1
  101. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-GP1Ivx8B.js → dagre-VZM6K2ZE-C4JRlglF.js} +1 -1
  102. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-ejrcK6L0.js → diagram-7IWD3JNH-B0dNeWYG.js} +1 -1
  103. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-YFkCCvUU.js → diagram-B4RE2ZJO-Cj35YvYM.js} +1 -1
  104. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-B4rrvIaL.js → diagram-LBJQPF4R-uzQoJ2-8.js} +1 -1
  105. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-C-YIZcUf.js → diagram-Q27KOJAE-D1a-Buoz.js} +1 -1
  106. package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPF5wcL1.js → diagram-UB23O5K3-XjRrLRSs.js} +1 -1
  107. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-CU5SzXjO.js → ebnfDiagram-BXEA7PRR-Da_O24cW.js} +1 -1
  108. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-CpL414DE.js → erDiagram-JOGREHBK-BLJ8jrYU.js} +1 -1
  109. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-MrhLzNau.js → flowDiagram-UKHOOZJN-B9GrLjM0.js} +1 -1
  110. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Bg4qFuUo.js → ganttDiagram-PKOTCBZU-CeJ0TqiK.js} +1 -1
  111. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-ColxSXLR.js → gitGraphDiagram-DS77QQ5N-Bm2eNNFX.js} +1 -1
  112. package/dist/worker/console/static/assets/index-H9rFJiGL.css +1 -0
  113. package/dist/worker/console/static/assets/index-xwu9GxEc.js +451 -0
  114. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Czvtm6gc.js → infoDiagram-6WML65LV-DB26i3d8.js} +1 -1
  115. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-B7Xp8HyJ.js → ishikawaDiagram-WSZJBQD7-BOGRyeZT.js} +1 -1
  116. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DWYZcvph.js → journeyDiagram-NVQOT4AX-CZRoBO_6.js} +1 -1
  117. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-KTLZRUjx.js → kanban-definition-27J2QSJJ-qlWhJyoB.js} +1 -1
  118. package/dist/worker/console/static/assets/{linear-_Exn7bfl.js → linear-CfUiDDB3.js} +1 -1
  119. package/dist/worker/console/static/assets/{mermaid.core-Cxg8kX8V.js → mermaid.core-BQe6fpqj.js} +5 -5
  120. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-oKrlo2Bt.js → mindmap-definition-FAOFIHXS-zFzWHw64.js} +1 -1
  121. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-XAeteiNd.js → pegDiagram-VL7TDLO6-DFGUqjZO.js} +1 -1
  122. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BTf1K-lp.js → pieDiagram-7S7Q4E2Y-BB5i0l8Z.js} +1 -1
  123. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-C0IlY0Jf.js → quadrantDiagram-CIZ2JOQS-obbVZ_X5.js} +1 -1
  124. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BVbKtZhW.js → railroadDiagram-AXF67PYL-BwdaNzc1.js} +1 -1
  125. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-C5e4JFcr.js → requirementDiagram-LRYGKXZP-CkTJaugh.js} +1 -1
  126. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cz30hc29.js → sankeyDiagram-W5VNT64P-BwJ-hIgq.js} +1 -1
  127. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DS33n6d0.js → sequenceDiagram-SI44F4Z6-BzSgmm7w.js} +1 -1
  128. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-46w7v-9T.js → sizeCapture-X5ZJPWSS-Bn3oj5Q1.js} +1 -1
  129. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-pbcD5SsZ.js → stateDiagram-OKZ733FA-dxhGtL8K.js} +1 -1
  130. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cld2qK9v.js +1 -0
  131. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DuivYBSj.js → swimlanes-SLNWSIFB-BLpEInya.js} +2 -2
  132. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-TIHpiT7w.js +8 -0
  133. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cb9VZVIl.js → timeline-definition-Z64GVDOM-ChJGYcXy.js} +1 -1
  134. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-D2-mfOgR.js → vennDiagram-T6HMQDX7-DK-qjRer.js} +1 -1
  135. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DeS2aC76.js → wardleyDiagram-T6FBY63Y-3qeaWAg-.js} +1 -1
  136. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-WGsrFjxH.js → xychartDiagram-ELKLHX3M-CootlyP9.js} +1 -1
  137. package/dist/worker/console/static/index.html +7 -2
  138. package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
  139. package/dist/worker/console/static-src/app/useRunProgress.js +9 -2
  140. package/dist/worker/console/static-src/operator-chat/dag-progress-link.js +22 -0
  141. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -0
  142. package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -0
  143. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +28 -3
  144. package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +106 -0
  145. package/dist/worker/console/static-src/shell/console-update-reload.js +25 -0
  146. package/dist/worker/console/workspace-context.js +28 -112
  147. package/dist/worker/console/workspace-registry.js +2 -2
  148. package/dist/worker/continuation/worker-continuation.js +278 -0
  149. package/dist/worker/observability/read-model.js +4 -0
  150. package/dist/worker/observe/health.js +1 -1
  151. package/dist/worker/observe/node-transparency.js +572 -0
  152. package/dist/worker/observe/routes.js +51 -1
  153. package/dist/worker/observe/static/api.js +69 -5
  154. package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
  155. package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
  156. package/dist/worker/observe/static/dag-node-purpose.js +0 -5
  157. package/dist/worker/observe/static/state.js +3 -1
  158. package/dist/worker/observe/static/styles.css +1037 -126
  159. package/dist/worker/observe/static/views/dag-inspector.js +1217 -65
  160. package/dist/worker/observe/static/views/session-timeline.js +121 -25
  161. package/dist/worker/outcomes/adapters.js +19 -0
  162. package/dist/worker/outcomes/types.js +1 -0
  163. package/dist/worker/pool/attempt-lease.js +97 -66
  164. package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
  165. package/dist/worker/pool/reconcile.js +46 -1
  166. package/dist/worker/pool/run-store.js +2 -0
  167. package/dist/worker/pool/state-projection.js +2 -0
  168. package/dist/worker/task-spec/workflow-routing.js +8 -3
  169. package/dist/workflows/dag/artifact-bindings.js +149 -0
  170. package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
  171. package/dist/workflows/dag/backend-test-result-contract.js +1 -1
  172. package/dist/workflows/dag/budget-enforcement.js +10 -0
  173. package/dist/workflows/dag/context-receipt.js +305 -0
  174. package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
  175. package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
  176. package/dist/workflows/dag/context-transfer/renderers.js +93 -0
  177. package/dist/workflows/dag/contract-validator-registrations.js +2 -1
  178. package/dist/workflows/dag/frontend-implementation-contract.js +162 -932
  179. package/dist/workflows/dag/frontend-plan-render.js +1 -2
  180. package/dist/workflows/dag/frontend-prewrite-gate.js +349 -255
  181. package/dist/workflows/dag/frontend-recovery-plan.js +10 -17
  182. package/dist/workflows/dag/frontend-recovery-run.js +20 -33
  183. package/dist/workflows/dag/frontend-repair.js +432 -1
  184. package/dist/workflows/dag/frontend-review-context.js +15 -261
  185. package/dist/workflows/dag/frontend-verification-trace.js +24 -249
  186. package/dist/workflows/dag/frontend-worktree-diff.js +17 -250
  187. package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
  188. package/dist/workflows/dag/init-hybrid.js +640 -685
  189. package/dist/workflows/dag/interrupt-request.js +0 -7
  190. package/dist/workflows/dag/node-execution.js +116 -759
  191. package/dist/workflows/dag/path-safety.js +1 -0
  192. package/dist/workflows/dag/prompt.js +30 -38
  193. package/dist/workflows/dag/report.js +1 -37
  194. package/dist/workflows/dag/rerun-feedback.js +1 -256
  195. package/dist/workflows/dag/rerun-plan.js +557 -11
  196. package/dist/workflows/dag/rerun-run.js +601 -38
  197. package/dist/workflows/dag/rerun-task.js +0 -29
  198. package/dist/workflows/dag/retry-policy.js +104 -214
  199. package/dist/workflows/dag/runner.js +142 -430
  200. package/dist/workflows/dag/scheduler.js +16 -114
  201. package/dist/workflows/dag/skill-snapshot.js +17 -0
  202. package/dist/workflows/dag/types.js +195 -246
  203. package/dist/workflows/dag/validate.js +12 -28
  204. package/docs/architecture/runtime-boundaries.md +6 -6
  205. package/docs/architecture/worker-and-feature.md +1 -1
  206. package/docs/init-surface.manifest.json +12 -30
  207. package/docs/skills/vetted-skill-registry.md +2 -4
  208. package/docs/templates/README.md +0 -2
  209. package/docs/templates/agent-dag-report.schema.json +2 -8
  210. package/docs/templates/agent-dag.schema.json +33 -1
  211. package/docs/templates/frontend-implementation-contract.schema.json +1 -4
  212. package/docs/templates/product-line/task.yaml +1 -1
  213. package/harness.json +3 -3
  214. package/package.json +3 -1
  215. package/skills/frontend-bounded-implement/SKILL.md +14 -15
  216. package/skills/frontend-design-review/SKILL.md +41 -22
  217. package/skills/frontend-implementation/SKILL.md +52 -0
  218. package/skills/frontend-implementation/references/code-standards.md +33 -0
  219. package/skills/frontend-implementation/references/design-spec.md +56 -0
  220. package/skills/frontend-implementation/references/node-contracts.md +31 -0
  221. package/skills/frontend-review/SKILL.md +15 -20
  222. package/skills/frontend-review/references/review-findings.md +7 -6
  223. package/skills/frontend-verification/SKILL.md +1 -1
  224. package/skills/loop-agent/references/command-reference.md +6 -1
  225. package/skills/loop-agent/references/hybrid-dag.md +2 -2
  226. package/dist/commands/dag-follow-up.js +0 -138
  227. package/dist/executors/pi-read-budget-policy.js +0 -239
  228. package/dist/worker/console/frontend-human-decision-adapter.js +0 -19
  229. package/dist/worker/console/frontend-split-operation-adapter.js +0 -20
  230. package/dist/worker/console/operation-store.js +0 -389
  231. package/dist/worker/console/static/assets/channel-bnXW0U3C.js +0 -1
  232. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-rZpq_twR.js +0 -1
  233. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-rZpq_twR.js +0 -1
  234. package/dist/worker/console/static/assets/index-D3CC4eUz.css +0 -1
  235. package/dist/worker/console/static/assets/index-DLt4ZFvD.js +0 -437
  236. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-zBMYiUyi.js +0 -1
  237. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-d_rms-5i.js +0 -8
  238. package/dist/worker/console/static-src/active-run-badge.js +0 -17
  239. package/dist/worker/materialize/frontend-split-task-materializer.js +0 -72
  240. package/dist/workflows/dag/dag-retry-schema.js +0 -138
  241. package/dist/workflows/dag/frontend-closeout.js +0 -221
  242. package/dist/workflows/dag/frontend-design-policy.js +0 -400
  243. package/dist/workflows/dag/frontend-human-decision.js +0 -182
  244. package/dist/workflows/dag/frontend-provider-capability-matrix.js +0 -159
  245. package/dist/workflows/dag/frontend-read-budgets.js +0 -12
  246. package/dist/workflows/dag/frontend-recovery-capsule.js +0 -455
  247. package/dist/workflows/dag/frontend-recovery-controller.js +0 -202
  248. package/dist/workflows/dag/frontend-recovery-lineage.js +0 -178
  249. package/dist/workflows/dag/frontend-review-findings.js +0 -270
  250. package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -914
  251. package/dist/workflows/dag/frontend-shape-capsule-store.js +0 -191
  252. package/dist/workflows/dag/frontend-shape-facts.js +0 -409
  253. package/dist/workflows/dag/frontend-shape.js +0 -427
  254. package/dist/workflows/dag/frontend-source-fidelity-ledger.js +0 -108
  255. package/dist/workflows/dag/frontend-split-application-service.js +0 -203
  256. package/dist/workflows/dag/frontend-split-orchestrator.js +0 -899
  257. package/dist/workflows/dag/frontend-typed-event-store.js +0 -452
  258. package/dist/workflows/dag/frontend-typed-event-transaction.js +0 -180
  259. package/dist/workflows/dag/frontend-writer-admission.js +0 -285
  260. package/dist/workflows/dag/frontend-writer-status.js +0 -256
  261. package/dist/workflows/dag/recovery-lease.js +0 -80
  262. package/docs/templates/frontend-implementation-dag.json +0 -89
  263. package/docs/templates/spec-registry.schema.json +0 -45
  264. package/skills/frontend-bounded-implement/references/code-standards.md +0 -19
  265. package/skills/frontend-contract/SKILL.md +0 -23
  266. package/skills/frontend-contract/references/contract-protocol.md +0 -34
  267. package/skills/frontend-plan/SKILL.md +0 -26
  268. package/skills/frontend-plan/references/decision-contract.md +0 -37
  269. package/skills/frontend-plan/references/design-decisions.md +0 -17
  270. package/skills/frontend-scout/SKILL.md +0 -25
  271. package/skills/frontend-scout/references/design-evidence.md +0 -16
  272. package/skills/frontend-scout/references/scout-evidence.md +0 -23
  273. /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
@@ -7,13 +7,14 @@ 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";
16
- import { runDagFollowUp } from "../commands/dag-follow-up.js";
17
18
  import { runDagRerun } from "../commands/dag-rerun.js";
18
19
  import { runDagReject } from "../commands/dag-reject.js";
19
20
  import { runDagCloseoutDraft, runDagReport } from "../commands/dag-report.js";
@@ -358,15 +359,18 @@ async function runDagAction(repoRoot, subcommand, rest) {
358
359
  case "rerun-task":
359
360
  await runDagRerunTask(repoRoot, args);
360
361
  return;
361
- case "follow-up":
362
- await runDagFollowUp(repoRoot, args);
363
- return;
364
362
  case "reconcile-tasks":
365
363
  await runDagReconcileTasks(repoRoot, args);
366
364
  return;
367
365
  case "final-verification":
368
366
  await runDagFinalVerification(repoRoot, args);
369
367
  return;
368
+ case "context":
369
+ await runDagContext(repoRoot, args);
370
+ return;
371
+ case "artifact":
372
+ await runDagArtifact(repoRoot, args);
373
+ return;
370
374
  case "decision": {
371
375
  const [decisionSubcommand, ...decisionArgs] = args;
372
376
  if (decisionSubcommand === "inspect") {
@@ -380,7 +384,7 @@ async function runDagAction(repoRoot, subcommand, rest) {
380
384
  throw new Error("usage: dag decision <inspect|validate> --run-id <id> [--node-id <node-id>]");
381
385
  }
382
386
  default:
383
- 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> ...");
387
+ 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> ...");
384
388
  }
385
389
  }
386
390
  function compactArgs(args) {
@@ -549,10 +553,23 @@ function addDagCommand(program, defaultRepoRoot) {
549
553
  "request-interrupt",
550
554
  "rerun",
551
555
  "rerun-task",
552
- "follow-up",
553
556
  "reconcile-tasks",
554
557
  "final-verification",
558
+ "context",
559
+ "artifact",
555
560
  ], defaultRepoRoot);
561
+ const context = dag.commands.find((command) => command.name() === "context");
562
+ if (!context)
563
+ throw new Error("dag context command registration failed");
564
+ context
565
+ .description("Inspect or export a captured effective node context")
566
+ .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]");
567
+ const artifact = dag.commands.find((command) => command.name() === "artifact");
568
+ if (!artifact)
569
+ throw new Error("dag artifact command registration failed");
570
+ artifact
571
+ .description("Inspect artifact consumers or manage staged artifact revisions")
572
+ .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]");
556
573
  const validate = createRawArgsCommand("validate", "Validate an Agent DAG spec", defaultRepoRoot);
557
574
  configureDagValidate(validate);
558
575
  replaceActionWithOptionAwareDispatch(validate, defaultRepoRoot);
@@ -1328,15 +1328,12 @@ export async function runClientRecovery(input) {
1328
1328
  }
1329
1329
  const plugin = await installProjectOpenCodePlugin({
1330
1330
  repoRoot: input.repoRoot,
1331
- onlyIfMissing: true,
1332
1331
  });
1333
1332
  const overflowPlugin = await installProjectOpenCodeOverflowCompactPlugin({
1334
1333
  repoRoot: input.repoRoot,
1335
- onlyIfMissing: true,
1336
1334
  });
1337
1335
  const piExtension = await installProjectPiContextOverflowExtension({
1338
1336
  repoRoot: input.repoRoot,
1339
- onlyIfMissing: true,
1340
1337
  });
1341
1338
  const pi = await applyProjectPiSettingsMerge({ repoRoot: input.repoRoot });
1342
1339
  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
+ }
@@ -0,0 +1,184 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { link, lstat, readFile, rm, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { buildContextBundle } from "../workflows/dag/context-transfer/context-bundle.js";
5
+ import { effectiveNodeContextV1Schema } from "../shared/context-transfer.js";
6
+ import { locateDagRun } from "../workflows/dag/lifecycle.js";
7
+ function parseContextArgs(args) {
8
+ const action = args[0];
9
+ if (action !== "show" && action !== "export") {
10
+ throw new Error("usage: dag context <show|export> --run-id <id> --node-id <id> --attempt <n> [--json]");
11
+ }
12
+ let runId = "";
13
+ let nodeId = "";
14
+ let attempt = 0;
15
+ let json = false;
16
+ let target = "generic";
17
+ let profile = "redacted";
18
+ let output;
19
+ let acknowledgeSensitiveData = false;
20
+ for (let index = 1; index < args.length; index += 1) {
21
+ const arg = args[index];
22
+ if (arg === "--run-id")
23
+ runId = args[++index] ?? "";
24
+ else if (arg.startsWith("--run-id="))
25
+ runId = arg.slice(9);
26
+ else if (arg === "--node-id")
27
+ nodeId = args[++index] ?? "";
28
+ else if (arg.startsWith("--node-id="))
29
+ nodeId = arg.slice(10);
30
+ else if (arg === "--attempt")
31
+ attempt = Number(args[++index]);
32
+ else if (arg.startsWith("--attempt="))
33
+ attempt = Number(arg.slice(10));
34
+ else if (arg === "--target")
35
+ target = (args[++index] ?? "");
36
+ else if (arg.startsWith("--target="))
37
+ target = arg.slice(9);
38
+ else if (arg === "--redaction")
39
+ profile = (args[++index] ?? "");
40
+ else if (arg.startsWith("--redaction="))
41
+ profile = arg.slice(12);
42
+ else if (arg === "--output")
43
+ output = args[++index];
44
+ else if (arg.startsWith("--output="))
45
+ output = arg.slice(9);
46
+ else if (arg === "--acknowledge-sensitive-data")
47
+ acknowledgeSensitiveData = true;
48
+ else if (arg === "--json")
49
+ json = true;
50
+ else
51
+ throw new Error(`unknown dag context flag: ${arg}`);
52
+ }
53
+ if (!runId || !nodeId || !Number.isSafeInteger(attempt) || attempt < 1) {
54
+ throw new Error("dag context requires --run-id, --node-id, and positive --attempt");
55
+ }
56
+ if (!["generic", "pi-session", "pi-handoff", "readable"].includes(target)) {
57
+ throw new Error(`unsupported context target: ${target}`);
58
+ }
59
+ if (profile !== "redacted" && profile !== "raw") {
60
+ throw new Error(`unsupported redaction profile: ${profile}`);
61
+ }
62
+ if (action === "export" && !output) {
63
+ throw new Error("dag context export requires --output <path>");
64
+ }
65
+ if (action === "export" && profile === "raw" && !acknowledgeSensitiveData) {
66
+ throw new Error("raw context export requires --acknowledge-sensitive-data");
67
+ }
68
+ return {
69
+ action,
70
+ runId,
71
+ nodeId,
72
+ attempt,
73
+ json,
74
+ target,
75
+ profile,
76
+ ...(output ? { output } : {}),
77
+ acknowledgeSensitiveData,
78
+ };
79
+ }
80
+ function receiptPath(runDir, nodeId, attempt) {
81
+ return path.join(runDir, nodeId, "context-receipts", `attempt-${attempt}.json`);
82
+ }
83
+ function outputLabel(repoRoot, outputPath) {
84
+ const relative = path
85
+ .relative(repoRoot, outputPath)
86
+ .split(path.sep)
87
+ .join("/");
88
+ return relative && !relative.startsWith("../") && relative !== ".."
89
+ ? relative
90
+ : path.basename(outputPath);
91
+ }
92
+ export async function runDagContextCore(repoRoot, args) {
93
+ const input = parseContextArgs(args);
94
+ const located = await locateDagRun(repoRoot, input.runId);
95
+ if (!located)
96
+ throw new Error(`dag run not found: ${input.runId}`);
97
+ if (input.action === "show") {
98
+ let receipt;
99
+ try {
100
+ receipt = effectiveNodeContextV1Schema.parse(JSON.parse(await readFile(receiptPath(located.runDir, input.nodeId, input.attempt), "utf8")));
101
+ }
102
+ catch {
103
+ throw new Error("CONTEXT_RECEIPT_MISSING");
104
+ }
105
+ return {
106
+ schemaVersion: 1,
107
+ runId: input.runId,
108
+ nodeId: input.nodeId,
109
+ attempt: input.attempt,
110
+ capturedAt: receipt.identity.capturedAt,
111
+ contextDigest: receipt.contextDigest,
112
+ componentCount: receipt.orderedComponents.length,
113
+ truncationCount: receipt.promptAssembly.truncations.length,
114
+ runtime: {
115
+ executor: receipt.runtime.executor,
116
+ model: receipt.runtime.resolvedModel ?? receipt.runtime.modelHint ?? null,
117
+ skills: receipt.runtime.skills,
118
+ extensions: receipt.runtime.extensions,
119
+ tools: receipt.runtime.activeTools,
120
+ },
121
+ };
122
+ }
123
+ const outputPath = path.resolve(repoRoot, input.output);
124
+ const parent = path.dirname(outputPath);
125
+ const parentStat = await lstat(parent).catch(() => undefined);
126
+ if (parentStat?.isSymbolicLink())
127
+ throw new Error("unsafe output parent symlink");
128
+ const existing = await lstat(outputPath).catch(() => undefined);
129
+ if (existing)
130
+ throw new Error("context export output already exists");
131
+ const bundle = await buildContextBundle({
132
+ runDir: located.runDir,
133
+ runId: input.runId,
134
+ nodeId: input.nodeId,
135
+ attempt: input.attempt,
136
+ profile: input.profile,
137
+ target: input.target,
138
+ ...(input.profile === "raw" && input.acknowledgeSensitiveData
139
+ ? {
140
+ rawAcknowledgement: {
141
+ requestId: `cli-${bundleRequestIdentity(input)}`,
142
+ acknowledgedAt: new Date().toISOString(),
143
+ },
144
+ }
145
+ : {}),
146
+ });
147
+ const temporaryPath = `${outputPath}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
148
+ try {
149
+ await writeFile(temporaryPath, bundle.zipBytes, { flag: "wx", mode: 0o600 });
150
+ await link(temporaryPath, outputPath);
151
+ await rm(temporaryPath, { force: true });
152
+ }
153
+ catch (error) {
154
+ await rm(temporaryPath, { force: true }).catch(() => undefined);
155
+ throw error;
156
+ }
157
+ return {
158
+ schemaVersion: 1,
159
+ bundleId: bundle.manifest.bundleId,
160
+ bundleSha256: bundle.manifest.bundleSha256,
161
+ profile: bundle.manifest.profile,
162
+ target: bundle.manifest.target,
163
+ bytes: bundle.zipBytes.byteLength,
164
+ output: outputLabel(repoRoot, outputPath),
165
+ };
166
+ }
167
+ export async function runDagContext(repoRoot, args) {
168
+ const input = parseContextArgs(args);
169
+ const result = await runDagContextCore(repoRoot, args);
170
+ if (input.action === "show") {
171
+ const summary = result;
172
+ process.stdout.write(input.json
173
+ ? `${JSON.stringify(result, null, 2)}\n`
174
+ : `Context ${summary.runId}/${summary.nodeId} attempt ${summary.attempt}\nDigest: ${summary.contextDigest}\nComponents: ${summary.componentCount}\n`);
175
+ return;
176
+ }
177
+ const exported = result;
178
+ process.stdout.write(input.json
179
+ ? `${JSON.stringify(result, null, 2)}\n`
180
+ : `Exported ${exported.bundleId} to ${exported.output} (${exported.bytes} bytes)\n`);
181
+ }
182
+ function bundleRequestIdentity(input) {
183
+ return `${input.runId}-${input.nodeId}-${input.attempt}`.replace(/[^a-zA-Z0-9._-]/g, "-");
184
+ }