@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
@@ -19,36 +19,11 @@ export const MAX_SESSION_RETRIES = 5;
19
19
  /** Hard cap for OpenCode overflow compact+resume recoveries (≤ 2). */
20
20
  export const MAX_OVERFLOW_RECOVERIES = 1;
21
21
  import { PI_RECOMMENDED_COMPACTION, PI_RECOMMENDED_RETRY, } from "../shared/pi-retry-settings.js";
22
+ import { CONTEXT_OVERFLOW_PHRASES, buildGeneratedOverflowClassifierSource, isContextOverflow as classifySharedOverflow, } from "../shared/pi-context-pressure/overflow.js";
22
23
  export { PI_RECOMMENDED_COMPACTION, PI_RECOMMENDED_RETRY };
23
- /**
24
- * Shared overflow phrase sources used by isContextOverflow and generated plugins.
25
- * Keep English + Chinese intranet gateway messages in one place to avoid drift.
26
- */
27
- export const CONTEXT_OVERFLOW_PHRASES = [
28
- "请求上下文过大",
29
- "context_length_exceeded",
30
- "context overflow",
31
- "context length overflow",
32
- "too many tokens",
33
- "maximum context",
34
- "prompt is too long",
35
- "request_too_large",
36
- "context window",
37
- ];
38
- function escapeRegExpLiteral(value) {
39
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
40
- }
41
- const CONTEXT_OVERFLOW_PATTERN = new RegExp(CONTEXT_OVERFLOW_PHRASES.map(escapeRegExpLiteral).join("|"), "i");
24
+ export { CONTEXT_OVERFLOW_PHRASES };
42
25
  function buildGeneratedContextOverflowPatternSource() {
43
- return `const CONTEXT_OVERFLOW_PHRASES = ${JSON.stringify([
44
- ...CONTEXT_OVERFLOW_PHRASES,
45
- ])};
46
- const OVERFLOW_PATTERN = new RegExp(
47
- CONTEXT_OVERFLOW_PHRASES.map((phrase) =>
48
- phrase.replace(/[.*+?^\${}()|[\\]\\\\]/g, "\\\\$&"),
49
- ).join("|"),
50
- "i",
51
- );`;
26
+ return buildGeneratedOverflowClassifierSource();
52
27
  }
53
28
  const PERMANENT_MESSAGE_PATTERNS = [
54
29
  {
@@ -65,7 +40,7 @@ const PERMANENT_MESSAGE_PATTERNS = [
65
40
  },
66
41
  {
67
42
  reason: "context-overflow",
68
- pattern: CONTEXT_OVERFLOW_PATTERN,
43
+ pattern: /context_length_exceeded|请求上下文过大/i,
69
44
  },
70
45
  {
71
46
  reason: "cancelled",
@@ -180,6 +155,8 @@ function permanentReason(error) {
180
155
  return "api-error";
181
156
  }
182
157
  const text = collectErrorText(error);
158
+ if (isContextOverflow(error))
159
+ return "context-overflow";
183
160
  for (const entry of PERMANENT_MESSAGE_PATTERNS) {
184
161
  if (entry.pattern.test(text))
185
162
  return entry.reason;
@@ -192,7 +169,7 @@ function permanentReason(error) {
192
169
  * business validation, or standard 502/LLMRequestError as overflow.
193
170
  */
194
171
  export function isContextOverflow(error) {
195
- return CONTEXT_OVERFLOW_PATTERN.test(collectErrorText(error));
172
+ return classifySharedOverflow(error);
196
173
  }
197
174
  function looksTransient(error) {
198
175
  const text = collectErrorText(error);
@@ -887,7 +864,7 @@ function textOf(error) {
887
864
  }
888
865
 
889
866
  function isContextOverflow(error) {
890
- return OVERFLOW_PATTERN.test(textOf(error));
867
+ return isOverflowMessage(textOf(error));
891
868
  }
892
869
 
893
870
  function buildOverflowResumePrompt(sessionId, error, attempt) {
@@ -1163,11 +1140,6 @@ const EXTENSION_PATH = ${JSON.stringify(PI_CONTEXT_OVERFLOW_EXTENSION_PATH)};
1163
1140
  ${buildGeneratedContextOverflowPatternSource()}
1164
1141
  const NORMALIZED_PREFIX = "context_length_exceeded:";
1165
1142
 
1166
- function isOverflowMessage(text) {
1167
- if (typeof text !== "string" || !text) return false;
1168
- return OVERFLOW_PATTERN.test(text);
1169
- }
1170
-
1171
1143
  export default function loopAgentContextOverflowExtension(pi) {
1172
1144
  void EXTENSION_PATH;
1173
1145
  pi.on("message_end", (event) => {
@@ -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
+ }