@ai-zen/agents-sdk 1.0.0-alpha.22 → 1.0.0-alpha.23

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +216 -184
  2. package/README.md +13 -0
  3. package/README.zh.md +13 -0
  4. package/dist/agent-plugins/AutoMigratePlugin.js +1 -1
  5. package/dist/agent-plugins/UnknownToolHintPlugin.js +4 -4
  6. package/dist/config/constants.js +24 -24
  7. package/dist/config/factory-skills.js +56 -56
  8. package/dist/runtime/HandoffGuardPlugin.js +2 -2
  9. package/dist/runtime/HandoffService.js +22 -22
  10. package/dist/runtime/createAgent.js +2 -2
  11. package/dist/runtime/createModel.js +3 -3
  12. package/dist/scope/PermissionEvaluator.js +1 -1
  13. package/dist/scope/Scope.js +1 -1
  14. package/dist/scope/plugins/builtin/BatchEditTool.js +8 -8
  15. package/dist/scope/plugins/builtin/CopyTool.js +4 -4
  16. package/dist/scope/plugins/builtin/CwdTool.js +1 -1
  17. package/dist/scope/plugins/builtin/DownloadFileTool.js +8 -8
  18. package/dist/scope/plugins/builtin/EditTool.js +9 -9
  19. package/dist/scope/plugins/builtin/ExecTool.js +8 -8
  20. package/dist/scope/plugins/builtin/ExistTool.js +2 -2
  21. package/dist/scope/plugins/builtin/FindTextTool.js +10 -10
  22. package/dist/scope/plugins/builtin/GenerateImageTool.js +13 -13
  23. package/dist/scope/plugins/builtin/GlobTool.js +4 -4
  24. package/dist/scope/plugins/builtin/InspectFileTool.js +7 -7
  25. package/dist/scope/plugins/builtin/LsTool.js +5 -5
  26. package/dist/scope/plugins/builtin/MkdirTool.js +3 -3
  27. package/dist/scope/plugins/builtin/ReadTool.js +8 -8
  28. package/dist/scope/plugins/builtin/RenameTool.js +3 -3
  29. package/dist/scope/plugins/builtin/RmTool.js +3 -3
  30. package/dist/scope/plugins/builtin/SleepTool.js +6 -6
  31. package/dist/scope/plugins/builtin/SpawnTool.js +9 -9
  32. package/dist/scope/plugins/builtin/ViewImageTool.js +12 -12
  33. package/dist/scope/plugins/builtin/WriteTool.js +3 -3
  34. package/dist/scope/plugins/mcp/McpConnectionManager.js +7 -7
  35. package/dist/scope/plugins/mcp/discover.js +1 -1
  36. package/dist/scope/plugins/mcp/mcpTools.js +29 -29
  37. package/dist/scope/plugins/skills/discover.js +7 -7
  38. package/dist/scope/plugins/skills/skillTools.js +17 -17
  39. package/dist/scope/plugins/subagents/subAgentTools.js +2 -2
  40. package/dist/scope/plugins/usertools/discover.js +2 -2
  41. package/dist/shared/content-blocks.js +9 -9
  42. package/dist/shared/errors.js +3 -3
  43. package/package.json +10 -1
