@amical/aiagent-sdk 0.1.11 → 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 +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +100 -17
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +100 -17
- package/dist/index.mjs.map +1 -1
- package/dist/runtime/nodeRuntime.d.mts +11 -2
- package/dist/runtime/nodeRuntime.d.ts +11 -2
- package/dist/runtime/nodeRuntime.js +230 -30
- package/dist/runtime/nodeRuntime.js.map +1 -1
- package/dist/runtime/nodeRuntime.mjs +230 -30
- package/dist/runtime/nodeRuntime.mjs.map +1 -1
- package/dist/runtime/webRuntime.d.mts +2 -1
- package/dist/runtime/webRuntime.d.ts +2 -1
- package/dist/runtime/webRuntime.js +230 -30
- package/dist/runtime/webRuntime.js.map +1 -1
- package/dist/runtime/webRuntime.mjs +230 -30
- package/dist/runtime/webRuntime.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -140,9 +140,11 @@ return (
|
|
|
140
140
|
},
|
|
141
141
|
{
|
|
142
142
|
name: "agent_read_file" /* FILE_READ */,
|
|
143
|
-
description: "Read the contents of a specific file.",
|
|
143
|
+
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.",
|
|
144
144
|
params: {
|
|
145
|
-
path: "(required) The absolute or relative file path to read."
|
|
145
|
+
path: "(required) The absolute or relative file path to read.",
|
|
146
|
+
start_line: "(optional) The 1-based line number to start reading from (inclusive). Defaults to 1.",
|
|
147
|
+
end_line: "(optional) The 1-based line number to stop reading at (inclusive). Defaults to the last line."
|
|
146
148
|
}
|
|
147
149
|
},
|
|
148
150
|
{
|
|
@@ -585,7 +587,17 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
585
587
|
"dockerfile",
|
|
586
588
|
"makefile",
|
|
587
589
|
"properties",
|
|
588
|
-
"lock"
|
|
590
|
+
"lock",
|
|
591
|
+
"node-version",
|
|
592
|
+
"nvmrc",
|
|
593
|
+
"editorconfig",
|
|
594
|
+
"prettierrc",
|
|
595
|
+
"eslintrc",
|
|
596
|
+
"babelrc",
|
|
597
|
+
"stylelintrc",
|
|
598
|
+
"huskyrc",
|
|
599
|
+
"commitlintrc",
|
|
600
|
+
"lintstagedrc"
|
|
589
601
|
]);
|
|
590
602
|
var AgentState = /* @__PURE__ */ ((AgentState2) => {
|
|
591
603
|
AgentState2["IDLE"] = "idle";
|
|
@@ -676,6 +688,13 @@ var ClineAgent = class {
|
|
|
676
688
|
}
|
|
677
689
|
this.skillDir = this.runtime.path.join(this.runtime.env.cwd(), ".agents/skills");
|
|
678
690
|
this.rulesDir = this.runtime.path.join(this.runtime.env.cwd(), ".agents/rules");
|
|
691
|
+
const originalOnCondense = this.memory.onCondense;
|
|
692
|
+
this.memory.onCondense = async () => {
|
|
693
|
+
this.api.clearConversationId();
|
|
694
|
+
if (originalOnCondense) {
|
|
695
|
+
await originalOnCondense();
|
|
696
|
+
}
|
|
697
|
+
};
|
|
679
698
|
}
|
|
680
699
|
setState(newState) {
|
|
681
700
|
this.state = newState;
|
|
@@ -733,16 +752,6 @@ var ClineAgent = class {
|
|
|
733
752
|
async getAgentRules() {
|
|
734
753
|
const cwd = this.runtime.env.cwd();
|
|
735
754
|
const rules = [];
|
|
736
|
-
const agentsMdPath = this.runtime.path.join(cwd, "Agents.md");
|
|
737
|
-
try {
|
|
738
|
-
if (await this.runtime.fs.fileExists(agentsMdPath)) {
|
|
739
|
-
const content = await this.runtime.fs.readFile(agentsMdPath);
|
|
740
|
-
if (content && content.trim()) {
|
|
741
|
-
rules.push(content.trim());
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
} catch (e) {
|
|
745
|
-
}
|
|
746
755
|
try {
|
|
747
756
|
if (await this.runtime.fs.fileExists(this.rulesDir)) {
|
|
748
757
|
const files = await this.runtime.fs.readDir(this.rulesDir);
|
|
@@ -768,6 +777,28 @@ ${content.trim()}\`\`\``);
|
|
|
768
777
|
}
|
|
769
778
|
} catch (e) {
|
|
770
779
|
}
|
|
780
|
+
try {
|
|
781
|
+
const getAgentsMdPath = async () => {
|
|
782
|
+
const agentsMdPathList = [
|
|
783
|
+
this.runtime.path.join(cwd, "AGENTS.md"),
|
|
784
|
+
this.runtime.path.join(cwd, "Agents.md"),
|
|
785
|
+
this.runtime.path.join(cwd, "agents.md")
|
|
786
|
+
];
|
|
787
|
+
for (const item of agentsMdPathList) {
|
|
788
|
+
if (await this.runtime.fs.fileExists(item)) {
|
|
789
|
+
return item;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
const agentsMdPath = await getAgentsMdPath();
|
|
794
|
+
if (agentsMdPath) {
|
|
795
|
+
const content = await this.runtime.fs.readFile(agentsMdPath);
|
|
796
|
+
if (content && content.trim()) {
|
|
797
|
+
rules.push(content.trim());
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
} catch (e) {
|
|
801
|
+
}
|
|
771
802
|
return rules.join("\n---\n");
|
|
772
803
|
}
|
|
773
804
|
async getSkills() {
|
|
@@ -905,7 +936,8 @@ ${finalPrompt}` : finalPrompt;
|
|
|
905
936
|
if (path3) {
|
|
906
937
|
try {
|
|
907
938
|
const content = await this.runtime.fs.readFile(path3);
|
|
908
|
-
|
|
939
|
+
const result = this.formatFileContentWithLineNumbers(content, tool.params.start_line, tool.params.end_line);
|
|
940
|
+
toolResults.push({ tool, result });
|
|
909
941
|
} catch (error) {
|
|
910
942
|
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
911
943
|
}
|
|
@@ -918,6 +950,8 @@ ${finalPrompt}` : finalPrompt;
|
|
|
918
950
|
const result = await this.runtime.terminal.execCommand(cmd);
|
|
919
951
|
toolResults.push({ tool, result: `STDOUT: ${result.stdout}
|
|
920
952
|
STDERR: ${result.stderr}` });
|
|
953
|
+
} else {
|
|
954
|
+
toolResults.push({ tool, result: "Error: No command provided." });
|
|
921
955
|
}
|
|
922
956
|
} else if (tool.name === "agent_write_to_file" /* FILE_NEW */) {
|
|
923
957
|
const path3 = tool.params.path;
|
|
@@ -929,12 +963,18 @@ STDERR: ${result.stderr}` });
|
|
|
929
963
|
} catch (error) {
|
|
930
964
|
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
931
965
|
}
|
|
966
|
+
} else {
|
|
967
|
+
toolResults.push({ tool, result: "Error: Missing path or content." });
|
|
932
968
|
}
|
|
933
969
|
} else if (tool.name === "agent_list_files" /* LIST_FILES */) {
|
|
934
970
|
const path3 = tool.params.path || this.runtime.env.cwd();
|
|
971
|
+
const recursive = tool.params.recursive === "true";
|
|
935
972
|
try {
|
|
936
|
-
const files = await this.runtime.fs.readDir(path3);
|
|
937
|
-
toolResults.push({
|
|
973
|
+
const files = await this.runtime.fs.readDir(path3, { recursive });
|
|
974
|
+
toolResults.push({
|
|
975
|
+
tool,
|
|
976
|
+
result: files.map((f) => `${f.isDirectory() ? "[DIR] " : " "}${f.name}${f.isDirectory() ? "/" : ""}`).join("\n")
|
|
977
|
+
});
|
|
938
978
|
} catch (e) {
|
|
939
979
|
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
940
980
|
}
|
|
@@ -1074,10 +1114,15 @@ STDERR: ${result.stderr}` });
|
|
|
1074
1114
|
try {
|
|
1075
1115
|
const res = await fetch(url);
|
|
1076
1116
|
const text = await res.text();
|
|
1077
|
-
|
|
1117
|
+
const MAX_LEN = 8e3;
|
|
1118
|
+
const truncated = text.length > MAX_LEN ? `${text.substring(0, MAX_LEN)}
|
|
1119
|
+
...[truncated ${text.length - MAX_LEN} chars]` : text;
|
|
1120
|
+
toolResults.push({ tool, result: truncated });
|
|
1078
1121
|
} catch (e) {
|
|
1079
1122
|
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
1080
1123
|
}
|
|
1124
|
+
} else {
|
|
1125
|
+
toolResults.push({ tool, result: "Error: No url provided." });
|
|
1081
1126
|
}
|
|
1082
1127
|
} else if (tool.name === "agent_focus_chain" /* TODO */) {
|
|
1083
1128
|
toolResults.push({ tool, result: `Focus chain updated successfully.` });
|
|
@@ -1165,6 +1210,44 @@ ${this.promptManager.getUsedToolResultInstructions()}`;
|
|
|
1165
1210
|
}
|
|
1166
1211
|
return this.getPrompt();
|
|
1167
1212
|
}
|
|
1213
|
+
/**
|
|
1214
|
+
* 将文件内容格式化为带行号文本(模拟编辑器行号显示),
|
|
1215
|
+
* 支持通过 start_line / end_line 截取读取范围。
|
|
1216
|
+
*/
|
|
1217
|
+
formatFileContentWithLineNumbers(content, startLineParam, endLineParam) {
|
|
1218
|
+
const lines = content.split(/\r?\n/);
|
|
1219
|
+
const normalized = lines.length > 0 && lines[lines.length - 1] === "" ? lines.slice(0, -1) : lines;
|
|
1220
|
+
const total = normalized.length;
|
|
1221
|
+
if (total === 0) {
|
|
1222
|
+
return "1|(empty file)";
|
|
1223
|
+
}
|
|
1224
|
+
const parseLine = (val, fallback) => {
|
|
1225
|
+
if (val === void 0 || val === null || val.trim() === "") {
|
|
1226
|
+
return fallback;
|
|
1227
|
+
}
|
|
1228
|
+
const num = Number.parseInt(val, 10);
|
|
1229
|
+
if (!Number.isFinite(num) || num <= 0) {
|
|
1230
|
+
return fallback;
|
|
1231
|
+
}
|
|
1232
|
+
return num;
|
|
1233
|
+
};
|
|
1234
|
+
const start = parseLine(startLineParam, 1);
|
|
1235
|
+
const end = parseLine(endLineParam, total);
|
|
1236
|
+
if (start > end) {
|
|
1237
|
+
return `Error: start_line (${start}) cannot be greater than end_line (${end}).`;
|
|
1238
|
+
}
|
|
1239
|
+
if (start > total) {
|
|
1240
|
+
return `Error: start_line (${start}) is out of range. The file has ${total} lines.`;
|
|
1241
|
+
}
|
|
1242
|
+
const from = Math.max(1, start);
|
|
1243
|
+
const to = Math.min(total, end);
|
|
1244
|
+
const width = String(total).length;
|
|
1245
|
+
const selected = normalized.slice(from - 1, to);
|
|
1246
|
+
const numbered = selected.map((line, idx) => `${String(from + idx).padStart(width)}|${line}`).join("\n");
|
|
1247
|
+
const header = from === 1 && to === total ? `1-${total}/${total}` : `${from}-${to}/${total}`;
|
|
1248
|
+
return `${header}
|
|
1249
|
+
${numbered}`;
|
|
1250
|
+
}
|
|
1168
1251
|
/**
|
|
1169
1252
|
* 简单的 Diff 解析与替换辅助方法
|
|
1170
1253
|
*/
|
|
@@ -1414,6 +1497,9 @@ var NodeApiHandler = class {
|
|
|
1414
1497
|
};
|
|
1415
1498
|
}
|
|
1416
1499
|
}
|
|
1500
|
+
clearConversationId() {
|
|
1501
|
+
this.conversationId = void 0;
|
|
1502
|
+
}
|
|
1417
1503
|
};
|
|
1418
1504
|
|
|
1419
1505
|
// src/prompt/NodePromptManager.ts
|
|
@@ -1718,14 +1804,125 @@ ${this?.getEnvironmentInfoPrompt?.(context)}`;
|
|
|
1718
1804
|
4. Ensure the list accurately reflects the current state`;
|
|
1719
1805
|
}
|
|
1720
1806
|
};
|
|
1807
|
+
|
|
1808
|
+
// src/memory/condense.ts
|
|
1809
|
+
var TOOL_RESULT_CONTENT_REGEX = /<tool_call_result[^>]*>[\s\S]*?<\/tool_call_result>/;
|
|
1810
|
+
var EXPIRED_MARKER_TEXT = "[The result has expired and has been removed from the context]";
|
|
1811
|
+
var ATTEMPT_TOOL_NAME = "agent_attempt_completion" /* ATTEMPT */;
|
|
1812
|
+
function getContentString(content) {
|
|
1813
|
+
return typeof content === "string" ? content : "";
|
|
1814
|
+
}
|
|
1815
|
+
function isToolResultMessage(content) {
|
|
1816
|
+
if (typeof content !== "string") {
|
|
1817
|
+
return false;
|
|
1818
|
+
}
|
|
1819
|
+
return TOOL_RESULT_CONTENT_REGEX.test(content);
|
|
1820
|
+
}
|
|
1821
|
+
function isAttemptCallMessage(message) {
|
|
1822
|
+
if (message.role !== "assistant") {
|
|
1823
|
+
return false;
|
|
1824
|
+
}
|
|
1825
|
+
const content = getContentString(message.content);
|
|
1826
|
+
return new RegExp(`<${ATTEMPT_TOOL_NAME}[\\s>]`).test(content) || new RegExp(`<name>\\s*${ATTEMPT_TOOL_NAME}\\s*</name>`).test(content);
|
|
1827
|
+
}
|
|
1828
|
+
function markExpiredToolResult(content) {
|
|
1829
|
+
if (typeof content !== "string") {
|
|
1830
|
+
return content;
|
|
1831
|
+
}
|
|
1832
|
+
return content.replace(
|
|
1833
|
+
/(<tool_call_result[^>]*tool_name="([^"]*)"[^>]*>)[\s\S]*?(<\/tool_call_result>)/g,
|
|
1834
|
+
(_match, openTag, _toolName, closeTag) => {
|
|
1835
|
+
return `${openTag}
|
|
1836
|
+
${EXPIRED_MARKER_TEXT}
|
|
1837
|
+
${closeTag}`;
|
|
1838
|
+
}
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1841
|
+
function splitConversations(messages) {
|
|
1842
|
+
const conversations = [];
|
|
1843
|
+
let current = [];
|
|
1844
|
+
for (const message of messages) {
|
|
1845
|
+
current.push(message);
|
|
1846
|
+
if (isAttemptCallMessage(message)) {
|
|
1847
|
+
conversations.push(current);
|
|
1848
|
+
current = [];
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
if (current.length > 0) {
|
|
1852
|
+
conversations.push(current);
|
|
1853
|
+
}
|
|
1854
|
+
return conversations;
|
|
1855
|
+
}
|
|
1856
|
+
function isCompleteConversation(conversation) {
|
|
1857
|
+
if (conversation.length === 0) {
|
|
1858
|
+
return false;
|
|
1859
|
+
}
|
|
1860
|
+
return isAttemptCallMessage(conversation[conversation.length - 1]);
|
|
1861
|
+
}
|
|
1862
|
+
function compressCompleteConversation(conversation) {
|
|
1863
|
+
if (conversation.length <= 2) {
|
|
1864
|
+
return conversation;
|
|
1865
|
+
}
|
|
1866
|
+
const head = conversation[0];
|
|
1867
|
+
const tail = conversation[conversation.length - 1];
|
|
1868
|
+
const mergedMarker = {
|
|
1869
|
+
role: "user",
|
|
1870
|
+
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."
|
|
1871
|
+
};
|
|
1872
|
+
return [head, mergedMarker, tail];
|
|
1873
|
+
}
|
|
1874
|
+
function markExpiredFirstHalf(messages) {
|
|
1875
|
+
const half = Math.floor(messages.length / 2);
|
|
1876
|
+
return messages.map((message, index) => {
|
|
1877
|
+
if (index < half && isToolResultMessage(message.content)) {
|
|
1878
|
+
return { ...message, content: markExpiredToolResult(message.content) };
|
|
1879
|
+
}
|
|
1880
|
+
return message;
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
function condenseMessages(messages, maxMessages) {
|
|
1884
|
+
if (messages.length <= maxMessages) {
|
|
1885
|
+
return messages;
|
|
1886
|
+
}
|
|
1887
|
+
const systemMessages = messages.filter((message) => message.role === "system");
|
|
1888
|
+
const restMessages = messages.filter((message) => message.role !== "system");
|
|
1889
|
+
const conversations = splitConversations(restMessages);
|
|
1890
|
+
const completeIndices = [];
|
|
1891
|
+
for (let i = 0; i < conversations.length; i++) {
|
|
1892
|
+
if (isCompleteConversation(conversations[i])) {
|
|
1893
|
+
completeIndices.push(i);
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
const currentLength = () => systemMessages.length + conversations.reduce((acc, c) => acc + c.length, 0);
|
|
1897
|
+
if (completeIndices.length >= 2) {
|
|
1898
|
+
const lastCompleteIndex = completeIndices[completeIndices.length - 1];
|
|
1899
|
+
for (const idx of completeIndices) {
|
|
1900
|
+
if (idx === lastCompleteIndex) {
|
|
1901
|
+
break;
|
|
1902
|
+
}
|
|
1903
|
+
conversations[idx] = compressCompleteConversation(conversations[idx]);
|
|
1904
|
+
if (currentLength() <= maxMessages) {
|
|
1905
|
+
break;
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
let condensed = conversations.flat();
|
|
1910
|
+
condensed = markExpiredFirstHalf(condensed);
|
|
1911
|
+
if (systemMessages.length + condensed.length > maxMessages) {
|
|
1912
|
+
const nonSystemBudget = Math.max(0, maxMessages - systemMessages.length);
|
|
1913
|
+
condensed = condensed.slice(-nonSystemBudget);
|
|
1914
|
+
}
|
|
1915
|
+
return [...systemMessages, ...condensed];
|
|
1916
|
+
}
|
|
1721
1917
|
var NodeMemoryManager = class {
|
|
1722
1918
|
filePath;
|
|
1723
1919
|
maxMessages;
|
|
1920
|
+
onCondense;
|
|
1724
1921
|
/**
|
|
1725
1922
|
* @param dbPath 存储历史记录的本地文件路径(例如:'./.cline_memory.json')
|
|
1726
|
-
* @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认
|
|
1923
|
+
* @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认 200)
|
|
1727
1924
|
*/
|
|
1728
|
-
constructor(dbPath, maxMessages =
|
|
1925
|
+
constructor(dbPath, maxMessages = 200) {
|
|
1729
1926
|
this.filePath = path.resolve(dbPath);
|
|
1730
1927
|
this.maxMessages = maxMessages;
|
|
1731
1928
|
}
|
|
@@ -1760,19 +1957,22 @@ var NodeMemoryManager = class {
|
|
|
1760
1957
|
await this.writeMessages(messages);
|
|
1761
1958
|
await this.condenseHistory();
|
|
1762
1959
|
}
|
|
1960
|
+
/**
|
|
1961
|
+
* 压缩历史:
|
|
1962
|
+
* - system 消息保持不变
|
|
1963
|
+
* - 以 agent_attempt_completion 为边界切分完整对话,仅合并“后面还有其他完整对话”的旧完整对话
|
|
1964
|
+
* - 其余需要压缩的场景下,对前半段工具调用结果标记为“结果已过期”
|
|
1965
|
+
* - 同时触发 onCondense 回调(宿主可据此清空 dify conversationId 新开窗口)
|
|
1966
|
+
*/
|
|
1763
1967
|
async condenseHistory() {
|
|
1764
1968
|
const messages = await this.readMessages();
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
} else {
|
|
1773
|
-
condensed = messages.slice(sliceCount);
|
|
1774
|
-
}
|
|
1775
|
-
await this.writeMessages(condensed);
|
|
1969
|
+
const condensed = condenseMessages(messages, this.maxMessages);
|
|
1970
|
+
if (condensed === messages) {
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
await this.writeMessages(condensed);
|
|
1974
|
+
if (this.onCondense) {
|
|
1975
|
+
await this.onCondense();
|
|
1776
1976
|
}
|
|
1777
1977
|
}
|
|
1778
1978
|
async clear() {
|