@zqagent/core 0.2.5 → 0.2.7
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.
- package/README.md +3 -0
- package/README.oss.en.md +5 -0
- package/README.oss.md +5 -0
- package/README.oss.zh-TW.md +5 -0
- package/dist/cli/index.cjs +1161 -600
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +1167 -606
- package/dist/cli/index.js.map +1 -1
- package/dist/{explore-D4lCKXbA.d.ts → explore-B_bIPzmB.d.ts} +49 -1
- package/dist/{explore-Dj1j_GyN.d.cts → explore-COqHKDoK.d.cts} +49 -1
- package/dist/index.cjs +654 -258
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +654 -258
- package/dist/index.js.map +1 -1
- package/dist/server/index.cjs +1000 -443
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +1006 -449
- package/dist/server/index.js.map +1 -1
- package/dist/tools/explore.cjs +528 -141
- package/dist/tools/explore.cjs.map +1 -1
- package/dist/tools/explore.d.cts +1 -1
- package/dist/tools/explore.d.ts +1 -1
- package/dist/tools/explore.js +528 -141
- package/dist/tools/explore.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -471,8 +471,11 @@ function formatTokens(tokens) {
|
|
|
471
471
|
return `${tokens}`;
|
|
472
472
|
}
|
|
473
473
|
var KEEP_RECENT_FRACTION = 0.25;
|
|
474
|
-
var
|
|
475
|
-
var
|
|
474
|
+
var SOFT_THRESHOLD_RATIO = 0.75;
|
|
475
|
+
var ELIDE_KEEP_FRACTION = 0.15;
|
|
476
|
+
var SUMMARY_MAX_TOKENS = 3e3;
|
|
477
|
+
var SPAN_TOOL_RESULT_CLIP = 200;
|
|
478
|
+
var SPAN_BUDGET_CHARS = 8e4;
|
|
476
479
|
var USER_MESSAGE_CLIP = 600;
|
|
477
480
|
var USER_MESSAGES_MAX = 40;
|
|
478
481
|
var TRIM_FRACTION = 0.1;
|
|
@@ -495,9 +498,22 @@ function triggerTokens(contextWindow, thresholdPct = DEFAULT_THRESHOLD_PCT, capT
|
|
|
495
498
|
}
|
|
496
499
|
return base;
|
|
497
500
|
}
|
|
501
|
+
function softTriggerTokens(contextWindow, thresholdPct = DEFAULT_THRESHOLD_PCT, capTokens, softThresholdPct) {
|
|
502
|
+
const hard = triggerTokens(contextWindow, thresholdPct, capTokens);
|
|
503
|
+
if (softThresholdPct != null && Number.isFinite(softThresholdPct) && softThresholdPct > 0) {
|
|
504
|
+
return Math.min(
|
|
505
|
+
triggerTokens(contextWindow, softThresholdPct, capTokens),
|
|
506
|
+
Math.floor(hard * 0.95)
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
return Math.floor(hard * SOFT_THRESHOLD_RATIO);
|
|
510
|
+
}
|
|
498
511
|
function shouldCompact(signal, contextWindow, thresholdPct, capTokens) {
|
|
499
512
|
return signal >= triggerTokens(contextWindow, thresholdPct, capTokens);
|
|
500
513
|
}
|
|
514
|
+
function elisionKeepChars(hardTriggerTokens, fraction = ELIDE_KEEP_FRACTION) {
|
|
515
|
+
return Math.max(4e3, Math.floor(hardTriggerTokens * fraction * 4));
|
|
516
|
+
}
|
|
501
517
|
function turnStarts(messages, start) {
|
|
502
518
|
const users = [];
|
|
503
519
|
const assistants = [];
|
|
@@ -647,10 +663,11 @@ var SUMMARY_SYSTEM_PROMPT = `\u4F60\u6B63\u5728\u538B\u7F29\u4E00\u4E2A AI \u534
|
|
|
647
663
|
|
|
648
664
|
\u89C4\u5219\uFF1A
|
|
649
665
|
- \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
|
|
666
|
+
- \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
|
|
650
667
|
- \u8981\u5177\u4F53\uFF1A\u8DEF\u5F84\u3001\u540D\u79F0\u3001\u547D\u4EE4\u3001id\u2014\u2014\u800C\u4E0D\u662F\u6A21\u7CCA\u7684\u5F15\u7528\u3002
|
|
651
668
|
- \u4EC5\u8F93\u51FA\u6458\u8981\u7AE0\u8282\uFF0C\u4E0D\u9644\u52A0\u524D\u8A00\u3002`;
|
|
652
669
|
var CONTINUATION_CONTRACT = "\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";
|
|
653
|
-
function renderSpan(span,
|
|
670
|
+
function renderSpan(span, budgetChars2 = SPAN_BUDGET_CHARS) {
|
|
654
671
|
const lines = [];
|
|
655
672
|
for (const m of span) {
|
|
656
673
|
const role = m.role;
|
|
@@ -674,8 +691,8 @@ function renderSpan(span, budgetChars = SPAN_BUDGET_CHARS) {
|
|
|
674
691
|
if (text) lines.push(`[${role}] ${text}`);
|
|
675
692
|
}
|
|
676
693
|
let rendered = lines.join("\n");
|
|
677
|
-
if (rendered.length >
|
|
678
|
-
rendered = "(\u2026oldest turns elided\u2026)\n" + rendered.slice(-
|
|
694
|
+
if (rendered.length > budgetChars2) {
|
|
695
|
+
rendered = "(\u2026oldest turns elided\u2026)\n" + rendered.slice(-budgetChars2);
|
|
679
696
|
}
|
|
680
697
|
return rendered;
|
|
681
698
|
}
|
|
@@ -689,9 +706,17 @@ function summarizerMessages(span, priorSummary = "") {
|
|
|
689
706
|
{ role: "user", content: body }
|
|
690
707
|
];
|
|
691
708
|
}
|
|
709
|
+
function summarizerReasoningEffort() {
|
|
710
|
+
const v = process.env.COMPACTION_REASONING_EFFORT?.toLowerCase();
|
|
711
|
+
if (v === "low" || v === "medium" || v === "high") return v;
|
|
712
|
+
return "low";
|
|
713
|
+
}
|
|
692
714
|
async function summarizeSpan(provider, model, span, priorSummary = "", opts) {
|
|
693
715
|
const msgs = summarizerMessages(span, priorSummary);
|
|
694
|
-
const chunks = await provider.stream(model, msgs, [],
|
|
716
|
+
const chunks = await provider.stream(model, msgs, [], {
|
|
717
|
+
...opts,
|
|
718
|
+
maxTokens: SUMMARY_MAX_TOKENS
|
|
719
|
+
});
|
|
695
720
|
let text = "";
|
|
696
721
|
for await (const chunk of chunks) {
|
|
697
722
|
if (chunk.content) text += chunk.content;
|
|
@@ -802,6 +827,166 @@ function applyToOutbound(messages, state) {
|
|
|
802
827
|
head.push({ role: "user", content: compactedBlock(state) });
|
|
803
828
|
return [...head, ...messages.slice(boundary)];
|
|
804
829
|
}
|
|
830
|
+
var ELIDE_EXEMPT_TOOLS = /* @__PURE__ */ new Set([
|
|
831
|
+
"explore",
|
|
832
|
+
"ask_user",
|
|
833
|
+
"propose_plan",
|
|
834
|
+
"request_directory",
|
|
835
|
+
"todo_write"
|
|
836
|
+
]);
|
|
837
|
+
function elidedToolContent(m) {
|
|
838
|
+
const name = m.tool_call_name || "tool";
|
|
839
|
+
const size = m.content.length;
|
|
840
|
+
const saved = /full output saved to ([^\]]+)/.exec(m.content)?.[1]?.trim();
|
|
841
|
+
return saved ? `[old tool result elided: ${name}, ${size} chars; full output saved to ${saved}]` : `[old tool result elided: ${name}, ${size} chars; re-run the tool if its content is still needed]`;
|
|
842
|
+
}
|
|
843
|
+
function isPlaceholder(content) {
|
|
844
|
+
return content.startsWith("[old tool result elided") || content.startsWith("[superseded by a later read_file");
|
|
845
|
+
}
|
|
846
|
+
var ONE_SHOT_TOOLS = /* @__PURE__ */ new Set(["git_diff"]);
|
|
847
|
+
var ONE_SHOT_KEEP_RECENT = 1;
|
|
848
|
+
function applyOneShotElision(messages, keepRecent = ONE_SHOT_KEEP_RECENT, tools = ONE_SHOT_TOOLS) {
|
|
849
|
+
const out = [...messages];
|
|
850
|
+
let seen = 0;
|
|
851
|
+
let elided = 0;
|
|
852
|
+
let freed = 0;
|
|
853
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
854
|
+
const m = messages[i];
|
|
855
|
+
if (m.role !== "tool" || !tools.has(m.tool_call_name ?? "")) continue;
|
|
856
|
+
if (isPlaceholder(m.content)) continue;
|
|
857
|
+
seen++;
|
|
858
|
+
if (seen <= keepRecent) continue;
|
|
859
|
+
const placeholder = elidedToolContent(m);
|
|
860
|
+
freed += m.content.length - placeholder.length;
|
|
861
|
+
out[i] = { ...m, content: placeholder };
|
|
862
|
+
elided++;
|
|
863
|
+
}
|
|
864
|
+
return elided > 0 ? { messages: out, elided, freedChars: freed } : { messages, elided: 0, freedChars: 0 };
|
|
865
|
+
}
|
|
866
|
+
function applyToolResultElision(messages, keepChars, exempt = ELIDE_EXEMPT_TOOLS) {
|
|
867
|
+
let total = 0;
|
|
868
|
+
for (const m of messages) {
|
|
869
|
+
if (m.role === "tool" && !exempt.has(m.tool_call_name ?? "")) {
|
|
870
|
+
total += m.content.length;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (total <= keepChars) return { messages, elided: 0, freedChars: 0 };
|
|
874
|
+
const out = [...messages];
|
|
875
|
+
let budget = 0;
|
|
876
|
+
let elided = 0;
|
|
877
|
+
let freed = 0;
|
|
878
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
879
|
+
const m = messages[i];
|
|
880
|
+
if (m.role !== "tool") continue;
|
|
881
|
+
if (exempt.has(m.tool_call_name ?? "")) continue;
|
|
882
|
+
if (elided === 0 && budget === 0 && m.content.length > keepChars) {
|
|
883
|
+
const kept = m.content.slice(0, keepChars) + "\n\u2026(truncated)";
|
|
884
|
+
freed += m.content.length - kept.length;
|
|
885
|
+
out[i] = { ...m, content: kept };
|
|
886
|
+
budget = keepChars;
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
if (budget + m.content.length <= keepChars) {
|
|
890
|
+
budget += m.content.length;
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
const placeholder = elidedToolContent(m);
|
|
894
|
+
freed += m.content.length - placeholder.length;
|
|
895
|
+
out[i] = { ...m, content: placeholder };
|
|
896
|
+
elided++;
|
|
897
|
+
}
|
|
898
|
+
return { messages: out, elided, freedChars: freed };
|
|
899
|
+
}
|
|
900
|
+
function normalizeToolPath(p) {
|
|
901
|
+
let s = p.trim().replace(/\\/g, "/");
|
|
902
|
+
while (s.startsWith("./")) s = s.slice(2);
|
|
903
|
+
return s.replace(/\/{2,}/g, "/");
|
|
904
|
+
}
|
|
905
|
+
var MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
906
|
+
"write_file",
|
|
907
|
+
"replace_in_file",
|
|
908
|
+
"apply_patch",
|
|
909
|
+
"apply_unified_diff",
|
|
910
|
+
"run_shell"
|
|
911
|
+
]);
|
|
912
|
+
function parseReadFileSpan(content) {
|
|
913
|
+
const body = content.replace(/^\[tool output truncated:[^\n]*\]\n\n/, "");
|
|
914
|
+
let path16;
|
|
915
|
+
let start;
|
|
916
|
+
let end;
|
|
917
|
+
try {
|
|
918
|
+
const obj = JSON.parse(body);
|
|
919
|
+
path16 = obj.path;
|
|
920
|
+
start = obj.start_line;
|
|
921
|
+
end = obj.end_line;
|
|
922
|
+
} catch {
|
|
923
|
+
const str2 = (key) => new RegExp(`"${key}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`).exec(body)?.[1];
|
|
924
|
+
const num = (key) => {
|
|
925
|
+
const m = new RegExp(`"${key}"\\s*:\\s*(\\d+)`).exec(body);
|
|
926
|
+
return m ? Number(m[1]) : void 0;
|
|
927
|
+
};
|
|
928
|
+
path16 = str2("path");
|
|
929
|
+
start = num("start_line");
|
|
930
|
+
end = num("end_line");
|
|
931
|
+
}
|
|
932
|
+
if (typeof path16 !== "string" || !path16) return null;
|
|
933
|
+
if (typeof start !== "number" || typeof end !== "number") return null;
|
|
934
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return null;
|
|
935
|
+
return { path: path16, start, end };
|
|
936
|
+
}
|
|
937
|
+
function addSpan(spans, start, end) {
|
|
938
|
+
spans.push([start, end]);
|
|
939
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
940
|
+
const merged = [];
|
|
941
|
+
for (const cur of spans) {
|
|
942
|
+
const last = merged[merged.length - 1];
|
|
943
|
+
if (last && cur[0] <= last[1] + 1) {
|
|
944
|
+
last[1] = Math.max(last[1], cur[1]);
|
|
945
|
+
} else {
|
|
946
|
+
merged.push([...cur]);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
spans.length = 0;
|
|
950
|
+
spans.push(...merged);
|
|
951
|
+
}
|
|
952
|
+
function coversSpans(spans, start, end) {
|
|
953
|
+
return spans.some(([a, b]) => a <= start && b >= end);
|
|
954
|
+
}
|
|
955
|
+
function applyReadFileDedup(messages) {
|
|
956
|
+
let candidates = 0;
|
|
957
|
+
for (const m of messages) {
|
|
958
|
+
if (m.role === "tool" && m.tool_call_name === "read_file") candidates++;
|
|
959
|
+
}
|
|
960
|
+
if (candidates < 2) return { messages, elided: 0 };
|
|
961
|
+
const out = [...messages];
|
|
962
|
+
const covered = /* @__PURE__ */ new Map();
|
|
963
|
+
let elided = 0;
|
|
964
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
965
|
+
const m = messages[i];
|
|
966
|
+
if (m.role === "assistant") {
|
|
967
|
+
if (m.tool_calls?.some((tc) => MUTATING_TOOLS.has(tc.function.name))) {
|
|
968
|
+
covered.clear();
|
|
969
|
+
}
|
|
970
|
+
continue;
|
|
971
|
+
}
|
|
972
|
+
if (m.role !== "tool" || m.tool_call_name !== "read_file") continue;
|
|
973
|
+
const span = parseReadFileSpan(m.content);
|
|
974
|
+
if (!span) continue;
|
|
975
|
+
const key = normalizeToolPath(span.path);
|
|
976
|
+
const seen = covered.get(key) ?? [];
|
|
977
|
+
if (coversSpans(seen, span.start, span.end)) {
|
|
978
|
+
out[i] = {
|
|
979
|
+
...m,
|
|
980
|
+
content: `[superseded by a later read_file of ${span.path} (lines ${span.start}-${span.end})]`
|
|
981
|
+
};
|
|
982
|
+
elided++;
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
addSpan(seen, span.start, span.end);
|
|
986
|
+
covered.set(key, seen);
|
|
987
|
+
}
|
|
988
|
+
return { messages: elided > 0 ? out : messages, elided };
|
|
989
|
+
}
|
|
805
990
|
var OVERFLOW_MARKERS = [
|
|
806
991
|
"context_length_exceeded",
|
|
807
992
|
"maximum context length",
|
|
@@ -817,9 +1002,108 @@ function isContextOverflow(exc) {
|
|
|
817
1002
|
return OVERFLOW_MARKERS.some((m) => text.includes(m));
|
|
818
1003
|
}
|
|
819
1004
|
|
|
1005
|
+
// src/engine/toolResultBudget.ts
|
|
1006
|
+
var fs = __toESM(require("fs"), 1);
|
|
1007
|
+
var path2 = __toESM(require("path"), 1);
|
|
1008
|
+
|
|
1009
|
+
// src/paths.ts
|
|
1010
|
+
var os = __toESM(require("os"), 1);
|
|
1011
|
+
var path = __toESM(require("path"), 1);
|
|
1012
|
+
var APP_DIR_NAME = "zqagent";
|
|
1013
|
+
var IS_WINDOWS = process.platform === "win32";
|
|
1014
|
+
function homeDir() {
|
|
1015
|
+
return process.env.HOME || process.env.USERPROFILE || os.homedir() || os.tmpdir();
|
|
1016
|
+
}
|
|
1017
|
+
function stateDir() {
|
|
1018
|
+
const base = process.env.XDG_STATE_HOME || (IS_WINDOWS ? process.env.LOCALAPPDATA : void 0) || path.join(homeDir(), ".local", "state");
|
|
1019
|
+
return path.join(base, APP_DIR_NAME);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// src/engine/toolResultBudget.ts
|
|
1023
|
+
var DEFAULT_TOOL_RESULT_MAX_CHARS = 24e3;
|
|
1024
|
+
var HEAD_RATIO = 0.6;
|
|
1025
|
+
function budgetChars() {
|
|
1026
|
+
const n = Number(process.env.TOOL_RESULT_MAX_CHARS);
|
|
1027
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : DEFAULT_TOOL_RESULT_MAX_CHARS;
|
|
1028
|
+
}
|
|
1029
|
+
function toolResultDir() {
|
|
1030
|
+
const env = process.env.TOOL_RESULT_DIR?.trim();
|
|
1031
|
+
return env || path2.join(stateDir(), "tool-results");
|
|
1032
|
+
}
|
|
1033
|
+
function clipText(text, budget) {
|
|
1034
|
+
if (text.length <= budget) return text;
|
|
1035
|
+
const room = Math.max(
|
|
1036
|
+
0,
|
|
1037
|
+
budget - `
|
|
1038
|
+
|
|
1039
|
+
\u2026 [${text.length} characters omitted] \u2026
|
|
1040
|
+
|
|
1041
|
+
`.length
|
|
1042
|
+
);
|
|
1043
|
+
const headLen = Math.floor(room * HEAD_RATIO);
|
|
1044
|
+
const tailLen = room - headLen;
|
|
1045
|
+
const omitted = text.length - headLen - tailLen;
|
|
1046
|
+
return text.slice(0, headLen) + `
|
|
1047
|
+
|
|
1048
|
+
\u2026 [${omitted} characters omitted] \u2026
|
|
1049
|
+
|
|
1050
|
+
` + text.slice(-tailLen);
|
|
1051
|
+
}
|
|
1052
|
+
function clipDeep(value, perField) {
|
|
1053
|
+
if (typeof value === "string") {
|
|
1054
|
+
return value.length <= perField ? value : clipText(value, perField);
|
|
1055
|
+
}
|
|
1056
|
+
if (Array.isArray(value)) {
|
|
1057
|
+
return value.map((v) => clipDeep(v, perField));
|
|
1058
|
+
}
|
|
1059
|
+
if (value && typeof value === "object") {
|
|
1060
|
+
const out = {};
|
|
1061
|
+
for (const [k, v] of Object.entries(value)) out[k] = clipDeep(v, perField);
|
|
1062
|
+
return out;
|
|
1063
|
+
}
|
|
1064
|
+
return value;
|
|
1065
|
+
}
|
|
1066
|
+
function saveRaw(tool, raw, dir) {
|
|
1067
|
+
try {
|
|
1068
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1069
|
+
const safe = tool.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40) || "tool";
|
|
1070
|
+
const name = `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}-${safe}.txt`;
|
|
1071
|
+
const file = path2.join(dir, name);
|
|
1072
|
+
fs.writeFileSync(file, raw, "utf-8");
|
|
1073
|
+
return file;
|
|
1074
|
+
} catch {
|
|
1075
|
+
return void 0;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
function applyToolResultBudget(tool, result, opts = {}) {
|
|
1079
|
+
const budget = opts.maxChars ?? budgetChars();
|
|
1080
|
+
const raw = typeof result === "string" ? result : JSON.stringify(result, null, 2) ?? String(result);
|
|
1081
|
+
if (raw.length <= budget) {
|
|
1082
|
+
return { content: raw, truncated: false };
|
|
1083
|
+
}
|
|
1084
|
+
let content = raw;
|
|
1085
|
+
if (typeof result === "object" && result !== null) {
|
|
1086
|
+
let perField = Math.floor(budget * 0.7);
|
|
1087
|
+
let clipped = JSON.stringify(clipDeep(result, perField), null, 2);
|
|
1088
|
+
for (let i = 0; i < 6 && clipped && clipped.length > budget && perField > 128; i++) {
|
|
1089
|
+
perField = Math.floor(perField / 2);
|
|
1090
|
+
clipped = JSON.stringify(clipDeep(result, perField), null, 2);
|
|
1091
|
+
}
|
|
1092
|
+
if (clipped && clipped.length <= budget) content = clipped;
|
|
1093
|
+
}
|
|
1094
|
+
if (content.length > budget) content = clipText(content, budget);
|
|
1095
|
+
const savedPath = saveRaw(tool, raw, opts.dir ?? toolResultDir());
|
|
1096
|
+
if (savedPath) {
|
|
1097
|
+
content = `[tool output truncated: ${raw.length} chars, full output saved to ${savedPath}]
|
|
1098
|
+
|
|
1099
|
+
` + content;
|
|
1100
|
+
}
|
|
1101
|
+
return { content, truncated: true, savedPath };
|
|
1102
|
+
}
|
|
1103
|
+
|
|
820
1104
|
// src/engine/TurnEngine.ts
|
|
821
1105
|
var crypto = __toESM(require("crypto"), 1);
|
|
822
|
-
var
|
|
1106
|
+
var fs2 = __toESM(require("fs"), 1);
|
|
823
1107
|
var _path = __toESM(require("path"), 1);
|
|
824
1108
|
|
|
825
1109
|
// src/prompts/index.ts
|
|
@@ -946,13 +1230,17 @@ var INTERACTIVE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
946
1230
|
"propose_plan",
|
|
947
1231
|
"request_directory"
|
|
948
1232
|
]);
|
|
949
|
-
|
|
950
|
-
|
|
1233
|
+
var MAX_COMPACT_ROUNDS = 4;
|
|
1234
|
+
var OVERFLOW_TARGET_RATIO = 0.5;
|
|
1235
|
+
var OVERFLOW_TRIM_FRACTION = 0.5;
|
|
1236
|
+
var COMPACT_STALL_RETRY_DELTA = 20;
|
|
1237
|
+
function toolResultMessage(tc, result, taskId, toolResultDir2) {
|
|
1238
|
+
const budgeted = applyToolResultBudget(tc.name, result, { dir: toolResultDir2 });
|
|
951
1239
|
return {
|
|
952
1240
|
role: "tool",
|
|
953
1241
|
tool_call_id: tc.id,
|
|
954
1242
|
tool_call_name: tc.name,
|
|
955
|
-
content,
|
|
1243
|
+
content: budgeted.content,
|
|
956
1244
|
taskId
|
|
957
1245
|
};
|
|
958
1246
|
}
|
|
@@ -1063,12 +1351,22 @@ var TurnEngine = class _TurnEngine {
|
|
|
1063
1351
|
// 上下文压缩
|
|
1064
1352
|
compactionState = null;
|
|
1065
1353
|
_lastContextTokens = null;
|
|
1354
|
+
/** 压缩无进展时的消息条数快照;-1 表示无冷却 */
|
|
1355
|
+
_compactStalledAt = -1;
|
|
1066
1356
|
/** 显式配置的上下文窗口大小(token 数),未设置时为 undefined(走自动检测) */
|
|
1067
1357
|
_contextWindow;
|
|
1068
1358
|
/** 压缩触发阈值比例(默认 0.8) */
|
|
1069
1359
|
_thresholdPct;
|
|
1360
|
+
/** 软阈值比例(undefined = 取硬阈值的 75%) */
|
|
1361
|
+
_softThresholdPct;
|
|
1362
|
+
/** 是否启用软阈值淘汰(COMPACTION_ELIDE=0 可关闭) */
|
|
1363
|
+
_elisionEnabled;
|
|
1364
|
+
/** 是否启用重复读取去重(COMPACTION_DEDUP=0 可关闭) */
|
|
1365
|
+
_dedupEnabled;
|
|
1070
1366
|
/** 压缩触发阈值绝对上限(null = 不限制) */
|
|
1071
1367
|
_capTokens;
|
|
1368
|
+
/** 超预算工具结果的落盘目录(undefined = 使用默认状态目录) */
|
|
1369
|
+
_toolResultDir;
|
|
1072
1370
|
// 产出文件追踪(本轮 Turn 中成功写入的文件)
|
|
1073
1371
|
_producedFiles = [];
|
|
1074
1372
|
_turnStartTime = 0;
|
|
@@ -1120,7 +1418,15 @@ var TurnEngine = class _TurnEngine {
|
|
|
1120
1418
|
this._hook = opts.hook;
|
|
1121
1419
|
this._contextWindow = opts.contextWindow;
|
|
1122
1420
|
this._thresholdPct = opts.thresholdPct ?? DEFAULT_THRESHOLD_PCT;
|
|
1421
|
+
this._softThresholdPct = opts.softThresholdPct;
|
|
1422
|
+
this._elisionEnabled = !/^(0|false|off|no)$/i.test(
|
|
1423
|
+
(process.env.COMPACTION_ELIDE ?? "").trim()
|
|
1424
|
+
);
|
|
1425
|
+
this._dedupEnabled = !/^(0|false|off|no)$/i.test(
|
|
1426
|
+
(process.env.COMPACTION_DEDUP ?? "").trim()
|
|
1427
|
+
);
|
|
1123
1428
|
this._capTokens = opts.capTokens ?? null;
|
|
1429
|
+
this._toolResultDir = opts.toolResultDir;
|
|
1124
1430
|
this._reasoningEffort = opts.reasoningEffort;
|
|
1125
1431
|
if (this.messages.length === 0 || this.messages[0].role !== "system") {
|
|
1126
1432
|
this.messages.unshift({ role: "system", content: this.instructions });
|
|
@@ -1176,6 +1482,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
1176
1482
|
this._cancel = false;
|
|
1177
1483
|
this._producedFiles = [];
|
|
1178
1484
|
this._turnStartTime = Date.now();
|
|
1485
|
+
this._compactStalledAt = -1;
|
|
1179
1486
|
this._emitHook((h) => h.onTurnStart?.(userInput));
|
|
1180
1487
|
yield {
|
|
1181
1488
|
type: "turn_start" /* TURN_START */,
|
|
@@ -1892,7 +2199,9 @@ var TurnEngine = class _TurnEngine {
|
|
|
1892
2199
|
for (let i = 0; i < concurrent.length; i++) {
|
|
1893
2200
|
const tc = concurrent[i];
|
|
1894
2201
|
const { result, status } = results[i];
|
|
1895
|
-
this.messages.push(
|
|
2202
|
+
this.messages.push(
|
|
2203
|
+
toolResultMessage(tc, result, this.taskId, this._toolResultDir)
|
|
2204
|
+
);
|
|
1896
2205
|
this._trackProducedFile(
|
|
1897
2206
|
tc,
|
|
1898
2207
|
result,
|
|
@@ -1955,7 +2264,9 @@ var TurnEngine = class _TurnEngine {
|
|
|
1955
2264
|
};
|
|
1956
2265
|
status = "error";
|
|
1957
2266
|
}
|
|
1958
|
-
this.messages.push(
|
|
2267
|
+
this.messages.push(
|
|
2268
|
+
toolResultMessage(tc, result, this.taskId, this._toolResultDir)
|
|
2269
|
+
);
|
|
1959
2270
|
this._trackProducedFile(
|
|
1960
2271
|
tc,
|
|
1961
2272
|
result,
|
|
@@ -1990,13 +2301,41 @@ var TurnEngine = class _TurnEngine {
|
|
|
1990
2301
|
* 过滤 notice 消息;tool 消息只保留必要字段;
|
|
1991
2302
|
* 其余消息剥离 display-only sidecar 字段(source/_display/ts/
|
|
1992
2303
|
* reasoning/usage),避免污染模型上下文。
|
|
2304
|
+
*
|
|
2305
|
+
* 达到软阈值时先淘汰旧工具结果(零 LLM 成本),再产出最终视图。
|
|
1993
2306
|
*/
|
|
1994
2307
|
_outboundMessages() {
|
|
1995
|
-
const
|
|
1996
|
-
const
|
|
1997
|
-
|
|
1998
|
-
|
|
2308
|
+
const base = applyToOutbound(this.messages, this.compactionState);
|
|
2309
|
+
const deduped = this._dedupEnabled ? applyReadFileDedup(base).messages : base;
|
|
2310
|
+
let view = this._finalizeOutbound(deduped);
|
|
2311
|
+
if (!this._elisionEnabled) return view;
|
|
2312
|
+
if (estimateTokens(view) < this._softTriggerTokens()) {
|
|
2313
|
+
return view;
|
|
2314
|
+
}
|
|
2315
|
+
const oneShot = applyOneShotElision(deduped);
|
|
2316
|
+
if (oneShot.freedChars > 0) {
|
|
2317
|
+
view = this._finalizeOutbound(oneShot.messages);
|
|
2318
|
+
if (estimateTokens(view) < this._softTriggerTokens()) {
|
|
2319
|
+
return view;
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
const keepChars = elisionKeepChars(
|
|
2323
|
+
triggerTokens(
|
|
2324
|
+
this._getContextWindow(),
|
|
2325
|
+
this._thresholdPct,
|
|
2326
|
+
this._capTokens
|
|
2327
|
+
)
|
|
2328
|
+
);
|
|
2329
|
+
const elided = applyToolResultElision(
|
|
2330
|
+
oneShot.messages,
|
|
2331
|
+
keepChars
|
|
1999
2332
|
);
|
|
2333
|
+
if (elided.freedChars === 0) return view;
|
|
2334
|
+
return this._finalizeOutbound(elided.messages);
|
|
2335
|
+
}
|
|
2336
|
+
/** 过滤 notice + 剥离 display-only 字段,得到最终发送视图 */
|
|
2337
|
+
_finalizeOutbound(sourceMessages) {
|
|
2338
|
+
const _STRIP = /* @__PURE__ */ new Set(["source", "_display", "ts", "reasoning", "usage"]);
|
|
2000
2339
|
return sourceMessages.filter((m) => m.role !== "notice").map((m) => {
|
|
2001
2340
|
if (m.role === "tool") {
|
|
2002
2341
|
return {
|
|
@@ -2014,6 +2353,15 @@ var TurnEngine = class _TurnEngine {
|
|
|
2014
2353
|
return stripped;
|
|
2015
2354
|
});
|
|
2016
2355
|
}
|
|
2356
|
+
/** 软阈值(token 数):达到它只淘汰旧工具结果 */
|
|
2357
|
+
_softTriggerTokens() {
|
|
2358
|
+
return softTriggerTokens(
|
|
2359
|
+
this._getContextWindow(),
|
|
2360
|
+
this._thresholdPct,
|
|
2361
|
+
this._capTokens,
|
|
2362
|
+
this._softThresholdPct
|
|
2363
|
+
);
|
|
2364
|
+
}
|
|
2017
2365
|
/** 通知消息:仅用于日志/展示,不发给模型 */
|
|
2018
2366
|
_appendNotice(kind, text) {
|
|
2019
2367
|
this.messages.push({
|
|
@@ -2055,18 +2403,36 @@ var TurnEngine = class _TurnEngine {
|
|
|
2055
2403
|
return false;
|
|
2056
2404
|
}
|
|
2057
2405
|
// ===================== 自动压缩 =====================
|
|
2058
|
-
/**
|
|
2406
|
+
/**
|
|
2407
|
+
* 当前上下文 token 用量。
|
|
2408
|
+
*
|
|
2409
|
+
* API 返回的 `usage.input` 是精确值,但反映的是「上一次请求」——工具结果
|
|
2410
|
+
* 与淘汰/压缩都会让它过时。因此同时估算当前 outbound 视图,取较大者:
|
|
2411
|
+
* 宁可早一点压缩,也不要漏判导致溢出。
|
|
2412
|
+
*/
|
|
2059
2413
|
_compactionTokens() {
|
|
2060
|
-
|
|
2061
|
-
|
|
2414
|
+
const estimated = estimateTokens(this._outboundMessages());
|
|
2415
|
+
if (this._lastContextTokens == null) {
|
|
2416
|
+
return { tokens: estimated, estimated: true };
|
|
2062
2417
|
}
|
|
2063
|
-
|
|
2064
|
-
tokens:
|
|
2065
|
-
|
|
2066
|
-
};
|
|
2418
|
+
if (estimated > this._lastContextTokens) {
|
|
2419
|
+
return { tokens: estimated, estimated: true };
|
|
2420
|
+
}
|
|
2421
|
+
return { tokens: this._lastContextTokens, estimated: false };
|
|
2422
|
+
}
|
|
2423
|
+
/** 压缩后重置精确计数:后续按估算值判断 */
|
|
2424
|
+
_resetContextTokens() {
|
|
2425
|
+
this._lastContextTokens = null;
|
|
2426
|
+
}
|
|
2427
|
+
/** 当前 outbound 视图的估算 token 数 */
|
|
2428
|
+
_estimateContextTokens() {
|
|
2429
|
+
return estimateTokens(this._outboundMessages());
|
|
2067
2430
|
}
|
|
2068
2431
|
/** 检查是否需要压缩(每轮 LLM 调用前) */
|
|
2069
2432
|
_compactionDue() {
|
|
2433
|
+
if (this._compactStalledAt >= 0 && this.messages.length < this._compactStalledAt + COMPACT_STALL_RETRY_DELTA) {
|
|
2434
|
+
return false;
|
|
2435
|
+
}
|
|
2070
2436
|
const { tokens } = this._compactionTokens();
|
|
2071
2437
|
return shouldCompact(
|
|
2072
2438
|
tokens,
|
|
@@ -2113,63 +2479,82 @@ var TurnEngine = class _TurnEngine {
|
|
|
2113
2479
|
async _compactNow(reqId, force = false) {
|
|
2114
2480
|
const contextWindow = this._getContextWindow();
|
|
2115
2481
|
const messagesBefore = this._outboundMessages().length;
|
|
2116
|
-
const
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
this._capTokens
|
|
2121
|
-
)
|
|
2482
|
+
const trigger = triggerTokens(
|
|
2483
|
+
contextWindow,
|
|
2484
|
+
this._thresholdPct,
|
|
2485
|
+
this._capTokens
|
|
2122
2486
|
);
|
|
2123
|
-
const
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2487
|
+
const target = Math.floor(
|
|
2488
|
+
trigger * (force ? OVERFLOW_TARGET_RATIO : 0.9)
|
|
2489
|
+
);
|
|
2490
|
+
const model = process.env.COMPACTION_MODEL?.trim() || this.model;
|
|
2491
|
+
const reasoningEffort = summarizerReasoningEffort();
|
|
2492
|
+
let keep = Math.floor(KEEP_RECENT_FRACTION * trigger);
|
|
2493
|
+
let method = null;
|
|
2494
|
+
let lastState = null;
|
|
2495
|
+
let progressed = false;
|
|
2496
|
+
for (let round = 0; round < MAX_COMPACT_ROUNDS; round++) {
|
|
2497
|
+
if (round > 0 && this._estimateContextTokens() <= target) break;
|
|
2498
|
+
let state = null;
|
|
2499
|
+
for (let attempt = 0; attempt < 2 && !state; attempt++) {
|
|
2500
|
+
try {
|
|
2501
|
+
const logTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
2502
|
+
state = await buildState(
|
|
2503
|
+
this.messages,
|
|
2504
|
+
this.provider,
|
|
2505
|
+
model,
|
|
2506
|
+
keep,
|
|
2507
|
+
this.compactionState,
|
|
2508
|
+
{ reqId, logTime, reasoningEffort }
|
|
2509
|
+
);
|
|
2510
|
+
} catch {
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
if (state) {
|
|
2514
|
+
this.compactionState = state;
|
|
2515
|
+
lastState = state;
|
|
2516
|
+
method = "summarized";
|
|
2517
|
+
progressed = true;
|
|
2518
|
+
} else {
|
|
2519
|
+
const trimmed = trimState(
|
|
2130
2520
|
this.messages,
|
|
2131
|
-
this.provider,
|
|
2132
|
-
model,
|
|
2133
|
-
keep,
|
|
2134
2521
|
this.compactionState,
|
|
2135
|
-
|
|
2522
|
+
force ? OVERFLOW_TRIM_FRACTION : 0.25
|
|
2136
2523
|
);
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
this.
|
|
2145
|
-
|
|
2524
|
+
if (!trimmed) break;
|
|
2525
|
+
this.compactionState = trimmed;
|
|
2526
|
+
lastState = trimmed;
|
|
2527
|
+
method = "trimmed";
|
|
2528
|
+
progressed = true;
|
|
2529
|
+
}
|
|
2530
|
+
this._resetContextTokens();
|
|
2531
|
+
if (this._estimateContextTokens() <= trigger) break;
|
|
2532
|
+
keep = Math.max(Math.floor(trigger * 0.05), Math.floor(keep / 2));
|
|
2533
|
+
}
|
|
2534
|
+
if (!progressed || !lastState) return { notice: null, method: null };
|
|
2535
|
+
this._compactStalledAt = this._estimateContextTokens() >= trigger ? this.messages.length : -1;
|
|
2536
|
+
const messagesAfter = this._outboundMessages().length;
|
|
2537
|
+
const common = {
|
|
2538
|
+
boundaryIndex: lastState.boundaryIndex,
|
|
2539
|
+
messagesBefore,
|
|
2540
|
+
messagesAfter
|
|
2541
|
+
};
|
|
2542
|
+
if (method === "summarized") {
|
|
2146
2543
|
return {
|
|
2147
2544
|
notice: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29 \u2014 \u65E9\u671F\u8F6E\u6B21\u5DF2\u6458\u8981",
|
|
2148
2545
|
method: "summarized",
|
|
2149
|
-
modelUsed:
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
messagesBefore,
|
|
2155
|
-
messagesAfter: this._outboundMessages().length
|
|
2546
|
+
modelUsed: lastState.modelUsed,
|
|
2547
|
+
summaryPreview: lastState.summaryText.slice(0, 200),
|
|
2548
|
+
keptUserMessages: lastState.userMessages.length,
|
|
2549
|
+
droppedUserMessages: lastState.userMessagesDropped,
|
|
2550
|
+
...common
|
|
2156
2551
|
};
|
|
2157
2552
|
}
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
return {
|
|
2164
|
-
notice: "\u4E0A\u4E0B\u6587\u5DF2\u88C1\u526A \u2014 \u6700\u65E7\u7684\u8F6E\u6B21\u5DF2\u4E22\u5F03\uFF08\u6458\u8981\u4E0D\u53EF\u7528\uFF09",
|
|
2165
|
-
method: "trimmed",
|
|
2166
|
-
boundaryIndex: trimmed.boundaryIndex,
|
|
2167
|
-
messagesBefore,
|
|
2168
|
-
messagesAfter: this._outboundMessages().length
|
|
2169
|
-
};
|
|
2170
|
-
}
|
|
2171
|
-
}
|
|
2172
|
-
return { notice: null, method: null };
|
|
2553
|
+
return {
|
|
2554
|
+
notice: "\u4E0A\u4E0B\u6587\u5DF2\u88C1\u526A \u2014 \u6700\u65E7\u7684\u8F6E\u6B21\u5DF2\u4E22\u5F03\uFF08\u6458\u8981\u4E0D\u53EF\u7528\uFF09",
|
|
2555
|
+
method: "trimmed",
|
|
2556
|
+
...common
|
|
2557
|
+
};
|
|
2173
2558
|
}
|
|
2174
2559
|
/** 追踪文件写入工具成功产生的文件 */
|
|
2175
2560
|
_trackProducedFile(tc, result, status) {
|
|
@@ -2179,9 +2564,9 @@ var TurnEngine = class _TurnEngine {
|
|
|
2179
2564
|
return;
|
|
2180
2565
|
}
|
|
2181
2566
|
if (result && typeof result === "object") {
|
|
2182
|
-
const
|
|
2183
|
-
if (
|
|
2184
|
-
this._producedFiles.push(
|
|
2567
|
+
const path16 = result.path;
|
|
2568
|
+
if (path16) {
|
|
2569
|
+
this._producedFiles.push(path16);
|
|
2185
2570
|
return;
|
|
2186
2571
|
}
|
|
2187
2572
|
const changed = result.changed_files;
|
|
@@ -2205,7 +2590,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
2205
2590
|
_fileExists(f, ws) {
|
|
2206
2591
|
if (!ws && !_path.isAbsolute(f)) return true;
|
|
2207
2592
|
try {
|
|
2208
|
-
return
|
|
2593
|
+
return fs2.existsSync(_path.isAbsolute(f) ? f : _path.join(ws, f));
|
|
2209
2594
|
} catch {
|
|
2210
2595
|
return false;
|
|
2211
2596
|
}
|
|
@@ -2228,7 +2613,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
2228
2613
|
if (depth > 4 || results.length >= 50) return;
|
|
2229
2614
|
let entries;
|
|
2230
2615
|
try {
|
|
2231
|
-
entries =
|
|
2616
|
+
entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
2232
2617
|
} catch {
|
|
2233
2618
|
return;
|
|
2234
2619
|
}
|
|
@@ -2243,7 +2628,7 @@ var TurnEngine = class _TurnEngine {
|
|
|
2243
2628
|
const ext = dotIdx !== -1 ? e.name.slice(dotIdx).toLowerCase() : "";
|
|
2244
2629
|
if (!_TurnEngine.SCAN_SUFFIXES.has(ext)) continue;
|
|
2245
2630
|
try {
|
|
2246
|
-
const st =
|
|
2631
|
+
const st = fs2.statSync(full);
|
|
2247
2632
|
if (st.mtimeMs >= since) {
|
|
2248
2633
|
results.push(
|
|
2249
2634
|
_path.relative(_path.resolve(ws), _path.resolve(full))
|
|
@@ -2264,8 +2649,8 @@ var TurnEngine = class _TurnEngine {
|
|
|
2264
2649
|
};
|
|
2265
2650
|
|
|
2266
2651
|
// src/providers/OpenAIProvider.ts
|
|
2267
|
-
var
|
|
2268
|
-
var
|
|
2652
|
+
var fs3 = __toESM(require("fs"), 1);
|
|
2653
|
+
var path3 = __toESM(require("path"), 1);
|
|
2269
2654
|
var import_openai = __toESM(require("openai"), 1);
|
|
2270
2655
|
|
|
2271
2656
|
// src/terminal.ts
|
|
@@ -2443,7 +2828,7 @@ var OpenAIProvider = class {
|
|
|
2443
2828
|
});
|
|
2444
2829
|
if (opts.logDir) {
|
|
2445
2830
|
this._logDir = opts.logDir;
|
|
2446
|
-
|
|
2831
|
+
fs3.mkdirSync(opts.logDir, { recursive: true });
|
|
2447
2832
|
}
|
|
2448
2833
|
}
|
|
2449
2834
|
// =====================================================================
|
|
@@ -2596,6 +2981,8 @@ var OpenAIProvider = class {
|
|
|
2596
2981
|
tools: this._toResponsesTools(tools, model),
|
|
2597
2982
|
// 推理强度:DeepSeek 不支持,跳过
|
|
2598
2983
|
...opts.reasoningEffort && !this._isDeepSeek(model) ? { reasoning: { effort: opts.reasoningEffort } } : {},
|
|
2984
|
+
// 限制输出长度(摘要器等轻量请求)
|
|
2985
|
+
...opts.maxTokens ? { max_output_tokens: opts.maxTokens } : {},
|
|
2599
2986
|
stream: true
|
|
2600
2987
|
}, requestOptions);
|
|
2601
2988
|
return this._responsesStream(reqId, startTime, rawStream, {
|
|
@@ -2694,6 +3081,8 @@ var OpenAIProvider = class {
|
|
|
2694
3081
|
})),
|
|
2695
3082
|
// 推理强度:DeepSeek 不支持,跳过
|
|
2696
3083
|
...opts.reasoningEffort && !this._isDeepSeek(model) ? { reasoning_effort: opts.reasoningEffort } : {},
|
|
3084
|
+
// 限制输出长度:o 系列 / gpt-5 推理模型仅支持 max_completion_tokens
|
|
3085
|
+
...opts.maxTokens ? isReasoningModel(model) ? { max_completion_tokens: opts.maxTokens } : { max_tokens: opts.maxTokens } : {},
|
|
2697
3086
|
stream: true,
|
|
2698
3087
|
// 要求返回 Token 用量统计(在流结束前的一个 chunk 中携带)
|
|
2699
3088
|
stream_options: { include_usage: true }
|
|
@@ -2807,8 +3196,8 @@ var OpenAIProvider = class {
|
|
|
2807
3196
|
if (!this._logDir) return;
|
|
2808
3197
|
try {
|
|
2809
3198
|
const today = logTime?.split("T")[0];
|
|
2810
|
-
const dir =
|
|
2811
|
-
|
|
3199
|
+
const dir = path3.join(this._logDir, today);
|
|
3200
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
2812
3201
|
const entry = {
|
|
2813
3202
|
reqId,
|
|
2814
3203
|
ts: logTime,
|
|
@@ -2820,8 +3209,8 @@ var OpenAIProvider = class {
|
|
|
2820
3209
|
toolNames: tools?.map((t) => t.function.name) ?? []
|
|
2821
3210
|
}
|
|
2822
3211
|
};
|
|
2823
|
-
|
|
2824
|
-
|
|
3212
|
+
fs3.writeFileSync(
|
|
3213
|
+
path3.join(dir, `${reqId}.json`),
|
|
2825
3214
|
JSON.stringify(entry, null, 2),
|
|
2826
3215
|
"utf-8"
|
|
2827
3216
|
);
|
|
@@ -2840,9 +3229,9 @@ var OpenAIProvider = class {
|
|
|
2840
3229
|
if (!this._logDir) return;
|
|
2841
3230
|
try {
|
|
2842
3231
|
const today = logTime?.split("T")[0];
|
|
2843
|
-
const filePath =
|
|
2844
|
-
if (!
|
|
2845
|
-
const entry = JSON.parse(
|
|
3232
|
+
const filePath = path3.join(this._logDir, today, `${reqId}.json`);
|
|
3233
|
+
if (!fs3.existsSync(filePath)) return;
|
|
3234
|
+
const entry = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
2846
3235
|
entry.elapsedMs = Date.now() - startTime;
|
|
2847
3236
|
if (data.response) {
|
|
2848
3237
|
entry.response = data.response;
|
|
@@ -2850,7 +3239,7 @@ var OpenAIProvider = class {
|
|
|
2850
3239
|
if (data.error) {
|
|
2851
3240
|
entry.error = data.error;
|
|
2852
3241
|
}
|
|
2853
|
-
|
|
3242
|
+
fs3.writeFileSync(filePath, JSON.stringify(entry, null, 2), "utf-8");
|
|
2854
3243
|
} catch (e) {
|
|
2855
3244
|
console.error(e);
|
|
2856
3245
|
}
|
|
@@ -3315,62 +3704,62 @@ var ToolRegistry = class {
|
|
|
3315
3704
|
};
|
|
3316
3705
|
|
|
3317
3706
|
// src/tools/pathSafety.ts
|
|
3318
|
-
var
|
|
3319
|
-
var
|
|
3707
|
+
var fs4 = __toESM(require("fs"), 1);
|
|
3708
|
+
var path4 = __toESM(require("path"), 1);
|
|
3320
3709
|
function isInsideRelative(rel) {
|
|
3321
|
-
return !
|
|
3710
|
+
return !path4.isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${path4.sep}`);
|
|
3322
3711
|
}
|
|
3323
3712
|
function isFilesystemRoot(p) {
|
|
3324
|
-
return
|
|
3713
|
+
return path4.parse(p).root === p;
|
|
3325
3714
|
}
|
|
3326
3715
|
function resolveWithinRoot(workspace, target) {
|
|
3327
|
-
const abs =
|
|
3716
|
+
const abs = path4.resolve(workspace, target);
|
|
3328
3717
|
let rootReal;
|
|
3329
3718
|
try {
|
|
3330
|
-
rootReal =
|
|
3719
|
+
rootReal = fs4.realpathSync(workspace);
|
|
3331
3720
|
} catch {
|
|
3332
|
-
const rootAbs =
|
|
3333
|
-
if (!isInsideRelative(
|
|
3721
|
+
const rootAbs = path4.resolve(workspace);
|
|
3722
|
+
if (!isInsideRelative(path4.relative(rootAbs, abs))) return null;
|
|
3334
3723
|
return { root: rootAbs, real: abs };
|
|
3335
3724
|
}
|
|
3336
3725
|
if (isFilesystemRoot(rootReal)) return null;
|
|
3337
3726
|
let real;
|
|
3338
3727
|
try {
|
|
3339
|
-
real =
|
|
3728
|
+
real = fs4.realpathSync(abs);
|
|
3340
3729
|
} catch {
|
|
3341
3730
|
let probe = abs;
|
|
3342
3731
|
const tail = [];
|
|
3343
3732
|
for (; ; ) {
|
|
3344
|
-
const exists =
|
|
3733
|
+
const exists = fs4.existsSync(probe) || fs4.lstatSync(probe, { throwIfNoEntry: false }) !== void 0;
|
|
3345
3734
|
if (exists) {
|
|
3346
3735
|
try {
|
|
3347
|
-
real =
|
|
3736
|
+
real = path4.join(fs4.realpathSync(probe), ...tail);
|
|
3348
3737
|
break;
|
|
3349
3738
|
} catch {
|
|
3350
3739
|
return null;
|
|
3351
3740
|
}
|
|
3352
3741
|
}
|
|
3353
|
-
tail.unshift(
|
|
3354
|
-
const parent =
|
|
3742
|
+
tail.unshift(path4.basename(probe));
|
|
3743
|
+
const parent = path4.dirname(probe);
|
|
3355
3744
|
if (parent === probe) return null;
|
|
3356
3745
|
probe = parent;
|
|
3357
3746
|
}
|
|
3358
3747
|
}
|
|
3359
|
-
if (!isInsideRelative(
|
|
3748
|
+
if (!isInsideRelative(path4.relative(rootReal, real))) return null;
|
|
3360
3749
|
return { root: rootReal, real };
|
|
3361
3750
|
}
|
|
3362
3751
|
function isWithinRoot(workspace, target) {
|
|
3363
3752
|
return resolveWithinRoot(workspace, target) !== null;
|
|
3364
3753
|
}
|
|
3365
3754
|
function resolveEntryWithinRoot(workspace, target) {
|
|
3366
|
-
const abs =
|
|
3367
|
-
const parent = resolveWithinRoot(workspace,
|
|
3755
|
+
const abs = path4.resolve(workspace, target);
|
|
3756
|
+
const parent = resolveWithinRoot(workspace, path4.dirname(abs));
|
|
3368
3757
|
if (!parent) return null;
|
|
3369
|
-
return { root: parent.root, real:
|
|
3758
|
+
return { root: parent.root, real: path4.join(parent.real, path4.basename(abs)) };
|
|
3370
3759
|
}
|
|
3371
3760
|
|
|
3372
3761
|
// src/permissions/PermissionEngine.ts
|
|
3373
|
-
var
|
|
3762
|
+
var path5 = __toESM(require("path"), 1);
|
|
3374
3763
|
var Mode = /* @__PURE__ */ ((Mode2) => {
|
|
3375
3764
|
Mode2["DISCUSS"] = "discuss";
|
|
3376
3765
|
Mode2["PLAN"] = "plan";
|
|
@@ -3427,7 +3816,7 @@ var PermissionEngine = class {
|
|
|
3427
3816
|
/** 会话级「总是允许此命令」集合 */
|
|
3428
3817
|
sessionAllowCommands;
|
|
3429
3818
|
constructor(opts) {
|
|
3430
|
-
this.workspaceRoot =
|
|
3819
|
+
this.workspaceRoot = path5.resolve(opts.workspaceRoot);
|
|
3431
3820
|
this.mode = opts.mode;
|
|
3432
3821
|
this.allowedCommands = Array.isArray(opts.allowedCommands) ? opts.allowedCommands : [];
|
|
3433
3822
|
this.sessionAllowTools = /* @__PURE__ */ new Set();
|
|
@@ -4342,14 +4731,14 @@ function approvalToHtmlTable(input, maxLen = APPROVAL_VALUE_MAX) {
|
|
|
4342
4731
|
}
|
|
4343
4732
|
|
|
4344
4733
|
// src/engine/FileLogHook.ts
|
|
4345
|
-
var
|
|
4346
|
-
var
|
|
4734
|
+
var fs5 = __toESM(require("fs"), 1);
|
|
4735
|
+
var path6 = __toESM(require("path"), 1);
|
|
4347
4736
|
var FileLogHook = class {
|
|
4348
|
-
constructor(sessionId,
|
|
4737
|
+
constructor(sessionId, stateDir2) {
|
|
4349
4738
|
this.sessionId = sessionId;
|
|
4350
|
-
|
|
4351
|
-
this._logPath =
|
|
4352
|
-
this._statePath =
|
|
4739
|
+
fs5.mkdirSync(stateDir2, { recursive: true });
|
|
4740
|
+
this._logPath = path6.join(stateDir2, `${sessionId}.jsonl`);
|
|
4741
|
+
this._statePath = path6.join(stateDir2, `${sessionId}_state.json`);
|
|
4353
4742
|
}
|
|
4354
4743
|
sessionId;
|
|
4355
4744
|
_logPath;
|
|
@@ -4443,13 +4832,13 @@ var FileLogHook = class {
|
|
|
4443
4832
|
...meta?.nameSource !== void 0 ? { nameSource: meta.nameSource } : {},
|
|
4444
4833
|
...meta?.taskCount !== void 0 ? { taskCount: meta.taskCount } : {}
|
|
4445
4834
|
};
|
|
4446
|
-
|
|
4835
|
+
fs5.writeFileSync(this._statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
4447
4836
|
}
|
|
4448
4837
|
/** 读取保存的引擎状态 */
|
|
4449
|
-
static loadState(sessionId,
|
|
4450
|
-
const p =
|
|
4838
|
+
static loadState(sessionId, stateDir2) {
|
|
4839
|
+
const p = path6.join(stateDir2, `${sessionId}_state.json`);
|
|
4451
4840
|
try {
|
|
4452
|
-
const raw =
|
|
4841
|
+
const raw = fs5.readFileSync(p, "utf-8");
|
|
4453
4842
|
return JSON.parse(raw);
|
|
4454
4843
|
} catch (e) {
|
|
4455
4844
|
console.error(e);
|
|
@@ -4457,10 +4846,10 @@ var FileLogHook = class {
|
|
|
4457
4846
|
}
|
|
4458
4847
|
}
|
|
4459
4848
|
/** 读取事件日志 */
|
|
4460
|
-
static loadEvents(sessionId,
|
|
4461
|
-
const p =
|
|
4849
|
+
static loadEvents(sessionId, stateDir2) {
|
|
4850
|
+
const p = path6.join(stateDir2, `${sessionId}.jsonl`);
|
|
4462
4851
|
try {
|
|
4463
|
-
const raw =
|
|
4852
|
+
const raw = fs5.readFileSync(p, "utf-8");
|
|
4464
4853
|
return raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
|
|
4465
4854
|
} catch (e) {
|
|
4466
4855
|
console.error(e);
|
|
@@ -4471,7 +4860,7 @@ var FileLogHook = class {
|
|
|
4471
4860
|
_append(entry) {
|
|
4472
4861
|
entry["ts"] = (/* @__PURE__ */ new Date()).toISOString();
|
|
4473
4862
|
try {
|
|
4474
|
-
|
|
4863
|
+
fs5.appendFileSync(this._logPath, JSON.stringify(entry) + "\n", "utf-8");
|
|
4475
4864
|
} catch (e) {
|
|
4476
4865
|
console.error(e);
|
|
4477
4866
|
}
|
|
@@ -4895,12 +5284,12 @@ var McpManager = class {
|
|
|
4895
5284
|
};
|
|
4896
5285
|
|
|
4897
5286
|
// src/tools/files.ts
|
|
4898
|
-
var
|
|
4899
|
-
var
|
|
5287
|
+
var fs6 = __toESM(require("fs"), 1);
|
|
5288
|
+
var path7 = __toESM(require("path"), 1);
|
|
4900
5289
|
var DEFAULT_MAX_LINES = 2e3;
|
|
4901
5290
|
var MAX_LINE_CHARS = 500;
|
|
4902
5291
|
function makeReadFile(workspace) {
|
|
4903
|
-
const root =
|
|
5292
|
+
const root = path7.resolve(workspace);
|
|
4904
5293
|
return function read_file(args) {
|
|
4905
5294
|
const filePath = String(args.path ?? "");
|
|
4906
5295
|
const startLine = Math.max(1, Number(args.start_line ?? 1));
|
|
@@ -4910,13 +5299,13 @@ function makeReadFile(workspace) {
|
|
|
4910
5299
|
return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
4911
5300
|
}
|
|
4912
5301
|
const target = resolved.real;
|
|
4913
|
-
if (!
|
|
5302
|
+
if (!fs6.existsSync(target) || !fs6.statSync(target).isFile()) {
|
|
4914
5303
|
return { error: `\u4E0D\u662F\u6587\u4EF6: ${filePath}` };
|
|
4915
5304
|
}
|
|
4916
5305
|
const selected = [];
|
|
4917
5306
|
let total = 0;
|
|
4918
5307
|
try {
|
|
4919
|
-
const content =
|
|
5308
|
+
const content = fs6.readFileSync(target, "utf-8");
|
|
4920
5309
|
const lines = content.split("\n");
|
|
4921
5310
|
total = lines.length;
|
|
4922
5311
|
for (let i = startLine - 1; i < Math.min(startLine - 1 + maxLines, lines.length); i++) {
|
|
@@ -4931,7 +5320,7 @@ function makeReadFile(workspace) {
|
|
|
4931
5320
|
}
|
|
4932
5321
|
const end = startLine + selected.length - 1;
|
|
4933
5322
|
const result = {
|
|
4934
|
-
path:
|
|
5323
|
+
path: path7.relative(resolved.root, target),
|
|
4935
5324
|
start_line: startLine,
|
|
4936
5325
|
end_line: end,
|
|
4937
5326
|
total_lines: total,
|
|
@@ -4944,7 +5333,7 @@ function makeReadFile(workspace) {
|
|
|
4944
5333
|
};
|
|
4945
5334
|
}
|
|
4946
5335
|
function makeWriteFile(workspace) {
|
|
4947
|
-
const root =
|
|
5336
|
+
const root = path7.resolve(workspace);
|
|
4948
5337
|
return function write_file(args) {
|
|
4949
5338
|
const filePath = String(args.path ?? "");
|
|
4950
5339
|
const content = String(args.content ?? "");
|
|
@@ -4954,34 +5343,34 @@ function makeWriteFile(workspace) {
|
|
|
4954
5343
|
return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
4955
5344
|
}
|
|
4956
5345
|
const target = resolved.real;
|
|
4957
|
-
if (
|
|
5346
|
+
if (fs6.existsSync(target) && fs6.statSync(target).isDirectory()) {
|
|
4958
5347
|
return { error: "\u8DEF\u5F84\u662F\u76EE\u5F55\uFF0C\u4E0D\u662F\u6587\u4EF6" };
|
|
4959
5348
|
}
|
|
4960
|
-
if (
|
|
5349
|
+
if (fs6.existsSync(target) && !overwrite) {
|
|
4961
5350
|
return { error: "\u6587\u4EF6\u5DF2\u5B58\u5728 (overwrite=false)" };
|
|
4962
5351
|
}
|
|
4963
5352
|
try {
|
|
4964
|
-
|
|
4965
|
-
|
|
5353
|
+
fs6.mkdirSync(path7.dirname(target), { recursive: true });
|
|
5354
|
+
fs6.writeFileSync(target, content, "utf-8");
|
|
4966
5355
|
} catch (exc) {
|
|
4967
5356
|
return { error: `\u5199\u5165\u5931\u8D25: ${String(exc)}` };
|
|
4968
5357
|
}
|
|
4969
|
-
return
|
|
5358
|
+
return path7.relative(resolved.root, target);
|
|
4970
5359
|
};
|
|
4971
5360
|
}
|
|
4972
5361
|
function makeListFiles(workspace) {
|
|
4973
|
-
const root =
|
|
5362
|
+
const root = path7.resolve(workspace);
|
|
4974
5363
|
return function list_files(args) {
|
|
4975
5364
|
const dirPath = String(args.path ?? ".");
|
|
4976
5365
|
const maxResults = Math.min(Number(args.max_results ?? 100), 100);
|
|
4977
5366
|
const resolved = resolveWithinRoot(root, dirPath);
|
|
4978
5367
|
if (!resolved) return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
4979
5368
|
const target = resolved.real;
|
|
4980
|
-
if (!
|
|
5369
|
+
if (!fs6.existsSync(target) || !fs6.statSync(target).isDirectory()) {
|
|
4981
5370
|
return { error: `\u4E0D\u662F\u76EE\u5F55: ${dirPath}` };
|
|
4982
5371
|
}
|
|
4983
5372
|
try {
|
|
4984
|
-
const entries =
|
|
5373
|
+
const entries = fs6.readdirSync(target, { withFileTypes: true });
|
|
4985
5374
|
const results = [];
|
|
4986
5375
|
for (const e of entries) {
|
|
4987
5376
|
if (results.length >= maxResults) break;
|
|
@@ -4990,14 +5379,14 @@ function makeListFiles(workspace) {
|
|
|
4990
5379
|
results.push(e.isDirectory() ? `${e.name}/` : e.name);
|
|
4991
5380
|
}
|
|
4992
5381
|
results.sort();
|
|
4993
|
-
return { path:
|
|
5382
|
+
return { path: path7.relative(resolved.root, target), entries: results.slice(0, maxResults) };
|
|
4994
5383
|
} catch (exc) {
|
|
4995
5384
|
return { error: `\u5217\u51FA\u6587\u4EF6\u5931\u8D25: ${String(exc)}` };
|
|
4996
5385
|
}
|
|
4997
5386
|
};
|
|
4998
5387
|
}
|
|
4999
5388
|
function makeReplaceInFile(workspace) {
|
|
5000
|
-
const root =
|
|
5389
|
+
const root = path7.resolve(workspace);
|
|
5001
5390
|
return function replace_in_file(args) {
|
|
5002
5391
|
const filePath = String(args.path ?? "");
|
|
5003
5392
|
const oldStr = String(args.old ?? "");
|
|
@@ -5007,19 +5396,19 @@ function makeReplaceInFile(workspace) {
|
|
|
5007
5396
|
const resolved = resolveWithinRoot(root, filePath);
|
|
5008
5397
|
if (!resolved) return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
5009
5398
|
const target = resolved.real;
|
|
5010
|
-
if (!
|
|
5399
|
+
if (!fs6.existsSync(target) || !fs6.statSync(target).isFile()) {
|
|
5011
5400
|
return { error: `\u4E0D\u662F\u6587\u4EF6: ${filePath}` };
|
|
5012
5401
|
}
|
|
5013
5402
|
try {
|
|
5014
|
-
const content =
|
|
5403
|
+
const content = fs6.readFileSync(target, "utf-8");
|
|
5015
5404
|
const count = content.split(oldStr).length - 1;
|
|
5016
5405
|
if (count !== expectedReplacements) {
|
|
5017
5406
|
return { error: `\u671F\u671B ${expectedReplacements} \u5904\u66FF\u6362\uFF0C\u5B9E\u9645\u627E\u5230 ${count} \u5904: ${filePath}` };
|
|
5018
5407
|
}
|
|
5019
5408
|
const updated = content.replaceAll(oldStr, newStr);
|
|
5020
|
-
|
|
5409
|
+
fs6.writeFileSync(target, updated, "utf-8");
|
|
5021
5410
|
return {
|
|
5022
|
-
path:
|
|
5411
|
+
path: path7.relative(resolved.root, target),
|
|
5023
5412
|
replacements: count,
|
|
5024
5413
|
chars_before: content.length,
|
|
5025
5414
|
chars_after: updated.length
|
|
@@ -5119,8 +5508,8 @@ function registerFileTools(registry, workspace) {
|
|
|
5119
5508
|
}
|
|
5120
5509
|
|
|
5121
5510
|
// src/tools/shell.ts
|
|
5122
|
-
var
|
|
5123
|
-
var
|
|
5511
|
+
var fs8 = __toESM(require("fs"), 1);
|
|
5512
|
+
var path9 = __toESM(require("path"), 1);
|
|
5124
5513
|
|
|
5125
5514
|
// src/tools/execAsync.ts
|
|
5126
5515
|
var import_node_child_process2 = require("child_process");
|
|
@@ -5282,15 +5671,15 @@ function execAsync(file, argv, opts = {}) {
|
|
|
5282
5671
|
}
|
|
5283
5672
|
|
|
5284
5673
|
// src/tools/shellRuntime.ts
|
|
5285
|
-
var
|
|
5286
|
-
var
|
|
5674
|
+
var fs7 = __toESM(require("fs"), 1);
|
|
5675
|
+
var path8 = __toESM(require("path"), 1);
|
|
5287
5676
|
var WINDOWS_SUFFIXES = [".exe", ".cmd", ".bat", ".com", ""];
|
|
5288
5677
|
function pathApi(platform) {
|
|
5289
|
-
return platform === "win32" ?
|
|
5678
|
+
return platform === "win32" ? path8.win32 : path8.posix;
|
|
5290
5679
|
}
|
|
5291
5680
|
function defaultFileExists(p) {
|
|
5292
5681
|
try {
|
|
5293
|
-
return
|
|
5682
|
+
return fs7.statSync(p).isFile();
|
|
5294
5683
|
} catch {
|
|
5295
5684
|
return false;
|
|
5296
5685
|
}
|
|
@@ -5321,7 +5710,7 @@ function kindFromBasename(base) {
|
|
|
5321
5710
|
return "sh";
|
|
5322
5711
|
}
|
|
5323
5712
|
function busyboxArgs(file, command) {
|
|
5324
|
-
const base = (file ?
|
|
5713
|
+
const base = (file ? path8.win32.basename(file) : "busybox").toLowerCase().replace(/\.(exe|cmd|bat|com)$/, "");
|
|
5325
5714
|
return base === "busybox" ? ["sh", "-c", command] : ["-c", command];
|
|
5326
5715
|
}
|
|
5327
5716
|
function argsFor(kind, command) {
|
|
@@ -5587,7 +5976,7 @@ var MAX_STDERR = 5e3;
|
|
|
5587
5976
|
var MAX_BUFFER = 1024 * 1024 * 5;
|
|
5588
5977
|
var shellSessions = /* @__PURE__ */ new Map();
|
|
5589
5978
|
function sessionFor(workspace) {
|
|
5590
|
-
const key =
|
|
5979
|
+
const key = path9.resolve(workspace);
|
|
5591
5980
|
let s = shellSessions.get(key);
|
|
5592
5981
|
if (!s) {
|
|
5593
5982
|
s = { cwd: key };
|
|
@@ -5644,8 +6033,8 @@ function makeRunShell(workspace, spec, tasks, scope) {
|
|
|
5644
6033
|
let toRun = command;
|
|
5645
6034
|
if (target) {
|
|
5646
6035
|
try {
|
|
5647
|
-
const resolved =
|
|
5648
|
-
if (
|
|
6036
|
+
const resolved = path9.resolve(session.cwd, target);
|
|
6037
|
+
if (fs8.existsSync(resolved) && fs8.statSync(resolved).isDirectory()) {
|
|
5649
6038
|
session.cwd = resolved;
|
|
5650
6039
|
}
|
|
5651
6040
|
} catch {
|
|
@@ -5770,8 +6159,8 @@ function registerShellTool(registry, workspace, scope) {
|
|
|
5770
6159
|
}
|
|
5771
6160
|
|
|
5772
6161
|
// src/tools/nodeGrep.ts
|
|
5773
|
-
var
|
|
5774
|
-
var
|
|
6162
|
+
var fs9 = __toESM(require("fs"), 1);
|
|
6163
|
+
var path10 = __toESM(require("path"), 1);
|
|
5775
6164
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
5776
6165
|
".git",
|
|
5777
6166
|
"node_modules",
|
|
@@ -5796,27 +6185,27 @@ function nodeGrep(opts) {
|
|
|
5796
6185
|
} catch (err) {
|
|
5797
6186
|
throw new Error(`\u65E0\u6548\u7684\u6B63\u5219\u8868\u8FBE\u5F0F: ${opts.pattern}\uFF08${String(err)}\uFF09`);
|
|
5798
6187
|
}
|
|
5799
|
-
const target =
|
|
5800
|
-
if (!
|
|
6188
|
+
const target = path10.resolve(opts.workspace, opts.target);
|
|
6189
|
+
if (!fs9.existsSync(target)) {
|
|
5801
6190
|
throw new Error(`\u641C\u7D22\u8DEF\u5F84\u4E0D\u5B58\u5728: ${opts.target}`);
|
|
5802
6191
|
}
|
|
5803
6192
|
const matches = [];
|
|
5804
6193
|
const scanFile = (file) => {
|
|
5805
6194
|
let stat2;
|
|
5806
6195
|
try {
|
|
5807
|
-
stat2 =
|
|
6196
|
+
stat2 = fs9.statSync(file);
|
|
5808
6197
|
} catch {
|
|
5809
6198
|
return false;
|
|
5810
6199
|
}
|
|
5811
6200
|
if (!stat2.isFile() || stat2.size > maxFileBytes) return false;
|
|
5812
6201
|
let buf;
|
|
5813
6202
|
try {
|
|
5814
|
-
buf =
|
|
6203
|
+
buf = fs9.readFileSync(file);
|
|
5815
6204
|
} catch {
|
|
5816
6205
|
return false;
|
|
5817
6206
|
}
|
|
5818
6207
|
if (looksBinary(buf)) return false;
|
|
5819
|
-
const rel =
|
|
6208
|
+
const rel = path10.relative(opts.workspace, file) || path10.basename(file);
|
|
5820
6209
|
const lines = buf.toString("utf-8").split("\n");
|
|
5821
6210
|
for (let i = 0; i < lines.length; i++) {
|
|
5822
6211
|
const raw = lines[i].replace(/\r$/, "");
|
|
@@ -5827,7 +6216,7 @@ function nodeGrep(opts) {
|
|
|
5827
6216
|
}
|
|
5828
6217
|
return false;
|
|
5829
6218
|
};
|
|
5830
|
-
const stat =
|
|
6219
|
+
const stat = fs9.statSync(target);
|
|
5831
6220
|
if (stat.isFile()) {
|
|
5832
6221
|
scanFile(target);
|
|
5833
6222
|
return matches;
|
|
@@ -5837,12 +6226,12 @@ function nodeGrep(opts) {
|
|
|
5837
6226
|
const dir = stack.pop();
|
|
5838
6227
|
let entries;
|
|
5839
6228
|
try {
|
|
5840
|
-
entries =
|
|
6229
|
+
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
5841
6230
|
} catch {
|
|
5842
6231
|
continue;
|
|
5843
6232
|
}
|
|
5844
6233
|
for (const entry of entries) {
|
|
5845
|
-
const full =
|
|
6234
|
+
const full = path10.join(dir, entry.name);
|
|
5846
6235
|
if (entry.isDirectory()) {
|
|
5847
6236
|
if (!SKIP_DIRS.has(entry.name)) stack.push(full);
|
|
5848
6237
|
} else if (entry.isFile()) {
|
|
@@ -5854,22 +6243,22 @@ function nodeGrep(opts) {
|
|
|
5854
6243
|
}
|
|
5855
6244
|
|
|
5856
6245
|
// src/tools/ripgrep.ts
|
|
5857
|
-
var
|
|
5858
|
-
var
|
|
5859
|
-
var
|
|
6246
|
+
var fs10 = __toESM(require("fs"), 1);
|
|
6247
|
+
var os2 = __toESM(require("os"), 1);
|
|
6248
|
+
var path11 = __toESM(require("path"), 1);
|
|
5860
6249
|
var import_node_module = require("module");
|
|
5861
6250
|
var cached2;
|
|
5862
6251
|
function extractBundledRipgrep() {
|
|
5863
6252
|
try {
|
|
5864
|
-
const req = typeof require === "function" ? require : (0, import_node_module.createRequire)(
|
|
6253
|
+
const req = typeof require === "function" ? require : (0, import_node_module.createRequire)(path11.join(process.cwd(), "__zqagent__.js"));
|
|
5865
6254
|
const sea = req("node:sea");
|
|
5866
6255
|
if (!sea?.isSea?.() || typeof sea.getAsset !== "function") return null;
|
|
5867
6256
|
const asset = sea.getAsset("rg");
|
|
5868
6257
|
if (!asset) return null;
|
|
5869
|
-
const dir =
|
|
5870
|
-
const file =
|
|
5871
|
-
|
|
5872
|
-
|
|
6258
|
+
const dir = fs10.mkdtempSync(path11.join(os2.tmpdir(), "zqagent-rg-"));
|
|
6259
|
+
const file = path11.join(dir, process.platform === "win32" ? "rg.exe" : "rg");
|
|
6260
|
+
fs10.writeFileSync(file, Buffer.from(asset));
|
|
6261
|
+
fs10.chmodSync(file, 493);
|
|
5873
6262
|
return file;
|
|
5874
6263
|
} catch {
|
|
5875
6264
|
return null;
|
|
@@ -5878,7 +6267,7 @@ function extractBundledRipgrep() {
|
|
|
5878
6267
|
function resolveRipgrep() {
|
|
5879
6268
|
if (cached2 !== void 0) return cached2;
|
|
5880
6269
|
const explicit = (process.env.ZQAGENT_RG ?? "").trim();
|
|
5881
|
-
if (explicit &&
|
|
6270
|
+
if (explicit && fs10.existsSync(explicit)) {
|
|
5882
6271
|
cached2 = explicit;
|
|
5883
6272
|
return cached2;
|
|
5884
6273
|
}
|
|
@@ -5892,7 +6281,7 @@ function resolveRipgrep() {
|
|
|
5892
6281
|
env: process.env,
|
|
5893
6282
|
fileExists: (p) => {
|
|
5894
6283
|
try {
|
|
5895
|
-
return
|
|
6284
|
+
return fs10.statSync(p).isFile();
|
|
5896
6285
|
} catch {
|
|
5897
6286
|
return false;
|
|
5898
6287
|
}
|
|
@@ -6439,8 +6828,8 @@ function registerWebTools(registry) {
|
|
|
6439
6828
|
}
|
|
6440
6829
|
|
|
6441
6830
|
// src/tools/git.ts
|
|
6442
|
-
var
|
|
6443
|
-
var
|
|
6831
|
+
var fs11 = __toESM(require("fs"), 1);
|
|
6832
|
+
var path12 = __toESM(require("path"), 1);
|
|
6444
6833
|
async function runGit(repoDir, args, timeout = 15e3) {
|
|
6445
6834
|
const res = await execAsync("git", ["-c", "core.quotepath=false", ...args], {
|
|
6446
6835
|
cwd: repoDir,
|
|
@@ -6457,9 +6846,9 @@ async function runGit(repoDir, args, timeout = 15e3) {
|
|
|
6457
6846
|
async function resolveRepoRoot(workspace, pathArg) {
|
|
6458
6847
|
let dir = workspace;
|
|
6459
6848
|
if (pathArg) {
|
|
6460
|
-
const abs =
|
|
6849
|
+
const abs = path12.resolve(workspace, pathArg);
|
|
6461
6850
|
try {
|
|
6462
|
-
dir =
|
|
6851
|
+
dir = fs11.statSync(abs).isDirectory() ? abs : path12.dirname(abs);
|
|
6463
6852
|
} catch {
|
|
6464
6853
|
dir = abs;
|
|
6465
6854
|
}
|
|
@@ -6472,24 +6861,24 @@ function repoDirArg(workspace, raw) {
|
|
|
6472
6861
|
if (typeof raw !== "string") return void 0;
|
|
6473
6862
|
const v = raw.trim();
|
|
6474
6863
|
if (!v) return void 0;
|
|
6475
|
-
const base =
|
|
6476
|
-
const abs =
|
|
6477
|
-
const rel =
|
|
6478
|
-
if (rel.startsWith("..") ||
|
|
6864
|
+
const base = path12.resolve(workspace);
|
|
6865
|
+
const abs = path12.resolve(base, v);
|
|
6866
|
+
const rel = path12.relative(base, abs);
|
|
6867
|
+
if (rel.startsWith("..") || path12.isAbsolute(rel)) return void 0;
|
|
6479
6868
|
return abs;
|
|
6480
6869
|
}
|
|
6481
6870
|
function toPathspec(repoRoot, workspace, pathArg) {
|
|
6482
6871
|
if (!pathArg) return null;
|
|
6483
|
-
const abs =
|
|
6484
|
-
let rel =
|
|
6872
|
+
const abs = path12.resolve(workspace, pathArg);
|
|
6873
|
+
let rel = path12.relative(repoRoot, abs);
|
|
6485
6874
|
if (rel === "") return ".";
|
|
6486
|
-
if (rel.startsWith("..") ||
|
|
6875
|
+
if (rel.startsWith("..") || path12.isAbsolute(rel)) {
|
|
6487
6876
|
try {
|
|
6488
|
-
const realRepo =
|
|
6489
|
-
const realAbs =
|
|
6490
|
-
const rel2 =
|
|
6877
|
+
const realRepo = fs11.realpathSync(repoRoot);
|
|
6878
|
+
const realAbs = fs11.realpathSync(abs);
|
|
6879
|
+
const rel2 = path12.relative(realRepo, realAbs);
|
|
6491
6880
|
if (rel2 === "") return ".";
|
|
6492
|
-
if (!rel2.startsWith("..") && !
|
|
6881
|
+
if (!rel2.startsWith("..") && !path12.isAbsolute(rel2)) return rel2;
|
|
6493
6882
|
} catch {
|
|
6494
6883
|
}
|
|
6495
6884
|
return null;
|
|
@@ -6634,10 +7023,10 @@ function registerGitTools(registry, workspace) {
|
|
|
6634
7023
|
}
|
|
6635
7024
|
|
|
6636
7025
|
// src/tools/diff.ts
|
|
6637
|
-
var
|
|
6638
|
-
var
|
|
7026
|
+
var fs12 = __toESM(require("fs"), 1);
|
|
7027
|
+
var path13 = __toESM(require("path"), 1);
|
|
6639
7028
|
function makeApplyPatch(workspace) {
|
|
6640
|
-
const root =
|
|
7029
|
+
const root = path13.resolve(workspace);
|
|
6641
7030
|
return function apply_patch(args) {
|
|
6642
7031
|
const patch = String(args.patch ?? "");
|
|
6643
7032
|
if (!patch.trim()) return { error: "\u8865\u4E01\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A" };
|
|
@@ -6676,12 +7065,12 @@ function makeApplyPatch(workspace) {
|
|
|
6676
7065
|
contentLines.push(current.slice(1));
|
|
6677
7066
|
i++;
|
|
6678
7067
|
}
|
|
6679
|
-
if (
|
|
7068
|
+
if (fs12.existsSync(target)) {
|
|
6680
7069
|
return { error: `\u6587\u4EF6\u5DF2\u5B58\u5728: ${filePath}` };
|
|
6681
7070
|
}
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
const rel =
|
|
7071
|
+
fs12.mkdirSync(path13.dirname(target), { recursive: true });
|
|
7072
|
+
fs12.writeFileSync(target, contentLines.join("\n") + "\n", "utf-8");
|
|
7073
|
+
const rel = path13.relative(resolved.root, target);
|
|
6685
7074
|
changedFiles.push(rel);
|
|
6686
7075
|
addedFiles.push(rel);
|
|
6687
7076
|
fileCount++;
|
|
@@ -6694,11 +7083,11 @@ function makeApplyPatch(workspace) {
|
|
|
6694
7083
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${filePath}` };
|
|
6695
7084
|
}
|
|
6696
7085
|
const target = entry.real;
|
|
6697
|
-
if (!
|
|
7086
|
+
if (!fs12.existsSync(target)) {
|
|
6698
7087
|
return { error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}` };
|
|
6699
7088
|
}
|
|
6700
|
-
|
|
6701
|
-
deletedFiles.push(
|
|
7089
|
+
fs12.unlinkSync(target);
|
|
7090
|
+
deletedFiles.push(path13.relative(entry.root, target));
|
|
6702
7091
|
fileCount++;
|
|
6703
7092
|
i++;
|
|
6704
7093
|
continue;
|
|
@@ -6734,10 +7123,10 @@ function makeApplyPatch(workspace) {
|
|
|
6734
7123
|
if (!chunkLines.length) {
|
|
6735
7124
|
return { error: `\u8865\u4E01\u66F4\u65B0\u6CA1\u6709 hunks: ${filePath}` };
|
|
6736
7125
|
}
|
|
6737
|
-
if (!
|
|
7126
|
+
if (!fs12.existsSync(target)) {
|
|
6738
7127
|
return { error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}` };
|
|
6739
7128
|
}
|
|
6740
|
-
const oldLines =
|
|
7129
|
+
const oldLines = fs12.readFileSync(target, "utf-8").split("\n");
|
|
6741
7130
|
const newLines = applyPatchHunks(oldLines, chunkLines, filePath);
|
|
6742
7131
|
let finalResolved = resolved;
|
|
6743
7132
|
if (moveTo) {
|
|
@@ -6748,20 +7137,20 @@ function makeApplyPatch(workspace) {
|
|
|
6748
7137
|
finalResolved = moved;
|
|
6749
7138
|
}
|
|
6750
7139
|
const finalTarget = finalResolved.real;
|
|
6751
|
-
if (moveTo &&
|
|
7140
|
+
if (moveTo && fs12.existsSync(finalTarget) && finalTarget !== target) {
|
|
6752
7141
|
return { error: `\u6587\u4EF6\u5DF2\u5B58\u5728: ${moveTo}` };
|
|
6753
7142
|
}
|
|
6754
|
-
|
|
6755
|
-
|
|
7143
|
+
fs12.mkdirSync(path13.dirname(finalTarget), { recursive: true });
|
|
7144
|
+
fs12.writeFileSync(finalTarget, newLines.join("\n"), "utf-8");
|
|
6756
7145
|
if (moveTo && finalTarget !== target) {
|
|
6757
7146
|
const entry = resolveEntryWithinRoot(root, filePath);
|
|
6758
7147
|
if (!entry) {
|
|
6759
7148
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${filePath}` };
|
|
6760
7149
|
}
|
|
6761
|
-
|
|
6762
|
-
deletedFiles.push(
|
|
7150
|
+
fs12.unlinkSync(entry.real);
|
|
7151
|
+
deletedFiles.push(path13.relative(resolved.root, target));
|
|
6763
7152
|
}
|
|
6764
|
-
changedFiles.push(
|
|
7153
|
+
changedFiles.push(path13.relative(finalResolved.root, finalTarget));
|
|
6765
7154
|
fileCount++;
|
|
6766
7155
|
continue;
|
|
6767
7156
|
}
|
|
@@ -6773,18 +7162,18 @@ function makeApplyPatch(workspace) {
|
|
|
6773
7162
|
return { changed_files: changedFiles, added_files: addedFiles, deleted_files: deletedFiles, file_count: fileCount };
|
|
6774
7163
|
};
|
|
6775
7164
|
}
|
|
6776
|
-
function applyPatchHunks(oldLines, chunks,
|
|
7165
|
+
function applyPatchHunks(oldLines, chunks, path16) {
|
|
6777
7166
|
let newLines = [...oldLines];
|
|
6778
7167
|
let cursor = 0;
|
|
6779
7168
|
for (const chunk of chunks) {
|
|
6780
7169
|
const oldBlock = chunk.filter((l) => l[0] === " " || l[0] === "-").map((l) => l.slice(1));
|
|
6781
7170
|
const newBlock = chunk.filter((l) => l[0] === " " || l[0] === "+").map((l) => l.slice(1));
|
|
6782
7171
|
if (!oldBlock.length) {
|
|
6783
|
-
throw new Error(`\u8865\u4E01 hunk \u5FC5\u987B\u5305\u542B\u4E0A\u4E0B\u6587\u6216\u5220\u9664\u884C: ${
|
|
7172
|
+
throw new Error(`\u8865\u4E01 hunk \u5FC5\u987B\u5305\u542B\u4E0A\u4E0B\u6587\u6216\u5220\u9664\u884C: ${path16}`);
|
|
6784
7173
|
}
|
|
6785
7174
|
const matchStart = findBlockMatch(newLines, oldBlock, cursor);
|
|
6786
7175
|
if (matchStart === -1) {
|
|
6787
|
-
throw new Error(`\u8865\u4E01\u4E0A\u4E0B\u6587\u4E0D\u5339\u914D: ${
|
|
7176
|
+
throw new Error(`\u8865\u4E01\u4E0A\u4E0B\u6587\u4E0D\u5339\u914D: ${path16}`);
|
|
6788
7177
|
}
|
|
6789
7178
|
const matchEnd = matchStart + oldBlock.length;
|
|
6790
7179
|
newLines.splice(matchStart, oldBlock.length, ...newBlock);
|
|
@@ -6801,7 +7190,7 @@ function findBlockMatch(lines, block, start) {
|
|
|
6801
7190
|
return -1;
|
|
6802
7191
|
}
|
|
6803
7192
|
function makeApplyUnifiedDiff(workspace) {
|
|
6804
|
-
const root =
|
|
7193
|
+
const root = path13.resolve(workspace);
|
|
6805
7194
|
return function apply_unified_diff(args) {
|
|
6806
7195
|
const diff = String(args.diff ?? "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
6807
7196
|
if (!diff.trim()) return { error: "diff \u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A" };
|
|
@@ -6820,26 +7209,26 @@ function makeApplyUnifiedDiff(workspace) {
|
|
|
6820
7209
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${targetPath}` };
|
|
6821
7210
|
}
|
|
6822
7211
|
const target = resolved.real;
|
|
6823
|
-
const oldLines = oldIsDevNull ? [] :
|
|
7212
|
+
const oldLines = oldIsDevNull ? [] : fs12.existsSync(target) ? fs12.readFileSync(target, "utf-8").split("\n") : [];
|
|
6824
7213
|
const newLines = applyDiffHunks(oldLines, patch.hunks, targetPath);
|
|
6825
7214
|
hunkCount += patch.hunks.length;
|
|
6826
7215
|
if (newIsDevNull) {
|
|
6827
|
-
if (
|
|
6828
|
-
if (!
|
|
7216
|
+
if (fs12.existsSync(target)) {
|
|
7217
|
+
if (!fs12.statSync(target).isFile()) {
|
|
6829
7218
|
return { error: `\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6: ${targetPath}` };
|
|
6830
7219
|
}
|
|
6831
7220
|
const entry = resolveEntryWithinRoot(root, targetPath);
|
|
6832
7221
|
if (!entry) {
|
|
6833
7222
|
return { error: `\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916: ${targetPath}` };
|
|
6834
7223
|
}
|
|
6835
|
-
|
|
7224
|
+
fs12.unlinkSync(entry.real);
|
|
6836
7225
|
}
|
|
6837
|
-
deletedFiles.push(
|
|
7226
|
+
deletedFiles.push(path13.relative(resolved.root, target));
|
|
6838
7227
|
continue;
|
|
6839
7228
|
}
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
const rel =
|
|
7229
|
+
fs12.mkdirSync(path13.dirname(target), { recursive: true });
|
|
7230
|
+
fs12.writeFileSync(target, newLines.join("\n"), "utf-8");
|
|
7231
|
+
const rel = path13.relative(resolved.root, target);
|
|
6843
7232
|
changedFiles.push(rel);
|
|
6844
7233
|
if (oldIsDevNull) addedFiles.push(rel);
|
|
6845
7234
|
}
|
|
@@ -7084,7 +7473,8 @@ function buildExplorerEngine(opts) {
|
|
|
7084
7473
|
model: opts.model,
|
|
7085
7474
|
instructions: EXPLORER_INSTRUCTIONS,
|
|
7086
7475
|
maxIterations: parseMaxIterations(process.env.EXPLORE_MAX_ITERATIONS, 30),
|
|
7087
|
-
hook: opts.hook
|
|
7476
|
+
hook: opts.hook,
|
|
7477
|
+
toolResultDir: opts.toolResultDir
|
|
7088
7478
|
});
|
|
7089
7479
|
}
|
|
7090
7480
|
function makeExploreTool(opts) {
|
|
@@ -7096,7 +7486,8 @@ function makeExploreTool(opts) {
|
|
|
7096
7486
|
provider: opts.provider,
|
|
7097
7487
|
model: opts.model,
|
|
7098
7488
|
readonlyRegistrars: opts.readonlyRegistrars,
|
|
7099
|
-
hook: opts.hookFactory?.()
|
|
7489
|
+
hook: opts.hookFactory?.(),
|
|
7490
|
+
toolResultDir: opts.toolResultDir
|
|
7100
7491
|
});
|
|
7101
7492
|
let report = "";
|
|
7102
7493
|
let status = "unknown";
|
|
@@ -7156,13 +7547,13 @@ function registerExploreTool(registry, opts) {
|
|
|
7156
7547
|
}
|
|
7157
7548
|
|
|
7158
7549
|
// src/tools/media.ts
|
|
7159
|
-
var
|
|
7160
|
-
var
|
|
7550
|
+
var fs14 = __toESM(require("fs"), 1);
|
|
7551
|
+
var path15 = __toESM(require("path"), 1);
|
|
7161
7552
|
|
|
7162
7553
|
// src/tools/ffmpegRuntime.ts
|
|
7163
|
-
var
|
|
7164
|
-
var
|
|
7165
|
-
var
|
|
7554
|
+
var fs13 = __toESM(require("fs"), 1);
|
|
7555
|
+
var os3 = __toESM(require("os"), 1);
|
|
7556
|
+
var path14 = __toESM(require("path"), 1);
|
|
7166
7557
|
var import_node_module2 = require("module");
|
|
7167
7558
|
var import_node_crypto = require("crypto");
|
|
7168
7559
|
var import_promises = require("stream/promises");
|
|
@@ -7185,29 +7576,29 @@ if (PROXY_ENV2) {
|
|
|
7185
7576
|
}
|
|
7186
7577
|
var DOWNLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
7187
7578
|
function agentBinDir(workspace) {
|
|
7188
|
-
return
|
|
7579
|
+
return path14.join(path14.resolve(workspace), ".agent", "bin");
|
|
7189
7580
|
}
|
|
7190
7581
|
function exeName(name, platform) {
|
|
7191
7582
|
return platform === "win32" ? `${name}.exe` : name;
|
|
7192
7583
|
}
|
|
7193
7584
|
function defaultFileExists2(p) {
|
|
7194
7585
|
try {
|
|
7195
|
-
return
|
|
7586
|
+
return fs13.statSync(p).isFile();
|
|
7196
7587
|
} catch {
|
|
7197
7588
|
return false;
|
|
7198
7589
|
}
|
|
7199
7590
|
}
|
|
7200
7591
|
function extractBundled(name, platform) {
|
|
7201
7592
|
try {
|
|
7202
|
-
const req = typeof require === "function" ? require : (0, import_node_module2.createRequire)(
|
|
7593
|
+
const req = typeof require === "function" ? require : (0, import_node_module2.createRequire)(path14.join(process.cwd(), "__zqagent__.js"));
|
|
7203
7594
|
const sea = req("node:sea");
|
|
7204
7595
|
if (!sea?.isSea?.() || typeof sea.getAsset !== "function") return null;
|
|
7205
7596
|
const asset = sea.getAsset(name);
|
|
7206
7597
|
if (!asset) return null;
|
|
7207
|
-
const dir =
|
|
7208
|
-
const file =
|
|
7209
|
-
|
|
7210
|
-
|
|
7598
|
+
const dir = fs13.mkdtempSync(path14.join(os3.tmpdir(), `zqagent-${name}-`));
|
|
7599
|
+
const file = path14.join(dir, exeName(name, platform));
|
|
7600
|
+
fs13.writeFileSync(file, Buffer.from(asset));
|
|
7601
|
+
fs13.chmodSync(file, 493);
|
|
7211
7602
|
return file;
|
|
7212
7603
|
} catch {
|
|
7213
7604
|
return null;
|
|
@@ -7217,7 +7608,7 @@ function resolveOne(name, workspace, o) {
|
|
|
7217
7608
|
const envKey = name === "ffmpeg" ? ENV_FFMPEG : ENV_FFPROBE;
|
|
7218
7609
|
const explicit = (o.env[envKey] ?? "").trim();
|
|
7219
7610
|
if (explicit && o.fileExists(explicit)) return { path: explicit, source: "env" };
|
|
7220
|
-
const local =
|
|
7611
|
+
const local = path14.join(agentBinDir(workspace), exeName(name, o.platform));
|
|
7221
7612
|
if (o.fileExists(local)) return { path: local, source: "agent-bin" };
|
|
7222
7613
|
const bundled = extractBundled(name, o.platform);
|
|
7223
7614
|
if (bundled) return { path: bundled, source: "bundled" };
|
|
@@ -7231,7 +7622,7 @@ function resolveOne(name, workspace, o) {
|
|
|
7231
7622
|
var cache = /* @__PURE__ */ new Map();
|
|
7232
7623
|
function resolveMedia(workspace, opts = {}) {
|
|
7233
7624
|
const platform = opts.platform ?? process.platform;
|
|
7234
|
-
const key = `${platform}:${
|
|
7625
|
+
const key = `${platform}:${path14.resolve(workspace)}`;
|
|
7235
7626
|
const hit = cache.get(key);
|
|
7236
7627
|
if (hit) return hit;
|
|
7237
7628
|
const ctx = {
|
|
@@ -7337,7 +7728,7 @@ function parseChecksumFile(text, filename) {
|
|
|
7337
7728
|
if (!m) continue;
|
|
7338
7729
|
const hash = m[1].toLowerCase();
|
|
7339
7730
|
const name = m[2]?.trim() ?? "";
|
|
7340
|
-
if (!name || name === filename ||
|
|
7731
|
+
if (!name || name === filename || path14.basename(name) === filename) return hash;
|
|
7341
7732
|
}
|
|
7342
7733
|
const single = lines[0]?.trim();
|
|
7343
7734
|
if (lines.length === 1 && single && /^[0-9a-f]{32,128}$/i.test(single)) {
|
|
@@ -7347,7 +7738,7 @@ function parseChecksumFile(text, filename) {
|
|
|
7347
7738
|
}
|
|
7348
7739
|
async function computeChecksum(file, algo) {
|
|
7349
7740
|
const hash = (0, import_node_crypto.createHash)(algo);
|
|
7350
|
-
await (0, import_promises.pipeline)(
|
|
7741
|
+
await (0, import_promises.pipeline)(fs13.createReadStream(file), hash);
|
|
7351
7742
|
return hash.digest("hex");
|
|
7352
7743
|
}
|
|
7353
7744
|
function fetchInit() {
|
|
@@ -7362,7 +7753,7 @@ async function downloadToFile(url, dest) {
|
|
|
7362
7753
|
const resp = await (0, import_node_fetch3.default)(url, fetchInit());
|
|
7363
7754
|
if (!resp.ok) throw new Error(`HTTP ${resp.status} ${resp.statusText}`);
|
|
7364
7755
|
if (!resp.body) throw new Error("\u54CD\u5E94\u6CA1\u6709\u5185\u5BB9");
|
|
7365
|
-
await (0, import_promises.pipeline)(resp.body,
|
|
7756
|
+
await (0, import_promises.pipeline)(resp.body, fs13.createWriteStream(dest));
|
|
7366
7757
|
}
|
|
7367
7758
|
async function resolveComponentUrl(c) {
|
|
7368
7759
|
if (c.url) return c.url;
|
|
@@ -7379,12 +7770,12 @@ function findBinaryInTree(dir, names) {
|
|
|
7379
7770
|
const cur = stack.pop();
|
|
7380
7771
|
let entries;
|
|
7381
7772
|
try {
|
|
7382
|
-
entries =
|
|
7773
|
+
entries = fs13.readdirSync(cur, { withFileTypes: true });
|
|
7383
7774
|
} catch {
|
|
7384
7775
|
continue;
|
|
7385
7776
|
}
|
|
7386
7777
|
for (const e of entries) {
|
|
7387
|
-
const full =
|
|
7778
|
+
const full = path14.join(cur, e.name);
|
|
7388
7779
|
if (e.isDirectory()) {
|
|
7389
7780
|
stack.push(full);
|
|
7390
7781
|
} else if (names.includes(e.name)) {
|
|
@@ -7395,7 +7786,7 @@ function findBinaryInTree(dir, names) {
|
|
|
7395
7786
|
return null;
|
|
7396
7787
|
}
|
|
7397
7788
|
async function extractArchive(archive, dest, kind, platform) {
|
|
7398
|
-
|
|
7789
|
+
fs13.mkdirSync(dest, { recursive: true });
|
|
7399
7790
|
let res;
|
|
7400
7791
|
if (kind === "tar.xz") {
|
|
7401
7792
|
res = await execAsync("tar", ["-xJf", archive, "-C", dest]);
|
|
@@ -7431,7 +7822,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
7431
7822
|
error: `\u6682\u4E0D\u652F\u6301\u81EA\u52A8\u4E0B\u8F7D\u7684\u5E73\u53F0: ${platform}/${arch}`
|
|
7432
7823
|
};
|
|
7433
7824
|
}
|
|
7434
|
-
const tmp =
|
|
7825
|
+
const tmp = fs13.mkdtempSync(path14.join(os3.tmpdir(), "zqagent-ffmpeg-"));
|
|
7435
7826
|
const binDir = agentBinDir(workspace);
|
|
7436
7827
|
const installed = {};
|
|
7437
7828
|
try {
|
|
@@ -7445,7 +7836,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
7445
7836
|
}
|
|
7446
7837
|
let archive = fetched.get(url);
|
|
7447
7838
|
if (!archive) {
|
|
7448
|
-
const archivePath =
|
|
7839
|
+
const archivePath = path14.join(tmp, path14.basename(new URL(url).pathname) || `${c.name}.${c.archive}`);
|
|
7449
7840
|
log(`\u4E0B\u8F7D ${url}`);
|
|
7450
7841
|
try {
|
|
7451
7842
|
await downloadToFile(url, archivePath);
|
|
@@ -7455,7 +7846,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
7455
7846
|
if (c.checksumUrl) {
|
|
7456
7847
|
const algo = c.checksumAlgo ?? "sha256";
|
|
7457
7848
|
try {
|
|
7458
|
-
const expected = parseChecksumFile(await fetchText(c.checksumUrl),
|
|
7849
|
+
const expected = parseChecksumFile(await fetchText(c.checksumUrl), path14.basename(archivePath));
|
|
7459
7850
|
if (!expected) {
|
|
7460
7851
|
notes.push(`\u672A\u80FD\u4ECE ${c.checksumUrl} \u89E3\u6790\u51FA\u6821\u9A8C\u548C\uFF0C\u5DF2\u8DF3\u8FC7\u6821\u9A8C`);
|
|
7461
7852
|
} else {
|
|
@@ -7472,24 +7863,24 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
7472
7863
|
notes.push(`\u6821\u9A8C\u548C\u83B7\u53D6\u5931\u8D25 (${c.checksumUrl}): ${String(e instanceof Error ? e.message : e)}`);
|
|
7473
7864
|
}
|
|
7474
7865
|
} else {
|
|
7475
|
-
notes.push(`${
|
|
7866
|
+
notes.push(`${path14.basename(archivePath)} \u672A\u63D0\u4F9B\u6821\u9A8C\u548C\uFF08\u4EC5 HTTPS \u4F20\u8F93\u4FDD\u62A4\uFF09`);
|
|
7476
7867
|
}
|
|
7477
7868
|
archive = { file: archivePath, kind: c.archive };
|
|
7478
7869
|
fetched.set(url, archive);
|
|
7479
7870
|
}
|
|
7480
|
-
const extractDir =
|
|
7871
|
+
const extractDir = fs13.mkdtempSync(path14.join(tmp, "x-"));
|
|
7481
7872
|
const err = await extractArchive(archive.file, extractDir, archive.kind, platform);
|
|
7482
7873
|
if (err) return { ok: false, error: err };
|
|
7483
7874
|
const found = findBinaryInTree(extractDir, [exeName(c.name, platform), c.name]);
|
|
7484
7875
|
if (!found) {
|
|
7485
|
-
return { ok: false, error: `\u5F52\u6863\u5185\u672A\u627E\u5230 ${c.name}: ${
|
|
7876
|
+
return { ok: false, error: `\u5F52\u6863\u5185\u672A\u627E\u5230 ${c.name}: ${path14.basename(archive.file)}` };
|
|
7486
7877
|
}
|
|
7487
|
-
|
|
7488
|
-
const target =
|
|
7878
|
+
fs13.mkdirSync(binDir, { recursive: true });
|
|
7879
|
+
const target = path14.join(binDir, exeName(c.name, platform));
|
|
7489
7880
|
const staging = `${target}.tmp-${process.pid}`;
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7881
|
+
fs13.copyFileSync(found, staging);
|
|
7882
|
+
fs13.chmodSync(staging, 493);
|
|
7883
|
+
fs13.renameSync(staging, target);
|
|
7493
7884
|
installed[c.name] = target;
|
|
7494
7885
|
}
|
|
7495
7886
|
resetFfmpegCache();
|
|
@@ -7498,7 +7889,7 @@ async function downloadFfmpeg(workspace, opts = {}) {
|
|
|
7498
7889
|
} catch (e) {
|
|
7499
7890
|
return { ok: false, error: `\u5B89\u88C5\u5931\u8D25: ${String(e instanceof Error ? e.message : e)}` };
|
|
7500
7891
|
} finally {
|
|
7501
|
-
|
|
7892
|
+
fs13.rmSync(tmp, { recursive: true, force: true });
|
|
7502
7893
|
}
|
|
7503
7894
|
}
|
|
7504
7895
|
function canAutoDownload(platform, arch) {
|
|
@@ -7763,13 +8154,13 @@ function resolveTarget(workspace, value) {
|
|
|
7763
8154
|
return { abs: resolved.real, root: resolved.root };
|
|
7764
8155
|
}
|
|
7765
8156
|
function relOut(workspace, abs) {
|
|
7766
|
-
return
|
|
8157
|
+
return path15.relative(workspace, abs) || abs;
|
|
7767
8158
|
}
|
|
7768
8159
|
function registerMediaTool(registry, workspace, opts = {}) {
|
|
7769
8160
|
const platform = opts.platform ?? process.platform;
|
|
7770
8161
|
const arch = process.arch;
|
|
7771
8162
|
const media = async (args) => {
|
|
7772
|
-
const root =
|
|
8163
|
+
const root = path15.resolve(workspace);
|
|
7773
8164
|
const op = str(args["op"]);
|
|
7774
8165
|
if (!op) {
|
|
7775
8166
|
return { error: `\u7F3A\u5C11\u53C2\u6570 op\u3002\u53EF\u9009\u503C\uFF1A${OPS.join(" / ")}` };
|
|
@@ -7814,7 +8205,7 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
7814
8205
|
if (!input) return { error: `\u64CD\u4F5C ${op} \u9700\u8981\u53C2\u6570 input\uFF08\u5DE5\u4F5C\u533A\u5185\u76F8\u5BF9\u8DEF\u5F84\uFF09` };
|
|
7815
8206
|
const r = resolveTarget(root, input);
|
|
7816
8207
|
if ("error" in r) return { error: r.error };
|
|
7817
|
-
if (!
|
|
8208
|
+
if (!fs14.existsSync(r.abs) || !fs14.statSync(r.abs).isFile()) {
|
|
7818
8209
|
return { error: `\u8F93\u5165\u6587\u4EF6\u4E0D\u5B58\u5728: ${input}` };
|
|
7819
8210
|
}
|
|
7820
8211
|
inputAbs = r.abs;
|
|
@@ -7826,7 +8217,7 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
7826
8217
|
if (!output) return { error: `\u64CD\u4F5C ${op} \u9700\u8981\u53C2\u6570 output\uFF08\u5DE5\u4F5C\u533A\u5185\u76F8\u5BF9\u8DEF\u5F84\uFF09` };
|
|
7827
8218
|
const r = resolveTarget(root, output);
|
|
7828
8219
|
if ("error" in r) return { error: r.error };
|
|
7829
|
-
|
|
8220
|
+
fs14.mkdirSync(path15.dirname(r.abs), { recursive: true });
|
|
7830
8221
|
outputAbs = r.abs;
|
|
7831
8222
|
relRoot = r.root;
|
|
7832
8223
|
}
|
|
@@ -7876,7 +8267,7 @@ function registerMediaTool(registry, workspace, opts = {}) {
|
|
|
7876
8267
|
};
|
|
7877
8268
|
if (outputAbs) {
|
|
7878
8269
|
try {
|
|
7879
|
-
result["size_bytes"] =
|
|
8270
|
+
result["size_bytes"] = fs14.statSync(outputAbs).size;
|
|
7880
8271
|
} catch {
|
|
7881
8272
|
}
|
|
7882
8273
|
}
|
|
@@ -8116,7 +8507,9 @@ function createDefaultEngine(opts) {
|
|
|
8116
8507
|
subagentHookFactory,
|
|
8117
8508
|
contextWindow,
|
|
8118
8509
|
thresholdPct,
|
|
8510
|
+
softThresholdPct,
|
|
8119
8511
|
capTokens,
|
|
8512
|
+
toolResultDir: toolResultDir2,
|
|
8120
8513
|
logDir
|
|
8121
8514
|
} = opts;
|
|
8122
8515
|
const envEffort = process.env.OPENAI_REASONING_EFFORT?.toLowerCase();
|
|
@@ -8139,7 +8532,8 @@ function createDefaultEngine(opts) {
|
|
|
8139
8532
|
workspace,
|
|
8140
8533
|
provider: effectiveProvider,
|
|
8141
8534
|
model,
|
|
8142
|
-
hookFactory: subagentHookFactory
|
|
8535
|
+
hookFactory: subagentHookFactory,
|
|
8536
|
+
toolResultDir: toolResultDir2
|
|
8143
8537
|
});
|
|
8144
8538
|
const permissions = new PermissionEngine({
|
|
8145
8539
|
workspaceRoot: workspace,
|
|
@@ -8161,7 +8555,9 @@ function createDefaultEngine(opts) {
|
|
|
8161
8555
|
hook,
|
|
8162
8556
|
contextWindow,
|
|
8163
8557
|
thresholdPct,
|
|
8164
|
-
|
|
8558
|
+
softThresholdPct,
|
|
8559
|
+
capTokens,
|
|
8560
|
+
toolResultDir: toolResultDir2
|
|
8165
8561
|
});
|
|
8166
8562
|
return engine;
|
|
8167
8563
|
}
|