@tea-agent/loop-agent 0.40.0-next.9 → 0.41.0-beta.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 (218) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +130 -3
  3. package/README.md +2 -2
  4. package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
  5. package/dist/adapters/context-transfer/pi-session.js +61 -0
  6. package/dist/application/dag/generate-task-dag.js +6 -2
  7. package/dist/build-stamp.json +3 -3
  8. package/dist/cli/command-definitions.js +21 -5
  9. package/dist/cli/program.js +61 -4
  10. package/dist/commands/client-recovery.js +8 -36
  11. package/dist/commands/dag-artifact.js +284 -0
  12. package/dist/commands/dag-context.js +184 -0
  13. package/dist/commands/dag-rerun.js +206 -1
  14. package/dist/commands/init-model-catalog.js +22 -0
  15. package/dist/commands/task-advance.js +23 -1
  16. package/dist/commands/task-source-prepare.js +5 -0
  17. package/dist/commands/task-status.js +1 -0
  18. package/dist/executors/dag-pi-executor.js +39 -12
  19. package/dist/executors/pi-sdk-executor.js +36 -5
  20. package/dist/executors/shell-executor.js +64 -17
  21. package/dist/governance/checks.js +1 -0
  22. package/dist/{worker → infrastructure}/console/app-data.js +4 -0
  23. package/dist/infrastructure/console/artifact-revision-store.js +430 -0
  24. package/dist/infrastructure/console/context-export-store.js +160 -0
  25. package/dist/infrastructure/console/dir-lock.js +132 -0
  26. package/dist/{worker → infrastructure}/console/operation-store.js +28 -0
  27. package/dist/infrastructure/harness/artifact-store.js +10 -1
  28. package/dist/infrastructure/harness/atomic-write.js +12 -2
  29. package/dist/shared/context-transfer/artifact-revision.js +172 -0
  30. package/dist/shared/context-transfer.js +418 -0
  31. package/dist/shared/operator/capabilities.js +23 -1
  32. package/dist/shared/operator/safe-run-summary.js +1 -0
  33. package/dist/shared/path-safety.js +93 -0
  34. package/dist/shared/pi-context-pressure/checkpoint.js +116 -0
  35. package/dist/shared/pi-context-pressure/compaction-policy.js +151 -0
  36. package/dist/shared/pi-context-pressure/env.js +58 -0
  37. package/dist/shared/pi-context-pressure/extension.js +100 -0
  38. package/dist/shared/pi-context-pressure/index.js +7 -0
  39. package/dist/shared/pi-context-pressure/overflow.js +252 -0
  40. package/dist/shared/pi-context-pressure/sift-bridge.js +386 -0
  41. package/dist/shared/pi-context-pressure/telemetry.js +51 -0
  42. package/dist/shared/preview.js +28 -4
  43. package/dist/task/config-types.js +2 -1
  44. package/dist/task/task-demand-routing.js +3 -0
  45. package/dist/worker/console/chat/assistant-content.js +7 -0
  46. package/dist/worker/console/chat/chat-event-store.js +84 -13
  47. package/dist/worker/console/chat/goal-round-driver.js +72 -0
  48. package/dist/worker/console/chat/goals.js +300 -0
  49. package/dist/worker/console/chat/pi-runtime.js +285 -8
  50. package/dist/worker/console/chat/provider-error.js +2 -1
  51. package/dist/worker/console/chat/repo-browser.js +94 -12
  52. package/dist/worker/console/chat/resource-preferences-store.js +1 -1
  53. package/dist/worker/console/chat/routes.js +487 -30
  54. package/dist/worker/console/chat/session-catalog.js +48 -0
  55. package/dist/worker/console/chat/session-stats.js +96 -0
  56. package/dist/worker/console/chat/session-store.js +28 -19
  57. package/dist/worker/console/chat/shortcuts.js +7 -0
  58. package/dist/worker/console/chat/sift-bridge.js +1 -0
  59. package/dist/worker/console/chat/turn-process.js +32 -3
  60. package/dist/worker/console/chat/user-questions.js +1 -1
  61. package/dist/worker/console/console-update-runtime.js +1 -1
  62. package/dist/worker/console/context-transfer-diagnostics.js +198 -0
  63. package/dist/worker/console/dag-confirmation.js +1 -1
  64. package/dist/worker/console/dag-execution-receipt.js +21 -3
  65. package/dist/worker/console/doctor.js +1 -1
  66. package/dist/worker/console/draft-store.js +1 -1
  67. package/dist/worker/console/human-gate-token.js +1 -1
  68. package/dist/worker/console/index.js +3 -3
  69. package/dist/worker/console/interview/assessment.js +1 -1
  70. package/dist/worker/console/interview/session.js +1 -1
  71. package/dist/worker/console/operation-runner.js +45 -4
  72. package/dist/worker/console/operation-sse.js +1 -1
  73. package/dist/worker/console/operation-wait.js +1 -1
  74. package/dist/worker/console/operator-actions.js +124 -8
  75. package/dist/worker/console/operator-selection.js +6 -2
  76. package/dist/worker/console/operator-surface-health.js +1 -1
  77. package/dist/worker/console/operator-user-error.js +169 -0
  78. package/dist/worker/console/pi-plugins.catalog.json +73658 -0
  79. package/dist/worker/console/pi-plugins.js +179 -0
  80. package/dist/worker/console/routes.js +864 -13
  81. package/dist/worker/console/security.js +35 -0
  82. package/dist/worker/console/server.js +177 -44
  83. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Bs-CDAXM.js → abnfDiagram-N423BO3Z-DSVcg1YO.js} +1 -1
  84. package/dist/worker/console/static/assets/{arc-CAkA3We3.js → arc-BtXhIqui.js} +1 -1
  85. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DEmI_zqr.js → architectureDiagram-T3A2C74G-B-nh-ilJ.js} +1 -1
  86. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-uDHANebE.js → blockDiagram-VBNYF7ZC-BIUSVdZI.js} +1 -1
  87. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-Bv97b4_B.js → c4Diagram-5PPSVZJV-Cmw3L4qM.js} +1 -1
  88. package/dist/worker/console/static/assets/channel-BhZFNefx.js +1 -0
  89. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-shmQKsFz.js → chunk-2GRJ4B5K-CGpMMjGm.js} +1 -1
  90. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-BAraNvcN.js → chunk-2Q5K7J3B-BNBkEpEJ.js} +1 -1
  91. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BirhxUop.js → chunk-5RXB4S5H-ubln3eaU.js} +1 -1
  92. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C9LgUJpR.js → chunk-5VM5RSS4-_XcBTt1i.js} +1 -1
  93. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DrU-s-t7.js → chunk-6Q2QTUOP-Ln30Pgrh.js} +1 -1
  94. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Tx-H_FA2.js → chunk-GF5L2VYU-ESH3lhSx.js} +1 -1
  95. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BjLtNFrR.js → chunk-JWPE2WC7-DAJt-qXB.js} +1 -1
  96. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-UNLVwSsy.js → chunk-KBJHAD2P-Cx10LIcH.js} +1 -1
  97. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CBEUX8XD.js → chunk-RYQCIY6F-CENeOxoJ.js} +1 -1
  98. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-WTL7IZOq.js → chunk-XXDRQBXY-DXqK-D38.js} +1 -1
  99. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Sbv-NJni.js +1 -0
  100. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Sbv-NJni.js +1 -0
  101. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CkHG6W3s.js → cose-bilkent-JH36ORCC-CE4dmvGY.js} +1 -1
  102. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BRzFqTaL.js → cynefin-VYW2F7L2-CRacN1zK.js} +1 -1
  103. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-2_CFp_i7.js → cynefinDiagram-MW4NZA55-Cneukjrt.js} +1 -1
  104. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BJtsmdkE.js → dagre-VZM6K2ZE-DPFboRoL.js} +1 -1
  105. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DNEFFo6w.js → diagram-7IWD3JNH-DIAQgcOP.js} +1 -1
  106. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B_IWV7eo.js → diagram-B4RE2ZJO-VrYMKxuy.js} +1 -1
  107. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DvvRx2dz.js → diagram-LBJQPF4R-CLHvBghn.js} +1 -1
  108. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-CsibsWLg.js → diagram-Q27KOJAE-DVCWBif8.js} +1 -1
  109. package/dist/worker/console/static/assets/{diagram-UB23O5K3-les_djBQ.js → diagram-UB23O5K3-BTMmbBzJ.js} +1 -1
  110. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BEm2n7qJ.js → ebnfDiagram-BXEA7PRR-BUZsKJ56.js} +1 -1
  111. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BI7vHHVm.js → erDiagram-JOGREHBK-BsBZ5NXC.js} +1 -1
  112. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CUFCERrB.js → flowDiagram-UKHOOZJN-D7fH_kDa.js} +1 -1
  113. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-wxyLiGWt.js → ganttDiagram-PKOTCBZU-Bza5OohY.js} +1 -1
  114. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bt897K8Z.js → gitGraphDiagram-DS77QQ5N-DpkoGR_Y.js} +1 -1
  115. package/dist/worker/console/static/assets/index-Ius3vxhf.css +1 -0
  116. package/dist/worker/console/static/assets/index-yASzcbBr.js +451 -0
  117. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DYNx_IPv.js → infoDiagram-6WML65LV-BYm3g6LT.js} +1 -1
  118. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-CzpLNEu6.js → ishikawaDiagram-WSZJBQD7-C2RS_H4v.js} +1 -1
  119. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-97f9owNU.js → journeyDiagram-NVQOT4AX-B4slKpCo.js} +1 -1
  120. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-BxM9hyBn.js → kanban-definition-27J2QSJJ-B7orRNpI.js} +1 -1
  121. package/dist/worker/console/static/assets/{linear-ByuxcHvp.js → linear-D1BeBxLQ.js} +1 -1
  122. package/dist/worker/console/static/assets/{mermaid.core-DwPGvpCv.js → mermaid.core-0TtMP8k4.js} +5 -5
  123. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-DCoqG9RT.js → mindmap-definition-FAOFIHXS-XL5EbCtT.js} +1 -1
  124. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BLSBmBMx.js → pegDiagram-VL7TDLO6-CwF7uas5.js} +1 -1
  125. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B0t_0oJW.js → pieDiagram-7S7Q4E2Y-A_Jo-72f.js} +1 -1
  126. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-sTzDk7Nt.js → quadrantDiagram-CIZ2JOQS-CxOa4gKL.js} +1 -1
  127. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-B3jRdATC.js → railroadDiagram-AXF67PYL-DoAoswIH.js} +1 -1
  128. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D-cTtXEN.js → requirementDiagram-LRYGKXZP-C1Lc1MlD.js} +1 -1
  129. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B25igJRE.js → sankeyDiagram-W5VNT64P-DFXOkq0U.js} +1 -1
  130. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-jW_lM1_D.js → sequenceDiagram-SI44F4Z6-B0ufUCuW.js} +1 -1
  131. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CrzTgsHE.js → sizeCapture-X5ZJPWSS-iPbW55g3.js} +1 -1
  132. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-B01MvlqE.js → stateDiagram-OKZ733FA-CHiXzEKP.js} +1 -1
  133. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BtMgzo_H.js +1 -0
  134. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-YcF12FiU.js → swimlanes-SLNWSIFB-LyFs7TpX.js} +2 -2
  135. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cb-OVd9Y.js +8 -0
  136. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-ClnRcg2s.js → timeline-definition-Z64GVDOM-tNkJnb6z.js} +1 -1
  137. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-B6gr_z-q.js → vennDiagram-T6HMQDX7-DFm7Oo8o.js} +1 -1
  138. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CvSgguGH.js → wardleyDiagram-T6FBY63Y-tpblTp9i.js} +1 -1
  139. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-23OtSZCb.js → xychartDiagram-ELKLHX3M-BCDLWR_D.js} +1 -1
  140. package/dist/worker/console/static/index.html +7 -2
  141. package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
  142. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +95 -25
  143. package/dist/worker/console/static-src/operator-chat/compaction-message.js +35 -1
  144. package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +3 -1
  145. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +26 -19
  146. package/dist/worker/console/static-src/operator-chat/useChatStream.js +0 -11
  147. package/dist/worker/console/static-src/operator-chat/useChatThread.js +14 -1
  148. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +34 -4
  149. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +28 -3
  150. package/dist/worker/console/static-src/operator-chat/workspace-reorder.js +18 -0
  151. package/dist/worker/console/static-src/shell/console-update-reload.js +25 -0
  152. package/dist/worker/console/static-src/shell/useWorkspaces.js +150 -34
  153. package/dist/worker/console/static-src/shell/workspace-route.js +3 -1
  154. package/dist/worker/console/workspace-context.js +56 -5
  155. package/dist/worker/console/workspace-registry.js +335 -62
  156. package/dist/worker/continuation/worker-continuation.js +278 -0
  157. package/dist/worker/observe/health.js +1 -1
  158. package/dist/worker/observe/node-transparency.js +572 -0
  159. package/dist/worker/observe/routes.js +51 -1
  160. package/dist/worker/observe/static/api.js +69 -5
  161. package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
  162. package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
  163. package/dist/worker/observe/static/inspect-bootstrap.d.ts +8 -0
  164. package/dist/worker/observe/static/inspect-bootstrap.js +57 -0
  165. package/dist/worker/observe/static/inspect-workspace.js +54 -10
  166. package/dist/worker/observe/static/operator-chrome.css +23 -4
  167. package/dist/worker/observe/static/operator-chrome.js +7 -2
  168. package/dist/worker/observe/static/state.js +3 -1
  169. package/dist/worker/observe/static/styles.css +1108 -135
  170. package/dist/worker/observe/static/views/dag-inspector.js +1257 -59
  171. package/dist/worker/observe/static/views/dags.js +7 -5
  172. package/dist/worker/observe/static/views/session-timeline.js +210 -10
  173. package/dist/worker/outcomes/adapters.js +19 -0
  174. package/dist/worker/outcomes/types.js +1 -0
  175. package/dist/worker/pool/attempt-lease.js +97 -66
  176. package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
  177. package/dist/worker/pool/reconcile.js +46 -1
  178. package/dist/worker/pool/run-store.js +2 -0
  179. package/dist/worker/pool/state-projection.js +2 -0
  180. package/dist/worker/task-spec/workflow-routing.js +8 -3
  181. package/dist/workflows/dag/artifact-bindings.js +149 -0
  182. package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
  183. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +162 -8
  184. package/dist/workflows/dag/backend-test-pytest-collection.js +70 -2
  185. package/dist/workflows/dag/backend-test-result-contract.js +4 -0
  186. package/dist/workflows/dag/backend-test-scenario-param.js +339 -53
  187. package/dist/workflows/dag/backend-test-writer-completeness.js +11 -0
  188. package/dist/workflows/dag/budget-enforcement.js +10 -0
  189. package/dist/workflows/dag/context-receipt.js +305 -0
  190. package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
  191. package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
  192. package/dist/workflows/dag/context-transfer/renderers.js +93 -0
  193. package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
  194. package/dist/workflows/dag/init-hybrid.js +151 -4
  195. package/dist/workflows/dag/node-execution.js +112 -27
  196. package/dist/workflows/dag/path-safety.js +1 -0
  197. package/dist/workflows/dag/prompt.js +29 -4
  198. package/dist/workflows/dag/rerun-plan.js +555 -1
  199. package/dist/workflows/dag/rerun-run.js +601 -15
  200. package/dist/workflows/dag/runner.js +20 -1
  201. package/dist/workflows/dag/skill-snapshot.js +17 -0
  202. package/dist/workflows/dag/types.js +183 -7
  203. package/docs/architecture/runtime-boundaries.md +8 -7
  204. package/docs/architecture/worker-and-feature.md +1 -1
  205. package/docs/templates/agent-dag.schema.json +32 -0
  206. package/docs/templates/backend-test-dag.json +6 -5
  207. package/docs/templates/init-managed-agents.md +1 -1
  208. package/docs/templates/product-line/task.yaml +1 -1
  209. package/package.json +5 -2
  210. package/skills/loop-agent/references/command-reference.md +21 -1
  211. package/dist/worker/console/static/assets/channel-amxUpk7o.js +0 -1
  212. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-bejwBVIz.js +0 -1
  213. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-bejwBVIz.js +0 -1
  214. package/dist/worker/console/static/assets/index-BHhUOTri.css +0 -1
  215. package/dist/worker/console/static/assets/index-DqJbO3-p.js +0 -407
  216. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BvFKEtm2.js +0 -1
  217. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DIB2eVg-.js +0 -8
  218. /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
