@zqagent/core 0.3.13 → 0.3.22

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 (37) hide show
  1. package/dist/{TurnEngine-BZjUGic_.d.ts → TurnEngine-C8qqbilC.d.ts} +18 -3
  2. package/dist/{TurnEngine-CMGdcSdp.d.cts → TurnEngine-DKSczYaA.d.cts} +18 -3
  3. package/dist/{conflictGuard-BBOOIxxK.d.cts → conflictGuard-BuICnWEI.d.cts} +8 -0
  4. package/dist/{conflictGuard-BBOOIxxK.d.ts → conflictGuard-BuICnWEI.d.ts} +8 -0
  5. package/dist/index.cjs +3451 -1129
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +242 -6
  8. package/dist/index.d.ts +242 -6
  9. package/dist/index.js +3500 -1178
  10. package/dist/index.js.map +1 -1
  11. package/dist/internal/engine/FileLogHook.d.cts +1 -1
  12. package/dist/internal/engine/FileLogHook.d.ts +1 -1
  13. package/dist/internal/update/controller.cjs +1 -1
  14. package/dist/internal/update/controller.js +1 -1
  15. package/dist/internal/update/platform.cjs +1 -1
  16. package/dist/internal/update/platform.js +1 -1
  17. package/dist/tools/diff.d.cts +1 -1
  18. package/dist/tools/diff.d.ts +1 -1
  19. package/dist/tools/explore.cjs +504 -127
  20. package/dist/tools/explore.cjs.map +1 -1
  21. package/dist/tools/explore.d.cts +1 -1
  22. package/dist/tools/explore.d.ts +1 -1
  23. package/dist/tools/explore.js +504 -127
  24. package/dist/tools/explore.js.map +1 -1
  25. package/dist/tools/files.cjs +267 -47
  26. package/dist/tools/files.cjs.map +1 -1
  27. package/dist/tools/files.d.cts +1 -1
  28. package/dist/tools/files.d.ts +1 -1
  29. package/dist/tools/files.js +267 -47
  30. package/dist/tools/files.js.map +1 -1
  31. package/dist/tools/search.cjs +103 -24
  32. package/dist/tools/search.cjs.map +1 -1
  33. package/dist/tools/search.d.cts +8 -2
  34. package/dist/tools/search.d.ts +8 -2
  35. package/dist/tools/search.js +103 -24
  36. package/dist/tools/search.js.map +1 -1
  37. package/package.json +6 -5
