@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IApiHandler, ApiProviderInfo, AgentMessage, ApiStreamChunk, IMemoryManager, IPromptManager, IMcpManager, McpServerInfo, McpTool, McpToolCallResult, IAgentRuntime, ClineAgent } from '../index.js';
|
|
2
2
|
export { AgentDefaultTool, AgentOptions, AgentState, AssistantMessageContent, CustomToolDefinition, 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-
|
|
3
|
+
import { W as WebMemoryManager } from '../WebMemoryManager-BZNjinHF.js';
|
|
4
4
|
|
|
5
5
|
interface NodeApiHandlerConfig {
|
|
6
6
|
providerId?: string;
|
|
@@ -20,6 +20,7 @@ declare class NodeApiHandler implements IApiHandler {
|
|
|
20
20
|
constructor(config: NodeApiHandlerConfig);
|
|
21
21
|
getModelInfo(): ApiProviderInfo;
|
|
22
22
|
createMessage(systemPrompt: string, messages: AgentMessage[]): AsyncGenerator<ApiStreamChunk, void, unknown>;
|
|
23
|
+
clearConversationId(): void;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -29,9 +30,10 @@ declare class NodeApiHandler implements IApiHandler {
|
|
|
29
30
|
declare class NodeMemoryManager implements IMemoryManager {
|
|
30
31
|
private filePath;
|
|
31
32
|
private maxMessages;
|
|
33
|
+
onCondense?: () => void | Promise<void>;
|
|
32
34
|
/**
|
|
33
35
|
* @param dbPath 存储历史记录的本地文件路径(例如:'./.cline_memory.json')
|
|
34
|
-
* @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认
|
|
36
|
+
* @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认 200)
|
|
35
37
|
*/
|
|
36
38
|
constructor(dbPath: string, maxMessages?: number);
|
|
37
39
|
private ensureFile;
|
|
@@ -39,6 +41,13 @@ declare class NodeMemoryManager implements IMemoryManager {
|
|
|
39
41
|
private writeMessages;
|
|
40
42
|
getHistory(): Promise<AgentMessage[]>;
|
|
41
43
|
appendMessage(message: AgentMessage): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* 压缩历史:
|
|
46
|
+
* - system 消息保持不变
|
|
47
|
+
* - 以 agent_attempt_completion 为边界切分完整对话,仅合并“后面还有其他完整对话”的旧完整对话
|
|
48
|
+
* - 其余需要压缩的场景下,对前半段工具调用结果标记为“结果已过期”
|
|
49
|
+
* - 同时触发 onCondense 回调(宿主可据此清空 dify conversationId 新开窗口)
|
|
50
|
+
*/
|
|
42
51
|
condenseHistory(): Promise<void>;
|
|
43
52
|
clear(): Promise<void>;
|
|
44
53
|
}
|
|
@@ -167,9 +167,11 @@ return (
|
|
|
167
167
|
},
|
|
168
168
|
{
|
|
169
169
|
name: "agent_read_file" /* FILE_READ */,
|
|
170
|
-
description: "Read the contents of a specific file.",
|
|
170
|
+
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.",
|
|
171
171
|
params: {
|
|
172
|
-
path: "(required) The absolute or relative file path to read."
|
|
172
|
+
path: "(required) The absolute or relative file path to read.",
|
|
173
|
+
start_line: "(optional) The 1-based line number to start reading from (inclusive). Defaults to 1.",
|
|
174
|
+
end_line: "(optional) The 1-based line number to stop reading at (inclusive). Defaults to the last line."
|
|
173
175
|
}
|
|
174
176
|
},
|
|
175
177
|
{
|
|
@@ -612,7 +614,17 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
612
614
|
"dockerfile",
|
|
613
615
|
"makefile",
|
|
614
616
|
"properties",
|
|
615
|
-
"lock"
|
|
617
|
+
"lock",
|
|
618
|
+
"node-version",
|
|
619
|
+
"nvmrc",
|
|
620
|
+
"editorconfig",
|
|
621
|
+
"prettierrc",
|
|
622
|
+
"eslintrc",
|
|
623
|
+
"babelrc",
|
|
624
|
+
"stylelintrc",
|
|
625
|
+
"huskyrc",
|
|
626
|
+
"commitlintrc",
|
|
627
|
+
"lintstagedrc"
|
|
616
628
|
]);
|
|
617
629
|
var AgentState = /* @__PURE__ */ ((AgentState2) => {
|
|
618
630
|
AgentState2["IDLE"] = "idle";
|
|
@@ -703,6 +715,13 @@ var ClineAgent = class {
|
|
|
703
715
|
}
|
|
704
716
|
this.skillDir = this.runtime.path.join(this.runtime.env.cwd(), ".agents/skills");
|
|
705
717
|
this.rulesDir = this.runtime.path.join(this.runtime.env.cwd(), ".agents/rules");
|
|
718
|
+
const originalOnCondense = this.memory.onCondense;
|
|
719
|
+
this.memory.onCondense = async () => {
|
|
720
|
+
this.api.clearConversationId();
|
|
721
|
+
if (originalOnCondense) {
|
|
722
|
+
await originalOnCondense();
|
|
723
|
+
}
|
|
724
|
+
};
|
|
706
725
|
}
|
|
707
726
|
setState(newState) {
|
|
708
727
|
this.state = newState;
|
|
@@ -760,16 +779,6 @@ var ClineAgent = class {
|
|
|
760
779
|
async getAgentRules() {
|
|
761
780
|
const cwd = this.runtime.env.cwd();
|
|
762
781
|
const rules = [];
|
|
763
|
-
const agentsMdPath = this.runtime.path.join(cwd, "Agents.md");
|
|
764
|
-
try {
|
|
765
|
-
if (await this.runtime.fs.fileExists(agentsMdPath)) {
|
|
766
|
-
const content = await this.runtime.fs.readFile(agentsMdPath);
|
|
767
|
-
if (content && content.trim()) {
|
|
768
|
-
rules.push(content.trim());
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
} catch (e) {
|
|
772
|
-
}
|
|
773
782
|
try {
|
|
774
783
|
if (await this.runtime.fs.fileExists(this.rulesDir)) {
|
|
775
784
|
const files = await this.runtime.fs.readDir(this.rulesDir);
|
|
@@ -795,6 +804,28 @@ ${content.trim()}\`\`\``);
|
|
|
795
804
|
}
|
|
796
805
|
} catch (e) {
|
|
797
806
|
}
|
|
807
|
+
try {
|
|
808
|
+
const getAgentsMdPath = async () => {
|
|
809
|
+
const agentsMdPathList = [
|
|
810
|
+
this.runtime.path.join(cwd, "AGENTS.md"),
|
|
811
|
+
this.runtime.path.join(cwd, "Agents.md"),
|
|
812
|
+
this.runtime.path.join(cwd, "agents.md")
|
|
813
|
+
];
|
|
814
|
+
for (const item of agentsMdPathList) {
|
|
815
|
+
if (await this.runtime.fs.fileExists(item)) {
|
|
816
|
+
return item;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
const agentsMdPath = await getAgentsMdPath();
|
|
821
|
+
if (agentsMdPath) {
|
|
822
|
+
const content = await this.runtime.fs.readFile(agentsMdPath);
|
|
823
|
+
if (content && content.trim()) {
|
|
824
|
+
rules.push(content.trim());
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
} catch (e) {
|
|
828
|
+
}
|
|
798
829
|
return rules.join("\n---\n");
|
|
799
830
|
}
|
|
800
831
|
async getSkills() {
|
|
@@ -932,7 +963,8 @@ ${finalPrompt}` : finalPrompt;
|
|
|
932
963
|
if (path3) {
|
|
933
964
|
try {
|
|
934
965
|
const content = await this.runtime.fs.readFile(path3);
|
|
935
|
-
|
|
966
|
+
const result = this.formatFileContentWithLineNumbers(content, tool.params.start_line, tool.params.end_line);
|
|
967
|
+
toolResults.push({ tool, result });
|
|
936
968
|
} catch (error) {
|
|
937
969
|
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
938
970
|
}
|
|
@@ -945,6 +977,8 @@ ${finalPrompt}` : finalPrompt;
|
|
|
945
977
|
const result = await this.runtime.terminal.execCommand(cmd);
|
|
946
978
|
toolResults.push({ tool, result: `STDOUT: ${result.stdout}
|
|
947
979
|
STDERR: ${result.stderr}` });
|
|
980
|
+
} else {
|
|
981
|
+
toolResults.push({ tool, result: "Error: No command provided." });
|
|
948
982
|
}
|
|
949
983
|
} else if (tool.name === "agent_write_to_file" /* FILE_NEW */) {
|
|
950
984
|
const path3 = tool.params.path;
|
|
@@ -956,12 +990,18 @@ STDERR: ${result.stderr}` });
|
|
|
956
990
|
} catch (error) {
|
|
957
991
|
toolResults.push({ tool, result: `Error: ${error.message}` });
|
|
958
992
|
}
|
|
993
|
+
} else {
|
|
994
|
+
toolResults.push({ tool, result: "Error: Missing path or content." });
|
|
959
995
|
}
|
|
960
996
|
} else if (tool.name === "agent_list_files" /* LIST_FILES */) {
|
|
961
997
|
const path3 = tool.params.path || this.runtime.env.cwd();
|
|
998
|
+
const recursive = tool.params.recursive === "true";
|
|
962
999
|
try {
|
|
963
|
-
const files = await this.runtime.fs.readDir(path3);
|
|
964
|
-
toolResults.push({
|
|
1000
|
+
const files = await this.runtime.fs.readDir(path3, { recursive });
|
|
1001
|
+
toolResults.push({
|
|
1002
|
+
tool,
|
|
1003
|
+
result: files.map((f) => `${f.isDirectory() ? "[DIR] " : " "}${f.name}${f.isDirectory() ? "/" : ""}`).join("\n")
|
|
1004
|
+
});
|
|
965
1005
|
} catch (e) {
|
|
966
1006
|
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
967
1007
|
}
|
|
@@ -1101,10 +1141,15 @@ STDERR: ${result.stderr}` });
|
|
|
1101
1141
|
try {
|
|
1102
1142
|
const res = await fetch(url);
|
|
1103
1143
|
const text = await res.text();
|
|
1104
|
-
|
|
1144
|
+
const MAX_LEN = 8e3;
|
|
1145
|
+
const truncated = text.length > MAX_LEN ? `${text.substring(0, MAX_LEN)}
|
|
1146
|
+
...[truncated ${text.length - MAX_LEN} chars]` : text;
|
|
1147
|
+
toolResults.push({ tool, result: truncated });
|
|
1105
1148
|
} catch (e) {
|
|
1106
1149
|
toolResults.push({ tool, result: `Error: ${e.message}` });
|
|
1107
1150
|
}
|
|
1151
|
+
} else {
|
|
1152
|
+
toolResults.push({ tool, result: "Error: No url provided." });
|
|
1108
1153
|
}
|
|
1109
1154
|
} else if (tool.name === "agent_focus_chain" /* TODO */) {
|
|
1110
1155
|
toolResults.push({ tool, result: `Focus chain updated successfully.` });
|
|
@@ -1192,6 +1237,44 @@ ${this.promptManager.getUsedToolResultInstructions()}`;
|
|
|
1192
1237
|
}
|
|
1193
1238
|
return this.getPrompt();
|
|
1194
1239
|
}
|
|
1240
|
+
/**
|
|
1241
|
+
* 将文件内容格式化为带行号文本(模拟编辑器行号显示),
|
|
1242
|
+
* 支持通过 start_line / end_line 截取读取范围。
|
|
1243
|
+
*/
|
|
1244
|
+
formatFileContentWithLineNumbers(content, startLineParam, endLineParam) {
|
|
1245
|
+
const lines = content.split(/\r?\n/);
|
|
1246
|
+
const normalized = lines.length > 0 && lines[lines.length - 1] === "" ? lines.slice(0, -1) : lines;
|
|
1247
|
+
const total = normalized.length;
|
|
1248
|
+
if (total === 0) {
|
|
1249
|
+
return "1|(empty file)";
|
|
1250
|
+
}
|
|
1251
|
+
const parseLine = (val, fallback) => {
|
|
1252
|
+
if (val === void 0 || val === null || val.trim() === "") {
|
|
1253
|
+
return fallback;
|
|
1254
|
+
}
|
|
1255
|
+
const num = Number.parseInt(val, 10);
|
|
1256
|
+
if (!Number.isFinite(num) || num <= 0) {
|
|
1257
|
+
return fallback;
|
|
1258
|
+
}
|
|
1259
|
+
return num;
|
|
1260
|
+
};
|
|
1261
|
+
const start = parseLine(startLineParam, 1);
|
|
1262
|
+
const end = parseLine(endLineParam, total);
|
|
1263
|
+
if (start > end) {
|
|
1264
|
+
return `Error: start_line (${start}) cannot be greater than end_line (${end}).`;
|
|
1265
|
+
}
|
|
1266
|
+
if (start > total) {
|
|
1267
|
+
return `Error: start_line (${start}) is out of range. The file has ${total} lines.`;
|
|
1268
|
+
}
|
|
1269
|
+
const from = Math.max(1, start);
|
|
1270
|
+
const to = Math.min(total, end);
|
|
1271
|
+
const width = String(total).length;
|
|
1272
|
+
const selected = normalized.slice(from - 1, to);
|
|
1273
|
+
const numbered = selected.map((line, idx) => `${String(from + idx).padStart(width)}|${line}`).join("\n");
|
|
1274
|
+
const header = from === 1 && to === total ? `1-${total}/${total}` : `${from}-${to}/${total}`;
|
|
1275
|
+
return `${header}
|
|
1276
|
+
${numbered}`;
|
|
1277
|
+
}
|
|
1195
1278
|
/**
|
|
1196
1279
|
* 简单的 Diff 解析与替换辅助方法
|
|
1197
1280
|
*/
|
|
@@ -1441,6 +1524,9 @@ var NodeApiHandler = class {
|
|
|
1441
1524
|
};
|
|
1442
1525
|
}
|
|
1443
1526
|
}
|
|
1527
|
+
clearConversationId() {
|
|
1528
|
+
this.conversationId = void 0;
|
|
1529
|
+
}
|
|
1444
1530
|
};
|
|
1445
1531
|
|
|
1446
1532
|
// src/prompt/NodePromptManager.ts
|
|
@@ -1745,14 +1831,125 @@ ${this?.getEnvironmentInfoPrompt?.(context)}`;
|
|
|
1745
1831
|
4. Ensure the list accurately reflects the current state`;
|
|
1746
1832
|
}
|
|
1747
1833
|
};
|
|
1834
|
+
|
|
1835
|
+
// src/memory/condense.ts
|
|
1836
|
+
var TOOL_RESULT_CONTENT_REGEX = /<tool_call_result[^>]*>[\s\S]*?<\/tool_call_result>/;
|
|
1837
|
+
var EXPIRED_MARKER_TEXT = "[The result has expired and has been removed from the context]";
|
|
1838
|
+
var ATTEMPT_TOOL_NAME = "agent_attempt_completion" /* ATTEMPT */;
|
|
1839
|
+
function getContentString(content) {
|
|
1840
|
+
return typeof content === "string" ? content : "";
|
|
1841
|
+
}
|
|
1842
|
+
function isToolResultMessage(content) {
|
|
1843
|
+
if (typeof content !== "string") {
|
|
1844
|
+
return false;
|
|
1845
|
+
}
|
|
1846
|
+
return TOOL_RESULT_CONTENT_REGEX.test(content);
|
|
1847
|
+
}
|
|
1848
|
+
function isAttemptCallMessage(message) {
|
|
1849
|
+
if (message.role !== "assistant") {
|
|
1850
|
+
return false;
|
|
1851
|
+
}
|
|
1852
|
+
const content = getContentString(message.content);
|
|
1853
|
+
return new RegExp(`<${ATTEMPT_TOOL_NAME}[\\s>]`).test(content) || new RegExp(`<name>\\s*${ATTEMPT_TOOL_NAME}\\s*</name>`).test(content);
|
|
1854
|
+
}
|
|
1855
|
+
function markExpiredToolResult(content) {
|
|
1856
|
+
if (typeof content !== "string") {
|
|
1857
|
+
return content;
|
|
1858
|
+
}
|
|
1859
|
+
return content.replace(
|
|
1860
|
+
/(<tool_call_result[^>]*tool_name="([^"]*)"[^>]*>)[\s\S]*?(<\/tool_call_result>)/g,
|
|
1861
|
+
(_match, openTag, _toolName, closeTag) => {
|
|
1862
|
+
return `${openTag}
|
|
1863
|
+
${EXPIRED_MARKER_TEXT}
|
|
1864
|
+
${closeTag}`;
|
|
1865
|
+
}
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1868
|
+
function splitConversations(messages) {
|
|
1869
|
+
const conversations = [];
|
|
1870
|
+
let current = [];
|
|
1871
|
+
for (const message of messages) {
|
|
1872
|
+
current.push(message);
|
|
1873
|
+
if (isAttemptCallMessage(message)) {
|
|
1874
|
+
conversations.push(current);
|
|
1875
|
+
current = [];
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
if (current.length > 0) {
|
|
1879
|
+
conversations.push(current);
|
|
1880
|
+
}
|
|
1881
|
+
return conversations;
|
|
1882
|
+
}
|
|
1883
|
+
function isCompleteConversation(conversation) {
|
|
1884
|
+
if (conversation.length === 0) {
|
|
1885
|
+
return false;
|
|
1886
|
+
}
|
|
1887
|
+
return isAttemptCallMessage(conversation[conversation.length - 1]);
|
|
1888
|
+
}
|
|
1889
|
+
function compressCompleteConversation(conversation) {
|
|
1890
|
+
if (conversation.length <= 2) {
|
|
1891
|
+
return conversation;
|
|
1892
|
+
}
|
|
1893
|
+
const head = conversation[0];
|
|
1894
|
+
const tail = conversation[conversation.length - 1];
|
|
1895
|
+
const mergedMarker = {
|
|
1896
|
+
role: "user",
|
|
1897
|
+
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."
|
|
1898
|
+
};
|
|
1899
|
+
return [head, mergedMarker, tail];
|
|
1900
|
+
}
|
|
1901
|
+
function markExpiredFirstHalf(messages) {
|
|
1902
|
+
const half = Math.floor(messages.length / 2);
|
|
1903
|
+
return messages.map((message, index) => {
|
|
1904
|
+
if (index < half && isToolResultMessage(message.content)) {
|
|
1905
|
+
return { ...message, content: markExpiredToolResult(message.content) };
|
|
1906
|
+
}
|
|
1907
|
+
return message;
|
|
1908
|
+
});
|
|
1909
|
+
}
|
|
1910
|
+
function condenseMessages(messages, maxMessages) {
|
|
1911
|
+
if (messages.length <= maxMessages) {
|
|
1912
|
+
return messages;
|
|
1913
|
+
}
|
|
1914
|
+
const systemMessages = messages.filter((message) => message.role === "system");
|
|
1915
|
+
const restMessages = messages.filter((message) => message.role !== "system");
|
|
1916
|
+
const conversations = splitConversations(restMessages);
|
|
1917
|
+
const completeIndices = [];
|
|
1918
|
+
for (let i = 0; i < conversations.length; i++) {
|
|
1919
|
+
if (isCompleteConversation(conversations[i])) {
|
|
1920
|
+
completeIndices.push(i);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
const currentLength = () => systemMessages.length + conversations.reduce((acc, c) => acc + c.length, 0);
|
|
1924
|
+
if (completeIndices.length >= 2) {
|
|
1925
|
+
const lastCompleteIndex = completeIndices[completeIndices.length - 1];
|
|
1926
|
+
for (const idx of completeIndices) {
|
|
1927
|
+
if (idx === lastCompleteIndex) {
|
|
1928
|
+
break;
|
|
1929
|
+
}
|
|
1930
|
+
conversations[idx] = compressCompleteConversation(conversations[idx]);
|
|
1931
|
+
if (currentLength() <= maxMessages) {
|
|
1932
|
+
break;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
let condensed = conversations.flat();
|
|
1937
|
+
condensed = markExpiredFirstHalf(condensed);
|
|
1938
|
+
if (systemMessages.length + condensed.length > maxMessages) {
|
|
1939
|
+
const nonSystemBudget = Math.max(0, maxMessages - systemMessages.length);
|
|
1940
|
+
condensed = condensed.slice(-nonSystemBudget);
|
|
1941
|
+
}
|
|
1942
|
+
return [...systemMessages, ...condensed];
|
|
1943
|
+
}
|
|
1748
1944
|
var NodeMemoryManager = class {
|
|
1749
1945
|
filePath;
|
|
1750
1946
|
maxMessages;
|
|
1947
|
+
onCondense;
|
|
1751
1948
|
/**
|
|
1752
1949
|
* @param dbPath 存储历史记录的本地文件路径(例如:'./.cline_memory.json')
|
|
1753
|
-
* @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认
|
|
1950
|
+
* @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认 200)
|
|
1754
1951
|
*/
|
|
1755
|
-
constructor(dbPath, maxMessages =
|
|
1952
|
+
constructor(dbPath, maxMessages = 200) {
|
|
1756
1953
|
this.filePath = path__namespace.resolve(dbPath);
|
|
1757
1954
|
this.maxMessages = maxMessages;
|
|
1758
1955
|
}
|
|
@@ -1787,19 +1984,22 @@ var NodeMemoryManager = class {
|
|
|
1787
1984
|
await this.writeMessages(messages);
|
|
1788
1985
|
await this.condenseHistory();
|
|
1789
1986
|
}
|
|
1987
|
+
/**
|
|
1988
|
+
* 压缩历史:
|
|
1989
|
+
* - system 消息保持不变
|
|
1990
|
+
* - 以 agent_attempt_completion 为边界切分完整对话,仅合并“后面还有其他完整对话”的旧完整对话
|
|
1991
|
+
* - 其余需要压缩的场景下,对前半段工具调用结果标记为“结果已过期”
|
|
1992
|
+
* - 同时触发 onCondense 回调(宿主可据此清空 dify conversationId 新开窗口)
|
|
1993
|
+
*/
|
|
1790
1994
|
async condenseHistory() {
|
|
1791
1995
|
const messages = await this.readMessages();
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
} else {
|
|
1800
|
-
condensed = messages.slice(sliceCount);
|
|
1801
|
-
}
|
|
1802
|
-
await this.writeMessages(condensed);
|
|
1996
|
+
const condensed = condenseMessages(messages, this.maxMessages);
|
|
1997
|
+
if (condensed === messages) {
|
|
1998
|
+
return;
|
|
1999
|
+
}
|
|
2000
|
+
await this.writeMessages(condensed);
|
|
2001
|
+
if (this.onCondense) {
|
|
2002
|
+
await this.onCondense();
|
|
1803
2003
|
}
|
|
1804
2004
|
}
|
|
1805
2005
|
async clear() {
|