@amical/aiagent-sdk 0.1.10 → 0.1.12
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/{WebMemoryManager-DXxLUDBB.d.ts → WebMemoryManager-BZNjinHF.d.ts} +10 -3
- package/dist/{WebMemoryManager-Bk19j8i6.d.mts → WebMemoryManager-DpaY-f0l.d.mts} +10 -3
- package/dist/index.d.mts +71 -4
- package/dist/index.d.ts +71 -4
- package/dist/index.js +223 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +220 -34
- package/dist/index.mjs.map +1 -1
- package/dist/runtime/nodeRuntime.d.mts +12 -3
- package/dist/runtime/nodeRuntime.d.ts +12 -3
- package/dist/runtime/nodeRuntime.js +357 -47
- package/dist/runtime/nodeRuntime.js.map +1 -1
- package/dist/runtime/nodeRuntime.mjs +354 -48
- package/dist/runtime/nodeRuntime.mjs.map +1 -1
- package/dist/runtime/webRuntime.d.mts +3 -2
- package/dist/runtime/webRuntime.d.ts +3 -2
- package/dist/runtime/webRuntime.js +357 -47
- package/dist/runtime/webRuntime.js.map +1 -1
- package/dist/runtime/webRuntime.mjs +354 -48
- package/dist/runtime/webRuntime.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -135,9 +135,11 @@ return (
|
|
|
135
135
|
},
|
|
136
136
|
{
|
|
137
137
|
name: "agent_read_file" /* FILE_READ */,
|
|
138
|
-
description: "Read the contents of a specific file.",
|
|
138
|
+
description: "Read the contents of a specific file. The returned content includes line numbers (e.g. '10|const x = 1;') so you can reference exact lines when editing.",
|
|
139
139
|
params: {
|
|
140
|
-
path: "(required) The absolute or relative file path to read."
|
|
140
|
+
path: "(required) The absolute or relative file path to read.",
|
|
141
|
+
start_line: "(optional) The 1-based line number to start reading from (inclusive). Defaults to 1.",
|
|
142
|
+
end_line: "(optional) The 1-based line number to stop reading at (inclusive). Defaults to the last line."
|
|
141
143
|
}
|
|
142
144
|
},
|
|
143
145
|
{
|
|
@@ -484,6 +486,44 @@ var toolParamNames = [
|
|
|
484
486
|
"start_line",
|
|
485
487
|
"end_line"
|
|
486
488
|
];
|
|
489
|
+
|
|
490
|
+
// src/shared/custom-tools.ts
|
|
491
|
+
function isCustomToolGroup(tool) {
|
|
492
|
+
return Array.isArray(tool.children) && tool.children.length > 0;
|
|
493
|
+
}
|
|
494
|
+
function collectCustomToolNames(tools) {
|
|
495
|
+
const names = [];
|
|
496
|
+
const visit = (list) => {
|
|
497
|
+
for (const tool of list) {
|
|
498
|
+
names.push(tool.name);
|
|
499
|
+
if (Array.isArray(tool.children) && tool.children.length > 0) {
|
|
500
|
+
visit(tool.children);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
visit(tools);
|
|
505
|
+
return names;
|
|
506
|
+
}
|
|
507
|
+
function renderCustomToolPrompt(tool, cwd = "") {
|
|
508
|
+
const params = tool.params ? Object.entries(tool.params) : [];
|
|
509
|
+
const replaceCwd = (text) => String(text).replaceAll("${context.cwd}", cwd);
|
|
510
|
+
const paramLines = params.map(([name, description]) => `- ${name}: ${replaceCwd(description)}`).join("\n");
|
|
511
|
+
const usageLines = params.map(([name, description]) => `<${name}>${replaceCwd(description)}</${name}>`).join("\n");
|
|
512
|
+
return `### ${tool.name}
|
|
513
|
+
Description: ${tool.description}
|
|
514
|
+
Parameters:
|
|
515
|
+
${paramLines}
|
|
516
|
+
- task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
|
|
517
|
+
Usage:
|
|
518
|
+
<${tool.name}>
|
|
519
|
+
${usageLines}
|
|
520
|
+
<task_progress>Checklist here (optional)</task_progress>
|
|
521
|
+
</${tool.name}>
|
|
522
|
+
`;
|
|
523
|
+
}
|
|
524
|
+
function renderCustomToolsPrompt(tools, cwd = "") {
|
|
525
|
+
return tools.map((tool) => renderCustomToolPrompt(tool, cwd)).join("\n");
|
|
526
|
+
}
|
|
487
527
|
var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
488
528
|
"txt",
|
|
489
529
|
"md",
|
|
@@ -542,7 +582,17 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
542
582
|
"dockerfile",
|
|
543
583
|
"makefile",
|
|
544
584
|
"properties",
|
|
545
|
-
"lock"
|
|
585
|
+
"lock",
|
|
586
|
+
"node-version",
|
|
587
|
+
"nvmrc",
|
|
588
|
+
"editorconfig",
|
|
589
|
+
"prettierrc",
|
|
590
|
+
"eslintrc",
|
|
591
|
+
"babelrc",
|
|
592
|
+
"stylelintrc",
|
|
593
|
+
"huskyrc",
|
|
594
|
+
"commitlintrc",
|
|
595
|
+
"lintstagedrc"
|
|
546
596
|
]);
|
|
547
597
|
var AgentState = /* @__PURE__ */ ((AgentState2) => {
|
|
548
598
|
AgentState2["IDLE"] = "idle";
|
|
@@ -567,9 +617,19 @@ var ClineAgent = class {
|
|
|
567
617
|
skillDir;
|
|
568
618
|
rulesDir;
|
|
569
619
|
customTools;
|
|
620
|
+
/** 当前可用(已展开集合)的顶层工具列表,传给 PromptManager 渲染 */
|
|
621
|
+
activeCustomTools;
|
|
622
|
+
/** 所有已知工具名(含未激活 children),便于查找定义 */
|
|
623
|
+
customToolsByName;
|
|
570
624
|
customToolNames;
|
|
571
625
|
allowDefaultTools;
|
|
572
626
|
customInstructions;
|
|
627
|
+
/** 已激活的工具集入口名 */
|
|
628
|
+
activatedGroups = /* @__PURE__ */ new Set();
|
|
629
|
+
/** activeCustomTools 变更版本号,用于 Prompt 缓存失效 */
|
|
630
|
+
activeToolsVersion = 0;
|
|
631
|
+
/** 缓存 sysPrompt 时记录的版本号 */
|
|
632
|
+
promptActiveToolsVersion = -1;
|
|
573
633
|
sysPrompt;
|
|
574
634
|
noUseTollSysPrompt;
|
|
575
635
|
// 简单的事件回调系统,供宿主环境注册以监听 Agent 内部事件
|
|
@@ -589,33 +649,47 @@ var ClineAgent = class {
|
|
|
589
649
|
this.allowDefaultTools.push("agent_attempt_completion" /* ATTEMPT */);
|
|
590
650
|
}
|
|
591
651
|
this.customInstructions = options.customInstructions;
|
|
652
|
+
this.customToolsByName = /* @__PURE__ */ new Map();
|
|
653
|
+
this.activeCustomTools = [...this.customTools];
|
|
654
|
+
this.customToolNames = new Set(collectCustomToolNames(this.customTools));
|
|
592
655
|
if (this.customTools.length > 0) {
|
|
593
|
-
const dynamicToolNames = /* @__PURE__ */ new Set();
|
|
594
656
|
const customToolParams = /* @__PURE__ */ new Set();
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
657
|
+
const visit = (tools) => {
|
|
658
|
+
for (const customTool of tools) {
|
|
659
|
+
this.customToolsByName.set(customTool.name, customTool);
|
|
660
|
+
if (customTool.params) {
|
|
661
|
+
for (const key in customTool.params) {
|
|
662
|
+
if (!Object.hasOwn(customTool.params, key)) continue;
|
|
663
|
+
customToolParams.add(key);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
if (Array.isArray(customTool.children) && customTool.children.length > 0) {
|
|
667
|
+
visit(customTool.children);
|
|
601
668
|
}
|
|
602
669
|
}
|
|
603
|
-
}
|
|
670
|
+
};
|
|
671
|
+
visit(this.customTools);
|
|
604
672
|
const defaultToolNames = Object.values(AgentDefaultTool);
|
|
605
|
-
const overlappingTools = Array.from(
|
|
673
|
+
const overlappingTools = Array.from(this.customToolNames).filter((name) => defaultToolNames.includes(name));
|
|
606
674
|
if (overlappingTools.length > 0) {
|
|
607
675
|
throw new Error(`Custom tools and default tools have overlapping names: ${overlappingTools.join(", ")}`);
|
|
608
676
|
}
|
|
609
|
-
if (
|
|
610
|
-
setDynamicToolUseNames("customUser", Array.from(
|
|
677
|
+
if (this.customToolNames.size > 0) {
|
|
678
|
+
setDynamicToolUseNames("customUser", Array.from(this.customToolNames));
|
|
611
679
|
}
|
|
612
680
|
if (customToolParams.size > 0) {
|
|
613
681
|
this.allToolParamNames = Array.from(/* @__PURE__ */ new Set([...this.allToolParamNames, ...Array.from(customToolParams)]));
|
|
614
682
|
}
|
|
615
683
|
}
|
|
616
|
-
this.customToolNames = new Set(this.customTools.map((t) => t.name));
|
|
617
684
|
this.skillDir = this.runtime.path.join(this.runtime.env.cwd(), ".agents/skills");
|
|
618
685
|
this.rulesDir = this.runtime.path.join(this.runtime.env.cwd(), ".agents/rules");
|
|
686
|
+
const originalOnCondense = this.memory.onCondense;
|
|
687
|
+
this.memory.onCondense = async () => {
|
|
688
|
+
this.api.clearConversationId();
|
|
689
|
+
if (originalOnCondense) {
|
|
690
|
+
await originalOnCondense();
|
|
691
|
+
}
|
|
692
|
+
};
|
|
619
693
|
}
|
|
620
694
|
setState(newState) {
|
|
621
695
|
this.state = newState;
|
|
@@ -637,10 +711,11 @@ var ClineAgent = class {
|
|
|
637
711
|
shell: this.runtime.env.shell(),
|
|
638
712
|
skills: await this.getSkills(),
|
|
639
713
|
agentRules: await this.getAgentRules(),
|
|
640
|
-
customTools: this.
|
|
714
|
+
customTools: this.activeCustomTools,
|
|
641
715
|
allowDefaultTools: this.allowDefaultTools,
|
|
642
716
|
customInstructions: this.customInstructions
|
|
643
717
|
};
|
|
718
|
+
this.promptActiveToolsVersion = this.activeToolsVersion;
|
|
644
719
|
this.sysPrompt = await this.promptManager.getSystemPrompt(context);
|
|
645
720
|
this.noUseTollSysPrompt = this.promptManager.getNoUseToolInstructions(context);
|
|
646
721
|
return { sysPrompt: this.sysPrompt, noUseTollSysPrompt: this.noUseTollSysPrompt };
|
|
@@ -672,16 +747,6 @@ var ClineAgent = class {
|
|
|
672
747
|
async getAgentRules() {
|
|
673
748
|
const cwd = this.runtime.env.cwd();
|
|
674
749
|
const rules = [];
|
|
675
|
-
const agentsMdPath = this.runtime.path.join(cwd, "Agents.md");
|
|
676
|
-
try {
|
|
677
|
-
if (await this.runtime.fs.fileExists(agentsMdPath)) {
|
|
678
|
-
const content = await this.runtime.fs.readFile(agentsMdPath);
|
|
679
|
-
if (content && content.trim()) {
|
|
680
|
-
rules.push(content.trim());
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
} catch (e) {
|
|
684
|
-
}
|
|
685
750
|
try {
|
|
686
751
|
if (await this.runtime.fs.fileExists(this.rulesDir)) {
|
|
687
752
|
const files = await this.runtime.fs.readDir(this.rulesDir);
|
|
@@ -707,6 +772,28 @@ ${content.trim()}\`\`\``);
|
|
|
707
772
|
}
|
|
708
773
|
} catch (e) {
|
|
709
774
|
}
|
|
775
|
+
try {
|
|
776
|
+
const getAgentsMdPath = async () => {
|
|
777
|
+
const agentsMdPathList = [
|
|
778
|
+
this.runtime.path.join(cwd, "AGENTS.md"),
|
|
779
|
+
this.runtime.path.join(cwd, "Agents.md"),
|
|
780
|
+
this.runtime.path.join(cwd, "agents.md")
|
|
781
|
+
];
|
|
782
|
+
for (const item of agentsMdPathList) {
|
|
783
|
+
if (await this.runtime.fs.fileExists(item)) {
|
|
784
|
+
return item;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
const agentsMdPath = await getAgentsMdPath();
|
|
789
|
+
if (agentsMdPath) {
|
|
790
|
+
const content = await this.runtime.fs.readFile(agentsMdPath);
|
|
791
|
+
if (content && content.trim()) {
|
|
792
|
+
rules.push(content.trim());
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
} catch (e) {
|
|
796
|
+
}
|
|
710
797
|
return rules.join("\n---\n");
|
|
711
798
|
}
|
|
712
799
|
async getSkills() {
|
|
@@ -749,9 +836,9 @@ ${content.trim()}\`\`\``);
|
|
|
749
836
|
* 核心任务循环
|
|
750
837
|
*/
|
|
751
838
|
async loop() {
|
|
752
|
-
const {
|
|
753
|
-
const { customTools, customToolNames } = this;
|
|
839
|
+
const { customToolsByName, customToolNames } = this;
|
|
754
840
|
while (this.state === "thinking" /* THINKING */) {
|
|
841
|
+
const { sysPrompt, noUseTollSysPrompt } = await this.getCurrentPrompt();
|
|
755
842
|
this.loopCount++;
|
|
756
843
|
if (this.loopCount > this.maxLoopCount) {
|
|
757
844
|
await this.memory.appendMessage({
|
|
@@ -796,7 +883,33 @@ ${content.trim()}\`\`\``);
|
|
|
796
883
|
}
|
|
797
884
|
}
|
|
798
885
|
if (customToolNames.has(tool.name)) {
|
|
799
|
-
const customTool =
|
|
886
|
+
const customTool = customToolsByName.get(tool.name);
|
|
887
|
+
if (!customTool) {
|
|
888
|
+
toolResults.push({ tool, result: `Error: Custom tool "${tool.name}" not found.` });
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
891
|
+
if (isCustomToolGroup(customTool)) {
|
|
892
|
+
let groupResult;
|
|
893
|
+
if (customTool.onCall) {
|
|
894
|
+
try {
|
|
895
|
+
groupResult = await customTool.onCall(tool.params);
|
|
896
|
+
} catch (error) {
|
|
897
|
+
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
const childrenPrompt = this.activateCustomToolGroup(customTool);
|
|
902
|
+
const finalPrompt = `# New Available Tools
|
|
903
|
+
|
|
904
|
+
The following are instructions for using the new available tools.
|
|
905
|
+
|
|
906
|
+
${childrenPrompt}`;
|
|
907
|
+
const promptBlock = groupResult !== void 0 ? `${typeof groupResult === "string" ? groupResult : JSON.stringify(groupResult)}
|
|
908
|
+
|
|
909
|
+
${finalPrompt}` : finalPrompt;
|
|
910
|
+
toolResults.push({ tool, result: promptBlock });
|
|
911
|
+
continue;
|
|
912
|
+
}
|
|
800
913
|
if (customTool.onCall) {
|
|
801
914
|
try {
|
|
802
915
|
const res = await customTool.onCall(tool.params);
|
|
@@ -818,7 +931,8 @@ ${content.trim()}\`\`\``);
|
|
|
818
931
|
if (path2) {
|
|
819
932
|
try {
|
|
820
933
|
const content = await this.runtime.fs.readFile(path2);
|
|
821
|
-
|
|
934
|
+
const result = this.formatFileContentWithLineNumbers(content, tool.params.start_line, tool.params.end_line);
|
|
935
|
+
toolResults.push({ tool, result });
|
|
822
936
|
} catch (error) {
|
|
823
937
|
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
824
938
|
}
|
|
@@ -831,6 +945,8 @@ ${content.trim()}\`\`\``);
|
|
|
831
945
|
const result = await this.runtime.terminal.execCommand(cmd);
|
|
832
946
|
toolResults.push({ tool, result: `STDOUT: ${result.stdout}
|
|
833
947
|
STDERR: ${result.stderr}` });
|
|
948
|
+
} else {
|
|
949
|
+
toolResults.push({ tool, result: "Error: No command provided." });
|
|
834
950
|
}
|
|
835
951
|
} else if (tool.name === "agent_write_to_file" /* FILE_NEW */) {
|
|
836
952
|
const path2 = tool.params.path;
|
|
@@ -842,12 +958,18 @@ STDERR: ${result.stderr}` });
|
|
|
842
958
|
} catch (error) {
|
|
843
959
|
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
844
960
|
}
|
|
961
|
+
} else {
|
|
962
|
+
toolResults.push({ tool, result: "Error: Missing path or content." });
|
|
845
963
|
}
|
|
846
964
|
} else if (tool.name === "agent_list_files" /* LIST_FILES */) {
|
|
847
965
|
const path2 = tool.params.path || this.runtime.env.cwd();
|
|
966
|
+
const recursive = tool.params.recursive === "true";
|
|
848
967
|
try {
|
|
849
|
-
const files = await this.runtime.fs.readDir(path2);
|
|
850
|
-
toolResults.push({
|
|
968
|
+
const files = await this.runtime.fs.readDir(path2, { recursive });
|
|
969
|
+
toolResults.push({
|
|
970
|
+
tool,
|
|
971
|
+
result: files.map((f) => `${f.isDirectory() ? "[DIR] " : " "}${f.name}${f.isDirectory() ? "/" : ""}`).join("\n")
|
|
972
|
+
});
|
|
851
973
|
} catch (e) {
|
|
852
974
|
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
853
975
|
}
|
|
@@ -987,10 +1109,15 @@ STDERR: ${result.stderr}` });
|
|
|
987
1109
|
try {
|
|
988
1110
|
const res = await fetch(url);
|
|
989
1111
|
const text = await res.text();
|
|
990
|
-
|
|
1112
|
+
const MAX_LEN = 8e3;
|
|
1113
|
+
const truncated = text.length > MAX_LEN ? `${text.substring(0, MAX_LEN)}
|
|
1114
|
+
...[truncated ${text.length - MAX_LEN} chars]` : text;
|
|
1115
|
+
toolResults.push({ tool, result: truncated });
|
|
991
1116
|
} catch (e) {
|
|
992
1117
|
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
993
1118
|
}
|
|
1119
|
+
} else {
|
|
1120
|
+
toolResults.push({ tool, result: "Error: No url provided." });
|
|
994
1121
|
}
|
|
995
1122
|
} else if (tool.name === "agent_focus_chain" /* TODO */) {
|
|
996
1123
|
toolResults.push({ tool, result: `Focus chain updated successfully.` });
|
|
@@ -1057,6 +1184,65 @@ ${this.promptManager.getUsedToolResultInstructions()}`;
|
|
|
1057
1184
|
}
|
|
1058
1185
|
}
|
|
1059
1186
|
}
|
|
1187
|
+
/**
|
|
1188
|
+
* 激活自定义工具集:将入口的 children 注册到活跃工具列表并返回渲染后的 prompt。
|
|
1189
|
+
*/
|
|
1190
|
+
activateCustomToolGroup(group) {
|
|
1191
|
+
if (this.activatedGroups.has(group.name)) {
|
|
1192
|
+
return renderCustomToolsPrompt(group.children || [], this.runtime.env.cwd());
|
|
1193
|
+
}
|
|
1194
|
+
this.activatedGroups.add(group.name);
|
|
1195
|
+
this.activeCustomTools = [...this.activeCustomTools, ...group.children || []];
|
|
1196
|
+
this.activeToolsVersion++;
|
|
1197
|
+
return renderCustomToolsPrompt(group.children || [], this.runtime.env.cwd());
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* 检查自上次获取 prompt 后是否有工具集被激活,若有则刷新缓存。
|
|
1201
|
+
*/
|
|
1202
|
+
async getCurrentPrompt() {
|
|
1203
|
+
if (this.promptActiveToolsVersion !== this.activeToolsVersion) {
|
|
1204
|
+
return this.getPrompt(true);
|
|
1205
|
+
}
|
|
1206
|
+
return this.getPrompt();
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* 将文件内容格式化为带行号文本(模拟编辑器行号显示),
|
|
1210
|
+
* 支持通过 start_line / end_line 截取读取范围。
|
|
1211
|
+
*/
|
|
1212
|
+
formatFileContentWithLineNumbers(content, startLineParam, endLineParam) {
|
|
1213
|
+
const lines = content.split(/\r?\n/);
|
|
1214
|
+
const normalized = lines.length > 0 && lines[lines.length - 1] === "" ? lines.slice(0, -1) : lines;
|
|
1215
|
+
const total = normalized.length;
|
|
1216
|
+
if (total === 0) {
|
|
1217
|
+
return "1|(empty file)";
|
|
1218
|
+
}
|
|
1219
|
+
const parseLine = (val, fallback) => {
|
|
1220
|
+
if (val === void 0 || val === null || val.trim() === "") {
|
|
1221
|
+
return fallback;
|
|
1222
|
+
}
|
|
1223
|
+
const num = Number.parseInt(val, 10);
|
|
1224
|
+
if (!Number.isFinite(num) || num <= 0) {
|
|
1225
|
+
return fallback;
|
|
1226
|
+
}
|
|
1227
|
+
return num;
|
|
1228
|
+
};
|
|
1229
|
+
const start = parseLine(startLineParam, 1);
|
|
1230
|
+
const end = parseLine(endLineParam, total);
|
|
1231
|
+
if (start > end) {
|
|
1232
|
+
return `Error: start_line (${start}) cannot be greater than end_line (${end}).`;
|
|
1233
|
+
}
|
|
1234
|
+
if (start > total) {
|
|
1235
|
+
return `Error: start_line (${start}) is out of range. The file has ${total} lines.`;
|
|
1236
|
+
}
|
|
1237
|
+
const from = Math.max(1, start);
|
|
1238
|
+
const to = Math.min(total, end);
|
|
1239
|
+
const width = String(total).length;
|
|
1240
|
+
const selected = normalized.slice(from - 1, to);
|
|
1241
|
+
const numbered = selected.map((line, idx) => `${String(from + idx).padStart(width)}|${line}`).join("\n");
|
|
1242
|
+
const header = from === 1 && to === total ? `1-${total}/${total}` : `${from}-${to}/${total}`;
|
|
1243
|
+
return `${header}
|
|
1244
|
+
${numbered}`;
|
|
1245
|
+
}
|
|
1060
1246
|
/**
|
|
1061
1247
|
* 简单的 Diff 解析与替换辅助方法
|
|
1062
1248
|
*/
|
|
@@ -1306,6 +1492,9 @@ var WebApiHandler = class {
|
|
|
1306
1492
|
};
|
|
1307
1493
|
}
|
|
1308
1494
|
}
|
|
1495
|
+
clearConversationId() {
|
|
1496
|
+
this.conversationId = void 0;
|
|
1497
|
+
}
|
|
1309
1498
|
};
|
|
1310
1499
|
|
|
1311
1500
|
// src/prompt/WebPromptManager.ts
|
|
@@ -1418,10 +1607,11 @@ ${context.customInstructions}
|
|
|
1418
1607
|
if (allowDefaultTools) {
|
|
1419
1608
|
useDefaultTools = defaultTools.filter((tool) => allowDefaultTools.includes(tool.name));
|
|
1420
1609
|
}
|
|
1610
|
+
const renderedCustomTools = renderCustomToolsPrompt(customTools, context.cwd);
|
|
1421
1611
|
return `# Available Tools
|
|
1422
1612
|
You can use the following tools by formatting your output as XML blocks. Only use ONE tool per message, and ALWAYS wait for the user's response with the execution result before proceeding.
|
|
1423
1613
|
|
|
1424
|
-
${[...useDefaultTools
|
|
1614
|
+
${[...useDefaultTools].map((tool) => {
|
|
1425
1615
|
return `### ${tool.name}
|
|
1426
1616
|
Description: ${tool.description}
|
|
1427
1617
|
Parameters:
|
|
@@ -1434,6 +1624,8 @@ ${Object.entries(tool.params).map(([name, description]) => `<${name}>${descripti
|
|
|
1434
1624
|
</${tool.name}>
|
|
1435
1625
|
`;
|
|
1436
1626
|
}).join("\n")}
|
|
1627
|
+
${renderedCustomTools ? `
|
|
1628
|
+
${renderedCustomTools}` : ""}
|
|
1437
1629
|
|
|
1438
1630
|
# Tool Use Examples
|
|
1439
1631
|
${useDefaultTools.filter((item) => Boolean(item.example)).map((item) => item.example).join("\n")}}
|
|
@@ -1608,16 +1800,127 @@ ${this?.getEnvironmentInfoPrompt?.(context)}`;
|
|
|
1608
1800
|
}
|
|
1609
1801
|
};
|
|
1610
1802
|
|
|
1803
|
+
// src/memory/condense.ts
|
|
1804
|
+
var TOOL_RESULT_CONTENT_REGEX = /<tool_call_result[^>]*>[\s\S]*?<\/tool_call_result>/;
|
|
1805
|
+
var EXPIRED_MARKER_TEXT = "[The result has expired and has been removed from the context]";
|
|
1806
|
+
var ATTEMPT_TOOL_NAME = "agent_attempt_completion" /* ATTEMPT */;
|
|
1807
|
+
function getContentString(content) {
|
|
1808
|
+
return typeof content === "string" ? content : "";
|
|
1809
|
+
}
|
|
1810
|
+
function isToolResultMessage(content) {
|
|
1811
|
+
if (typeof content !== "string") {
|
|
1812
|
+
return false;
|
|
1813
|
+
}
|
|
1814
|
+
return TOOL_RESULT_CONTENT_REGEX.test(content);
|
|
1815
|
+
}
|
|
1816
|
+
function isAttemptCallMessage(message) {
|
|
1817
|
+
if (message.role !== "assistant") {
|
|
1818
|
+
return false;
|
|
1819
|
+
}
|
|
1820
|
+
const content = getContentString(message.content);
|
|
1821
|
+
return new RegExp(`<${ATTEMPT_TOOL_NAME}[\\s>]`).test(content) || new RegExp(`<name>\\s*${ATTEMPT_TOOL_NAME}\\s*</name>`).test(content);
|
|
1822
|
+
}
|
|
1823
|
+
function markExpiredToolResult(content) {
|
|
1824
|
+
if (typeof content !== "string") {
|
|
1825
|
+
return content;
|
|
1826
|
+
}
|
|
1827
|
+
return content.replace(
|
|
1828
|
+
/(<tool_call_result[^>]*tool_name="([^"]*)"[^>]*>)[\s\S]*?(<\/tool_call_result>)/g,
|
|
1829
|
+
(_match, openTag, _toolName, closeTag) => {
|
|
1830
|
+
return `${openTag}
|
|
1831
|
+
${EXPIRED_MARKER_TEXT}
|
|
1832
|
+
${closeTag}`;
|
|
1833
|
+
}
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1836
|
+
function splitConversations(messages) {
|
|
1837
|
+
const conversations = [];
|
|
1838
|
+
let current = [];
|
|
1839
|
+
for (const message of messages) {
|
|
1840
|
+
current.push(message);
|
|
1841
|
+
if (isAttemptCallMessage(message)) {
|
|
1842
|
+
conversations.push(current);
|
|
1843
|
+
current = [];
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
if (current.length > 0) {
|
|
1847
|
+
conversations.push(current);
|
|
1848
|
+
}
|
|
1849
|
+
return conversations;
|
|
1850
|
+
}
|
|
1851
|
+
function isCompleteConversation(conversation) {
|
|
1852
|
+
if (conversation.length === 0) {
|
|
1853
|
+
return false;
|
|
1854
|
+
}
|
|
1855
|
+
return isAttemptCallMessage(conversation[conversation.length - 1]);
|
|
1856
|
+
}
|
|
1857
|
+
function compressCompleteConversation(conversation) {
|
|
1858
|
+
if (conversation.length <= 2) {
|
|
1859
|
+
return conversation;
|
|
1860
|
+
}
|
|
1861
|
+
const head = conversation[0];
|
|
1862
|
+
const tail = conversation[conversation.length - 1];
|
|
1863
|
+
const mergedMarker = {
|
|
1864
|
+
role: "user",
|
|
1865
|
+
content: "[History Condensed] The intermediate tool calls and tool call results of this conversation have been merged. Please refer to the `agent_attempt_completion` summary below for the final conclusion."
|
|
1866
|
+
};
|
|
1867
|
+
return [head, mergedMarker, tail];
|
|
1868
|
+
}
|
|
1869
|
+
function markExpiredFirstHalf(messages) {
|
|
1870
|
+
const half = Math.floor(messages.length / 2);
|
|
1871
|
+
return messages.map((message, index) => {
|
|
1872
|
+
if (index < half && isToolResultMessage(message.content)) {
|
|
1873
|
+
return { ...message, content: markExpiredToolResult(message.content) };
|
|
1874
|
+
}
|
|
1875
|
+
return message;
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
function condenseMessages(messages, maxMessages) {
|
|
1879
|
+
if (messages.length <= maxMessages) {
|
|
1880
|
+
return messages;
|
|
1881
|
+
}
|
|
1882
|
+
const systemMessages = messages.filter((message) => message.role === "system");
|
|
1883
|
+
const restMessages = messages.filter((message) => message.role !== "system");
|
|
1884
|
+
const conversations = splitConversations(restMessages);
|
|
1885
|
+
const completeIndices = [];
|
|
1886
|
+
for (let i = 0; i < conversations.length; i++) {
|
|
1887
|
+
if (isCompleteConversation(conversations[i])) {
|
|
1888
|
+
completeIndices.push(i);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
const currentLength = () => systemMessages.length + conversations.reduce((acc, c) => acc + c.length, 0);
|
|
1892
|
+
if (completeIndices.length >= 2) {
|
|
1893
|
+
const lastCompleteIndex = completeIndices[completeIndices.length - 1];
|
|
1894
|
+
for (const idx of completeIndices) {
|
|
1895
|
+
if (idx === lastCompleteIndex) {
|
|
1896
|
+
break;
|
|
1897
|
+
}
|
|
1898
|
+
conversations[idx] = compressCompleteConversation(conversations[idx]);
|
|
1899
|
+
if (currentLength() <= maxMessages) {
|
|
1900
|
+
break;
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
let condensed = conversations.flat();
|
|
1905
|
+
condensed = markExpiredFirstHalf(condensed);
|
|
1906
|
+
if (systemMessages.length + condensed.length > maxMessages) {
|
|
1907
|
+
const nonSystemBudget = Math.max(0, maxMessages - systemMessages.length);
|
|
1908
|
+
condensed = condensed.slice(-nonSystemBudget);
|
|
1909
|
+
}
|
|
1910
|
+
return [...systemMessages, ...condensed];
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1611
1913
|
// src/memory/WebMemoryManager.ts
|
|
1612
1914
|
var WebMemoryManager = class {
|
|
1613
1915
|
storageKey;
|
|
1614
1916
|
maxMessages;
|
|
1615
1917
|
inMemoryFallback = [];
|
|
1918
|
+
onCondense;
|
|
1616
1919
|
/**
|
|
1617
1920
|
* @param storageKey SessionStorage 中的键名
|
|
1618
|
-
* @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认
|
|
1921
|
+
* @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认 200)
|
|
1619
1922
|
*/
|
|
1620
|
-
constructor(storageKey = "cline_agent_memory", maxMessages =
|
|
1923
|
+
constructor(storageKey = "cline_agent_memory", maxMessages = 200) {
|
|
1621
1924
|
this.storageKey = storageKey;
|
|
1622
1925
|
this.maxMessages = maxMessages;
|
|
1623
1926
|
}
|
|
@@ -1658,19 +1961,22 @@ var WebMemoryManager = class {
|
|
|
1658
1961
|
this.writeMessages(messages);
|
|
1659
1962
|
await this.condenseHistory();
|
|
1660
1963
|
}
|
|
1964
|
+
/**
|
|
1965
|
+
* 压缩历史:
|
|
1966
|
+
* - system 消息保持不变
|
|
1967
|
+
* - 以 agent_attempt_completion 为边界切分完整对话,仅合并“后面还有其他完整对话”的旧完整对话
|
|
1968
|
+
* - 其余需要压缩的场景下,对前半段工具调用结果标记为“结果已过期”
|
|
1969
|
+
* - 同时触发 onCondense 回调(宿主可据此清空 dify conversationId 新开窗口)
|
|
1970
|
+
*/
|
|
1661
1971
|
async condenseHistory() {
|
|
1662
1972
|
const messages = this.readMessages();
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
} else {
|
|
1671
|
-
condensed = messages.slice(sliceCount);
|
|
1672
|
-
}
|
|
1673
|
-
this.writeMessages(condensed);
|
|
1973
|
+
const condensed = condenseMessages(messages, this.maxMessages);
|
|
1974
|
+
if (condensed === messages) {
|
|
1975
|
+
return Promise.resolve();
|
|
1976
|
+
}
|
|
1977
|
+
this.writeMessages(condensed);
|
|
1978
|
+
if (this.onCondense) {
|
|
1979
|
+
await this.onCondense();
|
|
1674
1980
|
}
|
|
1675
1981
|
return Promise.resolve();
|
|
1676
1982
|
}
|
|
@@ -1789,6 +2095,6 @@ function createWebAgent(options) {
|
|
|
1789
2095
|
return agent;
|
|
1790
2096
|
}
|
|
1791
2097
|
|
|
1792
|
-
export { AgentDefaultTool, AgentState, ClineAgent, DefaultWebRuntime, READ_ONLY_TOOLS, TOOL_RESULT_BLOCK, VERSION, WebApiHandler, WebMcpManager, WebMemoryManager, WebPromptManager, createWebAgent, defaultTools, getToolUseNames, parseAssistantMessageV2, setDynamicToolUseNames, toolParamNames, toolUseNames };
|
|
2098
|
+
export { AgentDefaultTool, AgentState, ClineAgent, DefaultWebRuntime, READ_ONLY_TOOLS, TOOL_RESULT_BLOCK, VERSION, WebApiHandler, WebMcpManager, WebMemoryManager, WebPromptManager, collectCustomToolNames, createWebAgent, defaultTools, getToolUseNames, isCustomToolGroup, parseAssistantMessageV2, renderCustomToolPrompt, renderCustomToolsPrompt, setDynamicToolUseNames, toolParamNames, toolUseNames };
|
|
1793
2099
|
//# sourceMappingURL=webRuntime.mjs.map
|
|
1794
2100
|
//# sourceMappingURL=webRuntime.mjs.map
|