@@ -314,8 +314,17 @@ var zh = {
314
314
  - \u4E0D\u8981\u5C06\u5B8C\u6574\u7684\u6587\u4EF6\u5185\u5BB9\u5F53\u4F5C\u771F\u7406\u3002\u53EA\u8BB0\u5F55\u67D0\u4E2A\u6587\u4EF6\u88AB\u8BFB\u53D6/\u7F16\u8F91\u8FC7\u8FD9\u4E00\u4E8B\u5B9E\uFF1B\u534F\u4F5C\u8005\u5982\u679C\u9700\u8981\u518D\u6B21\u83B7\u53D6\u5185\u5BB9\u4F1A\u91CD\u65B0\u8BFB\u53D6\u3002\u9648\u65E7\u7684\u6587\u4EF6\u8BB0\u5FC6\u6BD4\u6CA1\u6709\u8BB0\u5FC6\u66F4\u7CDF\u7CD5\u3002
315
315
  - \u82E5\u63D0\u4F9B\u4E86\u4E0A\u4E00\u7248\u6458\u8981\uFF1A\u53EA\u628A\u5B83\u4ECD\u7136\u6709\u6548\u7684\u7ED3\u8BBA\u5E76\u5165\u5BF9\u5E94\u7AE0\u8282\uFF0C\u4E0D\u8981\u6574\u6BB5\u590D\u8FF0\u6216\u9010\u6761\u8F6C\u6284\u3002
316
316
  - \u8981\u5177\u4F53\uFF1A\u8DEF\u5F84\u3001\u540D\u79F0\u3001\u547D\u4EE4\u3001id\u2014\u2014\u800C\u4E0D\u662F\u6A21\u7CCA\u7684\u5F15\u7528\u3002
317
+ - \u7528\u7B80\u4F53\u4E2D\u6587\u64B0\u5199\u6458\u8981\u3002\u6458\u8981\u5728\u540E\u7EED\u6BCF\u4E00\u8F6E\u90FD\u4F1A\u968F\u4E0A\u4E0B\u6587\u91CD\u53D1\uFF0C\u8BED\u8A00\u4E0D\u4E00\u81F4\u4F1A\u8BA9\u534F\u4F5C\u8005\u6539\u7528\u82F1\u6587\u4F5C\u7B54\u3002
317
318
  - \u4EC5\u8F93\u51FA\u6458\u8981\u7AE0\u8282\uFF0C\u4E0D\u9644\u52A0\u524D\u8A00\u3002`,
318
319
  continuationContract: "\u4ECE\u4F60\u4E4B\u524D\u4E2D\u65AD\u7684\u5730\u65B9\u7EE7\u7EED\uFF1A\u5B8C\u5168\u6309\u7167\u63CF\u8FF0\u63A5\u7EED\u5F53\u524D\u5DE5\u4F5C\u548C\u4E0B\u4E00\u6B65\u3002\u4E0D\u8981\u91CD\u65B0\u8BE2\u95EE\u5DF2\u56DE\u7B54\u7684\u95EE\u9898\uFF0C\u4E0D\u8981\u56DE\u987E\uFF0C\u4E0D\u8981\u63D0\u53CA\u4E0A\u4E0B\u6587\u5DF2\u88AB\u538B\u7F29\u3002\u5982\u679C\u4F60\u9700\u8981\u4E0A\u8FF0\u63D0\u5230\u7684\u6587\u4EF6\u7684\u5185\u5BB9\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u5B83\u3002",
320
+ compactedIntro: "\u672C\u4F1A\u8BDD\u8F83\u65E9\u7684\u8F6E\u6B21\u5DF2\u88AB\u538B\u7F29\u3002\u4E0B\u9762\u7684\u6458\u8981\u5C31\u662F\u4F60\u5BF9\u90A3\u4E9B\u8F6E\u6B21\u7684\u8BB0\u5FC6\u3002",
321
+ compactedUserMessagesHeader: "## \u538B\u7F29\u533A\u95F4\u5185\u7684\u7528\u6237\u6D88\u606F\uFF08\u539F\u6587\uFF0C\u6309\u65F6\u95F4\u987A\u5E8F\uFF09",
322
+ compactedOmittedNote: (n) => `\uFF08\u66F4\u65E9\u7684 ${n} \u6761\u7528\u6237\u6D88\u606F\u5DF2\u7701\u7565 \u2014\u2014 \u5176\u610F\u56FE\u5DF2\u5305\u542B\u5728\u4E0A\u9762\u7684\u6458\u8981\u4E2D\uFF09`,
323
+ compactedTrimmedNote: "\uFF08\u8F83\u65E9\u7684\u8F6E\u6B21\u56E0\u4E0A\u4E0B\u6587\u7A97\u53E3\u9650\u5236\u88AB\u88C1\u526A\uFF0C\u6CA1\u6709\u53EF\u7528\u6458\u8981\u3002\u5982\u679C\u9700\u8981\u65E9\u5148\u7684\u7ED3\u679C\uFF0C\u8BF7\u91CD\u65B0\u8BFB\u53D6\u6587\u4EF6\u6216\u91CD\u65B0\u6267\u884C\u547D\u4EE4\u3002\uFF09",
324
+ workingStateHeader: "## \u5DE5\u4F5C\u72B6\u6001\uFF08\u4ECE\u5DE5\u5177\u8BB0\u5F55\u4E2D\u673A\u68B0\u63D0\u53D6\uFF09",
325
+ workingStateFiles: "\u5DF2\u5199\u5165/\u4FEE\u6539\u7684\u6587\u4EF6\uFF08\u6700\u8FD1\u7684\u5728\u524D\uFF09\uFF1A",
326
+ workingStateCommands: "\u6700\u8FD1\u7684 shell \u547D\u4EE4\uFF1A",
327
+ workingStateTools: (list) => `\u538B\u7F29\u533A\u95F4\u5185\u4F7F\u7528\u8FC7\u7684\u5DE5\u5177\uFF1A${list}`,
319
328
  genCommitMessageInstructions: `\u4F60\u662F Git \u63D0\u4EA4\u8BF4\u660E\u751F\u6210\u52A9\u624B\u3002\u6839\u636E\u7ED9\u5B9A\u7684\u4EE3\u7801\u5DEE\u5F02\uFF0C\u751F\u6210\u4E00\u6761\u51C6\u786E\u3001\u7B80\u6D01\u7684\u63D0\u4EA4\u8BF4\u660E\u3002
320
329
 
321
330
  \u8981\u6C42\uFF1A
@@ -469,8 +478,17 @@ Rules:
469
478
  - Do not treat full file contents as truth. Record only the fact that a file was read/edited; the collaborator will re-read it if it needs the content again. Stale file memory is worse than no memory.
470
479
  - If a previous summary is provided: fold only its still-valid conclusions into the matching sections; do not restate it wholesale or copy it entry by entry.
471
480
  - Be specific: paths, names, commands, ids \u2014 not vague references.
481
+ - Write the summary in English. It is re-sent with the context on every later turn; a language mismatch makes the collaborator switch to another language.
472
482
  - Output only the summary sections, with no preamble.`,
473
483
  continuationContract: "Continue from where you left off: pick up the current work and next step exactly as described. Do not re-ask questions that were already answered, do not recap, and do not mention that the context was compacted. If you need the contents of a file mentioned above, read it again.",
484
+ compactedIntro: "Earlier turns of this session were compacted. The summary below is your memory of them.",
485
+ compactedUserMessagesHeader: "## User messages in the compacted span (verbatim, chronological)",
486
+ compactedOmittedNote: (n) => `(${n} earlier user messages omitted \u2014 their intent is covered by the summary above)`,
487
+ compactedTrimmedNote: "(Older turns were trimmed to fit the context window; no summary is available for them. Re-read files and re-run commands if earlier results are needed.)",
488
+ workingStateHeader: "## Working state (extracted mechanically from tool records)",
489
+ workingStateFiles: "Files written/edited (most recent first):",
490
+ workingStateCommands: "Recent shell commands:",
491
+ workingStateTools: (list) => `Tools used in the summarized span: ${list}`,
474
492
  genCommitMessageInstructions: `You are a Git commit message assistant. Given a code diff, produce an accurate, concise commit message.
475
493
 
476
494
  Requirements:
@@ -624,8 +642,17 @@ var zhTW = {
624
642
  - \u4E0D\u8981\u5C07\u5B8C\u6574\u7684\u6A94\u6848\u5167\u5BB9\u7576\u4F5C\u771F\u7406\u3002\u53EA\u8A18\u9304\u67D0\u500B\u6A94\u6848\u88AB\u8B80\u53D6/\u7DE8\u8F2F\u904E\u9019\u4E00\u4E8B\u5BE6\uFF1B\u5354\u4F5C\u8005\u5982\u679C\u9700\u8981\u518D\u6B21\u7372\u53D6\u5167\u5BB9\u6703\u91CD\u65B0\u8B80\u53D6\u3002\u9673\u820A\u7684\u6A94\u6848\u8A18\u61B6\u6BD4\u6C92\u6709\u8A18\u61B6\u66F4\u7CDF\u7CD5\u3002
625
643
  - \u82E5\u63D0\u4F9B\u4E86\u4E0A\u4E00\u7248\u6458\u8981\uFF1A\u53EA\u628A\u5B83\u4ECD\u7136\u6709\u6548\u7684\u7D50\u8AD6\u4F75\u5165\u5C0D\u61C9\u7AE0\u7BC0\uFF0C\u4E0D\u8981\u6574\u6BB5\u8907\u8FF0\u6216\u9010\u689D\u8F49\u6284\u3002
626
644
  - \u8981\u5177\u9AD4\uFF1A\u8DEF\u5F91\u3001\u540D\u7A31\u3001\u547D\u4EE4\u3001id\u2014\u2014\u800C\u4E0D\u662F\u6A21\u7CCA\u7684\u5F15\u7528\u3002
645
+ - \u7528\u7E41\u9AD4\u4E2D\u6587\u64B0\u5BEB\u6458\u8981\u3002\u6458\u8981\u5728\u5F8C\u7E8C\u6BCF\u4E00\u8F2A\u90FD\u6703\u96A8\u4E0A\u4E0B\u6587\u91CD\u767C\uFF0C\u8A9E\u8A00\u4E0D\u4E00\u81F4\u6703\u8B93\u5354\u4F5C\u8005\u6539\u7528\u5176\u4ED6\u8A9E\u8A00\u4F5C\u7B54\u3002
627
646
  - \u50C5\u8F38\u51FA\u6458\u8981\u7AE0\u7BC0\uFF0C\u4E0D\u9644\u52A0\u524D\u8A00\u3002`,
628
647
  continuationContract: "\u5F9E\u4F60\u4E4B\u524D\u4E2D\u65B7\u7684\u5730\u65B9\u7E7C\u7E8C\uFF1A\u5B8C\u5168\u6309\u7167\u63CF\u8FF0\u63A5\u7E8C\u7576\u524D\u5DE5\u4F5C\u548C\u4E0B\u4E00\u6B65\u3002\u4E0D\u8981\u91CD\u65B0\u8A62\u554F\u5DF2\u56DE\u7B54\u7684\u554F\u984C\uFF0C\u4E0D\u8981\u56DE\u9867\uFF0C\u4E0D\u8981\u63D0\u53CA\u4E0A\u4E0B\u6587\u5DF2\u88AB\u58D3\u7E2E\u3002\u5982\u679C\u4F60\u9700\u8981\u4E0A\u8FF0\u63D0\u5230\u7684\u6A94\u6848\u7684\u5167\u5BB9\uFF0C\u8ACB\u91CD\u65B0\u8B80\u53D6\u5B83\u3002",
648
+ compactedIntro: "\u672C\u6703\u8A71\u8F03\u65E9\u7684\u8F2A\u6B21\u5DF2\u88AB\u58D3\u7E2E\u3002\u4E0B\u9762\u7684\u6458\u8981\u5C31\u662F\u4F60\u5C0D\u90A3\u4E9B\u8F2A\u6B21\u7684\u8A18\u61B6\u3002",
649
+ compactedUserMessagesHeader: "## \u58D3\u7E2E\u5340\u9593\u5167\u7684\u4F7F\u7528\u8005\u8A0A\u606F\uFF08\u539F\u6587\uFF0C\u6309\u6642\u9593\u9806\u5E8F\uFF09",
650
+ compactedOmittedNote: (n) => `\uFF08\u66F4\u65E9\u7684 ${n} \u5247\u4F7F\u7528\u8005\u8A0A\u606F\u5DF2\u7701\u7565 \u2014\u2014 \u5176\u610F\u5716\u5DF2\u5305\u542B\u5728\u4E0A\u9762\u7684\u6458\u8981\u4E2D\uFF09`,
651
+ compactedTrimmedNote: "\uFF08\u8F03\u65E9\u7684\u8F2A\u6B21\u56E0\u4E0A\u4E0B\u6587\u8996\u7A97\u9650\u5236\u88AB\u88C1\u526A\uFF0C\u6C92\u6709\u53EF\u7528\u6458\u8981\u3002\u5982\u679C\u9700\u8981\u65E9\u5148\u7684\u7D50\u679C\uFF0C\u8ACB\u91CD\u65B0\u8B80\u53D6\u6A94\u6848\u6216\u91CD\u65B0\u57F7\u884C\u547D\u4EE4\u3002\uFF09",
652
+ workingStateHeader: "## \u5DE5\u4F5C\u72C0\u614B\uFF08\u5F9E\u5DE5\u5177\u8A18\u9304\u4E2D\u6A5F\u68B0\u63D0\u53D6\uFF09",
653
+ workingStateFiles: "\u5DF2\u5BEB\u5165/\u4FEE\u6539\u7684\u6A94\u6848\uFF08\u6700\u8FD1\u7684\u5728\u524D\uFF09\uFF1A",
654
+ workingStateCommands: "\u6700\u8FD1\u7684 shell \u547D\u4EE4\uFF1A",
655
+ workingStateTools: (list) => `\u58D3\u7E2E\u5340\u9593\u5167\u4F7F\u7528\u904E\u7684\u5DE5\u5177\uFF1A${list}`,
629
656
  genCommitMessageInstructions: `\u4F60\u662F Git \u63D0\u4EA4\u8AAA\u660E\u751F\u6210\u52A9\u624B\u3002\u6839\u64DA\u7D66\u5B9A\u7684\u7A0B\u5F0F\u78BC\u5DEE\u7570\uFF0C\u751F\u6210\u4E00\u689D\u6E96\u78BA\u3001\u7C21\u6F54\u7684\u63D0\u4EA4\u8AAA\u660E\u3002
630
657
 
631
658
  \u8981\u6C42\uFF1A
@@ -779,8 +806,17 @@ shell \u30B3\u30DE\u30F3\u30C9\u306B\u8907\u6570\u884C\u306E\u30B9\u30AF\u30EA\u
779
806
  - \u30D5\u30A1\u30A4\u30EB\u306E\u5185\u5BB9\u5168\u4F53\u3092\u771F\u5B9F\u3068\u3057\u3066\u6271\u308F\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002\u3042\u308B\u30D5\u30A1\u30A4\u30EB\u304C\u8AAD\u307E\u308C\u305F\uFF0F\u7DE8\u96C6\u3055\u308C\u305F\u3068\u3044\u3046\u4E8B\u5B9F\u3060\u3051\u3092\u8A18\u9332\u3057\u307E\u3059\u3002\u5354\u50CD\u8005\u306F\u5185\u5BB9\u304C\u518D\u3073\u5FC5\u8981\u306B\u306A\u308C\u3070\u8AAD\u307F\u76F4\u3057\u307E\u3059\u3002\u53E4\u3044\u30D5\u30A1\u30A4\u30EB\u8A18\u61B6\u306F\u8A18\u61B6\u304C\u306A\u3044\u3088\u308A\u60AA\u3044\u3002
780
807
  - \u524D\u56DE\u306E\u8981\u7D04\u304C\u63D0\u4F9B\u3055\u308C\u305F\u5834\u5408\uFF1A\u307E\u3060\u6709\u52B9\u306A\u7D50\u8AD6\u3060\u3051\u3092\u8A72\u5F53\u30BB\u30AF\u30B7\u30E7\u30F3\u306B\u7D71\u5408\u3057\u3001\u5168\u4F53\u3092\u66F8\u304D\u76F4\u3057\u305F\u308A\u9805\u76EE\u3054\u3068\u306B\u8EE2\u8A18\u3057\u305F\u308A\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002
781
808
  - \u5177\u4F53\u7684\u306B\uFF1A\u30D1\u30B9\u3001\u540D\u524D\u3001\u30B3\u30DE\u30F3\u30C9\u3001id \u3092\u66F8\u304F\u3002\u66D6\u6627\u306A\u53C2\u7167\u306B\u3057\u306A\u3044\u3002
809
+ - \u8981\u7D04\u306F\u65E5\u672C\u8A9E\u3067\u66F8\u3044\u3066\u304F\u3060\u3055\u3044\u3002\u8981\u7D04\u306F\u4EE5\u964D\u306E\u6BCE\u30BF\u30FC\u30F3\u3067\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u3068\u3068\u3082\u306B\u518D\u9001\u3055\u308C\u308B\u305F\u3081\u3001\u8A00\u8A9E\u304C\u98DF\u3044\u9055\u3046\u3068\u5354\u50CD\u8005\u304C\u5225\u306E\u8A00\u8A9E\u3067\u7B54\u3048\u308B\u3088\u3046\u306B\u306A\u308A\u307E\u3059\u3002
782
810
  - \u8981\u7D04\u30BB\u30AF\u30B7\u30E7\u30F3\u3060\u3051\u3092\u51FA\u529B\u3057\u3001\u524D\u7F6E\u304D\u3092\u4ED8\u3051\u306A\u3044\u3002`,
783
811
  continuationContract: "\u524D\u56DE\u4E2D\u65AD\u3057\u305F\u3068\u3053\u308D\u304B\u3089\u7D9A\u3051\u3066\u304F\u3060\u3055\u3044\uFF1A\u8AAC\u660E\u3069\u304A\u308A\u306B\u73FE\u5728\u306E\u4F5C\u696D\u3068\u6B21\u306E\u624B\u9806\u3092\u5F15\u304D\u7D99\u304E\u307E\u3059\u3002\u65E2\u306B\u56DE\u7B54\u6E08\u307F\u306E\u8CEA\u554F\u3092\u7E70\u308A\u8FD4\u3055\u305A\u3001\u632F\u308A\u8FD4\u3089\u305A\u3001\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u304C\u5727\u7E2E\u3055\u308C\u305F\u3053\u3068\u306B\u3082\u89E6\u308C\u306A\u3044\u3067\u304F\u3060\u3055\u3044\u3002\u4E0A\u8A18\u3067\u8A00\u53CA\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u306E\u5185\u5BB9\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F\u3001\u3082\u3046\u4E00\u5EA6\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044\u3002",
812
+ compactedIntro: "\u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u4EE5\u524D\u306E\u30BF\u30FC\u30F3\u306F\u5727\u7E2E\u3055\u308C\u307E\u3057\u305F\u3002\u4EE5\u4E0B\u306E\u8981\u7D04\u304C\u305D\u308C\u3089\u306E\u30BF\u30FC\u30F3\u306B\u3064\u3044\u3066\u306E\u3042\u306A\u305F\u306E\u8A18\u61B6\u3067\u3059\u3002",
813
+ compactedUserMessagesHeader: "## \u5727\u7E2E\u533A\u9593\u5185\u306E\u30E6\u30FC\u30B6\u30FC\u30E1\u30C3\u30BB\u30FC\u30B8\uFF08\u539F\u6587\u3001\u6642\u7CFB\u5217\uFF09",
814
+ compactedOmittedNote: (n) => `\uFF08\u4EE5\u524D\u306E\u30E6\u30FC\u30B6\u30FC\u30E1\u30C3\u30BB\u30FC\u30B8 ${n} \u4EF6\u306F\u7701\u7565\u3055\u308C\u307E\u3057\u305F \u2014 \u305D\u306E\u610F\u56F3\u306F\u4E0A\u8A18\u306E\u8981\u7D04\u306B\u542B\u307E\u308C\u3066\u3044\u307E\u3059\uFF09`,
815
+ compactedTrimmedNote: "\uFF08\u4EE5\u524D\u306E\u30BF\u30FC\u30F3\u306F\u30B3\u30F3\u30C6\u30AD\u30B9\u30C8\u30A6\u30A3\u30F3\u30C9\u30A6\u306E\u5236\u9650\u306B\u3088\u308A\u5207\u308A\u8A70\u3081\u3089\u308C\u3001\u8981\u7D04\u306F\u3042\u308A\u307E\u305B\u3093\u3002\u4EE5\u524D\u306E\u7D50\u679C\u304C\u5FC5\u8981\u306A\u5834\u5408\u306F\u3001\u30D5\u30A1\u30A4\u30EB\u3092\u518D\u8AAD\u307F\u8FBC\u307F\u3059\u308B\u304B\u30B3\u30DE\u30F3\u30C9\u3092\u518D\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\uFF09",
816
+ workingStateHeader: "## \u4F5C\u696D\u72B6\u614B\uFF08\u30C4\u30FC\u30EB\u8A18\u9332\u304B\u3089\u6A5F\u68B0\u7684\u306B\u62BD\u51FA\uFF09",
817
+ workingStateFiles: "\u66F8\u304D\u8FBC\u307F/\u7DE8\u96C6\u3057\u305F\u30D5\u30A1\u30A4\u30EB\uFF08\u65B0\u3057\u3044\u9806\uFF09\uFF1A",
818
+ workingStateCommands: "\u6700\u8FD1\u306E\u30B7\u30A7\u30EB\u30B3\u30DE\u30F3\u30C9\uFF1A",
819
+ workingStateTools: (list) => `\u5727\u7E2E\u533A\u9593\u5185\u3067\u4F7F\u7528\u3057\u305F\u30C4\u30FC\u30EB\uFF1A${list}`,
784
820
  genCommitMessageInstructions: `\u3042\u306A\u305F\u306F Git \u30B3\u30DF\u30C3\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8\u751F\u6210\u30A2\u30B7\u30B9\u30BF\u30F3\u30C8\u3067\u3059\u3002\u4E0E\u3048\u3089\u308C\u305F\u30B3\u30FC\u30C9\u5DEE\u5206\u304B\u3089\u3001\u6B63\u78BA\u3067\u7C21\u6F54\u306A\u30B3\u30DF\u30C3\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u751F\u6210\u3057\u3066\u304F\u3060\u3055\u3044\u3002
785
821
 
786
822
  \u8981\u4EF6\uFF1A
@@ -934,8 +970,17 @@ shell \uBA85\uB839\uC5D0 \uC5EC\uB7EC \uC904 \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uC7
934
970
  - \uD30C\uC77C \uB0B4\uC6A9 \uC804\uCCB4\uB97C \uC9C4\uC2E4\uB85C \uCDE8\uAE09\uD558\uC9C0 \uB9C8\uC138\uC694. \uC5B4\uB5A4 \uD30C\uC77C\uC774 \uC77D\uD788\uAC70\uB098 \uD3B8\uC9D1\uB418\uC5C8\uB2E4\uB294 \uC0AC\uC2E4\uB9CC \uAE30\uB85D\uD569\uB2C8\uB2E4. \uD611\uC5C5\uC790\uB294 \uB0B4\uC6A9\uC774 \uB2E4\uC2DC \uD544\uC694\uD558\uBA74 \uB2E4\uC2DC \uC77D\uC2B5\uB2C8\uB2E4. \uC624\uB798\uB41C \uD30C\uC77C \uAE30\uC5B5\uC740 \uAE30\uC5B5\uC774 \uC5C6\uB294 \uAC83\uBCF4\uB2E4 \uB098\uC069\uB2C8\uB2E4.
935
971
  - \uC774\uC804 \uC694\uC57D\uC774 \uC81C\uACF5\uB418\uC5C8\uB2E4\uBA74: \uC5EC\uC804\uD788 \uC720\uD6A8\uD55C \uACB0\uB860\uB9CC \uD574\uB2F9 \uC139\uC158\uC5D0 \uBCD1\uD569\uD558\uACE0, \uC804\uCCB4\uB97C \uB2E4\uC2DC \uC4F0\uAC70\uB098 \uD56D\uBAA9\uBCC4\uB85C \uC62E\uACA8 \uC801\uC9C0 \uB9C8\uC138\uC694.
936
972
  - \uAD6C\uCCB4\uC801\uC73C\uB85C: \uACBD\uB85C, \uC774\uB984, \uBA85\uB839, id\uB97C \uC4F8 \uAC83. \uBAA8\uD638\uD55C \uCC38\uC870\uB294 \uAE08\uC9C0.
973
+ - \uC694\uC57D\uC740 \uD55C\uAD6D\uC5B4\uB85C \uC791\uC131\uD558\uC138\uC694. \uC694\uC57D\uC740 \uC774\uD6C4 \uB9E4 \uD134\uB9C8\uB2E4 \uCEE8\uD14D\uC2A4\uD2B8\uC640 \uD568\uAED8 \uB2E4\uC2DC \uC804\uC1A1\uB418\uBBC0\uB85C, \uC5B8\uC5B4\uAC00 \uC5B4\uAE0B\uB098\uBA74 \uD611\uC5C5\uC790\uAC00 \uB2E4\uB978 \uC5B8\uC5B4\uB85C \uB2F5\uD558\uAC8C \uB429\uB2C8\uB2E4.
937
974
  - \uC694\uC57D \uC139\uC158\uB9CC \uCD9C\uB825\uD558\uACE0 \uC11C\uB450\uB97C \uBD99\uC774\uC9C0 \uB9C8\uC138\uC694.`,
938
975
  continuationContract: "\uC911\uB2E8\uD55C \uC9C0\uC810\uBD80\uD130 \uC774\uC5B4\uAC00\uC138\uC694: \uC124\uBA85\uD55C \uB300\uB85C \uD604\uC7AC \uC791\uC5C5\uACFC \uB2E4\uC74C \uB2E8\uACC4\uB97C \uADF8\uB300\uB85C \uC774\uC5B4\uBC1B\uC2B5\uB2C8\uB2E4. \uC774\uBBF8 \uB2F5\uD55C \uC9C8\uBB38\uC744 \uB2E4\uC2DC \uBB3B\uC9C0 \uB9D0\uACE0, \uB418\uB3CC\uC544\uBCF4\uC9C0 \uB9D0\uACE0, \uCEE8\uD14D\uC2A4\uD2B8\uAC00 \uC555\uCD95\uB418\uC5C8\uB2E4\uB294 \uC0AC\uC2E4\uB3C4 \uC5B8\uAE09\uD558\uC9C0 \uB9C8\uC138\uC694. \uC704\uC5D0\uC11C \uC5B8\uAE09\uD55C \uD30C\uC77C\uC758 \uB0B4\uC6A9\uC774 \uD544\uC694\uD558\uBA74 \uB2E4\uC2DC \uC77D\uC73C\uC138\uC694.",
976
+ compactedIntro: "\uC774 \uC138\uC158\uC758 \uC774\uC804 \uD134\uC740 \uC555\uCD95\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC544\uB798 \uC694\uC57D\uC774 \uADF8 \uD134\uB4E4\uC5D0 \uB300\uD55C \uB2F9\uC2E0\uC758 \uAE30\uC5B5\uC785\uB2C8\uB2E4.",
977
+ compactedUserMessagesHeader: "## \uC555\uCD95 \uAD6C\uAC04\uC758 \uC0AC\uC6A9\uC790 \uBA54\uC2DC\uC9C0 (\uC6D0\uBB38, \uC2DC\uAC04 \uC21C)",
978
+ compactedOmittedNote: (n) => `(\uC774\uC804 \uC0AC\uC6A9\uC790 \uBA54\uC2DC\uC9C0 ${n}\uAC1C\uB294 \uC0DD\uB7B5\uB428 \u2014 \uADF8 \uC758\uB3C4\uB294 \uC704 \uC694\uC57D\uC5D0 \uD3EC\uD568\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4)`,
979
+ compactedTrimmedNote: "(\uC774\uC804 \uD134\uC740 \uCEE8\uD14D\uC2A4\uD2B8 \uC708\uB3C4 \uC81C\uD55C\uC73C\uB85C \uC798\uB838\uC73C\uBA70 \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uC694\uC57D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC774\uC804 \uACB0\uACFC\uAC00 \uD544\uC694\uD558\uBA74 \uD30C\uC77C\uC744 \uB2E4\uC2DC \uC77D\uAC70\uB098 \uBA85\uB839\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.)",
980
+ workingStateHeader: "## \uC791\uC5C5 \uC0C1\uD0DC (\uB3C4\uAD6C \uAE30\uB85D\uC5D0\uC11C \uAE30\uACC4\uC801\uC73C\uB85C \uCD94\uCD9C)",
981
+ workingStateFiles: "\uC791\uC131/\uC218\uC815\uD55C \uD30C\uC77C (\uCD5C\uADFC \uC21C):",
982
+ workingStateCommands: "\uCD5C\uADFC \uC178 \uBA85\uB839:",
983
+ workingStateTools: (list) => `\uC555\uCD95 \uAD6C\uAC04\uC5D0\uC11C \uC0AC\uC6A9\uD55C \uB3C4\uAD6C: ${list}`,
939
984
  genCommitMessageInstructions: `\uB2F9\uC2E0\uC740 Git \uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uC0DD\uC131 \uC5B4\uC2DC\uC2A4\uD134\uD2B8\uC785\uB2C8\uB2E4. \uC8FC\uC5B4\uC9C4 \uCF54\uB4DC \uCC28\uC774\uB85C \uC815\uD655\uD558\uACE0 \uAC04\uACB0\uD55C \uCEE4\uBC0B \uBA54\uC2DC\uC9C0\uB97C \uC0DD\uC131\uD558\uC138\uC694.
940
985
 
941
986
  \uC694\uAD6C \uC0AC\uD56D:
@@ -1089,8 +1134,17 @@ Reglas:
1089
1134
  - No trates el contenido completo de un archivo como verdad. Registra solo el hecho de que un archivo se ley\xF3 o edit\xF3; el colaborador volver\xE1 a leerlo si necesita el contenido. Una memoria obsoleta de un archivo es peor que no tener memoria.
1090
1135
  - Si se proporciona un resumen anterior: integra solo sus conclusiones a\xFAn v\xE1lidas en las secciones correspondientes; no lo repitas entero ni lo copies entrada por entrada.
1091
1136
  - S\xE9 concreto: rutas, nombres, comandos, ids, no referencias vagas.
1137
+ - Escribe el resumen en espa\xF1ol. Se reenv\xEDa con el contexto en cada turno posterior; una diferencia de idioma hace que el colaborador responda en otro idioma.
1092
1138
  - Devuelve solo las secciones del resumen, sin pre\xE1mbulo.`,
1093
1139
  continuationContract: "Contin\xFAa donde lo dejaste: retoma el trabajo actual y el siguiente paso exactamente como se describe. No vuelvas a preguntar lo que ya se respondi\xF3, no recapitules y no menciones que el contexto se ha compactado. Si necesitas el contenido de alg\xFAn archivo mencionado arriba, vuelve a leerlo.",
1140
+ compactedIntro: "Los turnos anteriores de esta sesi\xF3n se compactaron. El resumen de abajo es tu memoria de ellos.",
1141
+ compactedUserMessagesHeader: "## Mensajes del usuario en el tramo compactado (textual, cronol\xF3gico)",
1142
+ compactedOmittedNote: (n) => `(${n} mensajes anteriores del usuario omitidos \u2014 su intenci\xF3n est\xE1 cubierta por el resumen de arriba)`,
1143
+ compactedTrimmedNote: "(Los turnos anteriores se recortaron para ajustarse a la ventana de contexto; no hay resumen disponible. Vuelve a leer archivos y reejecuta comandos si necesitas resultados previos.)",
1144
+ workingStateHeader: "## Estado de trabajo (extra\xEDdo mec\xE1nicamente de los registros de herramientas)",
1145
+ workingStateFiles: "Archivos escritos/editados (m\xE1s recientes primero):",
1146
+ workingStateCommands: "Comandos de shell recientes:",
1147
+ workingStateTools: (list) => `Herramientas usadas en el tramo resumido: ${list}`,
1094
1148
  genCommitMessageInstructions: `Eres un asistente de mensajes de commit de Git. Dado un diff de c\xF3digo, produce un mensaje de commit preciso y conciso.
1095
1149
 
1096
1150
  Requisitos:
@@ -1347,7 +1401,7 @@ function pickBoundary(messages, keepTokens) {
1347
1401
  return boundary;
1348
1402
  }
1349
1403
  var WRITE_HINTS = ["write", "edit", "append", "save", "create", "patch"];
1350
- function extractWorkingState(span) {
1404
+ function extractWorkingState(span, lang) {
1351
1405
  const files = [];
1352
1406
  const commands = [];
1353
1407
  const tools = /* @__PURE__ */ new Set();
@@ -1390,23 +1444,20 @@ function extractWorkingState(span) {
1390
1444
  }
1391
1445
  return seen;
1392
1446
  };
1393
- const lines = [
1394
- "## Working state (extracted mechanically from tool records)"
1395
- ];
1447
+ const p = getPrompts(lang);
1448
+ const lines = [p.workingStateHeader];
1396
1449
  const written = dedupe(files, 20);
1397
1450
  if (written.length) {
1398
- lines.push("Files written/edited (most recent first):");
1451
+ lines.push(p.workingStateFiles);
1399
1452
  lines.push(...written.map((f) => `- ${f}`));
1400
1453
  }
1401
1454
  const recentCmds = commands.slice(-10);
1402
1455
  if (recentCmds.length) {
1403
- lines.push("Recent shell commands:");
1456
+ lines.push(p.workingStateCommands);
1404
1457
  lines.push(...recentCmds.map((c) => `- ${c}`));
1405
1458
  }
1406
1459
  if (tools.size) {
1407
- lines.push(
1408
- "Tools used in the summarized span: " + [...tools].sort().join(", ")
1409
- );
1460
+ lines.push(p.workingStateTools([...tools].sort().join(", ")));
1410
1461
  }
1411
1462
  return lines.length > 1 ? lines.join("\n") : "";
1412
1463
  }
@@ -1530,7 +1581,7 @@ async function buildState(messages, provider, model, keepTokens, prior = null, o
1530
1581
  return {
1531
1582
  boundaryIndex: boundary,
1532
1583
  summaryText: summary,
1533
- workingState: extractWorkingState(span),
1584
+ workingState: extractWorkingState(span, lang),
1534
1585
  userMessages: cappedUsers,
1535
1586
  userMessagesDropped: dropped,
1536
1587
  createdAt: Date.now(),
@@ -1538,7 +1589,7 @@ async function buildState(messages, provider, model, keepTokens, prior = null, o
1538
1589
  trimmed: false
1539
1590
  };
1540
1591
  }
1541
- function trimState(messages, prior = null, fraction = TRIM_FRACTION) {
1592
+ function trimState(messages, prior = null, fraction = TRIM_FRACTION, lang) {
1542
1593
  const start = prior?.boundaryIndex ?? 0;
1543
1594
  const remaining = messages.length - start;
1544
1595
  if (remaining <= 2) return null;
@@ -1556,7 +1607,7 @@ function trimState(messages, prior = null, fraction = TRIM_FRACTION) {
1556
1607
  const span = messages.slice(start, boundary);
1557
1608
  const priorUsers = prior?.userMessages ? [...prior.userMessages] : [];
1558
1609
  const priorSummary = prior?.summaryText ?? "";
1559
- const summary = (priorSummary ? priorSummary + "\n\n" : "") + "(Older turns were trimmed to fit the context window; no summary is available for them. Re-read files and re-run commands if earlier results are needed.)";
1610
+ const summary = (priorSummary ? priorSummary + "\n\n" : "") + getPrompts(lang).compactedTrimmedNote;
1560
1611
  const allUsers = [...priorUsers, ...extractUserMessages(span)];
1561
1612
  const { messages: cappedUsers, dropped } = capUserMessages(
1562
1613
  allUsers,
@@ -1565,7 +1616,7 @@ function trimState(messages, prior = null, fraction = TRIM_FRACTION) {
1565
1616
  return {
1566
1617
  boundaryIndex: boundary,
1567
1618
  summaryText: summary,
1568
- workingState: extractWorkingState(span),
1619
+ workingState: extractWorkingState(span, lang),
1569
1620
  userMessages: cappedUsers,
1570
1621
  userMessagesDropped: dropped,
1571
1622
  createdAt: Date.now(),
@@ -1576,7 +1627,7 @@ function trimState(messages, prior = null, fraction = TRIM_FRACTION) {
1576
1627
  function compactedBlock(state, lang) {
1577
1628
  const parts = [
1578
1629
  "<compacted-history>",
1579
- "Earlier turns of this session were compacted. The summary below is your memory of them.",
1630
+ getPrompts(lang).compactedIntro,
1580
1631
  "",
1581
1632
  state.summaryText
1582
1633
  ];
@@ -1586,12 +1637,10 @@ function compactedBlock(state, lang) {
1586
1637
  if (state.userMessages.length > 0) {
1587
1638
  parts.push(
1588
1639
  "",
1589
- "## User messages in the compacted span (verbatim, chronological)"
1640
+ getPrompts(lang).compactedUserMessagesHeader
1590
1641
  );
1591
1642
  if (state.userMessagesDropped > 0) {
1592
- parts.push(
1593
- `(${state.userMessagesDropped} earlier user messages omitted \u2014 their intent is covered by the summary above)`
1594
- );
1643
+ parts.push(getPrompts(lang).compactedOmittedNote(state.userMessagesDropped));
1595
1644
  }
1596
1645
  parts.push(...state.userMessages.map((u) => `- ${u}`));
1597
1646
  }
@@ -1693,12 +1742,12 @@ var MUTATING_TOOLS = /* @__PURE__ */ new Set([
1693
1742
  ]);
1694
1743
  function parseReadFileSpan(content) {
1695
1744
  const body = content.replace(/^\[tool output truncated:[^\n]*\]\n\n/, "");
1696
- let path12;
1745
+ let path13;
1697
1746
  let start;
1698
1747
  let end;
1699
1748
  try {
1700
1749
  const obj = JSON.parse(body);
1701
- path12 = obj.path;
1750
+ path13 = obj.path;
1702
1751
  start = obj.start_line;
1703
1752
  end = obj.end_line;
1704
1753
  } catch {
@@ -1707,14 +1756,14 @@ function parseReadFileSpan(content) {
1707
1756
  const m = new RegExp(`"${key}"\\s*:\\s*(\\d+)`).exec(body);
1708
1757
  return m ? Number(m[1]) : void 0;
1709
1758
  };
1710
- path12 = str("path");
1759
+ path13 = str("path");
1711
1760
  start = num("start_line");
1712
1761
  end = num("end_line");
1713
1762
  }
1714
- if (typeof path12 !== "string" || !path12) return null;
1763
+ if (typeof path13 !== "string" || !path13) return null;
1715
1764
  if (typeof start !== "number" || typeof end !== "number") return null;
1716
1765
  if (!Number.isFinite(start) || !Number.isFinite(end)) return null;
1717
- return { path: path12, start, end };
1766
+ return { path: path13, start, end };
1718
1767
  }
1719
1768
  function addSpan(spans, start, end) {
1720
1769
  spans.push([start, end]);
@@ -1771,16 +1820,45 @@ function applyReadFileDedup(messages) {
1771
1820
  }
1772
1821
  var OVERFLOW_MARKERS = [
1773
1822
  "context_length_exceeded",
1774
- "maximum context length",
1823
+ "context length",
1775
1824
  "context window",
1776
1825
  "prompt is too long",
1777
1826
  "input is too long",
1778
1827
  "too many tokens",
1779
1828
  "input length and `max_tokens` exceed",
1780
- "exceeds the maximum number of tokens"
1829
+ "exceeds the maximum number of tokens",
1830
+ "\u8D85\u51FA\u6A21\u578B\u4E0A\u4E0B\u6587",
1831
+ "\u8D85\u8FC7\u6A21\u578B\u4E0A\u4E0B\u6587",
1832
+ "\u8D85\u904E\u6A21\u578B\u4E0A\u4E0B\u6587",
1833
+ "\u4E0A\u4E0B\u6587\u957F\u5EA6\u8D85\u8FC7",
1834
+ "\u8D85\u8FC7\u6700\u5927\u4E0A\u4E0B\u6587",
1835
+ "\u8D85\u904E\u6700\u5927\u4E0A\u4E0B\u6587",
1836
+ "\u4E0A\u4E0B\u6587\u8D85\u9650",
1837
+ "\u8F93\u5165\u8FC7\u957F"
1781
1838
  ];
1839
+ function overflowText(exc) {
1840
+ const parts = [];
1841
+ const push = (v) => {
1842
+ if (typeof v === "string" && v) parts.push(v);
1843
+ };
1844
+ push(String(exc));
1845
+ if (exc && typeof exc === "object") {
1846
+ const rec = exc;
1847
+ push(rec["message"]);
1848
+ push(rec["detail"]);
1849
+ const body = rec["error"];
1850
+ if (body && typeof body === "object") {
1851
+ const b = body;
1852
+ push(b["message"]);
1853
+ push(b["code"]);
1854
+ } else {
1855
+ push(body);
1856
+ }
1857
+ }
1858
+ return parts.join(" ").toLowerCase();
1859
+ }
1782
1860
  function isContextOverflow(exc) {
1783
- const text = String(exc).toLowerCase();
1861
+ const text = overflowText(exc);
1784
1862
  return OVERFLOW_MARKERS.some((m) => text.includes(m));
1785
1863
  }
1786
1864
 
@@ -1813,6 +1891,9 @@ var TOOL_MAX_CHARS = {
1813
1891
  web_search: 8e3,
1814
1892
  list_files: 8e3,
1815
1893
  shell_task_output: 8e3,
1894
+ /* repo_map 自带 16K 上限(tools/repoMap.ts 的 MAX_CHARS)。这里显式登记,
1895
+ * 而不是落到 24K 默认值 —— 免得将来工具上限调大时被二次裁剪,口径也保持一致。 */
1896
+ repo_map: 16e3,
1816
1897
  git_log: 6e3,
1817
1898
  git_status: 4e3
1818
1899
  };
@@ -2122,6 +2203,7 @@ var INTERACTIVE_TOOLS = /* @__PURE__ */ new Set([
2122
2203
  var MAX_COMPACT_ROUNDS = 4;
2123
2204
  var OVERFLOW_TARGET_RATIO = 0.5;
2124
2205
  var OVERFLOW_TRIM_FRACTION = 0.5;
2206
+ var OVERFLOW_ELIDE_KEEP_FRACTION = 0.02;
2125
2207
  var COMPACT_STALL_RETRY_DELTA = 20;
2126
2208
  var FINALIZE_ON_LIMIT_PROMPT = "\u5DF2\u8FBE\u5230\u672C\u8F6E\u8FED\u4EE3\u4E0A\u9650\uFF0C\u4F60\u4E0D\u80FD\u518D\u8C03\u7528\u4EFB\u4F55\u5DE5\u5177\u3002\u8BF7\u7ACB\u5373\u57FA\u4E8E\u4E0A\u9762\u5DF2\u7ECF\u6536\u96C6\u5230\u7684\u4FE1\u606F\u8F93\u51FA\u6700\u7EC8\u62A5\u544A\uFF1A\u76F4\u63A5\u56DE\u7B54\u4EFB\u52A1\u3001\u7ED9\u51FA\u7ED3\u8BBA\u4E0E path:line \u5F15\u7528\uFF0C\u5E76\u660E\u786E\u8BF4\u660E\u54EA\u4E9B\u90E8\u5206\u5C1A\u672A\u67E5\u8BC1\u3002\u4E0D\u8981\u8BE2\u95EE\u3001\u4E0D\u8981\u7EE7\u7EED\u63A2\u7D22\u3002";
2127
2209
  function toolResultMessage(tc, result, taskId, toolResultDir2) {
@@ -2250,6 +2332,8 @@ var TurnEngine = class _TurnEngine {
2250
2332
  _lastContextTokens = null;
2251
2333
  /** 压缩无进展时的消息条数快照;-1 表示无冷却 */
2252
2334
  _compactStalledAt = -1;
2335
+ /** 溢出兜底:已 400 时无条件淘汰旧工具结果(下一次成功调用后复位) */
2336
+ _overflowElide = false;
2253
2337
  /** 显式配置的上下文窗口大小(token 数),未设置时为 undefined(走自动检测) */
2254
2338
  _contextWindow;
2255
2339
  /** 压缩触发阈值比例(默认 0.8) */
@@ -2388,11 +2472,12 @@ ${notice}` : text,
2388
2472
  };
2389
2473
  }
2390
2474
  /** 排队 steering 消息(下一迭代间隙注入为 user 消息) */
2391
- queueSteering(text, attachments, attachmentRefs) {
2475
+ queueSteering(text, attachments, attachmentRefs, actor) {
2392
2476
  this._steering.push({
2393
2477
  text,
2394
2478
  ...attachments?.length ? { attachments } : {},
2395
- ...attachmentRefs?.length ? { attachmentRefs } : {}
2479
+ ...attachmentRefs?.length ? { attachmentRefs } : {},
2480
+ ...actor ? { actor } : {}
2396
2481
  });
2397
2482
  }
2398
2483
  /**
@@ -2417,7 +2502,7 @@ ${notice}` : text,
2417
2502
  * 追加用户输入到历史,产出 TURN_START 事件,然后转发 _loop()
2418
2503
  * 的全部事件。收到中断后仍会消费完剩余事件(保证 TURN_END 产出)。
2419
2504
  */
2420
- async *run(userInput, taskId, attachments, attachmentRefs) {
2505
+ async *run(userInput, taskId, attachments, attachmentRefs, actor) {
2421
2506
  if (!this.model.trim()) {
2422
2507
  throw new Error(
2423
2508
  "\u672A\u6307\u5B9A\u6A21\u578B\uFF1A\u8BF7\u5148\u9009\u62E9\u6A21\u578B\uFF08CLI \u8FD0\u884C /connect\uFF1B\u63A7\u5236\u53F0\u5728\u300C\u8BBE\u7F6E \u2192 AI \u6A21\u578B\u300D\u4E2D\u6DFB\u52A0\u4F9B\u5E94\u5546\u5E76\u9009\u62E9\u6A21\u578B\uFF09"
@@ -2440,12 +2525,15 @@ ${notice}` : text,
2440
2525
  this._cancel = false;
2441
2526
  this._producedFiles = [];
2442
2527
  this._compactStalledAt = -1;
2528
+ this._overflowElide = false;
2443
2529
  this._emitHook((h) => h.onTurnStart?.(userInput));
2444
2530
  yield {
2445
2531
  type: "turn_start" /* TURN_START */,
2446
2532
  data: {
2447
2533
  taskId: this.taskId,
2448
2534
  input: userInput,
2535
+ // 谁发起的本轮:会话详情据此标注(本地/旧网关没有该字段)
2536
+ ...actor ? { actor } : {},
2449
2537
  // 只报个数:附件字节不进事件流(事件会落盘,base64 会把日志撑爆)
2450
2538
  ...attachments?.length ? { attachmentCount: attachments.length } : {},
2451
2539
  // 落盘引用足够小,随事件带给控制台渲染缩略图 / 详情回看
@@ -2749,6 +2837,12 @@ ${notice}` : text,
2749
2837
  type: "compacting" /* COMPACTING */,
2750
2838
  data: { taskId: this.taskId, ...startInfo }
2751
2839
  };
2840
+ let elided = false;
2841
+ if (this._elisionEnabled) {
2842
+ const before = this._estimateContextTokens();
2843
+ this._overflowElide = true;
2844
+ elided = this._estimateContextTokens() < before;
2845
+ }
2752
2846
  const result = await this._compactNow(overflowReqId, true);
2753
2847
  if (result.notice) {
2754
2848
  const notice = result.notice;
@@ -2787,6 +2881,11 @@ ${notice}` : text,
2787
2881
  }
2788
2882
  continue;
2789
2883
  }
2884
+ if (elided) {
2885
+ this._appendNotice("compacted", "\u4E0A\u4E0B\u6587\u6EA2\u51FA \u2014 \u5DF2\u6DD8\u6C70\u65E7\u5DE5\u5177\u7ED3\u679C\u540E\u91CD\u8BD5");
2886
+ continue;
2887
+ }
2888
+ this._overflowElide = false;
2790
2889
  }
2791
2890
  if (!this._cancel && !streamed && isTransientNetworkError(exc) && netRetries < netMax) {
2792
2891
  netRetries++;
@@ -2857,6 +2956,7 @@ ${notice}` : text,
2857
2956
  return;
2858
2957
  }
2859
2958
  netRetries = 0;
2959
+ this._overflowElide = false;
2860
2960
  if (this._cancel && turn === null) {
2861
2961
  yield* this._finishInterrupted({
2862
2962
  iterations,
@@ -2915,6 +3015,7 @@ ${notice}` : text,
2915
3015
  data: {
2916
3016
  text: s.text,
2917
3017
  taskId: this.taskId,
3018
+ ...s.actor ? { actor: s.actor } : {},
2918
3019
  ...s.attachmentRefs?.length ? { attachments: s.attachmentRefs } : {}
2919
3020
  }
2920
3021
  };
@@ -2979,6 +3080,7 @@ ${notice}` : text,
2979
3080
  data: {
2980
3081
  text: s.text,
2981
3082
  taskId: this.taskId,
3083
+ ...s.actor ? { actor: s.actor } : {},
2982
3084
  ...s.attachmentRefs?.length ? { attachments: s.attachmentRefs } : {}
2983
3085
  }
2984
3086
  };
@@ -3403,13 +3505,14 @@ ${notice}` : text,
3403
3505
  const deduped = this._dedupEnabled ? applyReadFileDedup(base).messages : base;
3404
3506
  let view = this._finalizeOutbound(deduped);
3405
3507
  if (!this._elisionEnabled) return view;
3406
- if (estimateTokens(view) < this._softTriggerTokens()) {
3508
+ const soft = this._softTriggerTokens();
3509
+ if (!this._overflowElide && estimateTokens(view) < soft) {
3407
3510
  return view;
3408
3511
  }
3409
3512
  const oneShot = applyOneShotElision(deduped);
3410
3513
  if (oneShot.freedChars > 0) {
3411
3514
  view = this._finalizeOutbound(oneShot.messages);
3412
- if (estimateTokens(view) < this._softTriggerTokens()) {
3515
+ if (!this._overflowElide && estimateTokens(view) < soft) {
3413
3516
  return view;
3414
3517
  }
3415
3518
  }
@@ -3418,7 +3521,8 @@ ${notice}` : text,
3418
3521
  this._getContextWindow(),
3419
3522
  this._thresholdPct,
3420
3523
  this._capTokens
3421
- )
3524
+ ),
3525
+ this._overflowElide ? OVERFLOW_ELIDE_KEEP_FRACTION : ELIDE_KEEP_FRACTION
3422
3526
  );
3423
3527
  const elided = applyToolResultElision(
3424
3528
  oneShot.messages,
@@ -3617,7 +3721,8 @@ ${notice}` : text,
3617
3721
  const trimmed = trimState(
3618
3722
  this.messages,
3619
3723
  this.compactionState,
3620
- force ? OVERFLOW_TRIM_FRACTION : 0.25
3724
+ force ? OVERFLOW_TRIM_FRACTION : 0.25,
3725
+ this.lang
3621
3726
  );
3622
3727
  if (!trimmed) break;
3623
3728
  this.compactionState = trimmed;
@@ -3662,9 +3767,9 @@ ${notice}` : text,
3662
3767
  return;
3663
3768
  }
3664
3769
  if (result && typeof result === "object") {
3665
- const path12 = result.path;
3666
- if (path12) {
3667
- this._producedFiles.push(path12);
3770
+ const path13 = result.path;
3771
+ if (path13) {
3772
+ this._producedFiles.push(path13);
3668
3773
  return;
3669
3774
  }
3670
3775
  const changed = result.changed_files;
@@ -4203,62 +4308,196 @@ var PermissionEngine = class {
4203
4308
  };
4204
4309
 
4205
4310
  // src/tools/files.ts
4311
+ import * as fs6 from "fs";
4312
+ import * as path8 from "path";
4313
+ import { createHash } from "crypto";
4314
+ import { StringDecoder } from "string_decoder";
4315
+
4316
+ // src/tools/ignore.ts
4206
4317
  import * as fs5 from "fs";
4207
4318
  import * as path7 from "path";
4319
+ var ALWAYS_SKIP_DIRS = /* @__PURE__ */ new Set([
4320
+ // JS / TS
4321
+ "node_modules",
4322
+ "dist",
4323
+ "build",
4324
+ "out",
4325
+ ".next",
4326
+ ".nuxt",
4327
+ ".cache",
4328
+ // JVM
4329
+ "target",
4330
+ ".gradle",
4331
+ ".mvn",
4332
+ // .NET
4333
+ "bin",
4334
+ "obj",
4335
+ ".vs",
4336
+ "TestResults",
4337
+ // Python / Ruby / PHP
4338
+ "__pycache__",
4339
+ ".venv",
4340
+ "venv",
4341
+ ".pytest_cache",
4342
+ ".mypy_cache",
4343
+ "vendor",
4344
+ // 移动端
4345
+ "Pods",
4346
+ ".dart_tool",
4347
+ "DerivedData",
4348
+ // 覆盖率 / 通用
4349
+ "coverage",
4350
+ // 版本库
4351
+ ".git",
4352
+ ".svn",
4353
+ ".hg"
4354
+ ]);
4355
+ function loadGitignore(dir) {
4356
+ const file = path7.join(dir, ".gitignore");
4357
+ let text;
4358
+ try {
4359
+ const stat = fs5.statSync(file);
4360
+ if (stat.size > 64 * 1024) return [];
4361
+ text = fs5.readFileSync(file, "utf-8");
4362
+ } catch {
4363
+ return [];
4364
+ }
4365
+ const matchers = [];
4366
+ for (const rawLine of text.split("\n")) {
4367
+ let line = rawLine.replace(/\r$/, "").trim();
4368
+ if (!line || line.startsWith("#")) continue;
4369
+ if (line.startsWith("!")) continue;
4370
+ const anchored = rawLine.trim().startsWith("/");
4371
+ const dirOnly = /\/\s*$/.test(rawLine.trim());
4372
+ line = line.replace(/^\/+/, "").replace(/\/+$/, "");
4373
+ if (!line) continue;
4374
+ const pattern = line.split("").map((ch) => {
4375
+ if (ch === "*") return ".*";
4376
+ if (ch === "?") return ".";
4377
+ return ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
4378
+ }).join("");
4379
+ const re = new RegExp(line.includes("/") || anchored ? `^${pattern}$` : `(^|/)${pattern}$`);
4380
+ matchers.push((rel, isDir) => {
4381
+ if (dirOnly && !isDir) return false;
4382
+ return re.test(rel);
4383
+ });
4384
+ }
4385
+ return matchers;
4386
+ }
4387
+ function makeIgnoreFilter(root) {
4388
+ const matchers = loadGitignore(root);
4389
+ return (rel, isDir) => {
4390
+ const name = rel.slice(rel.lastIndexOf("/") + 1);
4391
+ if (name.startsWith(".")) return true;
4392
+ if (isDir && ALWAYS_SKIP_DIRS.has(name)) return true;
4393
+ return matchers.some((m) => m(rel, isDir));
4394
+ };
4395
+ }
4396
+
4397
+ // src/tools/files.ts
4208
4398
  var DEFAULT_MAX_LINES = 2e3;
4209
4399
  var MULTI_READ_MAX_TOTAL_LINES = 1500;
4210
4400
  var MAX_LINE_CHARS = 500;
4401
+ var READ_CHUNK_BYTES = 64 * 1024;
4402
+ var RESULT_META_RESERVE = 800;
4211
4403
  function makeReadFile(workspace, guard) {
4212
- const root = path7.resolve(workspace);
4213
- function readOne(filePath, startLine, maxLines) {
4404
+ const root = path8.resolve(workspace);
4405
+ function readWindow(target, startLine, maxLines, charBudget, onDigest) {
4406
+ const fd = fs6.openSync(target, "r");
4407
+ try {
4408
+ const decoder = new StringDecoder("utf-8");
4409
+ const hash = createHash("sha256");
4410
+ const buf = Buffer.allocUnsafe(READ_CHUNK_BYTES);
4411
+ const lines = [];
4412
+ let lineNo = 0;
4413
+ let pending = "";
4414
+ let chars = 0;
4415
+ let capped = false;
4416
+ let pos = 0;
4417
+ const takeLine = (text) => {
4418
+ lineNo += 1;
4419
+ if (capped || lineNo < startLine || lines.length >= maxLines) return;
4420
+ const clipped = text.length > MAX_LINE_CHARS ? text.slice(0, MAX_LINE_CHARS) + "\u2026 (line truncated)" : text;
4421
+ const rendered = `${lineNo}|${clipped}`;
4422
+ if (chars + rendered.length + 1 > charBudget) {
4423
+ capped = true;
4424
+ return;
4425
+ }
4426
+ chars += rendered.length + 1;
4427
+ lines.push(rendered);
4428
+ };
4429
+ for (; ; ) {
4430
+ const n = fs6.readSync(fd, buf, 0, READ_CHUNK_BYTES, pos);
4431
+ if (n <= 0) break;
4432
+ pos += n;
4433
+ const decoded = decoder.write(buf.subarray(0, n));
4434
+ hash.update(decoded, "utf8");
4435
+ const text = pending + decoded;
4436
+ let from = 0;
4437
+ for (; ; ) {
4438
+ const idx = text.indexOf("\n", from);
4439
+ if (idx < 0) break;
4440
+ takeLine(text.slice(from, idx).replace(/\r$/, ""));
4441
+ from = idx + 1;
4442
+ }
4443
+ pending = text.slice(from);
4444
+ }
4445
+ const rest = decoder.end();
4446
+ if (rest) hash.update(rest, "utf8");
4447
+ const tail = pending + rest;
4448
+ if (tail.length > 0) takeLine(tail.replace(/\r$/, ""));
4449
+ onDigest(hash.digest("hex").slice(0, 16));
4450
+ return { lines, totalLines: lineNo, capped };
4451
+ } finally {
4452
+ fs6.closeSync(fd);
4453
+ }
4454
+ }
4455
+ function readOne(filePath, startLine, maxLines, charBudget) {
4214
4456
  const resolved = resolveWithinRoot(root, filePath);
4215
4457
  if (!resolved) {
4216
4458
  return { path: filePath, error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
4217
4459
  }
4218
4460
  const target = resolved.real;
4219
- if (!fs5.existsSync(target) || !fs5.statSync(target).isFile()) {
4461
+ if (!fs6.existsSync(target) || !fs6.statSync(target).isFile()) {
4220
4462
  return { path: filePath, error: `\u4E0D\u662F\u6587\u4EF6: ${filePath}` };
4221
4463
  }
4222
- const selected = [];
4223
- let total = 0;
4464
+ let read;
4224
4465
  try {
4225
- const content = fs5.readFileSync(target, "utf-8");
4226
- guard?.noteRead(target, content);
4227
- const lines = content.split("\n");
4228
- total = lines.length;
4229
- for (let i = startLine - 1; i < Math.min(startLine - 1 + maxLines, lines.length); i++) {
4230
- let text = lines[i];
4231
- if (text.length > MAX_LINE_CHARS) {
4232
- text = text.slice(0, MAX_LINE_CHARS) + "\u2026 (line truncated)";
4233
- }
4234
- selected.push(`${i + 1}|${text}`);
4235
- }
4466
+ read = readWindow(target, startLine, maxLines, charBudget, (digest) => {
4467
+ guard?.noteReadDigest(target, digest);
4468
+ });
4236
4469
  } catch (exc) {
4237
4470
  return { path: filePath, error: `\u8BFB\u53D6\u5931\u8D25: ${String(exc)}` };
4238
4471
  }
4472
+ const { lines: selected, totalLines: total, capped } = read;
4239
4473
  const end = startLine + selected.length - 1;
4240
4474
  const result = {
4241
- path: path7.relative(resolved.root, target),
4475
+ path: path8.relative(resolved.root, target),
4242
4476
  start_line: startLine,
4243
4477
  end_line: end,
4244
4478
  total_lines: total,
4245
4479
  content: selected.join("\n")
4246
4480
  };
4247
- if (end < total) {
4481
+ if (capped) {
4482
+ result.note = `\u5DF2\u8FD4\u56DE\u7B2C ${startLine}-${end} \u884C\uFF08\u672C\u6B21\u5DF2\u8FBE\u5355\u6B21\u8BFB\u53D6\u4E0A\u9650\u7EA6 ${charBudget} \u5B57\u7B26\uFF0C\u5171 ${total} \u884C\uFF09\uFF1B\u8C03\u7528 read_file(start_line=${end + 1}) \u7EE7\u7EED\u9605\u8BFB`;
4483
+ } else if (end < total) {
4248
4484
  result.note = `\u663E\u793A\u7B2C ${startLine}-${end} \u884C\uFF0C\u5171 ${total} \u884C\uFF1B\u8C03\u7528 read_file(start_line=${end + 1}) \u7EE7\u7EED\u9605\u8BFB`;
4249
4485
  }
4250
4486
  return result;
4251
4487
  }
4252
4488
  return function read_file(args) {
4253
4489
  const startLine = Math.max(1, Number(args.start_line ?? 1));
4254
- const maxLines = Math.min(Number(args.max_lines ?? DEFAULT_MAX_LINES), DEFAULT_MAX_LINES);
4490
+ const requestedLines = Number(args.max_lines ?? DEFAULT_MAX_LINES);
4491
+ const maxLines = Number.isFinite(requestedLines) ? Math.min(Math.max(1, Math.floor(requestedLines)), DEFAULT_MAX_LINES) : DEFAULT_MAX_LINES;
4492
+ const fullBudget = Math.max(1e3, toolResultBudget("read_file") - RESULT_META_RESERVE);
4255
4493
  const paths = Array.isArray(args.paths) ? args.paths.map((p) => String(p ?? "")).filter(Boolean) : [];
4256
4494
  if (paths.length > 0) {
4495
+ const perFileBudget = Math.max(1e3, Math.floor(fullBudget / paths.length));
4257
4496
  const files = [];
4258
4497
  let remainingLines = MULTI_READ_MAX_TOTAL_LINES;
4259
4498
  for (const filePath2 of paths) {
4260
4499
  const perFile = Math.min(maxLines, Math.max(1, remainingLines));
4261
- const one = readOne(filePath2, startLine, perFile);
4500
+ const one = readOne(filePath2, startLine, perFile, perFileBudget);
4262
4501
  files.push(one);
4263
4502
  if (!one.error) remainingLines -= one.content.split("\n").length;
4264
4503
  if (remainingLines <= 0) break;
@@ -4273,31 +4512,79 @@ function makeReadFile(workspace, guard) {
4273
4512
  if (!filePath) {
4274
4513
  return { error: "\u7F3A\u5C11 path\uFF08\u6216 paths\uFF09\u53C2\u6570" };
4275
4514
  }
4276
- return readOne(filePath, startLine, maxLines);
4515
+ return readOne(filePath, startLine, maxLines, fullBudget);
4277
4516
  };
4278
4517
  }
4518
+ var DEFAULT_LIST_DEPTH = 3;
4519
+ var MAX_LIST_DEPTH = 6;
4520
+ var DEFAULT_RECURSIVE_LIMIT = 400;
4521
+ var MAX_RECURSIVE_LIMIT = 1e3;
4279
4522
  function makeListFiles(workspace) {
4280
- const root = path7.resolve(workspace);
4523
+ const root = path8.resolve(workspace);
4281
4524
  return function list_files(args) {
4282
4525
  const dirPath = String(args.path ?? ".");
4283
- const maxResults = Math.min(Number(args.max_results ?? 100), 100);
4526
+ const recursive = args.recursive === true;
4527
+ const requestedMax = Number(args.max_results ?? (recursive ? DEFAULT_RECURSIVE_LIMIT : 100));
4528
+ const hardCap = recursive ? MAX_RECURSIVE_LIMIT : 100;
4529
+ const maxResults = Number.isFinite(requestedMax) ? Math.max(1, Math.min(Math.floor(requestedMax), hardCap)) : hardCap;
4530
+ const requestedDepth = Number(args.max_depth ?? DEFAULT_LIST_DEPTH);
4531
+ const maxDepth = Number.isFinite(requestedDepth) ? Math.max(1, Math.min(Math.floor(requestedDepth), MAX_LIST_DEPTH)) : DEFAULT_LIST_DEPTH;
4284
4532
  const resolved = resolveWithinRoot(root, dirPath);
4285
4533
  if (!resolved) return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
4286
4534
  const target = resolved.real;
4287
- if (!fs5.existsSync(target) || !fs5.statSync(target).isDirectory()) {
4535
+ if (!fs6.existsSync(target) || !fs6.statSync(target).isDirectory()) {
4288
4536
  return { error: `\u4E0D\u662F\u76EE\u5F55: ${dirPath}` };
4289
4537
  }
4538
+ const realRoot = resolved.root;
4539
+ const isIgnored = makeIgnoreFilter(realRoot);
4290
4540
  try {
4291
- const entries = fs5.readdirSync(target, { withFileTypes: true });
4541
+ if (!recursive) {
4542
+ const results2 = [];
4543
+ for (const e of fs6.readdirSync(target, { withFileTypes: true })) {
4544
+ if (results2.length >= maxResults) break;
4545
+ const rel = path8.relative(realRoot, path8.join(target, e.name)).split(path8.sep).join("/");
4546
+ if (isIgnored(rel, e.isDirectory())) continue;
4547
+ results2.push(e.isDirectory() ? `${e.name}/` : e.name);
4548
+ }
4549
+ results2.sort();
4550
+ return { path: path8.relative(realRoot, target), entries: results2.slice(0, maxResults) };
4551
+ }
4292
4552
  const results = [];
4293
- for (const e of entries) {
4294
- if (results.length >= maxResults) break;
4295
- if (e.name.startsWith(".")) continue;
4296
- if (e.name === "node_modules" || e.name === "__pycache__" || e.name === "dist") continue;
4297
- results.push(e.isDirectory() ? `${e.name}/` : e.name);
4553
+ let truncated = false;
4554
+ const queue = [{ dir: target, depth: 1 }];
4555
+ while (queue.length > 0) {
4556
+ const { dir, depth } = queue.shift();
4557
+ let entries;
4558
+ try {
4559
+ entries = fs6.readdirSync(dir, { withFileTypes: true });
4560
+ } catch {
4561
+ continue;
4562
+ }
4563
+ entries.sort((a, b) => {
4564
+ if (a.isDirectory() !== b.isDirectory()) return a.isDirectory() ? -1 : 1;
4565
+ return a.name.localeCompare(b.name, "zh");
4566
+ });
4567
+ for (const e of entries) {
4568
+ if (results.length >= maxResults) {
4569
+ truncated = true;
4570
+ break;
4571
+ }
4572
+ const full = path8.join(dir, e.name);
4573
+ const rel = path8.relative(realRoot, full).split(path8.sep).join("/");
4574
+ const isDir = e.isDirectory();
4575
+ if (isIgnored(rel, isDir)) continue;
4576
+ results.push(isDir ? `${rel}/` : rel);
4577
+ if (isDir && depth < maxDepth) queue.push({ dir: full, depth: depth + 1 });
4578
+ }
4579
+ if (truncated) break;
4298
4580
  }
4299
- results.sort();
4300
- return { path: path7.relative(resolved.root, target), entries: results.slice(0, maxResults) };
4581
+ return {
4582
+ path: path8.relative(realRoot, target),
4583
+ entries: results,
4584
+ ...truncated ? {
4585
+ note: `\u5DF2\u8FBE\u5230\u6761\u76EE\u4E0A\u9650 ${maxResults}\uFF0C\u7ED3\u679C\u88AB\u622A\u65AD\uFF1B\u8BF7\u7F29\u5C0F path \u5230\u5177\u4F53\u5B50\u76EE\u5F55\uFF0C\u6216\u964D\u4F4E max_depth\uFF08\u5F53\u524D ${maxDepth}\uFF09`
4586
+ } : {}
4587
+ };
4301
4588
  } catch (exc) {
4302
4589
  return { error: `\u5217\u51FA\u6587\u4EF6\u5931\u8D25: ${String(exc)}` };
4303
4590
  }
@@ -4337,12 +4624,23 @@ function registerReadOnlyFileTools(registry, workspace, guard) {
4337
4624
  type: "function",
4338
4625
  function: {
4339
4626
  name: "list_files",
4340
- description: "\u5217\u51FA\u76EE\u5F55\u4E0B\u7684\u6587\u4EF6\u548C\u5B50\u76EE\u5F55\u3002\u8DF3\u8FC7\u9690\u85CF\u6587\u4EF6\u548C node_modules \u7B49\u76EE\u5F55\u3002",
4627
+ description: "\u5217\u51FA\u76EE\u5F55\u4E0B\u7684\u6587\u4EF6\u548C\u5B50\u76EE\u5F55\u3002\u8DF3\u8FC7\u9690\u85CF\u9879\u3001\u5E38\u89C1\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\uFF0C\u5E76\u9075\u5B88\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55\u7684 .gitignore\u3002\u4E86\u89E3\u5927\u4ED3\u5E93\u7ED3\u6784\u65F6\u7528 recursive=true \u4E00\u6B21\u62FF\u5230\u6709\u9650\u6DF1\u5EA6\uFF08\u9ED8\u8BA4 3 \u5C42\uFF09\u7684\u6811\uFF0C\u6BD4\u9010\u5C42\u8C03\u7528\u7701\u5F88\u591A\u5F80\u8FD4\uFF1B\u6761\u76EE\u8FC7\u591A\u65F6\u4F1A\u622A\u65AD\u5E76\u63D0\u793A\u3002",
4341
4628
  parameters: {
4342
4629
  type: "object",
4343
4630
  properties: {
4344
4631
  path: { type: "string", description: "\u76EE\u5F55\u8DEF\u5F84 (\u76F8\u5BF9\u4E8E\u5DE5\u4F5C\u533A\uFF0C\u9ED8\u8BA4 '.')" },
4345
- max_results: { type: "integer", description: "\u6700\u5927\u7ED3\u679C\u6570 (\u9ED8\u8BA4 100)" }
4632
+ recursive: {
4633
+ type: "boolean",
4634
+ description: "\u9012\u5F52\u5217\u51FA\u5B50\u76EE\u5F55\uFF08\u9ED8\u8BA4 false\uFF0C\u53EA\u5217\u4E00\u5C42\uFF09"
4635
+ },
4636
+ max_depth: {
4637
+ type: "integer",
4638
+ description: `\u9012\u5F52\u6DF1\u5EA6 (\u9ED8\u8BA4 ${DEFAULT_LIST_DEPTH}\uFF0C\u4E0A\u9650 ${MAX_LIST_DEPTH}\uFF1B\u4EC5 recursive \u65F6\u751F\u6548)`
4639
+ },
4640
+ max_results: {
4641
+ type: "integer",
4642
+ description: `\u6700\u5927\u7ED3\u679C\u6570\uFF08\u975E\u9012\u5F52\u9ED8\u8BA4 100 / \u4E0A\u9650 100\uFF1B\u9012\u5F52\u9ED8\u8BA4 ${DEFAULT_RECURSIVE_LIMIT} / \u4E0A\u9650 ${MAX_RECURSIVE_LIMIT}\uFF09`
4643
+ }
4346
4644
  }
4347
4645
  }
4348
4646
  }
@@ -4510,10 +4808,10 @@ function execAsync(file, argv, opts = {}) {
4510
4808
  }
4511
4809
 
4512
4810
  // src/tools/shellRuntime.ts
4513
- import * as path8 from "path";
4811
+ import * as path9 from "path";
4514
4812
  var WINDOWS_SUFFIXES = [".exe", ".cmd", ".bat", ".com", ""];
4515
4813
  function pathApi(platform) {
4516
- return platform === "win32" ? path8.win32 : path8.posix;
4814
+ return platform === "win32" ? path9.win32 : path9.posix;
4517
4815
  }
4518
4816
  function joinPath(platform, ...parts) {
4519
4817
  return pathApi(platform).join(...parts);
@@ -4726,8 +5024,8 @@ function decodeOutput(buf) {
4726
5024
  var shellTasks = new ShellTaskManager();
4727
5025
 
4728
5026
  // src/tools/nodeGrep.ts
4729
- import * as fs6 from "fs";
4730
- import * as path9 from "path";
5027
+ import * as fs7 from "fs";
5028
+ import * as path10 from "path";
4731
5029
  var SKIP_DIRS = /* @__PURE__ */ new Set([
4732
5030
  ".git",
4733
5031
  "node_modules",
@@ -4752,27 +5050,27 @@ function nodeGrep(opts) {
4752
5050
  } catch (err) {
4753
5051
  throw new Error(`\u65E0\u6548\u7684\u6B63\u5219\u8868\u8FBE\u5F0F: ${opts.pattern}\uFF08${String(err)}\uFF09`);
4754
5052
  }
4755
- const target = path9.resolve(opts.workspace, opts.target);
4756
- if (!fs6.existsSync(target)) {
5053
+ const target = path10.resolve(opts.workspace, opts.target);
5054
+ if (!fs7.existsSync(target)) {
4757
5055
  throw new Error(`\u641C\u7D22\u8DEF\u5F84\u4E0D\u5B58\u5728: ${opts.target}`);
4758
5056
  }
4759
5057
  const matches = [];
4760
5058
  const scanFile = (file) => {
4761
5059
  let stat2;
4762
5060
  try {
4763
- stat2 = fs6.statSync(file);
5061
+ stat2 = fs7.statSync(file);
4764
5062
  } catch {
4765
5063
  return false;
4766
5064
  }
4767
5065
  if (!stat2.isFile() || stat2.size > maxFileBytes) return false;
4768
5066
  let buf;
4769
5067
  try {
4770
- buf = fs6.readFileSync(file);
5068
+ buf = fs7.readFileSync(file);
4771
5069
  } catch {
4772
5070
  return false;
4773
5071
  }
4774
5072
  if (looksBinary(buf)) return false;
4775
- const rel = path9.relative(opts.workspace, file) || path9.basename(file);
5073
+ const rel = path10.relative(opts.workspace, file) || path10.basename(file);
4776
5074
  const lines = buf.toString("utf-8").split("\n");
4777
5075
  for (let i = 0; i < lines.length; i++) {
4778
5076
  const raw = lines[i].replace(/\r$/, "");
@@ -4783,7 +5081,7 @@ function nodeGrep(opts) {
4783
5081
  }
4784
5082
  return false;
4785
5083
  };
4786
- const stat = fs6.statSync(target);
5084
+ const stat = fs7.statSync(target);
4787
5085
  if (stat.isFile()) {
4788
5086
  scanFile(target);
4789
5087
  return matches;
@@ -4793,12 +5091,12 @@ function nodeGrep(opts) {
4793
5091
  const dir = stack.pop();
4794
5092
  let entries;
4795
5093
  try {
4796
- entries = fs6.readdirSync(dir, { withFileTypes: true });
5094
+ entries = fs7.readdirSync(dir, { withFileTypes: true });
4797
5095
  } catch {
4798
5096
  continue;
4799
5097
  }
4800
5098
  for (const entry of entries) {
4801
- const full = path9.join(dir, entry.name);
5099
+ const full = path10.join(dir, entry.name);
4802
5100
  if (entry.isDirectory()) {
4803
5101
  if (!SKIP_DIRS.has(entry.name)) stack.push(full);
4804
5102
  } else if (entry.isFile()) {
@@ -4810,22 +5108,22 @@ function nodeGrep(opts) {
4810
5108
  }
4811
5109
 
4812
5110
  // src/tools/ripgrep.ts
4813
- import * as fs7 from "fs";
5111
+ import * as fs8 from "fs";
4814
5112
  import * as os3 from "os";
4815
- import * as path10 from "path";
5113
+ import * as path11 from "path";
4816
5114
  import { createRequire } from "module";
4817
5115
  var cached;
4818
5116
  function extractBundledRipgrep() {
4819
5117
  try {
4820
- const req = typeof __require === "function" ? __require : createRequire(path10.join(process.cwd(), "__zqagent__.js"));
5118
+ const req = typeof __require === "function" ? __require : createRequire(path11.join(process.cwd(), "__zqagent__.js"));
4821
5119
  const sea = req("node:sea");
4822
5120
  if (!sea?.isSea?.() || typeof sea.getAsset !== "function") return null;
4823
5121
  const asset = sea.getAsset("rg");
4824
5122
  if (!asset) return null;
4825
- const dir = fs7.mkdtempSync(path10.join(os3.tmpdir(), "zqagent-rg-"));
4826
- const file = path10.join(dir, process.platform === "win32" ? "rg.exe" : "rg");
4827
- fs7.writeFileSync(file, Buffer.from(asset));
4828
- fs7.chmodSync(file, 493);
5123
+ const dir = fs8.mkdtempSync(path11.join(os3.tmpdir(), "zqagent-rg-"));
5124
+ const file = path11.join(dir, process.platform === "win32" ? "rg.exe" : "rg");
5125
+ fs8.writeFileSync(file, Buffer.from(asset));
5126
+ fs8.chmodSync(file, 493);
4829
5127
  return file;
4830
5128
  } catch {
4831
5129
  return null;
@@ -4834,7 +5132,7 @@ function extractBundledRipgrep() {
4834
5132
  function resolveRipgrep() {
4835
5133
  if (cached !== void 0) return cached;
4836
5134
  const explicit = (process.env.ZQAGENT_RG ?? "").trim();
4837
- if (explicit && fs7.existsSync(explicit)) {
5135
+ if (explicit && fs8.existsSync(explicit)) {
4838
5136
  cached = explicit;
4839
5137
  return cached;
4840
5138
  }
@@ -4848,7 +5146,7 @@ function resolveRipgrep() {
4848
5146
  env: process.env,
4849
5147
  fileExists: (p) => {
4850
5148
  try {
4851
- return fs7.statSync(p).isFile();
5149
+ return fs8.statSync(p).isFile();
4852
5150
  } catch {
4853
5151
  return false;
4854
5152
  }
@@ -4858,12 +5156,61 @@ function resolveRipgrep() {
4858
5156
  }
4859
5157
 
4860
5158
  // src/tools/search.ts
4861
- function parseRgOutput(result) {
4862
- return result.split("\n").map((l) => l.replace(/\r$/, "")).filter((l) => l.length > 0).slice(0, 50).map((line) => {
4863
- const m = /^(.+?):(\d+):(.*)$/.exec(line);
4864
- if (!m) return { file: line.trim(), line: 0, text: "" };
4865
- return { file: m[1], line: Number(m[2]), text: m[3].trim().slice(0, 200) };
4866
- });
5159
+ var DEFAULT_MAX_RESULTS = 50;
5160
+ var MAX_RESULTS_CAP = 200;
5161
+ var MAX_TEXT_CHARS = 200;
5162
+ var RG_TIMEOUT_MS = 1e4;
5163
+ function parseRgOutput(result, limit) {
5164
+ const out = [];
5165
+ for (const raw of result.split("\n")) {
5166
+ if (out.length >= limit) break;
5167
+ const line = raw.replace(/\r$/, "");
5168
+ if (!line) continue;
5169
+ const m = /^(.+?)[:-](\d+)[:-](.*)$/.exec(line);
5170
+ if (!m) continue;
5171
+ out.push({
5172
+ file: m[1],
5173
+ line: Number(m[2]),
5174
+ text: m[3].trim().slice(0, MAX_TEXT_CHARS)
5175
+ });
5176
+ }
5177
+ return out;
5178
+ }
5179
+ function parseRgFiles(result, limit) {
5180
+ const files = [];
5181
+ const seen = /* @__PURE__ */ new Set();
5182
+ for (const raw of result.split("\n")) {
5183
+ if (files.length >= limit) break;
5184
+ const f = raw.replace(/\r$/, "").trim();
5185
+ if (!f || seen.has(f)) continue;
5186
+ seen.add(f);
5187
+ files.push(f);
5188
+ }
5189
+ return files;
5190
+ }
5191
+ function rgArgs(opts) {
5192
+ const args = [];
5193
+ if (opts.filesOnly) {
5194
+ args.push("--files-with-matches");
5195
+ } else {
5196
+ args.push("--with-filename", "--line-number", "--no-heading");
5197
+ args.push("--max-count", String(Math.max(opts.maxResults, 1)));
5198
+ }
5199
+ if (opts.ignoreCase) args.push("--ignore-case");
5200
+ if (opts.glob) args.push("--glob", opts.glob);
5201
+ if (opts.contextLines > 0 && !opts.filesOnly) {
5202
+ args.push("--context", String(opts.contextLines));
5203
+ }
5204
+ args.push("--", opts.pattern, opts.path);
5205
+ return args;
5206
+ }
5207
+ function rgFailureHint(res) {
5208
+ if (res.timedOut) {
5209
+ return `\u641C\u7D22\u8D85\u65F6\uFF08${RG_TIMEOUT_MS / 1e3}s\uFF09\u3002\u5927\u4ED3\u5E93\u5168\u91CF\u641C\u7D22\u5F88\u5BB9\u6613\u8D85\u65F6\uFF1A\u8BF7\u7F29\u5C0F path \u5230\u5177\u4F53\u5B50\u76EE\u5F55\uFF0C\u6216\u7528 glob \u53C2\u6570\u9650\u5236\u6587\u4EF6\u7C7B\u578B\uFF08\u5982 "*.ts"\uFF09\u540E\u91CD\u8BD5`;
5210
+ }
5211
+ if (res.aborted) return "\u641C\u7D22\u5DF2\u4E2D\u65AD";
5212
+ const stderr = decodeOutput(res.stderr).trim().slice(0, 300);
5213
+ return `\u641C\u7D22\u5931\u8D25\uFF08rg \u9000\u51FA\u7801 ${String(res.status)}\uFF09${stderr ? `\uFF1A${stderr}` : ""}`;
4867
5214
  }
4868
5215
  function makeGrep(workspace) {
4869
5216
  return async function grep(args) {
@@ -4872,37 +5219,50 @@ function makeGrep(workspace) {
4872
5219
  if (!pattern.trim()) {
4873
5220
  return { error: "\u641C\u7D22\u6A21\u5F0F\u4E0D\u80FD\u4E3A\u7A7A" };
4874
5221
  }
5222
+ const filesOnly = args.files_only === true;
5223
+ const ignoreCase = args.ignore_case === true;
5224
+ const glob = typeof args.glob === "string" && args.glob.trim() ? args.glob.trim() : void 0;
5225
+ const contextLines = Math.max(
5226
+ 0,
5227
+ Math.min(Number(args.context_lines ?? 0) || 0, 5)
5228
+ );
5229
+ const maxResults = Math.max(
5230
+ 1,
5231
+ Math.min(Number(args.max_results ?? DEFAULT_MAX_RESULTS) || DEFAULT_MAX_RESULTS, MAX_RESULTS_CAP)
5232
+ );
4875
5233
  const rg = resolveRipgrep();
4876
5234
  if (rg) {
4877
5235
  const res = await execAsync(
4878
5236
  rg,
4879
- [
4880
- // --with-filename:单文件模式下 rg 默认不输出文件名前缀,会导致
4881
- // "行号:内容" 被误当成 "文件:行号:内容" 解析而错位;显式要求带文件名。
4882
- "--with-filename",
4883
- "--line-number",
4884
- "--no-heading",
4885
- "--max-count",
4886
- "50",
4887
- pattern,
4888
- filePath
4889
- ],
5237
+ rgArgs({ pattern, path: filePath, filesOnly, ignoreCase, glob, contextLines, maxResults }),
4890
5238
  {
4891
5239
  cwd: workspace,
4892
- timeoutMs: 1e4,
5240
+ timeoutMs: RG_TIMEOUT_MS,
4893
5241
  maxBuffer: 1024 * 1024 * 2
4894
5242
  }
4895
5243
  );
4896
5244
  if (res.status === 0) {
4897
- const matches = parseRgOutput(decodeOutput(res.stdout));
4898
- return { query: pattern, engine: "ripgrep", matches, count: matches.length };
5245
+ const out = decodeOutput(res.stdout);
5246
+ return filesOnly ? { query: pattern, engine: "ripgrep", files: parseRgFiles(out, maxResults) } : (() => {
5247
+ const matches = parseRgOutput(out, maxResults);
5248
+ return { query: pattern, engine: "ripgrep", matches, count: matches.length };
5249
+ })();
4899
5250
  }
4900
5251
  if (res.status === 1) {
4901
- return { query: pattern, engine: "ripgrep", matches: [], count: 0 };
5252
+ return filesOnly ? { query: pattern, engine: "ripgrep", files: [], count: 0 } : { query: pattern, engine: "ripgrep", matches: [], count: 0 };
5253
+ }
5254
+ if (!res.error) {
5255
+ return { query: pattern, error: rgFailureHint(res) };
4902
5256
  }
4903
5257
  }
5258
+ if (filesOnly || glob || ignoreCase || contextLines > 0) {
5259
+ return {
5260
+ query: pattern,
5261
+ error: "\u5F53\u524D\u8282\u70B9\u672A\u5B89\u88C5 ripgrep\uFF0Cfiles_only / glob / ignore_case / context_lines \u9700\u8981 ripgrep \u624D\u80FD\u751F\u6548\uFF1B\u8BF7\u53BB\u6389\u8FD9\u4E9B\u53C2\u6570\u540E\u91CD\u8BD5\uFF0C\u6216\u5148\u5B89\u88C5 ripgrep"
5262
+ };
5263
+ }
4904
5264
  try {
4905
- const matches = nodeGrep({ pattern, target: filePath, workspace });
5265
+ const matches = nodeGrep({ pattern, target: filePath, workspace, maxMatches: maxResults });
4906
5266
  return { query: pattern, engine: "node", matches, count: matches.length };
4907
5267
  } catch (exc) {
4908
5268
  return { error: `\u641C\u7D22\u5931\u8D25: ${String(exc instanceof Error ? exc.message : exc)}` };
@@ -4918,12 +5278,29 @@ function registerSearchTool(registry, workspace) {
4918
5278
  type: "function",
4919
5279
  function: {
4920
5280
  name: "grep",
4921
- description: "\u5728\u6587\u4EF6\u4E2D\u641C\u7D22\u6587\u672C\u6A21\u5F0F\uFF08\u652F\u6301\u5B8C\u6574 JS \u6B63\u5219\u8BED\u6CD5\uFF09\u3002\u8FD4\u56DE\u5339\u914D\u7684\u6587\u4EF6\u540D\u3001\u884C\u53F7\u548C\u5185\u5BB9\uFF0C\u6700\u591A 50 \u6761\u3002\u4F18\u5148\u4F7F\u7528 ripgrep (rg) \u4EE5\u83B7\u5F97\u66F4\u597D\u7684\u6027\u80FD\uFF1Brg \u4E0D\u53EF\u7528\u65F6\u81EA\u52A8\u56DE\u9000\u5230\u5185\u7F6E\u626B\u63CF\u5668\u3002",
5281
+ description: "\u5728\u6587\u4EF6\u4E2D\u641C\u7D22\u6587\u672C\u6A21\u5F0F\uFF08\u652F\u6301\u5B8C\u6574 JS \u6B63\u5219\u8BED\u6CD5\uFF09\u3002\u9ED8\u8BA4\u8FD4\u56DE\u5339\u914D\u7684\u6587\u4EF6\u540D\u3001\u884C\u53F7\u548C\u5185\u5BB9\uFF08\u6700\u591A 50 \u6761\uFF09\u3002\u5B9A\u4F4D\u4EE3\u7801\u4F4D\u7F6E\u65F6\u4F18\u5148\u7528 files_only=true \u53EA\u53D6\u6587\u4EF6\u5217\u8868\uFF08\u4F53\u79EF\u5C0F\u4E00\u4E2A\u6570\u91CF\u7EA7\uFF09\uFF0C\u518D\u7528 read_file \u7CBE\u51C6\u8BFB\u53D6\uFF1B\u5927\u4ED3\u5E93\u8BF7\u914D\u5408 path \u7F29\u5C0F\u8303\u56F4\u6216 glob \u9650\u5236\u6587\u4EF6\u7C7B\u578B\uFF0C\u907F\u514D\u5168\u91CF\u626B\u63CF\u8D85\u65F6\u3002",
4922
5282
  parameters: {
4923
5283
  type: "object",
4924
5284
  properties: {
4925
5285
  pattern: { type: "string", description: "\u641C\u7D22\u6A21\u5F0F (\u6587\u672C\u6216\u6B63\u5219\u8868\u8FBE\u5F0F)" },
4926
- path: { type: "string", description: "\u641C\u7D22\u8DEF\u5F84 (\u6587\u4EF6\u6216\u76EE\u5F55\uFF0C\u9ED8\u8BA4 '.')" }
5286
+ path: { type: "string", description: "\u641C\u7D22\u8DEF\u5F84 (\u6587\u4EF6\u6216\u76EE\u5F55\uFF0C\u9ED8\u8BA4 '.'\uFF1B\u5927\u4ED3\u5E93\u5EFA\u8BAE\u5148\u7F29\u5C0F\u5230\u5B50\u76EE\u5F55)" },
5287
+ files_only: {
5288
+ type: "boolean",
5289
+ description: "\u53EA\u8FD4\u56DE\u5339\u914D\u7684\u6587\u4EF6\u8DEF\u5F84\uFF0C\u4E0D\u8FD4\u56DE\u884C\u5185\u5BB9\uFF08\u63A8\u8350\u7528\u4E8E\u5B9A\u4F4D\uFF0C\u663E\u8457\u8282\u7701\u4E0A\u4E0B\u6587\uFF09"
5290
+ },
5291
+ glob: {
5292
+ type: "string",
5293
+ description: '\u6587\u4EF6\u8FC7\u6EE4\uFF0C\u5982 "*.ts" \u6216 "!*.min.js"\uFF08\u53EF\u591A\u6B21\u6392\u9664\u9700\u81EA\u884C\u7EC4\u5408\u4E3A {a,b} \u5F62\u5F0F\uFF09'
5294
+ },
5295
+ ignore_case: { type: "boolean", description: "\u5FFD\u7565\u5927\u5C0F\u5199" },
5296
+ context_lines: {
5297
+ type: "integer",
5298
+ description: "\u6BCF\u6761\u547D\u4E2D\u9644\u5E26\u7684\u524D\u540E\u4E0A\u4E0B\u6587\u884C\u6570\uFF080-5\uFF0C\u9ED8\u8BA4 0\uFF09"
5299
+ },
5300
+ max_results: {
5301
+ type: "integer",
5302
+ description: `\u6700\u591A\u8FD4\u56DE\u6761\u6570\uFF08\u9ED8\u8BA4 ${DEFAULT_MAX_RESULTS}\uFF0C\u4E0A\u9650 ${MAX_RESULTS_CAP}\uFF09`
5303
+ }
4927
5304
  },
4928
5305
  required: ["pattern"]
4929
5306
  }
@@ -4933,8 +5310,8 @@ function registerSearchTool(registry, workspace) {
4933
5310
  }
4934
5311
 
4935
5312
  // src/tools/git.ts
4936
- import * as fs8 from "fs";
4937
- import * as path11 from "path";
5313
+ import * as fs9 from "fs";
5314
+ import * as path12 from "path";
4938
5315
  async function runGit(repoDir, args, timeout = 15e3) {
4939
5316
  const res = await execAsync("git", ["-c", "core.quotepath=false", ...args], {
4940
5317
  cwd: repoDir,
@@ -4951,9 +5328,9 @@ async function runGit(repoDir, args, timeout = 15e3) {
4951
5328
  async function resolveRepoRoot(workspace, pathArg) {
4952
5329
  let dir = workspace;
4953
5330
  if (pathArg) {
4954
- const abs = path11.resolve(workspace, pathArg);
5331
+ const abs = path12.resolve(workspace, pathArg);
4955
5332
  try {
4956
- dir = fs8.statSync(abs).isDirectory() ? abs : path11.dirname(abs);
5333
+ dir = fs9.statSync(abs).isDirectory() ? abs : path12.dirname(abs);
4957
5334
  } catch {
4958
5335
  dir = abs;
4959
5336
  }
@@ -4966,22 +5343,22 @@ function repoDirArg(workspace, raw) {
4966
5343
  if (typeof raw !== "string") return void 0;
4967
5344
  const v = raw.trim();
4968
5345
  if (!v) return void 0;
4969
- const base = path11.resolve(workspace);
4970
- const abs = path11.resolve(base, v);
4971
- const rel = path11.relative(base, abs);
5346
+ const base = path12.resolve(workspace);
5347
+ const abs = path12.resolve(base, v);
5348
+ const rel = path12.relative(base, abs);
4972
5349
  if (!isInsideRelative(rel)) return void 0;
4973
5350
  return abs;
4974
5351
  }
4975
5352
  function toPathspec(repoRoot, workspace, pathArg) {
4976
5353
  if (!pathArg) return null;
4977
- const abs = path11.resolve(workspace, pathArg);
4978
- let rel = path11.relative(repoRoot, abs);
5354
+ const abs = path12.resolve(workspace, pathArg);
5355
+ let rel = path12.relative(repoRoot, abs);
4979
5356
  if (rel === "") return ".";
4980
5357
  if (!isInsideRelative(rel)) {
4981
5358
  try {
4982
- const realRepo = fs8.realpathSync(repoRoot);
4983
- const realAbs = fs8.realpathSync(abs);
4984
- const rel2 = path11.relative(realRepo, realAbs);
5359
+ const realRepo = fs9.realpathSync(repoRoot);
5360
+ const realAbs = fs9.realpathSync(abs);
5361
+ const rel2 = path12.relative(realRepo, realAbs);
4985
5362
  if (rel2 === "") return ".";
4986
5363
  if (isInsideRelative(rel2)) return rel2;
4987
5364
  } catch {