@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.
@@ -2,16 +2,16 @@ import { IMemoryManager, AgentMessage } from './index.js';
2
2
 
3
3
  /**
4
4
  * Web 端 MemoryManager 实现
5
- * 使用 IndexedDB (通过封装) 或 LocalStorage / SessionStorage 来持久化对话历史。
6
- * 此处提供一个基于 SessionStorage / 内存 回退的轻量实现。
5
+ * 使用 SessionStorage / 内存 回退来持久化对话历史。
7
6
  */
8
7
  declare class WebMemoryManager implements IMemoryManager {
9
8
  private storageKey;
10
9
  private maxMessages;
11
10
  private inMemoryFallback;
11
+ onCondense?: () => void | Promise<void>;
12
12
  /**
13
13
  * @param storageKey SessionStorage 中的键名
14
- * @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认 150)
14
+ * @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认 200)
15
15
  */
16
16
  constructor(storageKey?: string, maxMessages?: number);
17
17
  private isStorageAvailable;
@@ -19,6 +19,13 @@ declare class WebMemoryManager implements IMemoryManager {
19
19
  private writeMessages;
20
20
  getHistory(): Promise<AgentMessage[]>;
21
21
  appendMessage(message: AgentMessage): Promise<void>;
22
+ /**
23
+ * 压缩历史:
24
+ * - system 消息保持不变
25
+ * - 以 agent_attempt_completion 为边界切分完整对话,仅合并“后面还有其他完整对话”的旧完整对话
26
+ * - 其余需要压缩的场景下,对前半段工具调用结果标记为“结果已过期”
27
+ * - 同时触发 onCondense 回调(宿主可据此清空 dify conversationId 新开窗口)
28
+ */
22
29
  condenseHistory(): Promise<void>;
23
30
  clear(): Promise<void>;
24
31
  }
@@ -2,16 +2,16 @@ import { IMemoryManager, AgentMessage } from './index.mjs';
2
2
 
3
3
  /**
4
4
  * Web 端 MemoryManager 实现
5
- * 使用 IndexedDB (通过封装) 或 LocalStorage / SessionStorage 来持久化对话历史。
6
- * 此处提供一个基于 SessionStorage / 内存 回退的轻量实现。
5
+ * 使用 SessionStorage / 内存 回退来持久化对话历史。
7
6
  */
8
7
  declare class WebMemoryManager implements IMemoryManager {
9
8
  private storageKey;
10
9
  private maxMessages;
11
10
  private inMemoryFallback;
11
+ onCondense?: () => void | Promise<void>;
12
12
  /**
13
13
  * @param storageKey SessionStorage 中的键名
14
- * @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认 150)
14
+ * @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认 200)
15
15
  */
16
16
  constructor(storageKey?: string, maxMessages?: number);
17
17
  private isStorageAvailable;
@@ -19,6 +19,13 @@ declare class WebMemoryManager implements IMemoryManager {
19
19
  private writeMessages;
20
20
  getHistory(): Promise<AgentMessage[]>;
21
21
  appendMessage(message: AgentMessage): Promise<void>;
22
+ /**
23
+ * 压缩历史:
24
+ * - system 消息保持不变
25
+ * - 以 agent_attempt_completion 为边界切分完整对话,仅合并“后面还有其他完整对话”的旧完整对话
26
+ * - 其余需要压缩的场景下,对前半段工具调用结果标记为“结果已过期”
27
+ * - 同时触发 onCondense 回调(宿主可据此清空 dify conversationId 新开窗口)
28
+ */
22
29
  condenseHistory(): Promise<void>;
23
30
  clear(): Promise<void>;
24
31
  }
package/dist/index.d.mts CHANGED
@@ -113,9 +113,11 @@ declare const defaultTools: readonly [{
113
113
  readonly example: "## Example: Requesting to make targeted edits to a file\n\n<agent_replace_in_file>\n<path>src/components/App.tsx</path>\n<diff>\n------- SEARCH\nimport React from 'react';\n=======\nimport React, { useState } from 'react';\n+++++++ REPLACE\n\n------- SEARCH\nfunction handleSubmit() {\n saveData();\n setLoading(false);\n}\n\n=======\n+++++++ REPLACE\n\n------- SEARCH\nreturn (\n <div>\n=======\nfunction handleSubmit() {\n saveData();\n setLoading(false);\n}\n\nreturn (\n <div>\n+++++++ REPLACE\n</diff>\n<task_progress>\n- [x] Set up project structure\n- [x] Install dependencies\n- [ ] Create components\n- [ ] Test application\n</task_progress>\n</agent_replace_in_file>";
114
114
  }, {
115
115
  readonly name: AgentDefaultTool.FILE_READ;
116
- readonly description: "Read the contents of a specific file.";
116
+ readonly 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.";
117
117
  readonly params: {
118
118
  readonly path: "(required) The absolute or relative file path to read.";
119
+ readonly start_line: "(optional) The 1-based line number to start reading from (inclusive). Defaults to 1.";
120
+ readonly end_line: "(optional) The 1-based line number to stop reading at (inclusive). Defaults to the last line.";
119
121
  };
120
122
  }, {
121
123
  readonly name: AgentDefaultTool.FILE_NEW;
@@ -350,6 +352,10 @@ interface IMemoryManager {
350
352
  * 截断或压缩过长历史
351
353
  */
352
354
  condenseHistory?(): Promise<void>;
355
+ /**
356
+ * 当历史被压缩时触发(宿主可在此处理例如 dify provider 清空 conversationId 等逻辑)
357
+ */
358
+ onCondense?: () => void | Promise<void>;
353
359
  /**
354
360
  * 清空记忆
355
361
  */
@@ -389,6 +395,7 @@ interface IApiHandler {
389
395
  * 获取当前配置的模型信息
390
396
  */
391
397
  getModelInfo(): ApiProviderInfo;
398
+ clearConversationId(): void;
392
399
  }
393
400
 
394
401
  /**
@@ -541,6 +548,11 @@ declare class ClineAgent {
541
548
  * 检查自上次获取 prompt 后是否有工具集被激活,若有则刷新缓存。
542
549
  */
543
550
  private getCurrentPrompt;
551
+ /**
552
+ * 将文件内容格式化为带行号文本(模拟编辑器行号显示),
553
+ * 支持通过 start_line / end_line 截取读取范围。
554
+ */
555
+ private formatFileContentWithLineNumbers;
544
556
  /**
545
557
  * 简单的 Diff 解析与替换辅助方法
546
558
  */
package/dist/index.d.ts CHANGED
@@ -113,9 +113,11 @@ declare const defaultTools: readonly [{
113
113
  readonly example: "## Example: Requesting to make targeted edits to a file\n\n<agent_replace_in_file>\n<path>src/components/App.tsx</path>\n<diff>\n------- SEARCH\nimport React from 'react';\n=======\nimport React, { useState } from 'react';\n+++++++ REPLACE\n\n------- SEARCH\nfunction handleSubmit() {\n saveData();\n setLoading(false);\n}\n\n=======\n+++++++ REPLACE\n\n------- SEARCH\nreturn (\n <div>\n=======\nfunction handleSubmit() {\n saveData();\n setLoading(false);\n}\n\nreturn (\n <div>\n+++++++ REPLACE\n</diff>\n<task_progress>\n- [x] Set up project structure\n- [x] Install dependencies\n- [ ] Create components\n- [ ] Test application\n</task_progress>\n</agent_replace_in_file>";
114
114
  }, {
115
115
  readonly name: AgentDefaultTool.FILE_READ;
116
- readonly description: "Read the contents of a specific file.";
116
+ readonly 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.";
117
117
  readonly params: {
118
118
  readonly path: "(required) The absolute or relative file path to read.";
119
+ readonly start_line: "(optional) The 1-based line number to start reading from (inclusive). Defaults to 1.";
120
+ readonly end_line: "(optional) The 1-based line number to stop reading at (inclusive). Defaults to the last line.";
119
121
  };
120
122
  }, {
121
123
  readonly name: AgentDefaultTool.FILE_NEW;
@@ -350,6 +352,10 @@ interface IMemoryManager {
350
352
  * 截断或压缩过长历史
351
353
  */
352
354
  condenseHistory?(): Promise<void>;
355
+ /**
356
+ * 当历史被压缩时触发(宿主可在此处理例如 dify provider 清空 conversationId 等逻辑)
357
+ */
358
+ onCondense?: () => void | Promise<void>;
353
359
  /**
354
360
  * 清空记忆
355
361
  */
@@ -389,6 +395,7 @@ interface IApiHandler {
389
395
  * 获取当前配置的模型信息
390
396
  */
391
397
  getModelInfo(): ApiProviderInfo;
398
+ clearConversationId(): void;
392
399
  }
393
400
 
394
401
  /**
@@ -541,6 +548,11 @@ declare class ClineAgent {
541
548
  * 检查自上次获取 prompt 后是否有工具集被激活,若有则刷新缓存。
542
549
  */
543
550
  private getCurrentPrompt;
551
+ /**
552
+ * 将文件内容格式化为带行号文本(模拟编辑器行号显示),
553
+ * 支持通过 start_line / end_line 截取读取范围。
554
+ */
555
+ private formatFileContentWithLineNumbers;
544
556
  /**
545
557
  * 简单的 Diff 解析与替换辅助方法
546
558
  */
package/dist/index.js CHANGED
@@ -136,9 +136,11 @@ return (
136
136
  },
137
137
  {
138
138
  name: "agent_read_file" /* FILE_READ */,
139
- description: "Read the contents of a specific file.",
139
+ 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.",
140
140
  params: {
141
- path: "(required) The absolute or relative file path to read."
141
+ path: "(required) The absolute or relative file path to read.",
142
+ start_line: "(optional) The 1-based line number to start reading from (inclusive). Defaults to 1.",
143
+ end_line: "(optional) The 1-based line number to stop reading at (inclusive). Defaults to the last line."
142
144
  }
143
145
  },
144
146
  {
@@ -581,7 +583,17 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
581
583
  "dockerfile",
582
584
  "makefile",
583
585
  "properties",
584
- "lock"
586
+ "lock",
587
+ "node-version",
588
+ "nvmrc",
589
+ "editorconfig",
590
+ "prettierrc",
591
+ "eslintrc",
592
+ "babelrc",
593
+ "stylelintrc",
594
+ "huskyrc",
595
+ "commitlintrc",
596
+ "lintstagedrc"
585
597
  ]);
586
598
  var AgentState = /* @__PURE__ */ ((AgentState2) => {
587
599
  AgentState2["IDLE"] = "idle";
@@ -672,6 +684,13 @@ var ClineAgent = class {
672
684
  }
673
685
  this.skillDir = this.runtime.path.join(this.runtime.env.cwd(), ".agents/skills");
674
686
  this.rulesDir = this.runtime.path.join(this.runtime.env.cwd(), ".agents/rules");
687
+ const originalOnCondense = this.memory.onCondense;
688
+ this.memory.onCondense = async () => {
689
+ this.api.clearConversationId();
690
+ if (originalOnCondense) {
691
+ await originalOnCondense();
692
+ }
693
+ };
675
694
  }
676
695
  setState(newState) {
677
696
  this.state = newState;
@@ -729,16 +748,6 @@ var ClineAgent = class {
729
748
  async getAgentRules() {
730
749
  const cwd = this.runtime.env.cwd();
731
750
  const rules = [];
732
- const agentsMdPath = this.runtime.path.join(cwd, "Agents.md");
733
- try {
734
- if (await this.runtime.fs.fileExists(agentsMdPath)) {
735
- const content = await this.runtime.fs.readFile(agentsMdPath);
736
- if (content && content.trim()) {
737
- rules.push(content.trim());
738
- }
739
- }
740
- } catch (e) {
741
- }
742
751
  try {
743
752
  if (await this.runtime.fs.fileExists(this.rulesDir)) {
744
753
  const files = await this.runtime.fs.readDir(this.rulesDir);
@@ -764,6 +773,28 @@ ${content.trim()}\`\`\``);
764
773
  }
765
774
  } catch (e) {
766
775
  }
776
+ try {
777
+ const getAgentsMdPath = async () => {
778
+ const agentsMdPathList = [
779
+ this.runtime.path.join(cwd, "AGENTS.md"),
780
+ this.runtime.path.join(cwd, "Agents.md"),
781
+ this.runtime.path.join(cwd, "agents.md")
782
+ ];
783
+ for (const item of agentsMdPathList) {
784
+ if (await this.runtime.fs.fileExists(item)) {
785
+ return item;
786
+ }
787
+ }
788
+ };
789
+ const agentsMdPath = await getAgentsMdPath();
790
+ if (agentsMdPath) {
791
+ const content = await this.runtime.fs.readFile(agentsMdPath);
792
+ if (content && content.trim()) {
793
+ rules.push(content.trim());
794
+ }
795
+ }
796
+ } catch (e) {
797
+ }
767
798
  return rules.join("\n---\n");
768
799
  }
769
800
  async getSkills() {
@@ -901,7 +932,8 @@ ${finalPrompt}` : finalPrompt;
901
932
  if (path) {
902
933
  try {
903
934
  const content = await this.runtime.fs.readFile(path);
904
- toolResults.push({ tool, result: content });
935
+ const result = this.formatFileContentWithLineNumbers(content, tool.params.start_line, tool.params.end_line);
936
+ toolResults.push({ tool, result });
905
937
  } catch (error) {
906
938
  toolResults.push({ tool, result: `Error: ${error.message}` });
907
939
  }
@@ -914,6 +946,8 @@ ${finalPrompt}` : finalPrompt;
914
946
  const result = await this.runtime.terminal.execCommand(cmd);
915
947
  toolResults.push({ tool, result: `STDOUT: ${result.stdout}
916
948
  STDERR: ${result.stderr}` });
949
+ } else {
950
+ toolResults.push({ tool, result: "Error: No command provided." });
917
951
  }
918
952
  } else if (tool.name === "agent_write_to_file" /* FILE_NEW */) {
919
953
  const path = tool.params.path;
@@ -925,12 +959,18 @@ STDERR: ${result.stderr}` });
925
959
  } catch (error) {
926
960
  toolResults.push({ tool, result: `Error: ${error.message}` });
927
961
  }
962
+ } else {
963
+ toolResults.push({ tool, result: "Error: Missing path or content." });
928
964
  }
929
965
  } else if (tool.name === "agent_list_files" /* LIST_FILES */) {
930
966
  const path = tool.params.path || this.runtime.env.cwd();
967
+ const recursive = tool.params.recursive === "true";
931
968
  try {
932
- const files = await this.runtime.fs.readDir(path);
933
- toolResults.push({ tool, result: files.map((f) => f.name + (f.isDirectory() ? "/" : "")).join("\n") });
969
+ const files = await this.runtime.fs.readDir(path, { recursive });
970
+ toolResults.push({
971
+ tool,
972
+ result: files.map((f) => `${f.isDirectory() ? "[DIR] " : " "}${f.name}${f.isDirectory() ? "/" : ""}`).join("\n")
973
+ });
934
974
  } catch (e) {
935
975
  toolResults.push({ tool, result: `Error: ${e.message}` });
936
976
  }
@@ -1070,10 +1110,15 @@ STDERR: ${result.stderr}` });
1070
1110
  try {
1071
1111
  const res = await fetch(url);
1072
1112
  const text = await res.text();
1073
- toolResults.push({ tool, result: text.substring(0, 3e3) });
1113
+ const MAX_LEN = 8e3;
1114
+ const truncated = text.length > MAX_LEN ? `${text.substring(0, MAX_LEN)}
1115
+ ...[truncated ${text.length - MAX_LEN} chars]` : text;
1116
+ toolResults.push({ tool, result: truncated });
1074
1117
  } catch (e) {
1075
1118
  toolResults.push({ tool, result: `Error: ${e.message}` });
1076
1119
  }
1120
+ } else {
1121
+ toolResults.push({ tool, result: "Error: No url provided." });
1077
1122
  }
1078
1123
  } else if (tool.name === "agent_focus_chain" /* TODO */) {
1079
1124
  toolResults.push({ tool, result: `Focus chain updated successfully.` });
@@ -1161,6 +1206,44 @@ ${this.promptManager.getUsedToolResultInstructions()}`;
1161
1206
  }
1162
1207
  return this.getPrompt();
1163
1208
  }
1209
+ /**
1210
+ * 将文件内容格式化为带行号文本(模拟编辑器行号显示),
1211
+ * 支持通过 start_line / end_line 截取读取范围。
1212
+ */
1213
+ formatFileContentWithLineNumbers(content, startLineParam, endLineParam) {
1214
+ const lines = content.split(/\r?\n/);
1215
+ const normalized = lines.length > 0 && lines[lines.length - 1] === "" ? lines.slice(0, -1) : lines;
1216
+ const total = normalized.length;
1217
+ if (total === 0) {
1218
+ return "1|(empty file)";
1219
+ }
1220
+ const parseLine = (val, fallback) => {
1221
+ if (val === void 0 || val === null || val.trim() === "") {
1222
+ return fallback;
1223
+ }
1224
+ const num = Number.parseInt(val, 10);
1225
+ if (!Number.isFinite(num) || num <= 0) {
1226
+ return fallback;
1227
+ }
1228
+ return num;
1229
+ };
1230
+ const start = parseLine(startLineParam, 1);
1231
+ const end = parseLine(endLineParam, total);
1232
+ if (start > end) {
1233
+ return `Error: start_line (${start}) cannot be greater than end_line (${end}).`;
1234
+ }
1235
+ if (start > total) {
1236
+ return `Error: start_line (${start}) is out of range. The file has ${total} lines.`;
1237
+ }
1238
+ const from = Math.max(1, start);
1239
+ const to = Math.min(total, end);
1240
+ const width = String(total).length;
1241
+ const selected = normalized.slice(from - 1, to);
1242
+ const numbered = selected.map((line, idx) => `${String(from + idx).padStart(width)}|${line}`).join("\n");
1243
+ const header = from === 1 && to === total ? `1-${total}/${total}` : `${from}-${to}/${total}`;
1244
+ return `${header}
1245
+ ${numbered}`;
1246
+ }
1164
1247
  /**
1165
1248
  * 简单的 Diff 解析与替换辅助方法
1166
1249
  */