@@ -307,6 +307,19 @@ export async function runDag(spec, opts) {
307
307
  }
308
308
  }
309
309
  export async function runDagContinuation(opts) {
310
+ assertValidDagSpec(opts.spec);
311
+ assertFrozenBudget(opts.spec.budget, opts.state.budget, opts.state.runId);
312
+ if (opts.spec.budget) {
313
+ const ledger = opts.state.budgetLedger;
314
+ if (!ledger ||
315
+ ledger.mode !== opts.spec.budget.mode ||
316
+ JSON.stringify(ledger.limits) !== JSON.stringify(opts.spec.budget.limits)) {
317
+ throw new Error(`dag continuation ${opts.state.runId} is missing a matching cumulative budget ledger`);
318
+ }
319
+ }
320
+ const { assertDagSpecAllowsNewWriterExecution, assertTaskContractBindingConsistent, } = await import("./task-contract-binding.js");
321
+ assertDagSpecAllowsNewWriterExecution(opts.spec);
322
+ await assertTaskContractBindingConsistent({ repoRoot: opts.cwd, spec: opts.spec });
310
323
  const { maxConcurrent } = resolveEffectiveMaxConcurrent(Math.max(1, opts.maxConcurrent ?? 4), opts.spec.budget);
311
324
  const state = opts.state;
312
325
  state.status = "running";
@@ -328,6 +341,7 @@ export async function runDagContinuation(opts) {
328
341
  cwd: opts.cwd,
329
342
  maxConcurrent,
330
343
  executeNode: opts.executeNode,
344
+ beforeTerminalArchive: opts.beforeTerminalArchive,
331
345
  observer: opts.observer,
332
346
  abortSignal: opts.abortSignal,
333
347
  activeRunDir,
@@ -712,6 +726,10 @@ async function executeDagCheckpoint(input) {
712
726
  }
713
727
  await notifyRunObserver(input.observer, "onRunFinish", state);
714
728
  if (!recoveryPending) {
729
+ if (input.beforeTerminalArchive) {
730
+ await input.beforeTerminalArchive({ runDir, state });
731
+ await persistState();
732
+ }
715
733
  try {
716
734
  const terminalCheckpoint = await captureWorkspaceCheckpoint(cwd);
717
735
  await writeWorkspaceCheckpoint(runDir, WORKSPACE_CHECKPOINT_TERMINAL_REL, terminalCheckpoint);
@@ -942,7 +960,8 @@ export async function finalizeTerminalRunStatus(state, taskCount, runDir, cwd) {
942
960
  // mechanical partial_failed just because prewrite FINISHED while the writer
943
961
  // was SKIPPED. Fail closed on retryable-invalid/blocked before aggregation.
944
962
  let recoveryPending = false;
945
- const skipFrontendRecovery = state.failureCategory === "controller-interrupted";
963
+ const skipFrontendRecovery = state.failureCategory === "controller-interrupted" ||
964
+ state.continuation?.writerTransaction !== undefined;
946
965
  const hasFrontendWriter = Object.keys(state.nodes).some((id) => FRONTEND_WRITER_NODE_IDS.includes(id));
947
966
  if (hasFrontendWriter && !skipFrontendRecovery) {
948
967
  const admission = await readFrontendPrewriteResult(runDir);
@@ -333,11 +333,27 @@ function parseSnapshot(value) {
333
333
  export async function cloneParentSkillSnapshotForContinuation(input) {
334
334
  const parentSnapshot = await readSkillSnapshot(input.parentRunDir, input.parentRef, { expectedRunId: input.parentRunId });
335
335
  const parentSnapshotSha256 = hashSkillSnapshot(parentSnapshot);
336
+ const initialTaskIds = input.spec
337
+ ? input.spec.tasks.map((task) => task.id)
338
+ : parentSnapshot.initialTaskIds;
339
+ const bindings = input.spec
340
+ ? collectDagSkillSnapshotBindings(input.spec, initialTaskIds)
341
+ : parentSnapshot.bindings;
342
+ const boundProfileIds = new Set(bindings.map((binding) => binding.profileId));
343
+ const profiles = parentSnapshot.profiles.filter((profile) => boundProfileIds.has(profile.id));
344
+ for (const profileId of boundProfileIds) {
345
+ if (!profiles.some((profile) => profile.id === profileId)) {
346
+ throw new DagSkillSnapshotIntegrityError(`continuation run spec requires profile missing from parent snapshot: ${profileId}`);
347
+ }
348
+ }
336
349
  const cloned = {
337
350
  ...structuredClone(parentSnapshot),
338
351
  runId: input.newRunId,
339
352
  createdAt: (input.now ?? new Date()).toISOString(),
340
353
  mode: "continuation-from-parent",
354
+ initialTaskIds,
355
+ bindings,
356
+ profiles,
341
357
  };
342
358
  const ref = await writeSkillSnapshot(input.newRunDir, cloned);
343
359
  await writeJsonAtomic(path.join(input.newRunDir, ...SKILL_SNAPSHOT_CONTINUATION_REL_PATH.split("/")), {
@@ -540,6 +556,7 @@ export function buildNodePromptFromSnapshot(input) {
540
556
  spec: input.spec,
541
557
  task: input.task,
542
558
  upstream: input.upstream,
559
+ runDir: input.runDir,
543
560
  resolvedSkills: skillNames,
544
561
  resolvedSkillInstructions,
545
562
  maxUpstreamChars: policy.resolveMaxUpstreamChars(input.task),
@@ -80,7 +80,10 @@ export const dagShellVerifyEvidenceSchema = z.object({
80
80
  selectionReasons: z.array(z.string()).default([]),
81
81
  /** Non-executing generation-time cwd/local-script validation evidence. */
82
82
  preflight: z
83
- .array(z.object({ label: z.string(), status: z.enum(["ok", "future-delivery-script"]) }))
83
+ .array(z.object({
84
+ label: z.string(),
85
+ status: z.enum(["ok", "future-delivery-script"]),
86
+ }))
84
87
  .default([]),
85
88
  /** Effective timeout applied independently to each shell command. */
86
89
  commandTimeoutMs: z.number().int().positive().optional(),
@@ -734,7 +737,58 @@ export const structuredContractOutputSchemaIds = [
734
737
  "frontend-implementation-contract-plan-patch-v1",
735
738
  "frontend-implementation-contract-revision-patch-v1",
736
739
  ];
737
- export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/, "task id must be kebab-case"),
740
+ export const dagArtifactIdentitySchema = z
741
+ .string()
742
+ .regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/, "artifact id must be stable lower-case dot/kebab syntax");
743
+ const dagArtifactRelativePathSchema = z
744
+ .string()
745
+ .min(1)
746
+ .refine((value) => !value.includes("\0"), "artifact path must not contain NUL")
747
+ .refine((value) => !value.includes("\\"), "artifact path must use POSIX separators")
748
+ .refine((value) => !value.startsWith("/") && !/^[A-Za-z]:/.test(value), "artifact path must be run-relative")
749
+ .refine((value) => value.split("/").every((segment) => segment !== "" && segment !== ".."), "artifact path must not escape the run");
750
+ export const dagProducedArtifactSchema = z
751
+ .object({
752
+ artifactId: dagArtifactIdentitySchema,
753
+ kind: z.enum(["structured", "assistant", "stdout"]),
754
+ path: dagArtifactRelativePathSchema,
755
+ mediaType: z.string().min(1),
756
+ schemaId: z.string().min(1).optional(),
757
+ revisionPolicy: z.enum(["immutable", "replace-file"]),
758
+ })
759
+ .strict()
760
+ .superRefine((value, ctx) => {
761
+ if (value.kind === "structured" && !value.schemaId) {
762
+ ctx.addIssue({
763
+ code: z.ZodIssueCode.custom,
764
+ path: ["schemaId"],
765
+ message: "structured artifact requires schemaId",
766
+ });
767
+ }
768
+ if (value.kind !== "structured" && value.schemaId) {
769
+ ctx.addIssue({
770
+ code: z.ZodIssueCode.custom,
771
+ path: ["schemaId"],
772
+ message: "only structured artifacts may declare schemaId",
773
+ });
774
+ }
775
+ if (value.revisionPolicy === "replace-file" &&
776
+ value.kind !== "structured") {
777
+ ctx.addIssue({
778
+ code: z.ZodIssueCode.custom,
779
+ path: ["revisionPolicy"],
780
+ message: "MVP revisions are limited to structured artifacts",
781
+ });
782
+ }
783
+ });
784
+ export const dagConsumedArtifactSchema = z
785
+ .object({
786
+ artifactId: dagArtifactIdentitySchema,
787
+ required: z.boolean().default(true),
788
+ })
789
+ .strict();
790
+ export const dagTaskSchema = z.object({
791
+ id: z.string().regex(/^[a-z][a-z0-9-]*$/, "task id must be kebab-case"),
738
792
  depends_on: z.array(z.string()).default([]),
739
793
  /**
740
794
  * How SKIPPED upstreams affect readiness.
@@ -797,6 +851,15 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
797
851
  shell: dagShellConfigSchema.optional(),
798
852
  static: dagStaticConfigSchema.optional(),
799
853
  outputContract: z.string().optional(),
854
+ /**
855
+ * Frozen producer declarations for run-owned artifacts. These declarations,
856
+ * together with consumesArtifacts, are the only authority for artifact
857
+ * lineage and impact analysis. Prompt/outputContract/path heuristics are
858
+ * intentionally forbidden.
859
+ */
860
+ producesArtifacts: z.array(dagProducedArtifactSchema).optional(),
861
+ /** Frozen logical artifact inputs consumed by this node. */
862
+ consumesArtifacts: z.array(dagConsumedArtifactSchema).optional(),
800
863
  outputMode: dagOutputModeSchema.optional(),
801
864
  firstProtocolLine: z.string().min(1).optional(),
802
865
  /**
@@ -825,8 +888,7 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
825
888
  })
826
889
  .strict()
827
890
  .superRefine((value, ctx) => {
828
- if (value.schemaId ===
829
- "frontend-implementation-contract-plan-patch-v1" &&
891
+ if (value.schemaId === "frontend-implementation-contract-plan-patch-v1" &&
830
892
  !value.skeleton) {
831
893
  ctx.addIssue({
832
894
  code: "custom",
@@ -881,6 +943,121 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
881
943
  dynamicCondition: dagDynamicConditionSchema.optional(),
882
944
  dynamicLoopUntil: dagDynamicLoopUntilSchema.optional(),
883
945
  });
946
+ function hasTransitiveDependency(tasksById, consumerNodeId, producerNodeId) {
947
+ const queue = [...(tasksById.get(consumerNodeId)?.depends_on ?? [])];
948
+ const seen = new Set();
949
+ while (queue.length > 0) {
950
+ const nodeId = queue.shift();
951
+ if (nodeId === producerNodeId)
952
+ return true;
953
+ if (seen.has(nodeId))
954
+ continue;
955
+ seen.add(nodeId);
956
+ queue.push(...(tasksById.get(nodeId)?.depends_on ?? []));
957
+ }
958
+ return false;
959
+ }
960
+ function validateFrozenArtifactBindings(tasks, ctx) {
961
+ const tasksById = new Map(tasks.map((task) => [task.id, task]));
962
+ const producers = new Map();
963
+ const paths = new Map();
964
+ for (const [taskIndex, task] of tasks.entries()) {
965
+ const localIds = new Set();
966
+ for (const [artifactIndex, artifact] of (task.producesArtifacts ?? []).entries()) {
967
+ if (localIds.has(artifact.artifactId) ||
968
+ producers.has(artifact.artifactId)) {
969
+ ctx.addIssue({
970
+ code: z.ZodIssueCode.custom,
971
+ path: [
972
+ "tasks",
973
+ taskIndex,
974
+ "producesArtifacts",
975
+ artifactIndex,
976
+ "artifactId",
977
+ ],
978
+ message: `artifact ${artifact.artifactId} must have exactly one producer`,
979
+ });
980
+ }
981
+ else {
982
+ producers.set(artifact.artifactId, {
983
+ nodeId: task.id,
984
+ taskIndex,
985
+ artifactIndex,
986
+ path: artifact.path,
987
+ });
988
+ }
989
+ localIds.add(artifact.artifactId);
990
+ const foldedPath = artifact.path
991
+ .normalize("NFC")
992
+ .toLocaleLowerCase("en-US");
993
+ const existingArtifactId = paths.get(foldedPath);
994
+ if (existingArtifactId && existingArtifactId !== artifact.artifactId) {
995
+ ctx.addIssue({
996
+ code: z.ZodIssueCode.custom,
997
+ path: [
998
+ "tasks",
999
+ taskIndex,
1000
+ "producesArtifacts",
1001
+ artifactIndex,
1002
+ "path",
1003
+ ],
1004
+ message: `artifact path collides with ${existingArtifactId} under case folding`,
1005
+ });
1006
+ }
1007
+ else {
1008
+ paths.set(foldedPath, artifact.artifactId);
1009
+ }
1010
+ }
1011
+ }
1012
+ for (const [taskIndex, task] of tasks.entries()) {
1013
+ const consumed = new Set();
1014
+ for (const [artifactIndex, binding] of (task.consumesArtifacts ?? []).entries()) {
1015
+ if (consumed.has(binding.artifactId)) {
1016
+ ctx.addIssue({
1017
+ code: z.ZodIssueCode.custom,
1018
+ path: [
1019
+ "tasks",
1020
+ taskIndex,
1021
+ "consumesArtifacts",
1022
+ artifactIndex,
1023
+ "artifactId",
1024
+ ],
1025
+ message: `artifact ${binding.artifactId} is consumed more than once by ${task.id}`,
1026
+ });
1027
+ }
1028
+ consumed.add(binding.artifactId);
1029
+ const producer = producers.get(binding.artifactId);
1030
+ if (!producer) {
1031
+ ctx.addIssue({
1032
+ code: z.ZodIssueCode.custom,
1033
+ path: [
1034
+ "tasks",
1035
+ taskIndex,
1036
+ "consumesArtifacts",
1037
+ artifactIndex,
1038
+ "artifactId",
1039
+ ],
1040
+ message: `artifact ${binding.artifactId} has no declared producer`,
1041
+ });
1042
+ continue;
1043
+ }
1044
+ if (producer.nodeId === task.id ||
1045
+ !hasTransitiveDependency(tasksById, task.id, producer.nodeId)) {
1046
+ ctx.addIssue({
1047
+ code: z.ZodIssueCode.custom,
1048
+ path: [
1049
+ "tasks",
1050
+ taskIndex,
1051
+ "consumesArtifacts",
1052
+ artifactIndex,
1053
+ "artifactId",
1054
+ ],
1055
+ message: `consumer ${task.id} must be downstream of producer ${producer.nodeId}`,
1056
+ });
1057
+ }
1058
+ }
1059
+ }
1060
+ }
884
1061
  export const dagModelsSchema = z
885
1062
  .object({
886
1063
  HIGH: z.string().optional(),
@@ -910,9 +1087,7 @@ export const dagBudgetSchema = campaignBudgetSchema;
910
1087
  export const dagSourceBindingSourceSchema = z.object({
911
1088
  kind: z.enum(["requirement", "constraint", "reference"]),
912
1089
  path: z.string().min(1),
913
- sha256: z
914
- .string()
915
- .regex(/^[a-f0-9]{64}$/, "sha256 must be lowercase hex"),
1090
+ sha256: z.string().regex(/^[a-f0-9]{64}$/, "sha256 must be lowercase hex"),
916
1091
  });
917
1092
  /**
918
1093
  * DagSourceBinding v1:无 ledger 字段(schemaVersion=1,.strict() 拒绝 ledger
@@ -1063,6 +1238,7 @@ export const dagSpecSchema = z
1063
1238
  })
1064
1239
  .superRefine((spec, ctx) => {
1065
1240
  const supportsV3Fields = spec.version === 3 || spec.version === 4;
1241
+ validateFrozenArtifactBindings(spec.tasks, ctx);
1066
1242
  if (spec.evaluation && !supportsV3Fields) {
1067
1243
  ctx.addIssue({
1068
1244
  code: z.ZodIssueCode.custom,
@@ -160,18 +160,18 @@ Runner / Loop ──(迁移中)──> 逐步改为仅经 Store / Appli
160
160
 
161
161
  | From | May import | Must not import |
162
162
  | ------ | ------------ | ----------------- |
163
- | `src/commands/**` | application, workflows, infrastructure, task, records, executors, shared | — |
164
- | `src/workflows/**` | executors, task, records, shared, application(目标) | `src/commands/**` |
163
+ | `src/commands/**` | application, workflows, infrastructure, task, records, executors, shared | `src/worker/**` |
164
+ | `src/workflows/**` | executors, task, records, shared, infrastructure, application(目标) | `src/commands/**` |
165
165
  | `src/executors/**` | shared, 外部 SDK | `src/commands/**`, `src/cli/**` |
166
- | `src/worker/**` | worker-local modules, task contracts, shared, Node APIs | `src/commands/**`, `src/cli/**`, `src/application/**` |
167
- | `src/cli/**` | commands, adapters, shared, application(目标) | — |
166
+ | `src/worker/**` | worker-local modules, task contracts, shared, infrastructure, Node APIs | `src/commands/**`, `src/cli/**`, `src/application/**` |
167
+ | `src/cli/**` | commands, adapters, shared, infrastructure, application(目标) | — |
168
168
  | `.agents/skills/**` | (Markdown 引用,非 TS import) | 运行时 TS 模块 |
169
169
 
170
170
  ## 已知过渡耦合
171
171
 
172
- 当前没有允许的 `src/workflows/**` / `src/executors/**` forbidden import,也没有允许的 `src/worker/**` → `src/{cli,commands,application}/**` TypeScript import。
172
+ 当前没有允许的 `src/workflows/**` / `src/executors/**` forbidden import,也没有允许的 `src/worker/**` → `src/{cli,commands,application}/**` 或 `src/commands/**` → `src/worker/**` TypeScript import。Console app-data 存储(operations、context exports、artifact revisions、app-data path helpers)位于 `src/infrastructure/console/**`,由 Console(worker 层)与 CLI 命令共同向下依赖;Console 专属的 route/action 编排仍留在 `src/worker/console/**`。
173
173
 
174
- `scripts/check-architecture-boundaries.sh` 的 transitional allowlist 保持为空。任何新增的 `workflows/executors/worker` forbidden import 必须导致检查 **exit 1**;如果未来确有临时例外,必须先写入 active exec plan,说明移除时间和验证门禁。
174
+ `scripts/check-architecture-boundaries.sh` 的 transitional allowlist 保持为空。任何新增的 `workflows/executors/worker/commands` forbidden import 必须导致检查 **exit 1**;如果未来确有临时例外,必须先写入 active exec plan,说明移除时间和验证门禁。
175
175
 
176
176
  ## Governance 钩子
177
177
 
@@ -207,7 +207,8 @@ Runtime 变更另需 `npm run typecheck` 及对应 targeted Vitest(见 exec pl
207
207
  | --- | --- | --- |
208
208
  | OpenCode transient-retry | `.opencode/plugins/loop-agent-transient-retry.js`(init surface `generated`) | 仅瞬态 UnknownError resume;busy/retry 门控 + backoff;overflow/401/配额/取消/业务标 permanent(`plugin-ignore-permanent-error`),**不** compact |
209
209
  | OpenCode overflow-compact | `.opencode/plugins/loop-agent-context-overflow-compact.js`(init surface `generated`) | 仅 context overflow → `session.compact`(无则 `summarize`)+ 有限 `promptAsync` 续接;`MAX_OVERFLOW_RECOVERIES ≤ 2`;session 锁;非 overflow 不动作 |
210
- | Pi project extension | `.pi/extensions/loop-agent-context-overflow.js`(init surface `generated`) | `message_end` 上将 overflow `errorMessage` 幂等改写为 `context_length_exceeded:` 前缀,触发 Pi 原生 compact/retry;**不**写 `~/.pi` settings / compaction / 模型窗口 |
210
+ | Pi project extension | `.pi/extensions/loop-agent-context-overflow.js`(init surface `generated`) | `message_end` 上将 overflow `errorMessage` 幂等改写为 `context_length_exceeded:` 前缀,触发 Pi 原生 compact/retry;分类器与控制器共享 P1–P10 / N1–N5 语料(含嵌套 JSON);**不**写 `~/.pi` settings / compaction / 模型窗口,**不**投影 sift native |
211
+ | Console / DAG 运行时 | npm 内置 inline extension + 可选 `@agent-context/sift` | 默认开启 L0/L1/L2;`HARNESS_CONTEXT_PRESSURE_MODE=off` 关 L0/L2;`HARNESS_CONSOLE_SIFT` / `HARNESS_DAG_SIFT` 的 `0\|false\|off` 关 L1。DAG 不做 streaming 主动 compact。不进入目标项目 init 文件 |
211
212
  | Pi 用户配置 | `~/.pi/agent/settings.json` | **不**进入项目 `.harness/init-surface.json` hash;仅 `--client-recovery=user` 可字段级补缺并原子写;`auto`/`project`/`off` 与 `check-update` 默认零写 home;读取时只有 `ENOENT` 视为缺文件,其他 I/O 错误 fail closed |
212
213
  | CLI mode | `--client-recovery=auto\|project\|user\|off`(默认 `auto`) | `auto`/`project` 安装上述三份项目产物;`user` = 三产物 + 显式 Pi retry 合并;`off` 全跳过 |
213
214
  | Ownership | recorded sha256 + apply-safe | 三份 generated 缺失可补、与 recorded hash 一致可升级;用户改过 → model merge / human decision,禁止静默覆盖 |
@@ -61,7 +61,7 @@ controller identity 与 DAG skill snapshot 是两个不同冻结层(前者跨
61
61
 
62
62
  - schema/validate:`src/worker/task-spec/{schema,validate}.ts`。
63
63
  - 校验验收条件、依赖、验证命令;`agent-worker task validate-feature` 等用之。
64
- - **0.16.0+**:可选 `execution.workflow` 映射到 loop-agent `taskKind`(如 agent-dag / frontend-implementation / backend-test / frontend-test);typed Task Outcome 与 artifact-aware Ready 门禁见 completed `2026-07-19-taskspec-workflow-routing.md`。
64
+ - **0.16.0+**:可选 `execution.workflow` 映射到 loop-agent `taskKind`(agent-dag / frontend-implementation / backend-test-analysis / backend-test / frontend-test);`backend-test-analysis` 只读生成严格 analysis artifact,不执行测试;typed Task Outcome 与 artifact-aware Ready 门禁见 completed `2026-07-19-taskspec-workflow-routing.md`。
65
65
  - 文档映射镜像:`docs/architecture/taskspec-to-loop-agent-mapping.md`(runtime 真源仍在 `src/worker/` 与 materialize 路径)。
66
66
 
67
67
  ### Task Pool
@@ -483,6 +483,28 @@
483
483
  "sha256": { "type": "string", "minLength": 1 }
484
484
  }
485
485
  },
486
+ "producedArtifact": {
487
+ "type": "object",
488
+ "additionalProperties": false,
489
+ "required": ["artifactId", "kind", "path", "mediaType", "revisionPolicy"],
490
+ "properties": {
491
+ "artifactId": { "type": "string", "pattern": "^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$" },
492
+ "kind": { "enum": ["structured", "assistant", "stdout"] },
493
+ "path": { "type": "string", "pattern": "^(?!/)(?![A-Za-z]:)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*\\\\).+$" },
494
+ "mediaType": { "type": "string", "minLength": 1 },
495
+ "schemaId": { "type": "string", "minLength": 1 },
496
+ "revisionPolicy": { "enum": ["immutable", "replace-file"] }
497
+ }
498
+ },
499
+ "consumedArtifact": {
500
+ "type": "object",
501
+ "additionalProperties": false,
502
+ "required": ["artifactId"],
503
+ "properties": {
504
+ "artifactId": { "type": "string", "pattern": "^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$" },
505
+ "required": { "type": "boolean", "default": true }
506
+ }
507
+ },
486
508
  "task": {
487
509
  "type": "object",
488
510
  "additionalProperties": false,
@@ -570,6 +592,16 @@
570
592
  "type": "string",
571
593
  "minLength": 1
572
594
  },
595
+ "producesArtifacts": {
596
+ "type": "array",
597
+ "items": { "$ref": "#/$defs/producedArtifact" },
598
+ "description": "Frozen producer declarations. Artifact lineage and impact analysis must use these declarations, never prompt/outputContract/path heuristics."
599
+ },
600
+ "consumesArtifacts": {
601
+ "type": "array",
602
+ "items": { "$ref": "#/$defs/consumedArtifact" },
603
+ "description": "Frozen logical artifact inputs consumed by this node."
604
+ },
573
605
  "outputMode": {
574
606
  "enum": ["default", "structured-required"],
575
607
  "description": "Optional output semantics for Pi nodes. structured-required means oversized assistant output must not be accepted as a truncated success; eligible read-only nodes may compact-retry and downstream gates should rely on canonical structured artifacts."