jinzd-ai-cli 0.4.68 → 0.4.70
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/dist/{chunk-G5AISHJE.js → chunk-3YT2DUUT.js} +34 -24
- package/dist/{chunk-Q5QSCO5D.js → chunk-IVTWWDWZ.js} +196 -78
- package/dist/{chunk-3LCVJ4AF.js → chunk-ND3O5NQU.js} +1 -1
- package/dist/{chunk-VO5IZN2C.js → chunk-YJKPARSH.js} +1 -1
- package/dist/{hub-4VPTOMBP.js → hub-BGO4X72R.js} +1 -1
- package/dist/index.js +70 -39
- package/dist/{run-tests-WD53PYVA.js → run-tests-NPRCZYN3.js} +1 -1
- package/dist/{run-tests-OZ3OEOOB.js → run-tests-RFEETSJP.js} +1 -1
- package/dist/{server-MDBQX5UZ.js → server-WYL3OD5N.js} +34 -31
- package/dist/{task-orchestrator-WDRXASIC.js → task-orchestrator-EB2XPY5S.js} +2 -2
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
RateLimitError,
|
|
9
9
|
schemaToJsonSchema,
|
|
10
10
|
truncateForPersist
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-IVTWWDWZ.js";
|
|
12
12
|
import {
|
|
13
13
|
APP_NAME,
|
|
14
14
|
CONFIG_DIR_NAME,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
MCP_TOOL_PREFIX,
|
|
22
22
|
PLUGINS_DIR_NAME,
|
|
23
23
|
VERSION
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-ND3O5NQU.js";
|
|
25
25
|
|
|
26
26
|
// src/config/config-manager.ts
|
|
27
27
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
@@ -417,21 +417,30 @@ var ClaudeProvider = class extends BaseProvider {
|
|
|
417
417
|
}
|
|
418
418
|
/**
|
|
419
419
|
* Build a cacheable system prompt payload.
|
|
420
|
-
*
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
420
|
+
*
|
|
421
|
+
* stable — session-invariant content (guidelines, memory, context). Marked with
|
|
422
|
+
* cache_control: ephemeral when long enough, so Claude reuses the KV cache
|
|
423
|
+
* across every round in an agentic loop AND across turns within a session.
|
|
424
|
+
* volatile — per-request content (date/time, working directory). Appended as a second
|
|
425
|
+
* text block WITHOUT cache_control so it never busts the stable cache.
|
|
426
|
+
*
|
|
427
|
+
* Short prompts (below the Anthropic minimum) pass through as a plain string.
|
|
424
428
|
*/
|
|
425
|
-
buildSystemParam(
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
return
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
429
|
+
buildSystemParam(stable, volatile) {
|
|
430
|
+
const stableText = stable ?? "";
|
|
431
|
+
const volatileText = volatile ?? "";
|
|
432
|
+
if (!stableText && !volatileText) return void 0;
|
|
433
|
+
if (stableText.length < CACHE_MIN_SYSTEM_CHARS) {
|
|
434
|
+
const combined = [stableText, volatileText].filter(Boolean).join("\n\n---\n\n");
|
|
435
|
+
return combined || void 0;
|
|
436
|
+
}
|
|
437
|
+
const blocks = [
|
|
438
|
+
{ type: "text", text: stableText, cache_control: { type: "ephemeral" } }
|
|
434
439
|
];
|
|
440
|
+
if (volatileText) {
|
|
441
|
+
blocks.push({ type: "text", text: volatileText });
|
|
442
|
+
}
|
|
443
|
+
return blocks;
|
|
435
444
|
}
|
|
436
445
|
/**
|
|
437
446
|
* Mark the last tool definition with `cache_control: ephemeral` so the entire
|
|
@@ -511,7 +520,7 @@ var ClaudeProvider = class extends BaseProvider {
|
|
|
511
520
|
const response = await this.client.messages.create({
|
|
512
521
|
model: request.model,
|
|
513
522
|
messages,
|
|
514
|
-
system: this.buildSystemParam(request.systemPrompt),
|
|
523
|
+
system: this.buildSystemParam(request.systemPrompt, request.systemPromptVolatile),
|
|
515
524
|
max_tokens: request.maxTokens ?? 8192,
|
|
516
525
|
temperature,
|
|
517
526
|
thinking
|
|
@@ -536,7 +545,7 @@ var ClaudeProvider = class extends BaseProvider {
|
|
|
536
545
|
const stream = this.client.messages.stream({
|
|
537
546
|
model: request.model,
|
|
538
547
|
messages,
|
|
539
|
-
system: this.buildSystemParam(request.systemPrompt),
|
|
548
|
+
system: this.buildSystemParam(request.systemPrompt, request.systemPromptVolatile),
|
|
540
549
|
max_tokens: request.maxTokens ?? 8192,
|
|
541
550
|
temperature,
|
|
542
551
|
thinking
|
|
@@ -593,7 +602,7 @@ var ClaudeProvider = class extends BaseProvider {
|
|
|
593
602
|
model: request.model,
|
|
594
603
|
messages: allMessages,
|
|
595
604
|
tools: anthropicTools,
|
|
596
|
-
system: this.buildSystemParam(request.systemPrompt),
|
|
605
|
+
system: this.buildSystemParam(request.systemPrompt, request.systemPromptVolatile),
|
|
597
606
|
max_tokens: request.maxTokens ?? 8192,
|
|
598
607
|
temperature,
|
|
599
608
|
thinking
|
|
@@ -649,7 +658,7 @@ var ClaudeProvider = class extends BaseProvider {
|
|
|
649
658
|
model: request.model,
|
|
650
659
|
messages: allMessages,
|
|
651
660
|
tools: anthropicTools,
|
|
652
|
-
system: this.buildSystemParam(request.systemPrompt),
|
|
661
|
+
system: this.buildSystemParam(request.systemPrompt, request.systemPromptVolatile),
|
|
653
662
|
max_tokens: request.maxTokens ?? 8192,
|
|
654
663
|
temperature,
|
|
655
664
|
thinking
|
|
@@ -909,7 +918,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
909
918
|
}
|
|
910
919
|
const genModel = this.client.getGenerativeModel({
|
|
911
920
|
model: request.model,
|
|
912
|
-
systemInstruction: request.systemPrompt
|
|
921
|
+
systemInstruction: [request.systemPrompt, request.systemPromptVolatile].filter(Boolean).join("\n\n---\n\n") || void 0
|
|
913
922
|
}, this.reqOpts);
|
|
914
923
|
const history = this.toGeminiHistory(request.messages.slice(0, -1));
|
|
915
924
|
const lastMsgParts = this.contentToGeminiParts(
|
|
@@ -935,7 +944,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
935
944
|
}
|
|
936
945
|
const genModel = this.client.getGenerativeModel({
|
|
937
946
|
model: request.model,
|
|
938
|
-
systemInstruction: request.systemPrompt
|
|
947
|
+
systemInstruction: [request.systemPrompt, request.systemPromptVolatile].filter(Boolean).join("\n\n---\n\n") || void 0
|
|
939
948
|
}, this.reqOpts);
|
|
940
949
|
const history = this.toGeminiHistory(request.messages.slice(0, -1));
|
|
941
950
|
const lastMsgParts = this.contentToGeminiParts(
|
|
@@ -1003,7 +1012,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
1003
1012
|
}
|
|
1004
1013
|
const genModel = this.client.getGenerativeModel({
|
|
1005
1014
|
model: request.model,
|
|
1006
|
-
systemInstruction: request.systemPrompt
|
|
1015
|
+
systemInstruction: [request.systemPrompt, request.systemPromptVolatile].filter(Boolean).join("\n\n---\n\n") || void 0
|
|
1007
1016
|
}, this.reqOpts);
|
|
1008
1017
|
const chat = genModel.startChat({ history: fullHistory, tools: geminiTools });
|
|
1009
1018
|
const result = await chat.sendMessage(msgToSend);
|
|
@@ -1127,8 +1136,9 @@ var OpenAICompatibleProvider = class extends BaseProvider {
|
|
|
1127
1136
|
/** 将 systemPrompt + messages 合并为 OpenAI messages 数组(system 消息放首位)。 */
|
|
1128
1137
|
buildMessages(request) {
|
|
1129
1138
|
const msgs = this.normalizeMessages(request.messages);
|
|
1130
|
-
|
|
1131
|
-
|
|
1139
|
+
const systemContent = [request.systemPrompt, request.systemPromptVolatile].filter(Boolean).join("\n\n---\n\n");
|
|
1140
|
+
if (systemContent) {
|
|
1141
|
+
return [{ role: "system", content: systemContent }, ...msgs];
|
|
1132
1142
|
}
|
|
1133
1143
|
return msgs;
|
|
1134
1144
|
}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
11
11
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
12
12
|
runTestsTool
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ND3O5NQU.js";
|
|
14
14
|
|
|
15
15
|
// src/tools/builtin/bash.ts
|
|
16
16
|
import { execSync } from "child_process";
|
|
@@ -1839,16 +1839,123 @@ function findWhitespaceTolerant(fileLines, searchLines) {
|
|
|
1839
1839
|
}
|
|
1840
1840
|
return { matchStart, matchCount };
|
|
1841
1841
|
}
|
|
1842
|
+
function applyReplace(content, oldStr, newStr, options = {}) {
|
|
1843
|
+
if (oldStr === "") {
|
|
1844
|
+
return { ok: false, error: "old_str cannot be empty" };
|
|
1845
|
+
}
|
|
1846
|
+
if (options.ignoreWhitespace) {
|
|
1847
|
+
const fileLines = content.split("\n");
|
|
1848
|
+
const searchLines = oldStr.split("\n");
|
|
1849
|
+
const { matchStart, matchCount } = findWhitespaceTolerant(fileLines, searchLines);
|
|
1850
|
+
if (matchStart === -1) {
|
|
1851
|
+
const similar = findSimilarLines(content, oldStr);
|
|
1852
|
+
const hint = similar.length > 0 ? `
|
|
1853
|
+
Similar lines found (did you mean?):
|
|
1854
|
+
${similar.join("\n")}` : "";
|
|
1855
|
+
return {
|
|
1856
|
+
ok: false,
|
|
1857
|
+
error: `old_str not found in file (even with whitespace ignored). File has ${fileLines.length} lines.${hint}`
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
if (matchCount > 1) {
|
|
1861
|
+
return {
|
|
1862
|
+
ok: false,
|
|
1863
|
+
error: "old_str matches multiple locations with whitespace-tolerant matching. Please include more surrounding context to make it unique."
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
const before = fileLines.slice(0, matchStart);
|
|
1867
|
+
const after = fileLines.slice(matchStart + searchLines.length);
|
|
1868
|
+
const updated2 = [...before, newStr, ...after].join("\n");
|
|
1869
|
+
return {
|
|
1870
|
+
ok: true,
|
|
1871
|
+
content: updated2,
|
|
1872
|
+
info: { mode: "ignore_whitespace", replacedCount: 1, lineNumber: matchStart + 1 }
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
if (options.replaceAll) {
|
|
1876
|
+
const occurrences = content.split(oldStr).length - 1;
|
|
1877
|
+
if (occurrences === 0) {
|
|
1878
|
+
const similar = findSimilarLines(content, oldStr);
|
|
1879
|
+
const hint = similar.length > 0 ? `
|
|
1880
|
+
Similar lines found (did you mean?):
|
|
1881
|
+
${similar.join("\n")}` : "";
|
|
1882
|
+
return { ok: false, error: `old_str not found in file.${hint}` };
|
|
1883
|
+
}
|
|
1884
|
+
const updated2 = content.split(oldStr).join(newStr);
|
|
1885
|
+
return {
|
|
1886
|
+
ok: true,
|
|
1887
|
+
content: updated2,
|
|
1888
|
+
info: { mode: "replace_all", replacedCount: occurrences }
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
const firstIndex = content.indexOf(oldStr);
|
|
1892
|
+
if (firstIndex === -1) {
|
|
1893
|
+
const lines = content.split("\n");
|
|
1894
|
+
const similar = findSimilarLines(content, oldStr);
|
|
1895
|
+
const hint = similar.length > 0 ? `
|
|
1896
|
+
Similar lines found (did you mean?):
|
|
1897
|
+
${similar.join("\n")}` : "";
|
|
1898
|
+
return {
|
|
1899
|
+
ok: false,
|
|
1900
|
+
error: `old_str not found in file. File has ${lines.length} lines.${hint}
|
|
1901
|
+
Tip: try ignore_whitespace: true if indentation differs.`
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
const secondIndex = content.indexOf(oldStr, firstIndex + 1);
|
|
1905
|
+
if (secondIndex !== -1) {
|
|
1906
|
+
return {
|
|
1907
|
+
ok: false,
|
|
1908
|
+
error: `old_str appears multiple times in file (at positions ${firstIndex} and ${secondIndex}). Please include more surrounding context to make it unique, or set replace_all: true.`
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1911
|
+
const updated = content.slice(0, firstIndex) + newStr + content.slice(firstIndex + oldStr.length);
|
|
1912
|
+
const linesBefore = content.slice(0, firstIndex).split("\n").length;
|
|
1913
|
+
return {
|
|
1914
|
+
ok: true,
|
|
1915
|
+
content: updated,
|
|
1916
|
+
info: { mode: "exact", replacedCount: 1, lineNumber: linesBefore }
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
function parseEditsArg(raw) {
|
|
1920
|
+
if (!Array.isArray(raw)) {
|
|
1921
|
+
throw new ToolError("edit_file", "edits must be an array of { old_str, new_str } objects");
|
|
1922
|
+
}
|
|
1923
|
+
if (raw.length === 0) {
|
|
1924
|
+
throw new ToolError("edit_file", "edits array is empty \u2014 provide at least one edit");
|
|
1925
|
+
}
|
|
1926
|
+
if (raw.length > 200) {
|
|
1927
|
+
throw new ToolError("edit_file", `edits array too large (${raw.length}) \u2014 max 200 per call`);
|
|
1928
|
+
}
|
|
1929
|
+
return raw.map((e, i) => {
|
|
1930
|
+
if (e == null || typeof e !== "object") {
|
|
1931
|
+
throw new ToolError("edit_file", `edits[${i}] must be an object`);
|
|
1932
|
+
}
|
|
1933
|
+
const rec = e;
|
|
1934
|
+
if (typeof rec.old_str !== "string") {
|
|
1935
|
+
throw new ToolError("edit_file", `edits[${i}].old_str must be a string`);
|
|
1936
|
+
}
|
|
1937
|
+
if (rec.new_str !== void 0 && typeof rec.new_str !== "string") {
|
|
1938
|
+
throw new ToolError("edit_file", `edits[${i}].new_str must be a string`);
|
|
1939
|
+
}
|
|
1940
|
+
return {
|
|
1941
|
+
old_str: rec.old_str,
|
|
1942
|
+
new_str: String(rec.new_str ?? ""),
|
|
1943
|
+
ignore_whitespace: Boolean(rec.ignore_whitespace),
|
|
1944
|
+
replace_all: Boolean(rec.replace_all)
|
|
1945
|
+
};
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1842
1948
|
var editFileTool = {
|
|
1843
1949
|
definition: {
|
|
1844
1950
|
name: "edit_file",
|
|
1845
|
-
description: `Precisely edit file contents.
|
|
1846
|
-
1. String replace (most common): Provide old_str and new_str to replace an exact match. old_str must appear exactly once
|
|
1847
|
-
2. Line insert: Provide insert_after_line (1-based
|
|
1848
|
-
3. Line delete: Provide delete_from_line and delete_to_line (inclusive)
|
|
1849
|
-
|
|
1850
|
-
Optional
|
|
1851
|
-
|
|
1951
|
+
description: `Precisely edit file contents. Four modes:
|
|
1952
|
+
1. String replace (most common): Provide old_str and new_str to replace an exact match. old_str must appear exactly once (unless replace_all is true).
|
|
1953
|
+
2. Line insert: Provide insert_after_line (1-based) and insert_content.
|
|
1954
|
+
3. Line delete: Provide delete_from_line and delete_to_line (inclusive).
|
|
1955
|
+
4. Batch edits: Provide edits=[{old_str, new_str, ignore_whitespace?, replace_all?}, ...] to apply MULTIPLE edits in ONE call \u2014 saves tool rounds/tokens when refactoring a file. Edits are applied sequentially in-memory; by default any failure rolls back ALL edits (set stop_on_error=false to apply successful ones and report failures).
|
|
1956
|
+
Optional ignore_whitespace: true ignores indentation differences during matching.
|
|
1957
|
+
Optional replace_all: true replaces ALL occurrences of old_str.
|
|
1958
|
+
Note: Path can be absolute or relative to cwd.`,
|
|
1852
1959
|
parameters: {
|
|
1853
1960
|
path: {
|
|
1854
1961
|
type: "string",
|
|
@@ -1857,22 +1964,22 @@ Note: Path can be absolute or relative to the current working directory.`,
|
|
|
1857
1964
|
},
|
|
1858
1965
|
old_str: {
|
|
1859
1966
|
type: "string",
|
|
1860
|
-
description: "[
|
|
1967
|
+
description: "[Single-replace mode] Original string to replace, must appear exactly once (include enough context for uniqueness)",
|
|
1861
1968
|
required: false
|
|
1862
1969
|
},
|
|
1863
1970
|
new_str: {
|
|
1864
1971
|
type: "string",
|
|
1865
|
-
description: "[
|
|
1972
|
+
description: "[Single-replace mode] New replacement string, can be empty to delete old_str",
|
|
1866
1973
|
required: false
|
|
1867
1974
|
},
|
|
1868
1975
|
ignore_whitespace: {
|
|
1869
1976
|
type: "boolean",
|
|
1870
|
-
description: "[
|
|
1977
|
+
description: "[Single-replace mode] Whether to ignore leading/trailing whitespace per line when matching, defaults to false",
|
|
1871
1978
|
required: false
|
|
1872
1979
|
},
|
|
1873
1980
|
replace_all: {
|
|
1874
1981
|
type: "boolean",
|
|
1875
|
-
description: "[
|
|
1982
|
+
description: "[Single-replace mode] Replace ALL occurrences of old_str instead of requiring unique match. Useful for renaming variables/functions across the file in one call.",
|
|
1876
1983
|
required: false
|
|
1877
1984
|
},
|
|
1878
1985
|
insert_after_line: {
|
|
@@ -1895,6 +2002,16 @@ Note: Path can be absolute or relative to the current working directory.`,
|
|
|
1895
2002
|
description: "[Delete mode] Delete up to and including this line (1-based)",
|
|
1896
2003
|
required: false
|
|
1897
2004
|
},
|
|
2005
|
+
edits: {
|
|
2006
|
+
type: "array",
|
|
2007
|
+
description: "[Batch mode] Array of {old_str, new_str, ignore_whitespace?, replace_all?} objects. Each applied sequentially to the in-memory content. Max 200 per call.",
|
|
2008
|
+
required: false
|
|
2009
|
+
},
|
|
2010
|
+
stop_on_error: {
|
|
2011
|
+
type: "boolean",
|
|
2012
|
+
description: "[Batch mode] If true (default), any failing edit rolls back the whole batch and writes nothing. If false, successful edits are written and failed ones are reported.",
|
|
2013
|
+
required: false
|
|
2014
|
+
},
|
|
1898
2015
|
encoding: {
|
|
1899
2016
|
type: "string",
|
|
1900
2017
|
description: "File encoding, defaults to utf-8",
|
|
@@ -1910,83 +2027,84 @@ Note: Path can be absolute or relative to the current working directory.`,
|
|
|
1910
2027
|
if (!filePath) throw new ToolError("edit_file", "path is required");
|
|
1911
2028
|
if (!existsSync5(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
|
|
1912
2029
|
const original = readFileSync4(filePath, encoding);
|
|
2030
|
+
if (args["edits"] !== void 0) {
|
|
2031
|
+
const edits = parseEditsArg(args["edits"]);
|
|
2032
|
+
const stopOnError = args["stop_on_error"] !== false;
|
|
2033
|
+
let working = original;
|
|
2034
|
+
const reports = [];
|
|
2035
|
+
let appliedCount = 0;
|
|
2036
|
+
for (let i = 0; i < edits.length; i++) {
|
|
2037
|
+
const edit = edits[i];
|
|
2038
|
+
const res = applyReplace(working, edit.old_str, edit.new_str, {
|
|
2039
|
+
ignoreWhitespace: edit.ignore_whitespace,
|
|
2040
|
+
replaceAll: edit.replace_all
|
|
2041
|
+
});
|
|
2042
|
+
if (res.ok) {
|
|
2043
|
+
working = res.content;
|
|
2044
|
+
appliedCount++;
|
|
2045
|
+
reports.push({
|
|
2046
|
+
index: i,
|
|
2047
|
+
ok: true,
|
|
2048
|
+
summary: `${res.info?.mode} at ${res.info?.lineNumber != null ? `line ${res.info.lineNumber}` : `${res.info?.replacedCount} occurrence(s)`}: ${truncatePreview(edit.old_str)} \u2192 ${truncatePreview(edit.new_str)}`
|
|
2049
|
+
});
|
|
2050
|
+
} else {
|
|
2051
|
+
reports.push({ index: i, ok: false, error: res.error });
|
|
2052
|
+
if (stopOnError) break;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
const anyFailed = reports.some((r) => !r.ok);
|
|
2056
|
+
const writeChanges = appliedCount > 0 && !(anyFailed && stopOnError);
|
|
2057
|
+
if (writeChanges) {
|
|
2058
|
+
undoStack.push(filePath, `edit_file (batch ${appliedCount}/${edits.length}): ${filePath}`);
|
|
2059
|
+
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
2060
|
+
writeFileSync3(filePath, working, encoding);
|
|
2061
|
+
}
|
|
2062
|
+
const lines = [];
|
|
2063
|
+
if (anyFailed && stopOnError) {
|
|
2064
|
+
lines.push(`ERROR: Batch edit aborted \u2014 ${appliedCount}/${edits.length} applied, then edit #${appliedCount} failed. No changes written (stop_on_error=true).`);
|
|
2065
|
+
} else if (anyFailed) {
|
|
2066
|
+
lines.push(`Partial success: ${appliedCount}/${edits.length} edits applied to ${filePath} (stop_on_error=false).`);
|
|
2067
|
+
} else {
|
|
2068
|
+
lines.push(`Successfully applied ${appliedCount}/${edits.length} edit(s) to ${filePath}.`);
|
|
2069
|
+
}
|
|
2070
|
+
lines.push("");
|
|
2071
|
+
for (const r of reports) {
|
|
2072
|
+
if (r.ok) {
|
|
2073
|
+
lines.push(` \u2713 #${r.index + 1}: ${r.summary}`);
|
|
2074
|
+
} else {
|
|
2075
|
+
lines.push(` \u2717 #${r.index + 1}: ${r.error}`);
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
return lines.join("\n");
|
|
2079
|
+
}
|
|
1913
2080
|
if (args["old_str"] !== void 0) {
|
|
1914
2081
|
const oldStr = String(args["old_str"]);
|
|
1915
2082
|
const newStr = String(args["new_str"] ?? "");
|
|
1916
2083
|
const ignoreWs = Boolean(args["ignore_whitespace"]);
|
|
1917
2084
|
const replaceAll = Boolean(args["replace_all"]);
|
|
1918
2085
|
if (oldStr === "") throw new ToolError("edit_file", "old_str cannot be empty");
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
const hint = similar.length > 0 ? `
|
|
1926
|
-
Similar lines found (did you mean?):
|
|
1927
|
-
${similar.join("\n")}` : "";
|
|
1928
|
-
return `ERROR: old_str not found in file (even with whitespace ignored).
|
|
1929
|
-
File has ${fileLines.length} lines.${hint}
|
|
2086
|
+
const res = applyReplace(original, oldStr, newStr, {
|
|
2087
|
+
ignoreWhitespace: ignoreWs,
|
|
2088
|
+
replaceAll
|
|
2089
|
+
});
|
|
2090
|
+
if (!res.ok) {
|
|
2091
|
+
return `ERROR: ${res.error}
|
|
1930
2092
|
Please read the file first and use exact text.`;
|
|
1931
|
-
}
|
|
1932
|
-
if (matchCount > 1) {
|
|
1933
|
-
return `ERROR: old_str matches multiple locations with whitespace-tolerant matching. Please include more surrounding context to make it unique.`;
|
|
1934
|
-
}
|
|
1935
|
-
undoStack.push(filePath, `edit_file (ws-replace): ${filePath}`);
|
|
1936
|
-
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
1937
|
-
const before = fileLines.slice(0, matchStart);
|
|
1938
|
-
const after = fileLines.slice(matchStart + searchLines.length);
|
|
1939
|
-
const updated2 = [...before, newStr, ...after].join("\n");
|
|
1940
|
-
writeFileSync3(filePath, updated2, encoding);
|
|
1941
|
-
return `Successfully edited ${filePath} (whitespace-tolerant match)
|
|
1942
|
-
Location: around line ${matchStart + 1}
|
|
1943
|
-
Replaced: ${searchLines.length} line(s) \u2192 ${newStr.split("\n").length} line(s)
|
|
1944
|
-
Old: ${truncatePreview(oldStr)}
|
|
1945
|
-
New: ${truncatePreview(newStr)}`;
|
|
1946
2093
|
}
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
${
|
|
1954
|
-
|
|
1955
|
-
Please read the file first and use exact text.`;
|
|
1956
|
-
}
|
|
1957
|
-
undoStack.push(filePath, `edit_file (replace_all): ${filePath}`);
|
|
1958
|
-
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
1959
|
-
const updated2 = original.split(oldStr).join(newStr);
|
|
1960
|
-
writeFileSync3(filePath, updated2, encoding);
|
|
1961
|
-
return `Successfully edited ${filePath} (replace_all)
|
|
1962
|
-
Replaced: ${occurrences} occurrence(s) of ${truncatePreview(oldStr)}
|
|
2094
|
+
const modeLabel = res.info?.mode === "ignore_whitespace" ? " (whitespace-tolerant match)" : res.info?.mode === "replace_all" ? " (replace_all)" : "";
|
|
2095
|
+
const label = res.info?.mode === "ignore_whitespace" ? "edit_file (ws-replace)" : res.info?.mode === "replace_all" ? "edit_file (replace_all)" : "edit_file (replace)";
|
|
2096
|
+
undoStack.push(filePath, `${label}: ${filePath}`);
|
|
2097
|
+
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
2098
|
+
writeFileSync3(filePath, res.content, encoding);
|
|
2099
|
+
if (res.info?.mode === "replace_all") {
|
|
2100
|
+
return `Successfully edited ${filePath}${modeLabel}
|
|
2101
|
+
Replaced: ${res.info.replacedCount} occurrence(s) of ${truncatePreview(oldStr)}
|
|
1963
2102
|
With: ${truncatePreview(newStr)}`;
|
|
1964
2103
|
}
|
|
1965
|
-
const firstIndex = original.indexOf(oldStr);
|
|
1966
|
-
if (firstIndex === -1) {
|
|
1967
|
-
const lines = original.split("\n");
|
|
1968
|
-
const similar = findSimilarLines(original, oldStr);
|
|
1969
|
-
const hint = similar.length > 0 ? `
|
|
1970
|
-
Similar lines found (did you mean?):
|
|
1971
|
-
${similar.join("\n")}` : "";
|
|
1972
|
-
return `ERROR: old_str not found in file.
|
|
1973
|
-
File has ${lines.length} lines.${hint}
|
|
1974
|
-
Please read the file first and use exact text including whitespace/indentation.
|
|
1975
|
-
Tip: You can also try ignore_whitespace: true to match ignoring indentation differences.`;
|
|
1976
|
-
}
|
|
1977
|
-
const secondIndex = original.indexOf(oldStr, firstIndex + 1);
|
|
1978
|
-
if (secondIndex !== -1) {
|
|
1979
|
-
return `ERROR: old_str appears multiple times in file (at least at positions ${firstIndex} and ${secondIndex}). Please include more surrounding context to make it unique.`;
|
|
1980
|
-
}
|
|
1981
|
-
undoStack.push(filePath, `edit_file (replace): ${filePath}`);
|
|
1982
|
-
fileCheckpoints.snapshot(filePath, ToolExecutor.currentMessageIndex);
|
|
1983
|
-
const updated = original.slice(0, firstIndex) + newStr + original.slice(firstIndex + oldStr.length);
|
|
1984
|
-
writeFileSync3(filePath, updated, encoding);
|
|
1985
2104
|
const oldLines = oldStr.split("\n").length;
|
|
1986
2105
|
const newLines = newStr.split("\n").length;
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
Location: around line ${linesBefore}
|
|
2106
|
+
return `Successfully edited ${filePath}${modeLabel}
|
|
2107
|
+
Location: around line ${res.info?.lineNumber ?? "?"}
|
|
1990
2108
|
Replaced: ${oldLines} line(s) \u2192 ${newLines} line(s)
|
|
1991
2109
|
Old: ${truncatePreview(oldStr)}
|
|
1992
2110
|
New: ${truncatePreview(newStr)}`;
|
|
@@ -2022,7 +2140,7 @@ Tip: You can also try ignore_whitespace: true to match ignoring indentation diff
|
|
|
2022
2140
|
}
|
|
2023
2141
|
throw new ToolError(
|
|
2024
2142
|
"edit_file",
|
|
2025
|
-
"No operation specified. Provide
|
|
2143
|
+
"No operation specified. Provide one of: (old_str + new_str), (insert_after_line + insert_content), (delete_from_line + delete_to_line), or edits=[...]."
|
|
2026
2144
|
);
|
|
2027
2145
|
}
|
|
2028
2146
|
};
|
|
@@ -385,7 +385,7 @@ ${content}`);
|
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
388
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
388
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-EB2XPY5S.js");
|
|
389
389
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
390
390
|
let interrupted = false;
|
|
391
391
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
saveDevState,
|
|
32
32
|
sessionHasMeaningfulContent,
|
|
33
33
|
setupProxy
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-3YT2DUUT.js";
|
|
35
35
|
import {
|
|
36
36
|
ToolExecutor,
|
|
37
37
|
ToolRegistry,
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
spawnAgentContext,
|
|
48
48
|
theme,
|
|
49
49
|
undoStack
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-IVTWWDWZ.js";
|
|
51
51
|
import {
|
|
52
52
|
fileCheckpoints
|
|
53
53
|
} from "./chunk-4BKXL7SM.js";
|
|
@@ -72,7 +72,7 @@ import {
|
|
|
72
72
|
SKILLS_DIR_NAME,
|
|
73
73
|
VERSION,
|
|
74
74
|
buildUserIdentityPrompt
|
|
75
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-ND3O5NQU.js";
|
|
76
76
|
|
|
77
77
|
// src/index.ts
|
|
78
78
|
import { program } from "commander";
|
|
@@ -2267,7 +2267,7 @@ ${hint}` : "")
|
|
|
2267
2267
|
usage: "/test [command|filter]",
|
|
2268
2268
|
async execute(args, ctx) {
|
|
2269
2269
|
try {
|
|
2270
|
-
const { executeTests } = await import("./run-tests-
|
|
2270
|
+
const { executeTests } = await import("./run-tests-NPRCZYN3.js");
|
|
2271
2271
|
const argStr = args.join(" ").trim();
|
|
2272
2272
|
let testArgs = {};
|
|
2273
2273
|
if (argStr) {
|
|
@@ -4120,17 +4120,28 @@ ${projectContext}`);
|
|
|
4120
4120
|
if (gitContextStr) parts.push(gitContextStr);
|
|
4121
4121
|
return parts.length > 0 ? parts.join("\n\n---\n\n") : void 0;
|
|
4122
4122
|
}
|
|
4123
|
-
/**
|
|
4123
|
+
/**
|
|
4124
|
+
* Build the system prompt split into stable (cacheable) and volatile (non-cacheable) parts.
|
|
4125
|
+
*
|
|
4126
|
+
* stable — identity, guidelines, memory, dev state, project context, skills, plan mode.
|
|
4127
|
+
* Content is session-invariant → Claude caches it across every agentic round
|
|
4128
|
+
* AND across turns, so only the first request of a session pays full input cost.
|
|
4129
|
+
* volatile — current date/time + working directory. Appended as a separate uncached block
|
|
4130
|
+
* so it never invalidates the stable cache.
|
|
4131
|
+
*
|
|
4132
|
+
* ⚠️ 注意:不使用 toLocaleDateString/toLocaleTimeString,
|
|
4133
|
+
* pkg 打包的精简 ICU 不支持 locale 参数,会回退到错误格式。
|
|
4134
|
+
*/
|
|
4124
4135
|
buildCurrentSystemPrompt() {
|
|
4125
4136
|
const now = /* @__PURE__ */ new Date();
|
|
4126
4137
|
const WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
4127
4138
|
const pad = (n) => String(n).padStart(2, "0");
|
|
4128
4139
|
const dateStr = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${WEEKDAYS[now.getDay()]}`;
|
|
4129
4140
|
const timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
|
|
4130
|
-
const dateTimeInfo = `Current date and time: ${dateStr} ${timeStr}`;
|
|
4131
4141
|
const osName = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
|
|
4132
4142
|
const shellInfo = process.platform === "win32" ? "The bash tool uses PowerShell (not cmd). Use PowerShell command syntax. Do not use Unix commands (e.g. grep, head, find, wc, date +format, etc.), use PowerShell equivalents instead (e.g. Select-String, Select-Object -First, Get-ChildItem, Measure-Object, Get-Date -Format, etc.)." : `The bash tool uses ${process.env.SHELL || "/bin/bash"}.`;
|
|
4133
|
-
const
|
|
4143
|
+
const volatile = `Current date and time: ${dateStr} ${timeStr}
|
|
4144
|
+
OS: ${osName}
|
|
4134
4145
|
${shellInfo}
|
|
4135
4146
|
Working directory: ${process.cwd()}`;
|
|
4136
4147
|
const effectiveMaxRounds = this.maxToolRoundsOverride ?? this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
|
|
@@ -4142,24 +4153,20 @@ You have a maximum of ${effectiveMaxRounds} tool call rounds per conversation tu
|
|
|
4142
4153
|
- On Windows, use PowerShell cmdlets (Invoke-RestMethod, Get-ChildItem) instead of Unix commands (curl, grep, find).
|
|
4143
4154
|
- If starting a long-running server process via bash, use background execution \u2014 do not block the tool round.
|
|
4144
4155
|
- Prioritize the most critical tasks first in case you run out of rounds.`;
|
|
4145
|
-
const
|
|
4156
|
+
const stableParts = [];
|
|
4146
4157
|
const userProfile = this.config.get("userProfile");
|
|
4147
4158
|
if (userProfile) {
|
|
4148
4159
|
const identityPrompt = buildUserIdentityPrompt(userProfile);
|
|
4149
|
-
if (identityPrompt)
|
|
4150
|
-
parts.push(identityPrompt);
|
|
4151
|
-
}
|
|
4160
|
+
if (identityPrompt) stableParts.push(identityPrompt);
|
|
4152
4161
|
}
|
|
4153
|
-
|
|
4162
|
+
stableParts.push(AGENTIC_BEHAVIOR_GUIDELINE + budgetInfo);
|
|
4154
4163
|
const memory = this.loadMemoryContent();
|
|
4155
|
-
if (memory)
|
|
4156
|
-
parts.push(`# Persistent Memory
|
|
4164
|
+
if (memory) stableParts.push(`# Persistent Memory
|
|
4157
4165
|
|
|
4158
4166
|
${memory.content}`);
|
|
4159
|
-
}
|
|
4160
4167
|
const devState = loadDevState();
|
|
4161
4168
|
if (devState) {
|
|
4162
|
-
|
|
4169
|
+
stableParts.push(
|
|
4163
4170
|
`# Development State Handoff
|
|
4164
4171
|
|
|
4165
4172
|
The following is a development state snapshot generated by the previous AI model. Please seamlessly continue the user's work based on this.
|
|
@@ -4168,25 +4175,21 @@ Important: If the snapshot mentions key reference files (e.g. templates, format
|
|
|
4168
4175
|
` + devState
|
|
4169
4176
|
);
|
|
4170
4177
|
}
|
|
4171
|
-
if (this.activeSystemPrompt)
|
|
4172
|
-
parts.push(this.activeSystemPrompt);
|
|
4173
|
-
}
|
|
4178
|
+
if (this.activeSystemPrompt) stableParts.push(this.activeSystemPrompt);
|
|
4174
4179
|
for (const { dir, content } of this.extraContextDirs) {
|
|
4175
|
-
|
|
4180
|
+
stableParts.push(`# Added Directory Context: ${dir}
|
|
4176
4181
|
|
|
4177
4182
|
${content}`);
|
|
4178
4183
|
}
|
|
4179
4184
|
const skillContent = this.skillManager?.getActivePromptContent();
|
|
4180
4185
|
if (skillContent) {
|
|
4181
4186
|
const skillName = this.skillManager.getActive().meta.name;
|
|
4182
|
-
|
|
4187
|
+
stableParts.push(`# Active Skill: ${skillName}
|
|
4183
4188
|
|
|
4184
4189
|
${skillContent}`);
|
|
4185
4190
|
}
|
|
4186
|
-
if (this.planMode)
|
|
4187
|
-
|
|
4188
|
-
}
|
|
4189
|
-
return parts.join("\n\n---\n\n");
|
|
4191
|
+
if (this.planMode) stableParts.push(PLAN_MODE_SYSTEM_ADDON);
|
|
4192
|
+
return { stable: stableParts.join("\n\n---\n\n"), volatile };
|
|
4190
4193
|
}
|
|
4191
4194
|
/**
|
|
4192
4195
|
* 上下文压缩:调用当前 provider 生成对话摘要,替换旧消息,保留最近 COMPACT_KEEP_LAST 条。
|
|
@@ -4237,10 +4240,12 @@ ${skillContent}`);
|
|
|
4237
4240
|
...session.messages,
|
|
4238
4241
|
{ role: "user", content: summaryPromptText, timestamp: /* @__PURE__ */ new Date() }
|
|
4239
4242
|
];
|
|
4243
|
+
const { stable: syStable, volatile: syVolatile } = this.buildCurrentSystemPrompt();
|
|
4240
4244
|
const response = await provider.chat({
|
|
4241
4245
|
messages: summaryMessages,
|
|
4242
4246
|
model: this.currentModel,
|
|
4243
|
-
systemPrompt:
|
|
4247
|
+
systemPrompt: syStable,
|
|
4248
|
+
systemPromptVolatile: syVolatile,
|
|
4244
4249
|
stream: false,
|
|
4245
4250
|
temperature: 0.3,
|
|
4246
4251
|
maxTokens: Math.min(modelParams.maxTokens ?? DEFAULT_MAX_TOKENS, 4096),
|
|
@@ -4302,10 +4307,12 @@ ${response.content.trim()}
|
|
|
4302
4307
|
timestamp: /* @__PURE__ */ new Date()
|
|
4303
4308
|
}
|
|
4304
4309
|
];
|
|
4310
|
+
const { stable: snStable, volatile: snVolatile } = this.buildCurrentSystemPrompt();
|
|
4305
4311
|
const response = await provider.chat({
|
|
4306
4312
|
messages: snapshotMessages,
|
|
4307
4313
|
model: this.currentModel,
|
|
4308
|
-
systemPrompt:
|
|
4314
|
+
systemPrompt: snStable,
|
|
4315
|
+
systemPromptVolatile: snVolatile,
|
|
4309
4316
|
stream: false,
|
|
4310
4317
|
temperature: 0.3,
|
|
4311
4318
|
maxTokens: Math.min(modelParams.maxTokens ?? 4096, 4096),
|
|
@@ -4779,7 +4786,8 @@ Session '${this.resumeSessionId}' not found.
|
|
|
4779
4786
|
*/
|
|
4780
4787
|
estimateConversationTokens() {
|
|
4781
4788
|
let total = 0;
|
|
4782
|
-
const
|
|
4789
|
+
const { stable: estStable, volatile: estVolatile } = this.buildCurrentSystemPrompt();
|
|
4790
|
+
const sysPrompt = [estStable, estVolatile].filter(Boolean).join("\n\n---\n\n");
|
|
4783
4791
|
if (sysPrompt) total += this.estimateTokens(sysPrompt);
|
|
4784
4792
|
const session = this.sessions.current;
|
|
4785
4793
|
if (session) {
|
|
@@ -5068,13 +5076,15 @@ Session '${this.resumeSessionId}' not found.
|
|
|
5068
5076
|
const useStreaming = this.config.get("ui").streaming;
|
|
5069
5077
|
const effectiveModel = modelOverride ?? this.currentModel;
|
|
5070
5078
|
const modelParams = this.getModelParams(effectiveModel);
|
|
5079
|
+
const { stable: chatStable, volatile: chatVolatile } = this.buildCurrentSystemPrompt();
|
|
5071
5080
|
if (useStreaming) {
|
|
5072
5081
|
const ac = this.setupStreamInterrupt();
|
|
5073
5082
|
try {
|
|
5074
5083
|
const stream = provider.chatStream({
|
|
5075
5084
|
messages,
|
|
5076
5085
|
model: effectiveModel,
|
|
5077
|
-
systemPrompt:
|
|
5086
|
+
systemPrompt: chatStable,
|
|
5087
|
+
systemPromptVolatile: chatVolatile,
|
|
5078
5088
|
stream: true,
|
|
5079
5089
|
temperature: modelParams.temperature,
|
|
5080
5090
|
maxTokens: modelParams.maxTokens,
|
|
@@ -5108,7 +5118,8 @@ Session '${this.resumeSessionId}' not found.
|
|
|
5108
5118
|
const response = await provider.chat({
|
|
5109
5119
|
messages,
|
|
5110
5120
|
model: effectiveModel,
|
|
5111
|
-
systemPrompt:
|
|
5121
|
+
systemPrompt: chatStable,
|
|
5122
|
+
systemPromptVolatile: chatVolatile,
|
|
5112
5123
|
stream: false,
|
|
5113
5124
|
temperature: modelParams.temperature,
|
|
5114
5125
|
maxTokens: modelParams.maxTokens,
|
|
@@ -5275,7 +5286,7 @@ Session '${this.resumeSessionId}' not found.
|
|
|
5275
5286
|
const maxToolRounds = this.maxToolRoundsOverride ?? this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
|
|
5276
5287
|
const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
|
|
5277
5288
|
const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : DEFAULT_AUTO_PAUSE_INTERVAL;
|
|
5278
|
-
const
|
|
5289
|
+
const { stable: toolStable, volatile: toolVolatile } = this.buildCurrentSystemPrompt();
|
|
5279
5290
|
const pauseHint = autoPauseInterval > 0 ? `
|
|
5280
5291
|
- Every ${autoPauseInterval} rounds the user will be asked whether to continue \u2014 use this as a natural checkpoint to report progress.` : "";
|
|
5281
5292
|
const roundBudgetHint = this.planMode ? `
|
|
@@ -5295,9 +5306,10 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
|
|
|
5295
5306
|
- Do NOT read the same file more than once \u2014 use the content from previous reads.
|
|
5296
5307
|
- Prioritize the most critical tasks first in case rounds run out.
|
|
5297
5308
|
- When remaining rounds are low, focus on completing the current task and summarizing.${pauseHint}`;
|
|
5298
|
-
const systemPrompt =
|
|
5309
|
+
const systemPrompt = toolStable + TOOL_CALL_REMINDER + roundBudgetHint + (mcpBudgetNote ? `
|
|
5299
5310
|
|
|
5300
5311
|
${mcpBudgetNote}` : "");
|
|
5312
|
+
const systemPromptVolatile = toolVolatile;
|
|
5301
5313
|
const modelParams = this.getModelParams(effectiveModel);
|
|
5302
5314
|
const useStreaming = this.config.get("ui").streaming;
|
|
5303
5315
|
const spinner = this.renderer.showSpinner("Thinking...");
|
|
@@ -5418,6 +5430,7 @@ ${mcpBudgetNote}` : "");
|
|
|
5418
5430
|
messages: apiMessages,
|
|
5419
5431
|
model: effectiveModel,
|
|
5420
5432
|
systemPrompt,
|
|
5433
|
+
systemPromptVolatile,
|
|
5421
5434
|
stream: false,
|
|
5422
5435
|
temperature: modelParams.temperature,
|
|
5423
5436
|
maxTokens: modelParams.maxTokens,
|
|
@@ -5573,6 +5586,7 @@ ${mcpBudgetNote}` : "");
|
|
|
5573
5586
|
messages: apiMessages,
|
|
5574
5587
|
model: effectiveModel,
|
|
5575
5588
|
systemPrompt,
|
|
5589
|
+
systemPromptVolatile,
|
|
5576
5590
|
stream: true,
|
|
5577
5591
|
temperature: modelParams.temperature,
|
|
5578
5592
|
maxTokens: modelParams.maxTokens,
|
|
@@ -5623,14 +5637,22 @@ ${mcpBudgetNote}` : "");
|
|
|
5623
5637
|
googleSearchContext.configManager = this.config;
|
|
5624
5638
|
streamToFileContext.provider = provider;
|
|
5625
5639
|
streamToFileContext.model = effectiveModel;
|
|
5626
|
-
streamToFileContext.systemPrompt = systemPrompt
|
|
5640
|
+
streamToFileContext.systemPrompt = systemPromptVolatile ? `${systemPrompt}
|
|
5641
|
+
|
|
5642
|
+
---
|
|
5643
|
+
|
|
5644
|
+
${systemPromptVolatile}` : systemPrompt;
|
|
5627
5645
|
streamToFileContext.messages = apiMessages;
|
|
5628
5646
|
streamToFileContext.extraMessages = extraMessages;
|
|
5629
5647
|
streamToFileContext.temperature = modelParams.temperature;
|
|
5630
5648
|
streamToFileContext.timeout = modelParams.timeout;
|
|
5631
5649
|
spawnAgentContext.provider = provider;
|
|
5632
5650
|
spawnAgentContext.model = effectiveModel;
|
|
5633
|
-
spawnAgentContext.systemPrompt = systemPrompt
|
|
5651
|
+
spawnAgentContext.systemPrompt = systemPromptVolatile ? `${systemPrompt}
|
|
5652
|
+
|
|
5653
|
+
---
|
|
5654
|
+
|
|
5655
|
+
${systemPromptVolatile}` : systemPrompt;
|
|
5634
5656
|
spawnAgentContext.modelParams = modelParams;
|
|
5635
5657
|
spawnAgentContext.configManager = this.config;
|
|
5636
5658
|
ToolExecutor.currentMessageIndex = session.messages.length;
|
|
@@ -5791,6 +5813,7 @@ ${mcpBudgetNote}` : "");
|
|
|
5791
5813
|
messages: apiMessages,
|
|
5792
5814
|
model: effectiveModel,
|
|
5793
5815
|
systemPrompt,
|
|
5816
|
+
systemPromptVolatile,
|
|
5794
5817
|
stream: false,
|
|
5795
5818
|
temperature: modelParams.temperature,
|
|
5796
5819
|
maxTokens: modelParams.maxTokens,
|
|
@@ -5852,10 +5875,12 @@ Tip: You can continue the conversation by asking the AI to proceed.`
|
|
|
5852
5875
|
try {
|
|
5853
5876
|
const provider = this.providers.get(this.currentProvider);
|
|
5854
5877
|
const modelParams = this.getModelParams();
|
|
5878
|
+
const { stable: ccStable, volatile: ccVolatile } = this.buildCurrentSystemPrompt();
|
|
5855
5879
|
const response = await provider.chat({
|
|
5856
5880
|
messages: [{ role: "user", content: prompt, timestamp: /* @__PURE__ */ new Date() }],
|
|
5857
5881
|
model: this.currentModel,
|
|
5858
|
-
systemPrompt:
|
|
5882
|
+
systemPrompt: ccStable,
|
|
5883
|
+
systemPromptVolatile: ccVolatile,
|
|
5859
5884
|
stream: false,
|
|
5860
5885
|
temperature: 0.3,
|
|
5861
5886
|
maxTokens: modelParams.maxTokens ?? 4096,
|
|
@@ -5910,7 +5935,11 @@ Tip: You can continue the conversation by asking the AI to proceed.`
|
|
|
5910
5935
|
getCurrentProvider: () => this.currentProvider,
|
|
5911
5936
|
getCurrentModel: () => this.currentModel,
|
|
5912
5937
|
getContextLayers: () => [...this.contextLayers],
|
|
5913
|
-
getActiveSystemPrompt: () =>
|
|
5938
|
+
getActiveSystemPrompt: () => {
|
|
5939
|
+
const { stable, volatile } = this.buildCurrentSystemPrompt();
|
|
5940
|
+
const combined = [stable, volatile].filter(Boolean).join("\n\n---\n\n");
|
|
5941
|
+
return combined || void 0;
|
|
5942
|
+
},
|
|
5914
5943
|
reloadContext: () => {
|
|
5915
5944
|
const { layers, mergedContent } = this.loadHierarchicalContext();
|
|
5916
5945
|
this.contextLayers = layers;
|
|
@@ -5963,10 +5992,12 @@ Tip: You can continue the conversation by asking the AI to proceed.`
|
|
|
5963
5992
|
chatOnce: async (prompt, options) => {
|
|
5964
5993
|
const provider = this.providers.get(this.currentProvider);
|
|
5965
5994
|
const modelParams = this.getModelParams();
|
|
5995
|
+
const { stable: coStable, volatile: coVolatile } = this.buildCurrentSystemPrompt();
|
|
5966
5996
|
const response = await provider.chat({
|
|
5967
5997
|
messages: [{ role: "user", content: prompt, timestamp: /* @__PURE__ */ new Date() }],
|
|
5968
5998
|
model: this.currentModel,
|
|
5969
|
-
systemPrompt:
|
|
5999
|
+
systemPrompt: coStable,
|
|
6000
|
+
systemPromptVolatile: coVolatile,
|
|
5970
6001
|
stream: false,
|
|
5971
6002
|
temperature: options?.temperature ?? 0.3,
|
|
5972
6003
|
maxTokens: options?.maxTokens ?? modelParams.maxTokens ?? 4096,
|
|
@@ -6108,7 +6139,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
6108
6139
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
6109
6140
|
process.exit(1);
|
|
6110
6141
|
}
|
|
6111
|
-
const { startWebServer } = await import("./server-
|
|
6142
|
+
const { startWebServer } = await import("./server-WYL3OD5N.js");
|
|
6112
6143
|
await startWebServer({ port, host: options.host });
|
|
6113
6144
|
});
|
|
6114
6145
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -6341,7 +6372,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
6341
6372
|
}),
|
|
6342
6373
|
config.get("customProviders")
|
|
6343
6374
|
);
|
|
6344
|
-
const { startHub } = await import("./hub-
|
|
6375
|
+
const { startHub } = await import("./hub-BGO4X72R.js");
|
|
6345
6376
|
await startHub(
|
|
6346
6377
|
{
|
|
6347
6378
|
topic: topic ?? "",
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
persistToolRound,
|
|
22
22
|
rebuildExtraMessages,
|
|
23
23
|
setupProxy
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-3YT2DUUT.js";
|
|
25
25
|
import {
|
|
26
26
|
AuthManager
|
|
27
27
|
} from "./chunk-BYNY5JPB.js";
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
spawnAgentContext,
|
|
43
43
|
truncateOutput,
|
|
44
44
|
undoStack
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-IVTWWDWZ.js";
|
|
46
46
|
import "./chunk-4BKXL7SM.js";
|
|
47
47
|
import {
|
|
48
48
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
SKILLS_DIR_NAME,
|
|
63
63
|
VERSION,
|
|
64
64
|
buildUserIdentityPrompt
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-ND3O5NQU.js";
|
|
66
66
|
|
|
67
67
|
// src/web/server.ts
|
|
68
68
|
import express from "express";
|
|
@@ -393,7 +393,7 @@ var ToolExecutorWeb = class _ToolExecutorWeb {
|
|
|
393
393
|
// src/core/system-prompt-builder.ts
|
|
394
394
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
395
395
|
import { join } from "path";
|
|
396
|
-
function
|
|
396
|
+
function buildSystemPromptParts(ctx) {
|
|
397
397
|
const now = /* @__PURE__ */ new Date();
|
|
398
398
|
const WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
399
399
|
const pad = (n) => String(n).padStart(2, "0");
|
|
@@ -402,28 +402,25 @@ function buildSystemPrompt(ctx) {
|
|
|
402
402
|
const dateTimeInfo = `Current date and time: ${dateStr} ${timeStr}`;
|
|
403
403
|
const osName = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
|
|
404
404
|
const shellInfo = process.platform === "win32" ? "The bash tool uses PowerShell (not cmd). Use PowerShell command syntax. Do not use Unix commands (e.g. grep, head, find, wc, date +format, etc.), use PowerShell equivalents instead (e.g. Select-String, Select-Object -First, Get-ChildItem, Measure-Object, Get-Date -Format, etc.)." : `The bash tool uses ${process.env.SHELL || "/bin/bash"}.`;
|
|
405
|
-
const
|
|
405
|
+
const volatile = `${dateTimeInfo}
|
|
406
|
+
OS: ${osName}
|
|
406
407
|
${shellInfo}
|
|
407
408
|
Working directory: ${process.cwd()}`;
|
|
408
|
-
const
|
|
409
|
+
const stableParts = [];
|
|
409
410
|
if (ctx.userProfile) {
|
|
410
411
|
const identityPrompt = buildUserIdentityPrompt(ctx.userProfile);
|
|
411
|
-
if (identityPrompt)
|
|
412
|
-
parts.push(identityPrompt);
|
|
413
|
-
}
|
|
412
|
+
if (identityPrompt) stableParts.push(identityPrompt);
|
|
414
413
|
}
|
|
415
|
-
|
|
414
|
+
stableParts.push(AGENTIC_BEHAVIOR_GUIDELINE);
|
|
416
415
|
if (ctx.configDir) {
|
|
417
416
|
const memory = loadMemoryContent(ctx.configDir);
|
|
418
|
-
if (memory)
|
|
419
|
-
parts.push(`# Persistent Memory
|
|
417
|
+
if (memory) stableParts.push(`# Persistent Memory
|
|
420
418
|
|
|
421
419
|
${memory.content}`);
|
|
422
|
-
}
|
|
423
420
|
}
|
|
424
421
|
const devState = loadDevState();
|
|
425
422
|
if (devState) {
|
|
426
|
-
|
|
423
|
+
stableParts.push(
|
|
427
424
|
`# Development State Handoff
|
|
428
425
|
|
|
429
426
|
The following is a development state snapshot generated by the previous AI model. Please seamlessly continue the user's work based on this.
|
|
@@ -432,25 +429,21 @@ Important: If the snapshot mentions key reference files (e.g. templates, format
|
|
|
432
429
|
` + devState
|
|
433
430
|
);
|
|
434
431
|
}
|
|
435
|
-
if (ctx.activeSystemPrompt)
|
|
436
|
-
parts.push(ctx.activeSystemPrompt);
|
|
437
|
-
}
|
|
432
|
+
if (ctx.activeSystemPrompt) stableParts.push(ctx.activeSystemPrompt);
|
|
438
433
|
if (ctx.extraContextDirs) {
|
|
439
434
|
for (const { dir, content } of ctx.extraContextDirs) {
|
|
440
|
-
|
|
435
|
+
stableParts.push(`# Added Directory Context: ${dir}
|
|
441
436
|
|
|
442
437
|
${content}`);
|
|
443
438
|
}
|
|
444
439
|
}
|
|
445
440
|
if (ctx.activeSkill) {
|
|
446
|
-
|
|
441
|
+
stableParts.push(`# Active Skill: ${ctx.activeSkill.name}
|
|
447
442
|
|
|
448
443
|
${ctx.activeSkill.content}`);
|
|
449
444
|
}
|
|
450
|
-
if (ctx.planMode)
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
return parts.join("\n\n---\n\n");
|
|
445
|
+
if (ctx.planMode) stableParts.push(PLAN_MODE_SYSTEM_ADDON);
|
|
446
|
+
return { stable: stableParts.join("\n\n---\n\n"), volatile };
|
|
454
447
|
}
|
|
455
448
|
function loadMemoryContent(configDir) {
|
|
456
449
|
const memoryPath = join(configDir, MEMORY_FILE_NAME);
|
|
@@ -785,10 +778,12 @@ var SessionHandler = class _SessionHandler {
|
|
|
785
778
|
const ac = new AbortController();
|
|
786
779
|
this.abortController = ac;
|
|
787
780
|
try {
|
|
781
|
+
const { stable: simplStable, volatile: simplVolatile } = this.buildSystemPrompt();
|
|
788
782
|
const stream = provider.chatStream({
|
|
789
783
|
messages,
|
|
790
784
|
model: this.currentModel,
|
|
791
|
-
systemPrompt:
|
|
785
|
+
systemPrompt: simplStable,
|
|
786
|
+
systemPromptVolatile: simplVolatile,
|
|
792
787
|
stream: true,
|
|
793
788
|
temperature: modelParams.temperature,
|
|
794
789
|
maxTokens: modelParams.maxTokens,
|
|
@@ -830,7 +825,7 @@ var SessionHandler = class _SessionHandler {
|
|
|
830
825
|
const maxToolRounds = this.config.get("maxToolRounds") ?? DEFAULT_MAX_TOOL_ROUNDS;
|
|
831
826
|
const autoPauseIntervalRaw = this.config.get("autoPauseInterval");
|
|
832
827
|
const autoPauseInterval = typeof autoPauseIntervalRaw === "number" ? autoPauseIntervalRaw : 50;
|
|
833
|
-
const
|
|
828
|
+
const { stable: toolStable, volatile: toolVolatile } = this.buildSystemPrompt();
|
|
834
829
|
const pauseHint = autoPauseInterval > 0 ? `
|
|
835
830
|
- Every ${autoPauseInterval} rounds the user will be asked whether to continue \u2014 use this as a natural checkpoint to report progress.` : "";
|
|
836
831
|
const roundBudgetHint = `
|
|
@@ -840,9 +835,10 @@ You have a maximum of ${maxToolRounds} tool call rounds for this task. Plan effi
|
|
|
840
835
|
- Prefer batch operations (e.g. global find-and-replace) over repetitive single edits.
|
|
841
836
|
- Prioritize the most critical tasks first in case rounds run out.
|
|
842
837
|
- When remaining rounds are low, focus on completing the current task and summarizing.${pauseHint}`;
|
|
843
|
-
const systemPrompt =
|
|
838
|
+
const systemPrompt = toolStable + TOOL_CALL_REMINDER + roundBudgetHint + (mcpBudgetNote ? `
|
|
844
839
|
|
|
845
840
|
${mcpBudgetNote}` : "");
|
|
841
|
+
const systemPromptVolatile = toolVolatile;
|
|
846
842
|
const modelParams = this.getModelParams();
|
|
847
843
|
const roundUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
|
|
848
844
|
const supportsStreamingTools = typeof provider.chatWithToolsStream === "function";
|
|
@@ -929,6 +925,7 @@ Too much tool output accumulated this turn. Your work so far is preserved.
|
|
|
929
925
|
messages: apiMessages,
|
|
930
926
|
model: this.currentModel,
|
|
931
927
|
systemPrompt,
|
|
928
|
+
systemPromptVolatile,
|
|
932
929
|
stream: false,
|
|
933
930
|
temperature: modelParams.temperature,
|
|
934
931
|
maxTokens: modelParams.maxTokens,
|
|
@@ -1028,7 +1025,11 @@ Details: ${errMsg.split("\n")[0]}
|
|
|
1028
1025
|
googleSearchContext.configManager = this.config;
|
|
1029
1026
|
spawnAgentContext.provider = provider;
|
|
1030
1027
|
spawnAgentContext.model = this.currentModel;
|
|
1031
|
-
spawnAgentContext.systemPrompt = systemPrompt
|
|
1028
|
+
spawnAgentContext.systemPrompt = systemPromptVolatile ? `${systemPrompt}
|
|
1029
|
+
|
|
1030
|
+
---
|
|
1031
|
+
|
|
1032
|
+
${systemPromptVolatile}` : systemPrompt;
|
|
1032
1033
|
spawnAgentContext.modelParams = modelParams;
|
|
1033
1034
|
spawnAgentContext.configManager = this.config;
|
|
1034
1035
|
ToolExecutor.currentMessageIndex = this.sessions.current?.messages.length ?? 0;
|
|
@@ -1106,6 +1107,7 @@ Details: ${errMsg.split("\n")[0]}
|
|
|
1106
1107
|
messages: apiMessages,
|
|
1107
1108
|
model: this.currentModel,
|
|
1108
1109
|
systemPrompt,
|
|
1110
|
+
systemPromptVolatile,
|
|
1109
1111
|
stream: false,
|
|
1110
1112
|
temperature: modelParams.temperature,
|
|
1111
1113
|
maxTokens: modelParams.maxTokens,
|
|
@@ -1946,7 +1948,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1946
1948
|
case "test": {
|
|
1947
1949
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
1948
1950
|
try {
|
|
1949
|
-
const { executeTests } = await import("./run-tests-
|
|
1951
|
+
const { executeTests } = await import("./run-tests-NPRCZYN3.js");
|
|
1950
1952
|
const argStr = args.join(" ").trim();
|
|
1951
1953
|
let testArgs = {};
|
|
1952
1954
|
if (argStr) {
|
|
@@ -2500,13 +2502,14 @@ Add .md files to create commands.` });
|
|
|
2500
2502
|
buildSystemPrompt() {
|
|
2501
2503
|
const skillContent = this.skillManager?.getActivePromptContent();
|
|
2502
2504
|
const activeSkill = skillContent && this.skillManager?.getActive() ? { name: this.skillManager.getActive().meta.name, content: skillContent } : void 0;
|
|
2503
|
-
|
|
2505
|
+
const { stable: baseStable, volatile } = buildSystemPromptParts({
|
|
2504
2506
|
activeSystemPrompt: this.activeSystemPrompt,
|
|
2505
2507
|
activeSkill,
|
|
2506
2508
|
planMode: this.planMode,
|
|
2507
2509
|
configDir: this.config.getConfigDir(),
|
|
2508
2510
|
userProfile: this.config.get("userProfile")
|
|
2509
2511
|
});
|
|
2512
|
+
let stable = baseStable;
|
|
2510
2513
|
if (this.addedDirs.size > 0) {
|
|
2511
2514
|
const MAX_DIR_CONTEXT = 4e4;
|
|
2512
2515
|
let dirContext = "\n\n--- Added Directory Context ---\n";
|
|
@@ -2519,9 +2522,9 @@ Add .md files to create commands.` });
|
|
|
2519
2522
|
dirContext += this.scanDirTree(dir, 2, 40) + "\n";
|
|
2520
2523
|
totalLen = dirContext.length;
|
|
2521
2524
|
}
|
|
2522
|
-
|
|
2525
|
+
stable += dirContext;
|
|
2523
2526
|
}
|
|
2524
|
-
return
|
|
2527
|
+
return { stable, volatile };
|
|
2525
2528
|
}
|
|
2526
2529
|
getModelParams() {
|
|
2527
2530
|
const allParams = this.config.get("modelParams");
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-IVTWWDWZ.js";
|
|
8
8
|
import "./chunk-4BKXL7SM.js";
|
|
9
9
|
import {
|
|
10
10
|
SUBAGENT_ALLOWED_TOOLS
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ND3O5NQU.js";
|
|
12
12
|
|
|
13
13
|
// src/hub/task-orchestrator.ts
|
|
14
14
|
import { createInterface } from "readline";
|