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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +319 -1
  3. package/dist/application/dag/generate-task-dag.js +75 -19
  4. package/dist/application/dag/run-dag.js +41 -0
  5. package/dist/application/task-lifecycle/advance.js +24 -5
  6. package/dist/application/task-lifecycle/observe.js +171 -17
  7. package/dist/application/task-lifecycle/plan-transitions.js +42 -7
  8. package/dist/application/task-lifecycle/recommendations.js +13 -2
  9. package/dist/build-stamp.json +3 -3
  10. package/dist/cli/command-definitions.js +7 -0
  11. package/dist/cli/program.js +6 -1
  12. package/dist/commands/client-recovery.js +3 -0
  13. package/dist/commands/dag-follow-up.js +138 -0
  14. package/dist/commands/dag-rerun-task.js +2 -0
  15. package/dist/commands/init.js +27 -1
  16. package/dist/commands/task-advance.js +19 -0
  17. package/dist/executors/dag-pi-executor.js +3976 -76
  18. package/dist/executors/pi-executor.js +15 -4
  19. package/dist/executors/pi-read-budget-policy.js +239 -0
  20. package/dist/executors/shell-executor.js +978 -188
  21. package/dist/executors/shell-write-guard.js +7 -0
  22. package/dist/infrastructure/console/operation-store.js +226 -6
  23. package/dist/shared/dag-failure-category.js +12 -0
  24. package/dist/shared/openspec-spec.js +70 -4
  25. package/dist/shared/operator/capabilities.js +7 -0
  26. package/dist/task/config-types.js +105 -5
  27. package/dist/task/contract/adopt.js +4 -0
  28. package/dist/task/contract/import-revision.js +4 -0
  29. package/dist/task/contract/project.js +3 -0
  30. package/dist/task/contract/schema.js +2 -1
  31. package/dist/task/frontend-project-capability.js +203 -20
  32. package/dist/task/runtime.js +5 -2
  33. package/dist/task/source-prepare/build-draft.js +3 -3
  34. package/dist/task/source-prepare/fragment-inventory.js +64 -25
  35. package/dist/task/source-prepare/prepare.js +126 -21
  36. package/dist/task/source-prepare/semantic-intake.js +6 -2
  37. package/dist/task/source-references.js +22 -1
  38. package/dist/worker/console/chat/chat-ui-policy.js +4 -2
  39. package/dist/worker/console/chat/model-resolver.js +7 -4
  40. package/dist/worker/console/chat/pi-runtime.js +258 -11
  41. package/dist/worker/console/chat/repo-browser.js +10 -3
  42. package/dist/worker/console/chat/routes.js +165 -71
  43. package/dist/worker/console/chat/sdd-data-alignment.js +222 -0
  44. package/dist/worker/console/chat/session-catalog.js +2 -0
  45. package/dist/worker/console/chat/session-store.js +159 -70
  46. package/dist/worker/console/chat/shortcuts.js +19 -3
  47. package/dist/worker/console/chat/turn-process.js +111 -56
  48. package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
  49. package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
  50. package/dist/worker/console/index.js +3 -0
  51. package/dist/worker/console/operator-actions.js +169 -7
  52. package/dist/worker/console/pi-readiness.js +3 -2
  53. package/dist/worker/console/server.js +23 -1
  54. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-DZZ8m3PO.js → abnfDiagram-N423BO3Z-CcS17TBr.js} +1 -1
  55. package/dist/worker/console/static/assets/{arc-D6PvaVd-.js → arc-COptKq2S.js} +1 -1
  56. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-B_OTOiI8.js → architectureDiagram-T3A2C74G-h4LKMHjP.js} +1 -1
  57. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Bv6rqHBg.js → blockDiagram-VBNYF7ZC-COA1MOH0.js} +1 -1
  58. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B8eHr0oz.js → c4Diagram-5PPSVZJV-DJUf0QPm.js} +1 -1
  59. package/dist/worker/console/static/assets/channel-DdBCaOJ6.js +1 -0
  60. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DYwR0im2.js → chunk-2GRJ4B5K-mtWfKrUX.js} +1 -1
  61. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-D2WPGqXt.js → chunk-2Q5K7J3B-B8pXsxDQ.js} +1 -1
  62. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CQISJ_I7.js → chunk-5RXB4S5H-ipKzByl1.js} +1 -1
  63. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C0o2Du1e.js → chunk-5VM5RSS4-DYi3Ald_.js} +1 -1
  64. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-4f8kr-U7.js → chunk-6Q2QTUOP-DQtGYoty.js} +1 -1
  65. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D_OWvXzX.js → chunk-GF5L2VYU-BU0qS2YV.js} +1 -1
  66. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CasdPz5X.js → chunk-JWPE2WC7-DVH9dIGE.js} +1 -1
  67. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Cj8lRrla.js → chunk-KBJHAD2P-Dt60SmgM.js} +1 -1
  68. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CUvD1FSd.js → chunk-RYQCIY6F-BoaUGuEY.js} +1 -1
  69. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CqLw_eqB.js → chunk-XXDRQBXY-_WHFDTp2.js} +1 -1
  70. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DZFra1GO.js +1 -0
  71. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DZFra1GO.js +1 -0
  72. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-ku-WqJPx.js → cose-bilkent-JH36ORCC-BxkbTIRd.js} +1 -1
  73. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-Bq_PYDhl.js → cynefin-VYW2F7L2-Bf2UVnoG.js} +1 -1
  74. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BiGZV641.js → cynefinDiagram-MW4NZA55-Bo23q1J_.js} +1 -1
  75. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-C4JRlglF.js → dagre-VZM6K2ZE-Dd_UU49i.js} +1 -1
  76. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-B0dNeWYG.js → diagram-7IWD3JNH-ebUa1a9y.js} +1 -1
  77. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Cj35YvYM.js → diagram-B4RE2ZJO-BucthU8r.js} +1 -1
  78. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-uzQoJ2-8.js → diagram-LBJQPF4R-BgqNpAkm.js} +1 -1
  79. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D1a-Buoz.js → diagram-Q27KOJAE-DC4q5NGa.js} +1 -1
  80. package/dist/worker/console/static/assets/{diagram-UB23O5K3-XjRrLRSs.js → diagram-UB23O5K3-CBqCaMeb.js} +1 -1
  81. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Da_O24cW.js → ebnfDiagram-BXEA7PRR-CGtnbQ3-.js} +1 -1
  82. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BLJ8jrYU.js → erDiagram-JOGREHBK-CG3LUao5.js} +1 -1
  83. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-B9GrLjM0.js → flowDiagram-UKHOOZJN-D2ZVgoFS.js} +1 -1
  84. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CeJ0TqiK.js → ganttDiagram-PKOTCBZU-DoYFZiKt.js} +1 -1
  85. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bm2eNNFX.js → gitGraphDiagram-DS77QQ5N-CKoP1s6j.js} +1 -1
  86. package/dist/worker/console/static/assets/index-CAZ2fC_X.css +1 -0
  87. package/dist/worker/console/static/assets/index-vbTcFnFs.js +449 -0
  88. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DB26i3d8.js → infoDiagram-6WML65LV-Duofv8p2.js} +1 -1
  89. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BOGRyeZT.js → ishikawaDiagram-WSZJBQD7-D2nlCkA1.js} +1 -1
  90. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CZRoBO_6.js → journeyDiagram-NVQOT4AX-Dd4IHdus.js} +1 -1
  91. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-qlWhJyoB.js → kanban-definition-27J2QSJJ-Bi20AOdt.js} +1 -1
  92. package/dist/worker/console/static/assets/{linear-CfUiDDB3.js → linear-D59YJ9kB.js} +1 -1
  93. package/dist/worker/console/static/assets/{mermaid.core-BQe6fpqj.js → mermaid.core-Bl13LpNa.js} +5 -5
  94. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-zFzWHw64.js → mindmap-definition-FAOFIHXS-C6DLP5eY.js} +1 -1
  95. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DFGUqjZO.js → pegDiagram-VL7TDLO6-BjO4cy64.js} +1 -1
  96. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BB5i0l8Z.js → pieDiagram-7S7Q4E2Y-D7i2qZTG.js} +1 -1
  97. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-obbVZ_X5.js → quadrantDiagram-CIZ2JOQS-Dx8o_h0y.js} +1 -1
  98. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BwdaNzc1.js → railroadDiagram-AXF67PYL-B55orI_t.js} +1 -1
  99. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CkTJaugh.js → requirementDiagram-LRYGKXZP-zWaehp6f.js} +1 -1
  100. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BwJ-hIgq.js → sankeyDiagram-W5VNT64P-CcA-pjvD.js} +1 -1
  101. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BzSgmm7w.js → sequenceDiagram-SI44F4Z6-BOFbzNHI.js} +1 -1
  102. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-Bn3oj5Q1.js → sizeCapture-X5ZJPWSS-BjAejah1.js} +1 -1
  103. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-dxhGtL8K.js → stateDiagram-OKZ733FA-DXYwxJwZ.js} +1 -1
  104. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Clg3V9t1.js +1 -0
  105. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BLpEInya.js → swimlanes-SLNWSIFB-OYiOch8n.js} +2 -2
  106. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DX1dxAAW.js +8 -0
  107. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-ChJGYcXy.js → timeline-definition-Z64GVDOM-cZfH3nmU.js} +1 -1
  108. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DK-qjRer.js → vennDiagram-T6HMQDX7-BDE7b3E1.js} +1 -1
  109. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-3qeaWAg-.js → wardleyDiagram-T6FBY63Y-kyyGy9WJ.js} +1 -1
  110. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CootlyP9.js → xychartDiagram-ELKLHX3M-CJj6VTog.js} +1 -1
  111. package/dist/worker/console/static/index.html +2 -2
  112. package/dist/worker/console/static-src/active-run-badge.js +17 -0
  113. package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
  114. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +19 -8
  115. package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
  116. package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +15 -7
  117. package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +10 -0
  118. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +258 -23
  119. package/dist/worker/console/static-src/operator-chat/useChatThread.js +112 -4
  120. package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -4
  121. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +29 -6
  122. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +8 -2
  123. package/dist/worker/console/static-src/shell/console-update-reload.js +36 -9
  124. package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
  125. package/dist/worker/console/workspace-context.js +115 -1
  126. package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
  127. package/dist/worker/observe/routes.js +4 -0
  128. package/dist/worker/observe/static/dag-helpers.js +4 -2
  129. package/dist/worker/observe/static/dag-node-purpose.js +5 -0
  130. package/dist/worker/observe/static/inspect-workspace.js +23 -0
  131. package/dist/worker/observe/static/kpi.js +2 -2
  132. package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
  133. package/dist/worker/observe/static/operator-chrome.js +37 -23
  134. package/dist/worker/observe/static/relations.js +2 -2
  135. package/dist/worker/observe/static/router.d.ts +12 -1
  136. package/dist/worker/observe/static/router.js +48 -6
  137. package/dist/worker/observe/static/run-processing.js +4 -2
  138. package/dist/worker/observe/static/shell-chrome.js +2 -2
  139. package/dist/worker/observe/static/state.js +5 -0
  140. package/dist/worker/observe/static/styles.css +95 -0
  141. package/dist/worker/observe/static/views/dag-inspector.js +14 -7
  142. package/dist/worker/observe/static/views/dag.js +10 -2
  143. package/dist/worker/observe/static/views/dags.js +3 -1
  144. package/dist/worker/observe/static/views/dashboard.js +14 -6
  145. package/dist/worker/observe/static/views/pool.js +7 -2
  146. package/dist/worker/observe/static/views/run.js +12 -3
  147. package/dist/worker/observe/static/views/session-timeline.js +69 -10
  148. package/dist/worker/observe/static/views/task.js +7 -2
  149. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +215 -28
  150. package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
  151. package/dist/workflows/dag/backend-test-plan-protocol.js +106 -0
  152. package/dist/workflows/dag/backend-test-scenario-param.js +172 -25
  153. package/dist/workflows/dag/backend-test-scenario-partitions.js +62 -1
  154. package/dist/workflows/dag/backend-test-writer-completeness.js +104 -12
  155. package/dist/workflows/dag/contract-validator-registrations.js +1 -2
  156. package/dist/workflows/dag/dag-retry-schema.js +138 -0
  157. package/dist/workflows/dag/frontend-closeout.js +221 -0
  158. package/dist/workflows/dag/frontend-design-policy.js +400 -0
  159. package/dist/workflows/dag/frontend-human-decision.js +182 -0
  160. package/dist/workflows/dag/frontend-implementation-contract.js +1237 -192
  161. package/dist/workflows/dag/frontend-plan-render.js +2 -1
  162. package/dist/workflows/dag/frontend-prewrite-gate.js +256 -350
  163. package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
  164. package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
  165. package/dist/workflows/dag/frontend-recovery-controller.js +226 -0
  166. package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
  167. package/dist/workflows/dag/frontend-recovery-plan.js +21 -10
  168. package/dist/workflows/dag/frontend-recovery-run.js +166 -34
  169. package/dist/workflows/dag/frontend-repair.js +1 -432
  170. package/dist/workflows/dag/frontend-review-context.js +261 -15
  171. package/dist/workflows/dag/frontend-review-findings.js +270 -0
  172. package/dist/workflows/dag/frontend-shadow-dual-write.js +941 -0
  173. package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
  174. package/dist/workflows/dag/frontend-shape-facts.js +419 -0
  175. package/dist/workflows/dag/frontend-shape.js +435 -0
  176. package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
  177. package/dist/workflows/dag/frontend-split-application-service.js +203 -0
  178. package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
  179. package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
  180. package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
  181. package/dist/workflows/dag/frontend-verification-trace.js +252 -25
  182. package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
  183. package/dist/workflows/dag/frontend-writer-admission.js +319 -0
  184. package/dist/workflows/dag/frontend-writer-status.js +256 -0
  185. package/dist/workflows/dag/init-hybrid.js +1028 -562
  186. package/dist/workflows/dag/interrupt-request.js +7 -0
  187. package/dist/workflows/dag/node-execution.js +828 -4
  188. package/dist/workflows/dag/prompt.js +70 -3
  189. package/dist/workflows/dag/recovery-lease.js +170 -0
  190. package/dist/workflows/dag/report.js +37 -1
  191. package/dist/workflows/dag/rerun-feedback.js +315 -1
  192. package/dist/workflows/dag/rerun-plan.js +17 -6
  193. package/dist/workflows/dag/rerun-run.js +44 -6
  194. package/dist/workflows/dag/rerun-task.js +251 -13
  195. package/dist/workflows/dag/retry-policy.js +219 -104
  196. package/dist/workflows/dag/runner.js +580 -124
  197. package/dist/workflows/dag/scheduler.js +133 -20
  198. package/dist/workflows/dag/types.js +244 -16
  199. package/dist/workflows/dag/validate.js +28 -12
  200. package/docs/examples/README.md +5 -0
  201. package/docs/init-surface.manifest.json +30 -12
  202. package/docs/skills/vetted-skill-registry.md +4 -2
  203. package/docs/templates/README.md +2 -0
  204. package/docs/templates/agent-dag-report.schema.json +8 -2
  205. package/docs/templates/agent-dag.schema.json +1 -1
  206. package/docs/templates/backend-test-dag.json +23 -20
  207. package/docs/templates/frontend-implementation-contract.schema.json +4 -1
  208. package/docs/templates/frontend-implementation-dag.json +89 -0
  209. package/docs/templates/spec-registry.schema.json +45 -0
  210. package/harness.json +1 -1
  211. package/package.json +4 -3
  212. package/skills/frontend-bounded-implement/SKILL.md +15 -14
  213. package/skills/frontend-bounded-implement/references/code-standards.md +19 -0
  214. package/skills/frontend-contract/SKILL.md +23 -0
  215. package/skills/frontend-contract/references/contract-protocol.md +34 -0
  216. package/skills/frontend-design-review/SKILL.md +22 -41
  217. package/skills/frontend-plan/SKILL.md +26 -0
  218. package/skills/frontend-plan/references/decision-contract.md +37 -0
  219. package/skills/frontend-plan/references/design-decisions.md +17 -0
  220. package/skills/frontend-review/SKILL.md +20 -15
  221. package/skills/frontend-review/references/review-findings.md +6 -7
  222. package/skills/frontend-scout/SKILL.md +25 -0
  223. package/skills/frontend-scout/references/design-evidence.md +16 -0
  224. package/skills/frontend-scout/references/scout-evidence.md +23 -0
  225. package/skills/frontend-verification/SKILL.md +1 -1
  226. package/skills/loop-agent/references/command-reference.md +1 -0
  227. package/skills/loop-agent/references/hybrid-dag.md +2 -2
  228. package/dist/worker/console/static/assets/channel-BU5gOilw.js +0 -1
  229. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DIzKJGHr.js +0 -1
  230. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DIzKJGHr.js +0 -1
  231. package/dist/worker/console/static/assets/index-H9rFJiGL.css +0 -1
  232. package/dist/worker/console/static/assets/index-xwu9GxEc.js +0 -451
  233. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cld2qK9v.js +0 -1
  234. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-TIHpiT7w.js +0 -8
  235. package/skills/frontend-implementation/SKILL.md +0 -52
  236. package/skills/frontend-implementation/references/code-standards.md +0 -33
  237. package/skills/frontend-implementation/references/design-spec.md +0 -56
  238. package/skills/frontend-implementation/references/node-contracts.md +0 -31