@@ -4,33 +4,33 @@ export class BatchEditTool extends SdkCallbackTool {
4
4
  function = {
5
5
  name: "batch_edit",
6
6
  description: [
7
- "批量编辑文件文本,可以优先使用这个工具对文件进行编辑。",
8
- "对同一个文件的多处修改应在一次调用中全部提交(replacements 数组),而不是并行多次调用 edit——并行编辑同一文件会互相覆盖。",
7
+ "Edit text in a file in batches. Prefer this tool when making edits to a file.",
8
+ "Submit all changes to the same file in a single call (the replacements array) instead of calling edit in parallel multiple times — parallel edits to the same file overwrite each other.",
9
9
  ].join("\n"),
10
10
  parameters: {
11
11
  type: "object",
12
12
  properties: {
13
13
  path: {
14
14
  type: "string",
15
- description: "文件路径",
15
+ description: "File path",
16
16
  },
17
17
  replacements: {
18
18
  type: "array",
19
- description: "要替换的文本数组",
19
+ description: "Array of replacements",
20
20
  items: {
21
21
  type: "object",
22
22
  properties: {
23
23
  oldText: {
24
24
  type: "string",
25
- description: "要替换的文本",
25
+ description: "Text to replace",
26
26
  },
27
27
  newText: {
28
28
  type: "string",
29
- description: "替换后的文本",
29
+ description: "Replacement text",
30
30
  },
31
31
  isReplaceAll: {
32
32
  type: "boolean",
33
- description: "是否替换所有匹配的文本(默认仅替换首次匹配)。使用此功能前应确保你提供的 oldText 足够精确,避免误替换",
33
+ description: "Whether to replace all matches (by default only the first match is replaced). Make sure the oldText you provide is precise enough to avoid unintended replacements",
34
34
  default: false,
35
35
  },
36
36
  },
@@ -60,7 +60,7 @@ export class BatchEditTool extends SdkCallbackTool {
60
60
  failed.push({
61
61
  oldText: replacement.oldText,
62
62
  newText: replacement.newText,
63
- reason: "文件中未精确匹配到要替换的文本",
63
+ reason: "No exact match found for the text to replace",
64
64
  });
65
65
  continue;
66
66
  }
@@ -3,21 +3,21 @@ import { SdkCallbackTool } from "./SdkCallbackTool.js";
3
3
  export class CopyTool extends SdkCallbackTool {
4
4
  function = {
5
5
  name: "copy",
6
- description: "复制文件或目录(相当于 cp 命令)",
6
+ description: "Copy a file or directory (equivalent to the cp command)",
7
7
  parameters: {
8
8
  type: "object",
9
9
  properties: {
10
10
  src: {
11
11
  type: "string",
12
- description: "源路径",
12
+ description: "Source path",
13
13
  },
14
14
  dest: {
15
15
  type: "string",
16
- description: "目标路径",
16
+ description: "Destination path",
17
17
  },
18
18
  recursive: {
19
19
  type: "boolean",
20
- description: "是否递归复制目录,复制目录时需设为 true",
20
+ description: "Whether to copy directories recursively; must be true when copying a directory",
21
21
  },
22
22
  },
23
23
  required: ["src", "dest"],
@@ -2,7 +2,7 @@ import { SdkCallbackTool } from "./SdkCallbackTool.js";
2
2
  export class CwdTool extends SdkCallbackTool {
3
3
  function = {
4
4
  name: "cwd",
5
- description: "获取当前工作目录 cwd",
5
+ description: "Get the current working directory (cwd)",
6
6
  parameters: {
7
7
  type: "object",
8
8
  properties: {},
@@ -4,17 +4,17 @@ import { SdkCallbackTool } from "./SdkCallbackTool.js";
4
4
  export class DownloadFileTool extends SdkCallbackTool {
5
5
  function = {
6
6
  name: "download_file",
7
- description: "从 URL 下载文件(图片、PDF 等二进制文件)并保存到本地磁盘。自动从 URL 推断文件名,也可指定保存路径。",
7
+ description: "Download a file from a URL (images, PDFs and other binary files) and save it to the local disk. The file name is inferred from the URL, or you can specify the save path.",
8
8
  parameters: {
9
9
  type: "object",
10
10
  properties: {
11
11
  url: {
12
12
  type: "string",
13
- description: "文件下载链接",
13
+ description: "URL to download the file from",
14
14
  },
15
15
  outputPath: {
16
16
  type: "string",
17
- description: "保存路径(可选)。可以是目录或完整文件路径。不指定则下载到当前工作目录,自动从 URL 推断文件名。",
17
+ description: "Save path (optional). May be a directory or a full file path. If omitted, the file is saved to the current working directory with the file name inferred from the URL.",
18
18
  },
19
19
  },
20
20
  required: ["url"],
@@ -30,7 +30,7 @@ export class DownloadFileTool extends SdkCallbackTool {
30
30
  try {
31
31
  const url = input.url;
32
32
  if (!url)
33
- throw new Error("url 不能为空");
33
+ throw new Error("url must not be empty");
34
34
  if (input.outputPath) {
35
35
  savePath = this.resolve(input.outputPath);
36
36
  const stat = await fsp.stat(savePath).catch(() => null);
@@ -48,7 +48,7 @@ export class DownloadFileTool extends SdkCallbackTool {
48
48
  await fsp.mkdir(path.dirname(savePath), { recursive: true });
49
49
  const response = await fetch(url, { signal });
50
50
  if (!response.ok) {
51
- throw new Error(`下载失败: HTTP ${response.status} ${response.statusText}`);
51
+ throw new Error(`Download failed: HTTP ${response.status} ${response.statusText}`);
52
52
  }
53
53
  const contentType = response.headers.get("content-type") || "";
54
54
  const arrayBuffer = await response.arrayBuffer();
@@ -61,7 +61,7 @@ export class DownloadFileTool extends SdkCallbackTool {
61
61
  filePath: path.resolve(savePath),
62
62
  fileSize: `${fileSizeKB} KB`,
63
63
  contentType,
64
- note: "文件已保存到本地",
64
+ note: "File saved to local disk",
65
65
  });
66
66
  }
67
67
  catch (error) {
@@ -71,12 +71,12 @@ export class DownloadFileTool extends SdkCallbackTool {
71
71
  return JSON.stringify({
72
72
  success: false,
73
73
  aborted: true,
74
- error: "下载被中断(aborted)",
74
+ error: "Download aborted",
75
75
  });
76
76
  }
77
77
  return JSON.stringify({
78
78
  success: false,
79
- error: error?.message || "下载失败",
79
+ error: error?.message || "Download failed",
80
80
  });
81
81
  }
82
82
  }
@@ -16,29 +16,29 @@ export class EditTool extends SdkCallbackTool {
16
16
  function = {
17
17
  name: "edit",
18
18
  description: [
19
- "编辑文件中的文本,支持精确替换或替换所有匹配项。",
19
+ "Edit text in a file, supporting exact replacement or replacing all matches.",
20
20
  "",
21
- "并发限制(重要):同一个文件禁止并行编辑。不要在同一轮回复中对同一路径发起多个 edit 调用——这些调用会各自读取旧内容再写回,后写者覆盖先写者,导致修改丢失或文件被写坏。",
22
- "如需对同一个文件施加多处修改,请改用 batch_edit,在一次调用里提交全部替换。只有编辑不同文件时才可以并行调用 edit。",
21
+ "Concurrency limit (important): never edit the same file in parallel. Do not issue multiple edit calls for the same path within one reply — each call reads the old content and writes it back, so the later write overwrites the earlier one, losing changes or corrupting the file.",
22
+ "To apply multiple changes to one file, use batch_edit instead and submit all replacements in a single call. Only edit different files in parallel.",
23
23
  ].join("\n"),
24
24
  parameters: {
25
25
  type: "object",
26
26
  properties: {
27
27
  path: {
28
28
  type: "string",
29
- description: "文件路径",
29
+ description: "File path",
30
30
  },
31
31
  oldText: {
32
32
  type: "string",
33
- description: "要替换的文本",
33
+ description: "Text to replace",
34
34
  },
35
35
  newText: {
36
36
  type: "string",
37
- description: "替换后的文本",
37
+ description: "Replacement text",
38
38
  },
39
39
  isReplaceAll: {
40
40
  type: "boolean",
41
- description: "是否替换所有匹配的文本(默认 false,仅替换首次匹配)。使用此功能前应确保你提供的 oldText 足够精确,避免误替换",
41
+ description: "Whether to replace all matches (default false, only the first match is replaced). Make sure the oldText you provide is precise enough to avoid unintended replacements",
42
42
  default: false,
43
43
  },
44
44
  },
@@ -59,7 +59,7 @@ export class EditTool extends SdkCallbackTool {
59
59
  const count = countOccurrences(content, oldText);
60
60
  if (count === 0) {
61
61
  return JSON.stringify({
62
- result: "文件中未精确匹配到要替换的文本",
62
+ result: "No exact match found for the text to replace",
63
63
  matchCount: 0,
64
64
  });
65
65
  }
@@ -69,7 +69,7 @@ export class EditTool extends SdkCallbackTool {
69
69
  await fsp.writeFile(filePath, newContent);
70
70
  if (!isReplaceAll && count > 1) {
71
71
  return JSON.stringify({
72
- result: `警告:未开启替换所有(isReplaceAll=false),但文本 "${oldText}" 在文件中出现了 ${count} 次,仅替换了首次匹配。如果希望替换所有匹配,请将 isReplaceAll 设为 true`,
72
+ result: `Warning: replace-all is off (isReplaceAll=false), but the text "${oldText}" occurs ${count} times in the file; only the first match was replaced. Set isReplaceAll to true to replace all matches`,
73
73
  matchCount: count,
74
74
  replacedCount: 1,
75
75
  });
@@ -4,17 +4,17 @@ import { DEFAULT_MAX_TOOL_OUTPUT, guardOutput, headTailPreview } from "./outputG
4
4
  export class ExecTool extends SdkCallbackTool {
5
5
  function = {
6
6
  name: "exec",
7
- description: "执行命令",
7
+ description: "Execute a shell command",
8
8
  parameters: {
9
9
  type: "object",
10
10
  properties: {
11
11
  command: {
12
12
  type: "string",
13
- description: "要执行的命令",
13
+ description: "Command to execute",
14
14
  },
15
15
  timeout: {
16
16
  type: "number",
17
- description: "超时时间(毫秒),必填。超时后会终止进程;超过该时长仍未完成即视为超时并终止。建议长时间运行的命令使用 spawn 异步执行。",
17
+ description: "Timeout in milliseconds (required). The process is killed on timeout. For long-running commands, prefer spawn to run them asynchronously.",
18
18
  },
19
19
  },
20
20
  required: ["command", "timeout"],
@@ -29,7 +29,7 @@ export class ExecTool extends SdkCallbackTool {
29
29
  const { timeout } = input;
30
30
  // 运行时双校验:timeout 必填且必须为正数
31
31
  if (typeof timeout !== "number" || !Number.isFinite(timeout) || timeout <= 0) {
32
- throw new Error(`exec: 参数 timeout 为必填项,且必须是正数(毫秒),当前值: ${JSON.stringify(timeout)}`);
32
+ throw new Error(`exec: timeout is required and must be a positive number (milliseconds), got: ${JSON.stringify(timeout)}`);
33
33
  }
34
34
  const signal = ctx?.signal;
35
35
  const result = await new Promise((resolve) => {
@@ -37,7 +37,7 @@ export class ExecTool extends SdkCallbackTool {
37
37
  if (signal?.aborted) {
38
38
  resolve({
39
39
  stdout: "",
40
- stderr: "进程已中断(aborted)",
40
+ stderr: "Process aborted",
41
41
  exitCode: null,
42
42
  killed: true,
43
43
  terminated: "aborted",
@@ -72,7 +72,7 @@ export class ExecTool extends SdkCallbackTool {
72
72
  // 即便 exec 回调因信号处理而未能及时触发,也立即返回中断结果,避免挂起
73
73
  settle({
74
74
  stdout: "",
75
- stderr: "进程已中断(aborted)",
75
+ stderr: "Process aborted",
76
76
  exitCode: null,
77
77
  killed: true,
78
78
  terminated: "aborted",
@@ -97,8 +97,8 @@ export class ExecTool extends SdkCallbackTool {
97
97
  const stderr = headTailPreview(result.stderr);
98
98
  return JSON.stringify({
99
99
  outputTooLarge: true,
100
- warning: `输出过大:stdout + stderr 合计 ${result.stdout.length + result.stderr.length} 字符,` +
101
- `超过上限 ${limit} 字符。完整内容已分文件落盘(stdout.log / stderr.log),可用 read 分批读取。`,
100
+ warning: `Output too large: stdout + stderr total ${result.stdout.length + result.stderr.length} characters, ` +
101
+ `exceeds the limit of ${limit} characters. Full content was written to separate files (stdout.log / stderr.log); use read to fetch it in chunks`,
102
102
  files: ctx.files,
103
103
  stdout: { chars: result.stdout.length, lines: countLines(result.stdout), ...stdout },
104
104
  stderr: { chars: result.stderr.length, lines: countLines(result.stderr), ...stderr },
@@ -3,13 +3,13 @@ import { SdkCallbackTool } from "./SdkCallbackTool.js";
3
3
  export class ExistTool extends SdkCallbackTool {
4
4
  function = {
5
5
  name: "exist",
6
- description: "检查文件或目录是否存在",
6
+ description: "Check whether a file or directory exists",
7
7
  parameters: {
8
8
  type: "object",
9
9
  properties: {
10
10
  path: {
11
11
  type: "string",
12
- description: "文件或目录路径",
12
+ description: "File or directory path",
13
13
  },
14
14
  },
15
15
  required: ["path"],
@@ -11,7 +11,7 @@ const DEFAULT_MAX_LINE_LENGTH = 200;
11
11
  export class FindTextTool extends SdkCallbackTool {
12
12
  function = {
13
13
  name: "find_text",
14
- description: "查找文本出现的位置,支持普通文本或正则匹配,返回文件名及具体行号、行内容。为避免结果撑爆上下文,默认限制总匹配数、单文件大小与行内容长度,超限会截断并标记 truncated。",
14
+ description: "Find where text occurs, supporting plain text or regex matching; returns file names with line numbers and line content. To avoid blowing up the context, the total match count, per-file size and line content length are limited by default, and results beyond the limits are truncated and marked truncated.",
15
15
  parameters: {
16
16
  type: "object",
17
17
  properties: {
@@ -25,30 +25,30 @@ export class FindTextTool extends SdkCallbackTool {
25
25
  },
26
26
  text: {
27
27
  type: "string",
28
- description: "要查找的文本(与 regex 二选一)",
28
+ description: "Text to search for (mutually exclusive with regex)",
29
29
  },
30
30
  regex: {
31
31
  type: "string",
32
- description: "正则表达式(与 text 二选一,例如 \\bconst\\s+\\w+)",
32
+ description: "Regular expression (mutually exclusive with text, e.g. \\bconst\\s+\\w+)",
33
33
  },
34
34
  exclude: {
35
35
  type: "array",
36
- description: "要排除的 glob 模式数组",
36
+ description: "Array of glob patterns to exclude",
37
37
  items: {
38
38
  type: "string",
39
39
  },
40
40
  },
41
41
  maxMatches: {
42
42
  type: "number",
43
- description: "总匹配数上限,默认 200",
43
+ description: "Maximum total matches (default 200)",
44
44
  },
45
45
  maxFileSize: {
46
46
  type: "number",
47
- description: "单文件大小上限(字节),超过则跳过该文件,默认 307200(300KB)",
47
+ description: "Maximum file size in bytes; larger files are skipped (default 307200, i.e. 300KB)",
48
48
  },
49
49
  maxLineLength: {
50
50
  type: "number",
51
- description: "行内容与匹配子串的截断长度,默认 200",
51
+ description: "Truncation length for line content and matched substrings (default 200)",
52
52
  },
53
53
  },
54
54
  required: ["path", "pattern"],
@@ -65,7 +65,7 @@ export class FindTextTool extends SdkCallbackTool {
65
65
  const text = input.text;
66
66
  const regexStr = input.regex;
67
67
  if (!text && !regexStr) {
68
- return "请提供 text 或 regex 参数";
68
+ return "Provide either the text or the regex parameter";
69
69
  }
70
70
  const regex = regexStr ? new RegExp(regexStr) : null;
71
71
  const maxMatches = input.maxMatches ?? DEFAULT_MAX_MATCHES;
@@ -153,8 +153,8 @@ export class FindTextTool extends SdkCallbackTool {
153
153
  const preview = headPreview(json);
154
154
  return JSON.stringify({
155
155
  outputTooLarge: true,
156
- warning: `输出过大:结果共 ${json.length} 字符,超过上限 ${limit} 字符。` +
157
- `完整结果已落盘(result.json),可用 read 分批读取。`,
156
+ warning: `Output too large: the results total ${json.length} characters, exceeding the limit of ${limit} characters. ` +
157
+ `The full result has been written to disk (result.json) and can be read in chunks with read.`,
158
158
  files: ctx.files,
159
159
  chars: json.length,
160
160
  ...preview,
@@ -15,25 +15,25 @@ export class GenerateImageTool extends SdkCallbackTool {
15
15
  }
16
16
  function = {
17
17
  name: "generate_image",
18
- description: "根据文字描述生成图片,返回图片 URL 列表(JSON)。如需查看图片内容,请用 view_image 工具(传入图片 URL);如需保存到本地,用 download_file 下载。",
18
+ description: "Generate an image from a text description and return a list of image URLs (JSON). To inspect the image content, use the view_image tool (pass the image URL); to save it locally, use download_file.",
19
19
  parameters: {
20
20
  type: "object",
21
21
  properties: {
22
22
  prompt: {
23
23
  type: "string",
24
- description: "所需图像的文本描述,应详细描述画面内容、风格、构图等",
24
+ description: "Text description of the desired image; describe the subject, style, composition and other details",
25
25
  },
26
26
  model: {
27
27
  type: "string",
28
- description: "图片模型 ID,不指定则使用配置中的默认图片模型;若指定的模型不存在,本工具会返回当前可用的模型列表。",
28
+ description: "Image model ID. Defaults to the configured default image model; if the given model does not exist, this tool returns the list of currently available models.",
29
29
  },
30
30
  size: {
31
31
  type: "string",
32
- description: '图片尺寸。不指定则使用模型的默认尺寸。cogview系列: 1024x1024, 768x1344, 864x1152, 1344x768, 1152x864, 1440x720, 720x1440。glm-image: 1280x1280, 1568x1056, 1056x1568, 1472x1088, 1088x1472, 1728x960, 960x1728。',
32
+ description: 'Image size. Defaults to the model default size. cogview series: 1024x1024, 768x1344, 864x1152, 1344x768, 1152x864, 1440x720, 720x1440. glm-image: 1280x1280, 1568x1056, 1056x1568, 1472x1088, 1088x1472, 1728x960, 960x1728.',
33
33
  },
34
34
  quality: {
35
35
  type: "string",
36
- description: "图像质量。hd: 精细(约20秒), standard: 快速(约5-10秒)",
36
+ description: "Image quality. hd: fine (about 20 seconds), standard: fast (about 5-10 seconds)",
37
37
  enum: ["hd", "standard"],
38
38
  },
39
39
  },
@@ -49,7 +49,7 @@ export class GenerateImageTool extends SdkCallbackTool {
49
49
  try {
50
50
  const prompt = input.prompt;
51
51
  if (!prompt || !prompt.trim()) {
52
- return JSON.stringify({ success: false, error: "prompt 不能为空" });
52
+ return JSON.stringify({ success: false, error: "prompt must not be empty" });
53
53
  }
54
54
  const config = this.env.config;
55
55
  const imageModels = config.imageModels;
@@ -57,7 +57,7 @@ export class GenerateImageTool extends SdkCallbackTool {
57
57
  if (!imageModels || imageModels.length === 0) {
58
58
  return JSON.stringify({
59
59
  success: false,
60
- error: "未配置图片生成模型。请在配置中添加 imageModels。",
60
+ error: "No image generation model configured. Add imageModels to the configuration.",
61
61
  });
62
62
  }
63
63
  const modelId = input.model || defaultImageModel;
@@ -67,20 +67,20 @@ export class GenerateImageTool extends SdkCallbackTool {
67
67
  if (!imageModel) {
68
68
  return JSON.stringify({
69
69
  success: false,
70
- error: `图片模型 "${modelId}" 不存在。可用的图片模型: ${imageModels.map((m) => m.id).join(", ")}`,
70
+ error: `Image model "${modelId}" does not exist. Available image models: ${imageModels.map((m) => m.id).join(", ")}`,
71
71
  });
72
72
  }
73
73
  const endpoint = config.endpoints.find((e) => e.id === imageModel.endpointId);
74
74
  if (!endpoint) {
75
75
  return JSON.stringify({
76
76
  success: false,
77
- error: `图片模型 "${imageModel.name}" 对应的端点 "${imageModel.endpointId}" 未配置`,
77
+ error: `Endpoint "${imageModel.endpointId}" for image model "${imageModel.name}" is not configured`,
78
78
  });
79
79
  }
80
80
  if (!endpoint.apiKey) {
81
81
  return JSON.stringify({
82
82
  success: false,
83
- error: `端点 "${endpoint.name}" 的 API Key 未设置`,
83
+ error: `API Key for endpoint "${endpoint.name}" is not set`,
84
84
  });
85
85
  }
86
86
  const client = new OpenAI({
@@ -107,7 +107,7 @@ export class GenerateImageTool extends SdkCallbackTool {
107
107
  modelId: imageModel.id,
108
108
  created: result.created,
109
109
  images: urls.map((url, i) => ({ index: i, url })),
110
- note: "图片临时链接有效期为30天。如需查看图片内容,可用 view_image 工具(传入图片 URL);如需保存,可用 download_file 下载。",
110
+ note: "Image links are temporary and expire after 30 days. To inspect the image content, use the view_image tool (pass the image URL); to save it, use download_file.",
111
111
  }, null, 2);
112
112
  }
113
113
  catch (error) {
@@ -116,12 +116,12 @@ export class GenerateImageTool extends SdkCallbackTool {
116
116
  return JSON.stringify({
117
117
  success: false,
118
118
  aborted: true,
119
- error: "图片生成被中断(aborted)",
119
+ error: "Image generation aborted",
120
120
  });
121
121
  }
122
122
  return JSON.stringify({
123
123
  success: false,
124
- error: error instanceof Error ? error.message : "图片生成失败",
124
+ error: error instanceof Error ? error.message : "Image generation failed",
125
125
  });
126
126
  }
127
127
  }
@@ -4,7 +4,7 @@ import { DEFAULT_MAX_TOOL_OUTPUT, guardOutput, headPreview } from "./outputGuard
4
4
  export class GlobTool extends SdkCallbackTool {
5
5
  function = {
6
6
  name: "glob",
7
- description: "使用 glob 模式递归扫描和查找文件。这是进行文件系统搜索的首选工具,功能远优于简单的 'ls' 列表命令。当你需要查找特定类型的文件、遍历目录树或需要排除特定文件时,请优先使用此函数。",
7
+ description: "Recursively scan for and find files using a glob pattern. This is the preferred tool for file system searches, far better than a plain 'ls' listing. Prefer this tool when you need to find files of a specific type, walk a directory tree, or exclude specific files.",
8
8
  parameters: {
9
9
  type: "object",
10
10
  properties: {
@@ -68,9 +68,9 @@ export class GlobTool extends SdkCallbackTool {
68
68
  const preview = headPreview(json);
69
69
  return JSON.stringify({
70
70
  outputTooLarge: true,
71
- warning: `输出过大:匹配结果共 ${json.length} 字符,超过上限 ${limit} 字符。` +
72
- `完整结果已落盘(result.json),可用 read 分批读取;` +
73
- `也可用更精确的 pattern 或 exclude 缩小范围后重新查找。`,
71
+ warning: `Output too large: the matches total ${json.length} characters, exceeding the limit of ${limit} characters. ` +
72
+ `The full result has been written to disk (result.json) and can be read in chunks with read; ` +
73
+ `alternatively, narrow the scope with a more precise pattern or exclude and search again.`,
74
74
  files: ctx.files,
75
75
  chars: json.length,
76
76
  ...preview,
@@ -12,17 +12,17 @@ import { DEFAULT_MAX_TOOL_OUTPUT } from "./outputGuard.js";
12
12
  export class InspectFileTool extends SdkCallbackTool {
13
13
  function = {
14
14
  name: "inspect_file",
15
- description: "勘察文件的结构概况:总行数、总字符数、最长行的列数及其行索引、平均列数、行尾类型(LF/CRLF)。可选返回每行的列数映射。适用于在 read 之前先了解文件规模,再决定用 range 读取哪一段。仅做流式扫描,不受 read 的 300KB 读取阈值限制。",
15
+ description: "Inspect a file's structural overview: total lines, total characters, the longest line's column count and its line index, average column count, and line ending type (LF/CRLF). Optionally returns a per-line column count map. Useful for sizing up a file before reading it, so you can decide which range to read. Streaming scan only, not subject to the 300KB read threshold of read.",
16
16
  parameters: {
17
17
  type: "object",
18
18
  properties: {
19
19
  path: {
20
20
  type: "string",
21
- description: "文件路径",
21
+ description: "File path",
22
22
  },
23
23
  withColCountMap: {
24
24
  type: "boolean",
25
- description: "是否返回每行列数映射(对象:行索引 → 列数,行索引从 0 开始)。默认 false。行数很多时该映射可能过大,此时将只返回概况字段。",
25
+ description: "Whether to return a per-line column count map (object: line index → column count, line index is 0-based). Default false. When there are many lines the map may be too large; in that case only the overview fields are returned.",
26
26
  default: false,
27
27
  },
28
28
  },
@@ -38,7 +38,7 @@ export class InspectFileTool extends SdkCallbackTool {
38
38
  const filePath = this.resolve(input.path);
39
39
  const stats = await fsp.stat(filePath);
40
40
  if (!stats.isFile()) {
41
- return `"${filePath}" 不是文件`;
41
+ return `"${filePath}" is not a file`;
42
42
  }
43
43
  const withColCountMap = input.withColCountMap ?? false;
44
44
  const result = await scan(filePath, withColCountMap);
@@ -61,9 +61,9 @@ export class InspectFileTool extends SdkCallbackTool {
61
61
  return JSON.stringify({
62
62
  ...summary,
63
63
  colCountMapOmitted: true,
64
- warning: `行长度明细过大:概况与 colCountMap 合计 ${payload.length} 字符,` +
65
- `超过上限 ${limit} 字符,未落盘返回。文件规模超出可处理范围,` +
66
- `建议改用 find_text 定位后再按 range 分段读取。`,
64
+ warning: `Line length details too large: overview plus colCountMap total ${payload.length} characters, ` +
65
+ `exceeding the limit of ${limit} characters; not written to disk. The file is beyond the supported size, ` +
66
+ `so use find_text to locate the target and then read it in chunks by range.`,
67
67
  });
68
68
  }
69
69
  return payload;
@@ -4,13 +4,13 @@ import { DEFAULT_MAX_TOOL_OUTPUT, guardOutput, headPreview } from "./outputGuard
4
4
  export class LsTool extends SdkCallbackTool {
5
5
  function = {
6
6
  name: "ls",
7
- description: "列出目录",
7
+ description: "List a directory",
8
8
  parameters: {
9
9
  type: "object",
10
10
  properties: {
11
11
  path: {
12
12
  type: "string",
13
- description: "目录路径",
13
+ description: "Directory path",
14
14
  },
15
15
  },
16
16
  required: ["path"],
@@ -35,9 +35,9 @@ export class LsTool extends SdkCallbackTool {
35
35
  const preview = headPreview(json);
36
36
  return JSON.stringify({
37
37
  outputTooLarge: true,
38
- warning: `输出过大:目录条目共 ${entries.length} 项、${json.length} 字符,超过上限 ${limit} 字符。` +
39
- `完整列表已落盘(result.json),可用 read 分批读取;` +
40
- `也可改用 glob 按模式筛选。`,
38
+ warning: `Output too large: ${entries.length} directory entries, ${json.length} characters in total, exceeding the limit of ${limit} characters. ` +
39
+ `The full list has been written to disk (result.json) and can be read in chunks with read; ` +
40
+ `alternatively, use glob to filter by pattern.`,
41
41
  files: ctx.files,
42
42
  chars: json.length,
43
43
  ...preview,
@@ -3,17 +3,17 @@ import { SdkCallbackTool } from "./SdkCallbackTool.js";
3
3
  export class MkdirTool extends SdkCallbackTool {
4
4
  function = {
5
5
  name: "mkdir",
6
- description: "创建目录",
6
+ description: "Create a directory",
7
7
  parameters: {
8
8
  type: "object",
9
9
  properties: {
10
10
  path: {
11
11
  type: "string",
12
- description: "目录路径",
12
+ description: "Directory path",
13
13
  },
14
14
  recursive: {
15
15
  type: "boolean",
16
- description: "是否递归创建子目录",
16
+ description: "Whether to create parent directories recursively",
17
17
  },
18
18
  },
19
19
  required: ["path"],
@@ -4,18 +4,18 @@ import { DEFAULT_MAX_TOOL_OUTPUT, guardOutput } from "./outputGuard.js";
4
4
  export class ReadTool extends SdkCallbackTool {
5
5
  function = {
6
6
  name: "read",
7
- description: "读取文件",
7
+ description: "Read a file",
8
8
  parameters: {
9
9
  type: "object",
10
10
  properties: {
11
11
  path: {
12
12
  type: "string",
13
- description: "文件路径",
13
+ description: "File path",
14
14
  },
15
15
  range: {
16
16
  type: "array",
17
- description: "读取范围 [起始行, 起始列, 结束行, 结束列],行列均从 0 开始,-1 表示末位;" +
18
- "例如 [0, 0, 100, -1] 表示从第 0 行第 0 列读到第 100 行最后一列。省略则读取整个文件。",
17
+ description: "Read range [startLine, startCol, endLine, endCol]; rows and columns are 0-based, -1 means the last position. " +
18
+ "For example, [0, 0, 100, -1] reads from line 0, column 0 through the last column of line 100; omit it to read the whole file.",
19
19
  items: {
20
20
  type: "number",
21
21
  },
@@ -34,7 +34,7 @@ export class ReadTool extends SdkCallbackTool {
34
34
  const stats = await fsp.stat(filePath);
35
35
  // 读取阈值:文件过大时拒绝读取(与输出阈值 maxToolOutput 无关)
36
36
  if (stats.size > 300 * 1024) {
37
- throw new Error(`文件过大,无法读取,当前文件大小 ${stats.size} 字节`);
37
+ throw new Error(`File too large to read: ${stats.size} bytes`);
38
38
  }
39
39
  const content = await fsp.readFile(filePath, "utf-8");
40
40
  const output = input.range ? sliceByRange(content, input.range) : content;
@@ -44,9 +44,9 @@ export class ReadTool extends SdkCallbackTool {
44
44
  tool: "read",
45
45
  content: output,
46
46
  isOverLimit: (text) => text.length > limit,
47
- buildWarning: () => `读取内容过大:本次输出 ${output.length} 字符,超过上限 ${limit} 字符,未落盘。` +
48
- `文件共 ${countLines(content)} 行、${content.length} 字符,` +
49
- `请用 range 参数分范围读取(形如 [起始行, 起始列, 结束行, 结束列],行列从 0 开始,-1 表示末位)。`,
47
+ buildWarning: () => `Output too large: this read returned ${output.length} characters, exceeding the limit of ${limit} characters; not written to disk. ` +
48
+ `The file has ${countLines(content)} lines and ${content.length} characters. ` +
49
+ `Use the range parameter to read it in chunks (format [startLine, startCol, endLine, endCol], 0-based, -1 means the last position).`,
50
50
  });
51
51
  }
52
52
  catch (error) {