@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IApiHandler, ApiProviderInfo, AgentMessage, ApiStreamChunk, IPromptManager, IMcpManager, McpServerInfo, McpTool, McpToolCallResult, IAgentRuntime, ClineAgent } from '../index.mjs';
|
|
2
|
-
export { AgentDefaultTool, AgentOptions, AgentState, AssistantMessageContent, IMemoryManager, READ_ONLY_TOOLS, ReasoningStreamContent, TOOL_RESULT_BLOCK, TextStreamContent, ToolParamName, ToolUse, VERSION, defaultTools, getToolUseNames, parseAssistantMessageV2, setDynamicToolUseNames, toolParamNames, toolUseNames } from '../index.mjs';
|
|
3
|
-
import { W as WebMemoryManager } from '../WebMemoryManager-
|
|
2
|
+
export { AgentDefaultTool, AgentOptions, AgentState, AssistantMessageContent, CustomToolDefinition, IMemoryManager, READ_ONLY_TOOLS, ReasoningStreamContent, TOOL_RESULT_BLOCK, TextStreamContent, ToolParamName, ToolUse, VERSION, collectCustomToolNames, defaultTools, getToolUseNames, isCustomToolGroup, parseAssistantMessageV2, renderCustomToolPrompt, renderCustomToolsPrompt, setDynamicToolUseNames, toolParamNames, toolUseNames } from '../index.mjs';
|
|
3
|
+
import { W as WebMemoryManager } from '../WebMemoryManager-DpaY-f0l.mjs';
|
|
4
4
|
|
|
5
5
|
interface WebApiHandlerConfig {
|
|
6
6
|
providerId?: string;
|
|
@@ -21,6 +21,7 @@ declare class WebApiHandler implements IApiHandler {
|
|
|
21
21
|
constructor(config: WebApiHandlerConfig);
|
|
22
22
|
getModelInfo(): ApiProviderInfo;
|
|
23
23
|
createMessage(systemPrompt: string, messages: AgentMessage[]): AsyncGenerator<ApiStreamChunk, void, unknown>;
|
|
24
|
+
clearConversationId(): void;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
declare class WebPromptManager implements IPromptManager {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IApiHandler, ApiProviderInfo, AgentMessage, ApiStreamChunk, IPromptManager, IMcpManager, McpServerInfo, McpTool, McpToolCallResult, IAgentRuntime, ClineAgent } from '../index.js';
|
|
2
|
-
export { AgentDefaultTool, AgentOptions, AgentState, AssistantMessageContent, IMemoryManager, READ_ONLY_TOOLS, ReasoningStreamContent, TOOL_RESULT_BLOCK, TextStreamContent, ToolParamName, ToolUse, VERSION, defaultTools, getToolUseNames, parseAssistantMessageV2, setDynamicToolUseNames, toolParamNames, toolUseNames } from '../index.js';
|
|
3
|
-
import { W as WebMemoryManager } from '../WebMemoryManager-
|
|
2
|
+
export { AgentDefaultTool, AgentOptions, AgentState, AssistantMessageContent, CustomToolDefinition, IMemoryManager, READ_ONLY_TOOLS, ReasoningStreamContent, TOOL_RESULT_BLOCK, TextStreamContent, ToolParamName, ToolUse, VERSION, collectCustomToolNames, defaultTools, getToolUseNames, isCustomToolGroup, parseAssistantMessageV2, renderCustomToolPrompt, renderCustomToolsPrompt, setDynamicToolUseNames, toolParamNames, toolUseNames } from '../index.js';
|
|
3
|
+
import { W as WebMemoryManager } from '../WebMemoryManager-BZNjinHF.js';
|
|
4
4
|
|
|
5
5
|
interface WebApiHandlerConfig {
|
|
6
6
|
providerId?: string;
|
|
@@ -21,6 +21,7 @@ declare class WebApiHandler implements IApiHandler {
|
|
|
21
21
|
constructor(config: WebApiHandlerConfig);
|
|
22
22
|
getModelInfo(): ApiProviderInfo;
|
|
23
23
|
createMessage(systemPrompt: string, messages: AgentMessage[]): AsyncGenerator<ApiStreamChunk, void, unknown>;
|
|
24
|
+
clearConversationId(): void;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
declare class WebPromptManager implements IPromptManager {
|
|
@@ -142,9 +142,11 @@ return (
|
|
|
142
142
|
},
|
|
143
143
|
{
|
|
144
144
|
name: "agent_read_file" /* FILE_READ */,
|
|
145
|
-
description: "Read the contents of a specific file.",
|
|
145
|
+
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.",
|
|
146
146
|
params: {
|
|
147
|
-
path: "(required) The absolute or relative file path to read."
|
|
147
|
+
path: "(required) The absolute or relative file path to read.",
|
|
148
|
+
start_line: "(optional) The 1-based line number to start reading from (inclusive). Defaults to 1.",
|
|
149
|
+
end_line: "(optional) The 1-based line number to stop reading at (inclusive). Defaults to the last line."
|
|
148
150
|
}
|
|
149
151
|
},
|
|
150
152
|
{
|
|
@@ -491,6 +493,44 @@ var toolParamNames = [
|
|
|
491
493
|
"start_line",
|
|
492
494
|
"end_line"
|
|
493
495
|
];
|
|
496
|
+
|
|
497
|
+
// src/shared/custom-tools.ts
|
|
498
|
+
function isCustomToolGroup(tool) {
|
|
499
|
+
return Array.isArray(tool.children) && tool.children.length > 0;
|
|
500
|
+
}
|
|
501
|
+
function collectCustomToolNames(tools) {
|
|
502
|
+
const names = [];
|
|
503
|
+
const visit = (list) => {
|
|
504
|
+
for (const tool of list) {
|
|
505
|
+
names.push(tool.name);
|
|
506
|
+
if (Array.isArray(tool.children) && tool.children.length > 0) {
|
|
507
|
+
visit(tool.children);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
visit(tools);
|
|
512
|
+
return names;
|
|
513
|
+
}
|
|
514
|
+
function renderCustomToolPrompt(tool, cwd = "") {
|
|
515
|
+
const params = tool.params ? Object.entries(tool.params) : [];
|
|
516
|
+
const replaceCwd = (text) => String(text).replaceAll("${context.cwd}", cwd);
|
|
517
|
+
const paramLines = params.map(([name, description]) => `- ${name}: ${replaceCwd(description)}`).join("\n");
|
|
518
|
+
const usageLines = params.map(([name, description]) => `<${name}>${replaceCwd(description)}</${name}>`).join("\n");
|
|
519
|
+
return `### ${tool.name}
|
|
520
|
+
Description: ${tool.description}
|
|
521
|
+
Parameters:
|
|
522
|
+
${paramLines}
|
|
523
|
+
- 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)
|
|
524
|
+
Usage:
|
|
525
|
+
<${tool.name}>
|
|
526
|
+
${usageLines}
|
|
527
|
+
<task_progress>Checklist here (optional)</task_progress>
|
|
528
|
+
</${tool.name}>
|
|
529
|
+
`;
|
|
530
|
+
}
|
|
531
|
+
function renderCustomToolsPrompt(tools, cwd = "") {
|
|
532
|
+
return tools.map((tool) => renderCustomToolPrompt(tool, cwd)).join("\n");
|
|
533
|
+
}
|
|
494
534
|
var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
495
535
|
"txt",
|
|
496
536
|
"md",
|
|
@@ -549,7 +589,17 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
549
589
|
"dockerfile",
|
|
550
590
|
"makefile",
|
|
551
591
|
"properties",
|
|
552
|
-
"lock"
|
|
592
|
+
"lock",
|
|
593
|
+
"node-version",
|
|
594
|
+
"nvmrc",
|
|
595
|
+
"editorconfig",
|
|
596
|
+
"prettierrc",
|
|
597
|
+
"eslintrc",
|
|
598
|
+
"babelrc",
|
|
599
|
+
"stylelintrc",
|
|
600
|
+
"huskyrc",
|
|
601
|
+
"commitlintrc",
|
|
602
|
+
"lintstagedrc"
|
|
553
603
|
]);
|
|
554
604
|
var AgentState = /* @__PURE__ */ ((AgentState2) => {
|
|
555
605
|
AgentState2["IDLE"] = "idle";
|
|
@@ -574,9 +624,19 @@ var ClineAgent = class {
|
|
|
574
624
|
skillDir;
|
|
575
625
|
rulesDir;
|
|
576
626
|
customTools;
|
|
627
|
+
/** 当前可用(已展开集合)的顶层工具列表,传给 PromptManager 渲染 */
|
|
628
|
+
activeCustomTools;
|
|
629
|
+
/** 所有已知工具名(含未激活 children),便于查找定义 */
|
|
630
|
+
customToolsByName;
|
|
577
631
|
customToolNames;
|
|
578
632
|
allowDefaultTools;
|
|
579
633
|
customInstructions;
|
|
634
|
+
/** 已激活的工具集入口名 */
|
|
635
|
+
activatedGroups = /* @__PURE__ */ new Set();
|
|
636
|
+
/** activeCustomTools 变更版本号,用于 Prompt 缓存失效 */
|
|
637
|
+
activeToolsVersion = 0;
|
|
638
|
+
/** 缓存 sysPrompt 时记录的版本号 */
|
|
639
|
+
promptActiveToolsVersion = -1;
|
|
580
640
|
sysPrompt;
|
|
581
641
|
noUseTollSysPrompt;
|
|
582
642
|
// 简单的事件回调系统,供宿主环境注册以监听 Agent 内部事件
|
|
@@ -596,33 +656,47 @@ var ClineAgent = class {
|
|
|
596
656
|
this.allowDefaultTools.push("agent_attempt_completion" /* ATTEMPT */);
|
|
597
657
|
}
|
|
598
658
|
this.customInstructions = options.customInstructions;
|
|
659
|
+
this.customToolsByName = /* @__PURE__ */ new Map();
|
|
660
|
+
this.activeCustomTools = [...this.customTools];
|
|
661
|
+
this.customToolNames = new Set(collectCustomToolNames(this.customTools));
|
|
599
662
|
if (this.customTools.length > 0) {
|
|
600
|
-
const dynamicToolNames = /* @__PURE__ */ new Set();
|
|
601
663
|
const customToolParams = /* @__PURE__ */ new Set();
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
664
|
+
const visit = (tools) => {
|
|
665
|
+
for (const customTool of tools) {
|
|
666
|
+
this.customToolsByName.set(customTool.name, customTool);
|
|
667
|
+
if (customTool.params) {
|
|
668
|
+
for (const key in customTool.params) {
|
|
669
|
+
if (!Object.hasOwn(customTool.params, key)) continue;
|
|
670
|
+
customToolParams.add(key);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
if (Array.isArray(customTool.children) && customTool.children.length > 0) {
|
|
674
|
+
visit(customTool.children);
|
|
608
675
|
}
|
|
609
676
|
}
|
|
610
|
-
}
|
|
677
|
+
};
|
|
678
|
+
visit(this.customTools);
|
|
611
679
|
const defaultToolNames = Object.values(AgentDefaultTool);
|
|
612
|
-
const overlappingTools = Array.from(
|
|
680
|
+
const overlappingTools = Array.from(this.customToolNames).filter((name) => defaultToolNames.includes(name));
|
|
613
681
|
if (overlappingTools.length > 0) {
|
|
614
682
|
throw new Error(`Custom tools and default tools have overlapping names: ${overlappingTools.join(", ")}`);
|
|
615
683
|
}
|
|
616
|
-
if (
|
|
617
|
-
setDynamicToolUseNames("customUser", Array.from(
|
|
684
|
+
if (this.customToolNames.size > 0) {
|
|
685
|
+
setDynamicToolUseNames("customUser", Array.from(this.customToolNames));
|
|
618
686
|
}
|
|
619
687
|
if (customToolParams.size > 0) {
|
|
620
688
|
this.allToolParamNames = Array.from(/* @__PURE__ */ new Set([...this.allToolParamNames, ...Array.from(customToolParams)]));
|
|
621
689
|
}
|
|
622
690
|
}
|
|
623
|
-
this.customToolNames = new Set(this.customTools.map((t) => t.name));
|
|
624
691
|
this.skillDir = this.runtime.path.join(this.runtime.env.cwd(), ".agents/skills");
|
|
625
692
|
this.rulesDir = this.runtime.path.join(this.runtime.env.cwd(), ".agents/rules");
|
|
693
|
+
const originalOnCondense = this.memory.onCondense;
|
|
694
|
+
this.memory.onCondense = async () => {
|
|
695
|
+
this.api.clearConversationId();
|
|
696
|
+
if (originalOnCondense) {
|
|
697
|
+
await originalOnCondense();
|
|
698
|
+
}
|
|
699
|
+
};
|
|
626
700
|
}
|
|
627
701
|
setState(newState) {
|
|
628
702
|
this.state = newState;
|
|
@@ -644,10 +718,11 @@ var ClineAgent = class {
|
|
|
644
718
|
shell: this.runtime.env.shell(),
|
|
645
719
|
skills: await this.getSkills(),
|
|
646
720
|
agentRules: await this.getAgentRules(),
|
|
647
|
-
customTools: this.
|
|
721
|
+
customTools: this.activeCustomTools,
|
|
648
722
|
allowDefaultTools: this.allowDefaultTools,
|
|
649
723
|
customInstructions: this.customInstructions
|
|
650
724
|
};
|
|
725
|
+
this.promptActiveToolsVersion = this.activeToolsVersion;
|
|
651
726
|
this.sysPrompt = await this.promptManager.getSystemPrompt(context);
|
|
652
727
|
this.noUseTollSysPrompt = this.promptManager.getNoUseToolInstructions(context);
|
|
653
728
|
return { sysPrompt: this.sysPrompt, noUseTollSysPrompt: this.noUseTollSysPrompt };
|
|
@@ -679,16 +754,6 @@ var ClineAgent = class {
|
|
|
679
754
|
async getAgentRules() {
|
|
680
755
|
const cwd = this.runtime.env.cwd();
|
|
681
756
|
const rules = [];
|
|
682
|
-
const agentsMdPath = this.runtime.path.join(cwd, "Agents.md");
|
|
683
|
-
try {
|
|
684
|
-
if (await this.runtime.fs.fileExists(agentsMdPath)) {
|
|
685
|
-
const content = await this.runtime.fs.readFile(agentsMdPath);
|
|
686
|
-
if (content && content.trim()) {
|
|
687
|
-
rules.push(content.trim());
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
} catch (e) {
|
|
691
|
-
}
|
|
692
757
|
try {
|
|
693
758
|
if (await this.runtime.fs.fileExists(this.rulesDir)) {
|
|
694
759
|
const files = await this.runtime.fs.readDir(this.rulesDir);
|
|
@@ -714,6 +779,28 @@ ${content.trim()}\`\`\``);
|
|
|
714
779
|
}
|
|
715
780
|
} catch (e) {
|
|
716
781
|
}
|
|
782
|
+
try {
|
|
783
|
+
const getAgentsMdPath = async () => {
|
|
784
|
+
const agentsMdPathList = [
|
|
785
|
+
this.runtime.path.join(cwd, "AGENTS.md"),
|
|
786
|
+
this.runtime.path.join(cwd, "Agents.md"),
|
|
787
|
+
this.runtime.path.join(cwd, "agents.md")
|
|
788
|
+
];
|
|
789
|
+
for (const item of agentsMdPathList) {
|
|
790
|
+
if (await this.runtime.fs.fileExists(item)) {
|
|
791
|
+
return item;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
const agentsMdPath = await getAgentsMdPath();
|
|
796
|
+
if (agentsMdPath) {
|
|
797
|
+
const content = await this.runtime.fs.readFile(agentsMdPath);
|
|
798
|
+
if (content && content.trim()) {
|
|
799
|
+
rules.push(content.trim());
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
} catch (e) {
|
|
803
|
+
}
|
|
717
804
|
return rules.join("\n---\n");
|
|
718
805
|
}
|
|
719
806
|
async getSkills() {
|
|
@@ -756,9 +843,9 @@ ${content.trim()}\`\`\``);
|
|
|
756
843
|
* 核心任务循环
|
|
757
844
|
*/
|
|
758
845
|
async loop() {
|
|
759
|
-
const {
|
|
760
|
-
const { customTools, customToolNames } = this;
|
|
846
|
+
const { customToolsByName, customToolNames } = this;
|
|
761
847
|
while (this.state === "thinking" /* THINKING */) {
|
|
848
|
+
const { sysPrompt, noUseTollSysPrompt } = await this.getCurrentPrompt();
|
|
762
849
|
this.loopCount++;
|
|
763
850
|
if (this.loopCount > this.maxLoopCount) {
|
|
764
851
|
await this.memory.appendMessage({
|
|
@@ -803,7 +890,33 @@ ${content.trim()}\`\`\``);
|
|
|
803
890
|
}
|
|
804
891
|
}
|
|
805
892
|
if (customToolNames.has(tool.name)) {
|
|
806
|
-
const customTool =
|
|
893
|
+
const customTool = customToolsByName.get(tool.name);
|
|
894
|
+
if (!customTool) {
|
|
895
|
+
toolResults.push({ tool, result: `Error: Custom tool "${tool.name}" not found.` });
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
if (isCustomToolGroup(customTool)) {
|
|
899
|
+
let groupResult;
|
|
900
|
+
if (customTool.onCall) {
|
|
901
|
+
try {
|
|
902
|
+
groupResult = await customTool.onCall(tool.params);
|
|
903
|
+
} catch (error) {
|
|
904
|
+
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
const childrenPrompt = this.activateCustomToolGroup(customTool);
|
|
909
|
+
const finalPrompt = `# New Available Tools
|
|
910
|
+
|
|
911
|
+
The following are instructions for using the new available tools.
|
|
912
|
+
|
|
913
|
+
${childrenPrompt}`;
|
|
914
|
+
const promptBlock = groupResult !== void 0 ? `${typeof groupResult === "string" ? groupResult : JSON.stringify(groupResult)}
|
|
915
|
+
|
|
916
|
+
${finalPrompt}` : finalPrompt;
|
|
917
|
+
toolResults.push({ tool, result: promptBlock });
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
807
920
|
if (customTool.onCall) {
|
|
808
921
|
try {
|
|
809
922
|
const res = await customTool.onCall(tool.params);
|
|
@@ -825,7 +938,8 @@ ${content.trim()}\`\`\``);
|
|
|
825
938
|
if (path2) {
|
|
826
939
|
try {
|
|
827
940
|
const content = await this.runtime.fs.readFile(path2);
|
|
828
|
-
|
|
941
|
+
const result = this.formatFileContentWithLineNumbers(content, tool.params.start_line, tool.params.end_line);
|
|
942
|
+
toolResults.push({ tool, result });
|
|
829
943
|
} catch (error) {
|
|
830
944
|
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
831
945
|
}
|
|
@@ -838,6 +952,8 @@ ${content.trim()}\`\`\``);
|
|
|
838
952
|
const result = await this.runtime.terminal.execCommand(cmd);
|
|
839
953
|
toolResults.push({ tool, result: `STDOUT: ${result.stdout}
|
|
840
954
|
STDERR: ${result.stderr}` });
|
|
955
|
+
} else {
|
|
956
|
+
toolResults.push({ tool, result: "Error: No command provided." });
|
|
841
957
|
}
|
|
842
958
|
} else if (tool.name === "agent_write_to_file" /* FILE_NEW */) {
|
|
843
959
|
const path2 = tool.params.path;
|
|
@@ -849,12 +965,18 @@ STDERR: ${result.stderr}` });
|
|
|
849
965
|
} catch (error) {
|
|
850
966
|
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
851
967
|
}
|
|
968
|
+
} else {
|
|
969
|
+
toolResults.push({ tool, result: "Error: Missing path or content." });
|
|
852
970
|
}
|
|
853
971
|
} else if (tool.name === "agent_list_files" /* LIST_FILES */) {
|
|
854
972
|
const path2 = tool.params.path || this.runtime.env.cwd();
|
|
973
|
+
const recursive = tool.params.recursive === "true";
|
|
855
974
|
try {
|
|
856
|
-
const files = await this.runtime.fs.readDir(path2);
|
|
857
|
-
toolResults.push({
|
|
975
|
+
const files = await this.runtime.fs.readDir(path2, { recursive });
|
|
976
|
+
toolResults.push({
|
|
977
|
+
tool,
|
|
978
|
+
result: files.map((f) => `${f.isDirectory() ? "[DIR] " : " "}${f.name}${f.isDirectory() ? "/" : ""}`).join("\n")
|
|
979
|
+
});
|
|
858
980
|
} catch (e) {
|
|
859
981
|
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
860
982
|
}
|
|
@@ -994,10 +1116,15 @@ STDERR: ${result.stderr}` });
|
|
|
994
1116
|
try {
|
|
995
1117
|
const res = await fetch(url);
|
|
996
1118
|
const text = await res.text();
|
|
997
|
-
|
|
1119
|
+
const MAX_LEN = 8e3;
|
|
1120
|
+
const truncated = text.length > MAX_LEN ? `${text.substring(0, MAX_LEN)}
|
|
1121
|
+
...[truncated ${text.length - MAX_LEN} chars]` : text;
|
|
1122
|
+
toolResults.push({ tool, result: truncated });
|
|
998
1123
|
} catch (e) {
|
|
999
1124
|
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
1000
1125
|
}
|
|
1126
|
+
} else {
|
|
1127
|
+
toolResults.push({ tool, result: "Error: No url provided." });
|
|
1001
1128
|
}
|
|
1002
1129
|
} else if (tool.name === "agent_focus_chain" /* TODO */) {
|
|
1003
1130
|
toolResults.push({ tool, result: `Focus chain updated successfully.` });
|
|
@@ -1064,6 +1191,65 @@ ${this.promptManager.getUsedToolResultInstructions()}`;
|
|
|
1064
1191
|
}
|
|
1065
1192
|
}
|
|
1066
1193
|
}
|
|
1194
|
+
/**
|
|
1195
|
+
* 激活自定义工具集:将入口的 children 注册到活跃工具列表并返回渲染后的 prompt。
|
|
1196
|
+
*/
|
|
1197
|
+
activateCustomToolGroup(group) {
|
|
1198
|
+
if (this.activatedGroups.has(group.name)) {
|
|
1199
|
+
return renderCustomToolsPrompt(group.children || [], this.runtime.env.cwd());
|
|
1200
|
+
}
|
|
1201
|
+
this.activatedGroups.add(group.name);
|
|
1202
|
+
this.activeCustomTools = [...this.activeCustomTools, ...group.children || []];
|
|
1203
|
+
this.activeToolsVersion++;
|
|
1204
|
+
return renderCustomToolsPrompt(group.children || [], this.runtime.env.cwd());
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* 检查自上次获取 prompt 后是否有工具集被激活,若有则刷新缓存。
|
|
1208
|
+
*/
|
|
1209
|
+
async getCurrentPrompt() {
|
|
1210
|
+
if (this.promptActiveToolsVersion !== this.activeToolsVersion) {
|
|
1211
|
+
return this.getPrompt(true);
|
|
1212
|
+
}
|
|
1213
|
+
return this.getPrompt();
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* 将文件内容格式化为带行号文本(模拟编辑器行号显示),
|
|
1217
|
+
* 支持通过 start_line / end_line 截取读取范围。
|
|
1218
|
+
*/
|
|
1219
|
+
formatFileContentWithLineNumbers(content, startLineParam, endLineParam) {
|
|
1220
|
+
const lines = content.split(/\r?\n/);
|
|
1221
|
+
const normalized = lines.length > 0 && lines[lines.length - 1] === "" ? lines.slice(0, -1) : lines;
|
|
1222
|
+
const total = normalized.length;
|
|
1223
|
+
if (total === 0) {
|
|
1224
|
+
return "1|(empty file)";
|
|
1225
|
+
}
|
|
1226
|
+
const parseLine = (val, fallback) => {
|
|
1227
|
+
if (val === void 0 || val === null || val.trim() === "") {
|
|
1228
|
+
return fallback;
|
|
1229
|
+
}
|
|
1230
|
+
const num = Number.parseInt(val, 10);
|
|
1231
|
+
if (!Number.isFinite(num) || num <= 0) {
|
|
1232
|
+
return fallback;
|
|
1233
|
+
}
|
|
1234
|
+
return num;
|
|
1235
|
+
};
|
|
1236
|
+
const start = parseLine(startLineParam, 1);
|
|
1237
|
+
const end = parseLine(endLineParam, total);
|
|
1238
|
+
if (start > end) {
|
|
1239
|
+
return `Error: start_line (${start}) cannot be greater than end_line (${end}).`;
|
|
1240
|
+
}
|
|
1241
|
+
if (start > total) {
|
|
1242
|
+
return `Error: start_line (${start}) is out of range. The file has ${total} lines.`;
|
|
1243
|
+
}
|
|
1244
|
+
const from = Math.max(1, start);
|
|
1245
|
+
const to = Math.min(total, end);
|
|
1246
|
+
const width = String(total).length;
|
|
1247
|
+
const selected = normalized.slice(from - 1, to);
|
|
1248
|
+
const numbered = selected.map((line, idx) => `${String(from + idx).padStart(width)}|${line}`).join("\n");
|
|
1249
|
+
const header = from === 1 && to === total ? `1-${total}/${total}` : `${from}-${to}/${total}`;
|
|
1250
|
+
return `${header}
|
|
1251
|
+
${numbered}`;
|
|
1252
|
+
}
|
|
1067
1253
|
/**
|
|
1068
1254
|
* 简单的 Diff 解析与替换辅助方法
|
|
1069
1255
|
*/
|
|
@@ -1313,6 +1499,9 @@ var WebApiHandler = class {
|
|
|
1313
1499
|
};
|
|
1314
1500
|
}
|
|
1315
1501
|
}
|
|
1502
|
+
clearConversationId() {
|
|
1503
|
+
this.conversationId = void 0;
|
|
1504
|
+
}
|
|
1316
1505
|
};
|
|
1317
1506
|
|
|
1318
1507
|
// src/prompt/WebPromptManager.ts
|
|
@@ -1425,10 +1614,11 @@ ${context.customInstructions}
|
|
|
1425
1614
|
if (allowDefaultTools) {
|
|
1426
1615
|
useDefaultTools = defaultTools.filter((tool) => allowDefaultTools.includes(tool.name));
|
|
1427
1616
|
}
|
|
1617
|
+
const renderedCustomTools = renderCustomToolsPrompt(customTools, context.cwd);
|
|
1428
1618
|
return `# Available Tools
|
|
1429
1619
|
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.
|
|
1430
1620
|
|
|
1431
|
-
${[...useDefaultTools
|
|
1621
|
+
${[...useDefaultTools].map((tool) => {
|
|
1432
1622
|
return `### ${tool.name}
|
|
1433
1623
|
Description: ${tool.description}
|
|
1434
1624
|
Parameters:
|
|
@@ -1441,6 +1631,8 @@ ${Object.entries(tool.params).map(([name, description]) => `<${name}>${descripti
|
|
|
1441
1631
|
</${tool.name}>
|
|
1442
1632
|
`;
|
|
1443
1633
|
}).join("\n")}
|
|
1634
|
+
${renderedCustomTools ? `
|
|
1635
|
+
${renderedCustomTools}` : ""}
|
|
1444
1636
|
|
|
1445
1637
|
# Tool Use Examples
|
|
1446
1638
|
${useDefaultTools.filter((item) => Boolean(item.example)).map((item) => item.example).join("\n")}}
|
|
@@ -1615,16 +1807,127 @@ ${this?.getEnvironmentInfoPrompt?.(context)}`;
|
|
|
1615
1807
|
}
|
|
1616
1808
|
};
|
|
1617
1809
|
|
|
1810
|
+
// src/memory/condense.ts
|
|
1811
|
+
var TOOL_RESULT_CONTENT_REGEX = /<tool_call_result[^>]*>[\s\S]*?<\/tool_call_result>/;
|
|
1812
|
+
var EXPIRED_MARKER_TEXT = "[The result has expired and has been removed from the context]";
|
|
1813
|
+
var ATTEMPT_TOOL_NAME = "agent_attempt_completion" /* ATTEMPT */;
|
|
1814
|
+
function getContentString(content) {
|
|
1815
|
+
return typeof content === "string" ? content : "";
|
|
1816
|
+
}
|
|
1817
|
+
function isToolResultMessage(content) {
|
|
1818
|
+
if (typeof content !== "string") {
|
|
1819
|
+
return false;
|
|
1820
|
+
}
|
|
1821
|
+
return TOOL_RESULT_CONTENT_REGEX.test(content);
|
|
1822
|
+
}
|
|
1823
|
+
function isAttemptCallMessage(message) {
|
|
1824
|
+
if (message.role !== "assistant") {
|
|
1825
|
+
return false;
|
|
1826
|
+
}
|
|
1827
|
+
const content = getContentString(message.content);
|
|
1828
|
+
return new RegExp(`<${ATTEMPT_TOOL_NAME}[\\s>]`).test(content) || new RegExp(`<name>\\s*${ATTEMPT_TOOL_NAME}\\s*</name>`).test(content);
|
|
1829
|
+
}
|
|
1830
|
+
function markExpiredToolResult(content) {
|
|
1831
|
+
if (typeof content !== "string") {
|
|
1832
|
+
return content;
|
|
1833
|
+
}
|
|
1834
|
+
return content.replace(
|
|
1835
|
+
/(<tool_call_result[^>]*tool_name="([^"]*)"[^>]*>)[\s\S]*?(<\/tool_call_result>)/g,
|
|
1836
|
+
(_match, openTag, _toolName, closeTag) => {
|
|
1837
|
+
return `${openTag}
|
|
1838
|
+
${EXPIRED_MARKER_TEXT}
|
|
1839
|
+
${closeTag}`;
|
|
1840
|
+
}
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1843
|
+
function splitConversations(messages) {
|
|
1844
|
+
const conversations = [];
|
|
1845
|
+
let current = [];
|
|
1846
|
+
for (const message of messages) {
|
|
1847
|
+
current.push(message);
|
|
1848
|
+
if (isAttemptCallMessage(message)) {
|
|
1849
|
+
conversations.push(current);
|
|
1850
|
+
current = [];
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
if (current.length > 0) {
|
|
1854
|
+
conversations.push(current);
|
|
1855
|
+
}
|
|
1856
|
+
return conversations;
|
|
1857
|
+
}
|
|
1858
|
+
function isCompleteConversation(conversation) {
|
|
1859
|
+
if (conversation.length === 0) {
|
|
1860
|
+
return false;
|
|
1861
|
+
}
|
|
1862
|
+
return isAttemptCallMessage(conversation[conversation.length - 1]);
|
|
1863
|
+
}
|
|
1864
|
+
function compressCompleteConversation(conversation) {
|
|
1865
|
+
if (conversation.length <= 2) {
|
|
1866
|
+
return conversation;
|
|
1867
|
+
}
|
|
1868
|
+
const head = conversation[0];
|
|
1869
|
+
const tail = conversation[conversation.length - 1];
|
|
1870
|
+
const mergedMarker = {
|
|
1871
|
+
role: "user",
|
|
1872
|
+
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."
|
|
1873
|
+
};
|
|
1874
|
+
return [head, mergedMarker, tail];
|
|
1875
|
+
}
|
|
1876
|
+
function markExpiredFirstHalf(messages) {
|
|
1877
|
+
const half = Math.floor(messages.length / 2);
|
|
1878
|
+
return messages.map((message, index) => {
|
|
1879
|
+
if (index < half && isToolResultMessage(message.content)) {
|
|
1880
|
+
return { ...message, content: markExpiredToolResult(message.content) };
|
|
1881
|
+
}
|
|
1882
|
+
return message;
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
function condenseMessages(messages, maxMessages) {
|
|
1886
|
+
if (messages.length <= maxMessages) {
|
|
1887
|
+
return messages;
|
|
1888
|
+
}
|
|
1889
|
+
const systemMessages = messages.filter((message) => message.role === "system");
|
|
1890
|
+
const restMessages = messages.filter((message) => message.role !== "system");
|
|
1891
|
+
const conversations = splitConversations(restMessages);
|
|
1892
|
+
const completeIndices = [];
|
|
1893
|
+
for (let i = 0; i < conversations.length; i++) {
|
|
1894
|
+
if (isCompleteConversation(conversations[i])) {
|
|
1895
|
+
completeIndices.push(i);
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
const currentLength = () => systemMessages.length + conversations.reduce((acc, c) => acc + c.length, 0);
|
|
1899
|
+
if (completeIndices.length >= 2) {
|
|
1900
|
+
const lastCompleteIndex = completeIndices[completeIndices.length - 1];
|
|
1901
|
+
for (const idx of completeIndices) {
|
|
1902
|
+
if (idx === lastCompleteIndex) {
|
|
1903
|
+
break;
|
|
1904
|
+
}
|
|
1905
|
+
conversations[idx] = compressCompleteConversation(conversations[idx]);
|
|
1906
|
+
if (currentLength() <= maxMessages) {
|
|
1907
|
+
break;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
let condensed = conversations.flat();
|
|
1912
|
+
condensed = markExpiredFirstHalf(condensed);
|
|
1913
|
+
if (systemMessages.length + condensed.length > maxMessages) {
|
|
1914
|
+
const nonSystemBudget = Math.max(0, maxMessages - systemMessages.length);
|
|
1915
|
+
condensed = condensed.slice(-nonSystemBudget);
|
|
1916
|
+
}
|
|
1917
|
+
return [...systemMessages, ...condensed];
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1618
1920
|
// src/memory/WebMemoryManager.ts
|
|
1619
1921
|
var WebMemoryManager = class {
|
|
1620
1922
|
storageKey;
|
|
1621
1923
|
maxMessages;
|
|
1622
1924
|
inMemoryFallback = [];
|
|
1925
|
+
onCondense;
|
|
1623
1926
|
/**
|
|
1624
1927
|
* @param storageKey SessionStorage 中的键名
|
|
1625
|
-
* @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认
|
|
1928
|
+
* @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认 200)
|
|
1626
1929
|
*/
|
|
1627
|
-
constructor(storageKey = "cline_agent_memory", maxMessages =
|
|
1930
|
+
constructor(storageKey = "cline_agent_memory", maxMessages = 200) {
|
|
1628
1931
|
this.storageKey = storageKey;
|
|
1629
1932
|
this.maxMessages = maxMessages;
|
|
1630
1933
|
}
|
|
@@ -1665,19 +1968,22 @@ var WebMemoryManager = class {
|
|
|
1665
1968
|
this.writeMessages(messages);
|
|
1666
1969
|
await this.condenseHistory();
|
|
1667
1970
|
}
|
|
1971
|
+
/**
|
|
1972
|
+
* 压缩历史:
|
|
1973
|
+
* - system 消息保持不变
|
|
1974
|
+
* - 以 agent_attempt_completion 为边界切分完整对话,仅合并“后面还有其他完整对话”的旧完整对话
|
|
1975
|
+
* - 其余需要压缩的场景下,对前半段工具调用结果标记为“结果已过期”
|
|
1976
|
+
* - 同时触发 onCondense 回调(宿主可据此清空 dify conversationId 新开窗口)
|
|
1977
|
+
*/
|
|
1668
1978
|
async condenseHistory() {
|
|
1669
1979
|
const messages = this.readMessages();
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
} else {
|
|
1678
|
-
condensed = messages.slice(sliceCount);
|
|
1679
|
-
}
|
|
1680
|
-
this.writeMessages(condensed);
|
|
1980
|
+
const condensed = condenseMessages(messages, this.maxMessages);
|
|
1981
|
+
if (condensed === messages) {
|
|
1982
|
+
return Promise.resolve();
|
|
1983
|
+
}
|
|
1984
|
+
this.writeMessages(condensed);
|
|
1985
|
+
if (this.onCondense) {
|
|
1986
|
+
await this.onCondense();
|
|
1681
1987
|
}
|
|
1682
1988
|
return Promise.resolve();
|
|
1683
1989
|
}
|
|
@@ -1807,10 +2113,14 @@ exports.WebApiHandler = WebApiHandler;
|
|
|
1807
2113
|
exports.WebMcpManager = WebMcpManager;
|
|
1808
2114
|
exports.WebMemoryManager = WebMemoryManager;
|
|
1809
2115
|
exports.WebPromptManager = WebPromptManager;
|
|
2116
|
+
exports.collectCustomToolNames = collectCustomToolNames;
|
|
1810
2117
|
exports.createWebAgent = createWebAgent;
|
|
1811
2118
|
exports.defaultTools = defaultTools;
|
|
1812
2119
|
exports.getToolUseNames = getToolUseNames;
|
|
2120
|
+
exports.isCustomToolGroup = isCustomToolGroup;
|
|
1813
2121
|
exports.parseAssistantMessageV2 = parseAssistantMessageV2;
|
|
2122
|
+
exports.renderCustomToolPrompt = renderCustomToolPrompt;
|
|
2123
|
+
exports.renderCustomToolsPrompt = renderCustomToolsPrompt;
|
|
1814
2124
|
exports.setDynamicToolUseNames = setDynamicToolUseNames;
|
|
1815
2125
|
exports.toolParamNames = toolParamNames;
|
|
1816
2126
|
exports.toolUseNames = toolUseNames;
|