@@ -1014,17 +1014,28 @@ export function classifyPiFailure(input) {
1014
1014
  if (input.outputTooLarge)
1015
1015
  return "output-too-large";
1016
1016
  const combined = `${input.stderr}\n${input.stdout}`.toLowerCase();
1017
+ // Context overflow (400 request-too-large / context window exceeded).
1018
+ // Must be detected BEFORE the generic rate-limit/quota phrases: an overflow
1019
+ // error often contains "too many tokens" / "maximum context" which would
1020
+ // otherwise be misread as rate-limit or quota. Kept in sync with
1021
+ // CONTEXT_OVERFLOW_PHRASES in commands/client-recovery.ts.
1022
+ if (/请求上下文过大|context_length_exceeded|context overflow|context length overflow|too many tokens|maximum context|prompt is too long|request_too_large|context window/.test(combined))
1023
+ return "context-overflow";
1017
1024
  // Prefer explicit rate-limit / HTTP 429 before generic "limit" quota phrases.
1018
1025
  // Chinese gateways often say "请求频率已达到限制" without English "rate limit".
1019
- if (/rate.?limit|too many requests|\b429\b|请求频率|频率.*限制|rpm|tpm/.test(combined))
1026
+ // Patterns need error-shaped context: the captured stream also contains
1027
+ // serialized session events, where a bare "429" is an output-token counter,
1028
+ // bare "rpm"/"tpm" are base64 substrings of encrypted reasoning, and bare
1029
+ // "network" is a "no-network" UI state name.
1030
+ if (/rate.?limit|too many requests|(?:http|status)[^\d]{0,4}429\b|"code"\s*:\s*"?429\b|(?:^|\n)429[\s:]|请求频率|频率.*限制|\b(?:rpm|tpm)\s*(?:limit|quota|exceeded)/.test(combined))
1020
1031
  return "rate-limit";
1021
- if (/quota|usage limit|reached.*limit|5\s*小时|5小时|insufficient_quota|billing/.test(combined))
1032
+ if (/quota|usage limit|reached[^\n.]{0,60}limit|5\s*小时|5小时|insufficient_quota|billing/.test(combined))
1022
1033
  return "quota";
1023
1034
  if (/\bunauthorized\b|\bhttp\s*40[13]\b|invalid api key|authentication (?:failed|required|error)|auth(?:entication)? failed/.test(combined))
1024
1035
  return "auth";
1025
- if (/unknown provider|unknown model|model.*unavailable|provider.*unavailable|\bunavailable\b|overloaded|capacity|temporarily unavailable/.test(combined))
1036
+ if (/unknown provider|unknown model|(?:provider|model|service|gateway)\s+(?:is\s+)?unavailable|overloaded|at capacity|temporarily unavailable/.test(combined))
1026
1037
  return "unavailable";
1027
- if (/network|econnreset|etimedout|socket hang up|connection reset|connection error|request timed out|dns|fetch failed/.test(combined))
1038
+ if (/network\s*(?:error|failure|issue|timeout|unreachable|reset|abort)|networkerror|connection (?:reset|error|refused|closed|timed\s*out)|request timed out|socket hang up|econn\w*|etimedout|fetch failed|dns (?:error|failure|lookup failed)/.test(combined))
1028
1039
  return "network";
1029
1040
  if (input.exitCode === 0 && !input.assistantText.trim())
1030
1041
  return "empty-output";
@@ -0,0 +1,239 @@
1
+ import { access, open, readFile, stat, } from "node:fs/promises";
2
+ import path from "node:path";
3
+ class PiReadBudgetSoftLimitError extends Error {
4
+ }
5
+ class PiReadAccessDeniedError extends Error {
6
+ }
7
+ class PiReadBudgetGuard {
8
+ budget;
9
+ files = new Set();
10
+ violations = [];
11
+ bytes = 0;
12
+ operations = 0;
13
+ startedAt = Date.now();
14
+ allowedReadPaths;
15
+ allowedReadRoots;
16
+ constructor(budget, readAccess) {
17
+ this.budget = budget;
18
+ this.allowedReadPaths = readAccess?.allowedReadPaths
19
+ ? new Set(readAccess.allowedReadPaths.map((candidate) => path.resolve(candidate)))
20
+ : undefined;
21
+ this.allowedReadRoots = (readAccess?.allowedReadRoots ?? []).map((candidate) => path.resolve(candidate));
22
+ }
23
+ issues() {
24
+ return [...this.violations];
25
+ }
26
+ report(message) {
27
+ if (!this.violations.includes(message))
28
+ this.violations.push(message);
29
+ }
30
+ exhaust(message) {
31
+ if (this.budget.onExhaustion === "return-guidance") {
32
+ throw new PiReadBudgetSoftLimitError(message);
33
+ }
34
+ this.report(message);
35
+ throw new Error(message);
36
+ }
37
+ assertElapsed() {
38
+ if (this.budget.maxMs === undefined)
39
+ return;
40
+ const elapsedMs = Date.now() - this.startedAt;
41
+ if (elapsedMs > this.budget.maxMs) {
42
+ const message = `read budget exhausted: ${elapsedMs}ms (budget ${this.budget.maxMs}ms)`;
43
+ this.exhaust(message);
44
+ }
45
+ }
46
+ beforeToolOperation() {
47
+ this.assertElapsed();
48
+ if (this.budget.maxOperations === undefined)
49
+ return true;
50
+ if (this.operations >= this.budget.maxOperations) {
51
+ const message = `read budget exhausted: operation ${this.operations + 1} would exceed ${this.budget.maxOperations} read-only tool calls`;
52
+ if (this.budget.onExhaustion === "return-guidance")
53
+ return false;
54
+ this.exhaust(message);
55
+ }
56
+ this.operations += 1;
57
+ return true;
58
+ }
59
+ operationLimitGuidance() {
60
+ return {
61
+ content: [
62
+ {
63
+ type: "text",
64
+ text: "[read limit reached: no repository data was read. Do not call discovery tools again; use injected facts, commit remaining record_* facts, then call finalize_plan exactly once.]",
65
+ },
66
+ ],
67
+ };
68
+ }
69
+ readAccessGuidance() {
70
+ return {
71
+ content: [
72
+ {
73
+ type: "text",
74
+ text: "[read denied: this node may read only named upstream artifacts or Scout-confirmed paths. Do not explore a new repository path; use committed facts or record an evidence gap, then finish the required terminal protocol.]",
75
+ },
76
+ ],
77
+ };
78
+ }
79
+ assertReadablePath(absolutePath) {
80
+ if (!this.allowedReadPaths)
81
+ return;
82
+ const resolved = path.resolve(absolutePath);
83
+ if (this.allowedReadPaths.has(resolved))
84
+ return;
85
+ if (this.allowedReadRoots.some((root) => resolved === root || resolved.startsWith(`${root}${path.sep}`)))
86
+ return;
87
+ throw new PiReadAccessDeniedError(`read access denied: ${resolved}`);
88
+ }
89
+ reserveFile(absolutePath) {
90
+ this.assertElapsed();
91
+ if (this.files.has(absolutePath))
92
+ return;
93
+ if (this.files.size >= this.budget.maxFiles) {
94
+ const message = `read budget exhausted: file ${absolutePath} would exceed ${this.budget.maxFiles} files`;
95
+ this.exhaust(message);
96
+ }
97
+ this.files.add(absolutePath);
98
+ }
99
+ reserveBytes(bytes) {
100
+ this.assertElapsed();
101
+ const remaining = Math.max(0, this.budget.maxBytes - this.bytes);
102
+ if (remaining === 0) {
103
+ const message = `read budget exhausted: ${this.bytes} bytes (budget ${this.budget.maxBytes})`;
104
+ this.exhaust(message);
105
+ }
106
+ const granted = Math.min(bytes, remaining);
107
+ if (granted < bytes && this.budget.onExhaustion === "return-guidance") {
108
+ this.exhaust(`read budget exhausted: ${bytes} requested bytes would exceed ${this.budget.maxBytes} bytes`);
109
+ }
110
+ this.bytes += granted;
111
+ if (granted < bytes) {
112
+ this.report(`read budget truncated: ${bytes} requested bytes at ${this.bytes}/${this.budget.maxBytes} bytes`);
113
+ }
114
+ return granted;
115
+ }
116
+ async readFile(absolutePath) {
117
+ this.assertReadablePath(absolutePath);
118
+ this.reserveFile(absolutePath);
119
+ const metadata = await stat(absolutePath);
120
+ const granted = this.reserveBytes(metadata.size);
121
+ if (granted === metadata.size)
122
+ return await readFile(absolutePath);
123
+ const handle = await open(absolutePath, "r");
124
+ try {
125
+ const buffer = Buffer.allocUnsafe(granted);
126
+ const { bytesRead } = await handle.read(buffer, 0, granted, 0);
127
+ return buffer.subarray(0, bytesRead);
128
+ }
129
+ finally {
130
+ await handle.close();
131
+ }
132
+ }
133
+ async access(absolutePath) {
134
+ this.assertReadablePath(absolutePath);
135
+ this.reserveFile(absolutePath);
136
+ await access(absolutePath);
137
+ }
138
+ limitToolResult(result) {
139
+ if (!result || typeof result !== "object" || !Array.isArray(result.content)) {
140
+ return result;
141
+ }
142
+ const content = result.content.map((item) => {
143
+ if (!item || item.type !== "text" || typeof item.text !== "string") {
144
+ this.report("read budget truncated: non-text tool result omitted");
145
+ return {
146
+ type: "text",
147
+ text: "[read budget reached: non-text result omitted]",
148
+ };
149
+ }
150
+ const granted = this.reserveBytes(Buffer.byteLength(item.text));
151
+ return {
152
+ ...item,
153
+ text: truncateUtf8(item.text, granted),
154
+ };
155
+ });
156
+ return { ...result, content };
157
+ }
158
+ }
159
+ function truncateUtf8(text, maxBytes) {
160
+ if (Buffer.byteLength(text) <= maxBytes)
161
+ return text;
162
+ const marker = "\n[read result truncated by node budget]";
163
+ const markerBytes = Buffer.byteLength(marker);
164
+ if (maxBytes <= markerBytes)
165
+ return truncateUtf8Prefix(marker, maxBytes);
166
+ const contentBudget = maxBytes - markerBytes;
167
+ return `${truncateUtf8Prefix(text, contentBudget)}${marker}`;
168
+ }
169
+ function truncateUtf8Prefix(text, maxBytes) {
170
+ let result = "";
171
+ let bytes = 0;
172
+ for (const codePoint of text) {
173
+ const size = Buffer.byteLength(codePoint);
174
+ if (bytes + size > maxBytes)
175
+ break;
176
+ result += codePoint;
177
+ bytes += size;
178
+ }
179
+ return result;
180
+ }
181
+ function wrapTool(definition, guard, consumeResult) {
182
+ return {
183
+ ...definition,
184
+ async execute(...args) {
185
+ try {
186
+ if (!guard.beforeToolOperation())
187
+ return guard.operationLimitGuidance();
188
+ const result = await definition.execute(...args);
189
+ return consumeResult ? guard.limitToolResult(result) : result;
190
+ }
191
+ catch (error) {
192
+ if (error instanceof PiReadBudgetSoftLimitError) {
193
+ return guard.operationLimitGuidance();
194
+ }
195
+ if (error instanceof PiReadAccessDeniedError) {
196
+ return guard.readAccessGuidance();
197
+ }
198
+ throw error;
199
+ }
200
+ },
201
+ };
202
+ }
203
+ /**
204
+ * Override Pi's built-in read-only tools with SDK definitions that meter the
205
+ * content before it reaches the model. The read tool caps filesystem bytes at
206
+ * the operation boundary; grep/find/ls have their returned payload clipped
207
+ * before the agent receives it. This is SDK-only by design: the CLI backend
208
+ * cannot inject same-name built-in overrides safely.
209
+ */
210
+ export async function createPiReadBudgetCustomTools(input) {
211
+ const sdk = (await import("@earendil-works/pi-coding-agent"));
212
+ const createReadToolDefinition = sdk.createReadToolDefinition;
213
+ const createGrepToolDefinition = sdk.createGrepToolDefinition;
214
+ const createFindToolDefinition = sdk.createFindToolDefinition;
215
+ const createLsToolDefinition = sdk.createLsToolDefinition;
216
+ if (typeof createReadToolDefinition !== "function" ||
217
+ typeof createGrepToolDefinition !== "function" ||
218
+ typeof createFindToolDefinition !== "function" ||
219
+ typeof createLsToolDefinition !== "function") {
220
+ throw new Error("pi read budget policy unavailable: SDK read/grep/find/ls tool factories missing");
221
+ }
222
+ const guard = new PiReadBudgetGuard(input.budget, input.readAccess);
223
+ const read = wrapTool(createReadToolDefinition(input.repoRoot, {
224
+ operations: {
225
+ readFile: (absolutePath) => guard.readFile(absolutePath),
226
+ access: (absolutePath) => guard.access(absolutePath),
227
+ },
228
+ }), guard, false);
229
+ const grep = wrapTool(createGrepToolDefinition(input.repoRoot), guard, true);
230
+ const find = wrapTool(createFindToolDefinition(input.repoRoot), guard, true);
231
+ const ls = wrapTool(createLsToolDefinition(input.repoRoot), guard, true);
232
+ if (read.name !== "read" ||
233
+ grep.name !== "grep" ||
234
+ find.name !== "find" ||
235
+ ls.name !== "ls") {
236
+ throw new Error("pi read budget policy unavailable: SDK tool name mismatch");
237
+ }
238
+ return { customTools: [read, grep, find, ls], issues: () => guard.issues() };
239
+ }