@ai-zen/agents-sdk 1.0.0-alpha.2 → 1.0.0-alpha.21

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 (67) hide show
  1. package/CHANGELOG.md +368 -0
  2. package/README.md +15 -14
  3. package/README.zh.md +15 -14
  4. package/dist/agent-plugins/AutoMigratePlugin.d.ts +35 -9
  5. package/dist/agent-plugins/AutoMigratePlugin.js +23 -10
  6. package/dist/agent-plugins/ContextGuardPlugin.d.ts +14 -16
  7. package/dist/agent-plugins/ContextGuardPlugin.js +13 -17
  8. package/dist/config/ConfigManager.d.ts +57 -8
  9. package/dist/config/ConfigManager.js +166 -25
  10. package/dist/config/constants.d.ts +7 -0
  11. package/dist/config/constants.js +38 -13
  12. package/dist/config/factory-skills.d.ts +33 -0
  13. package/dist/config/factory-skills.js +126 -0
  14. package/dist/config/migrate.d.ts +67 -0
  15. package/dist/config/migrate.js +127 -0
  16. package/dist/config/reasoning-presets.d.ts +37 -0
  17. package/dist/config/reasoning-presets.js +64 -0
  18. package/dist/index.d.ts +11 -5
  19. package/dist/index.js +10 -2
  20. package/dist/runtime/HandoffGuardPlugin.d.ts +35 -0
  21. package/dist/runtime/HandoffGuardPlugin.js +65 -0
  22. package/dist/runtime/HandoffService.d.ts +245 -0
  23. package/dist/runtime/HandoffService.js +472 -0
  24. package/dist/runtime/SdkAgent.d.ts +7 -1
  25. package/dist/runtime/SdkAgent.js +3 -0
  26. package/dist/runtime/createAgent.d.ts +16 -1
  27. package/dist/runtime/createAgent.js +10 -5
  28. package/dist/runtime/createModel.d.ts +8 -4
  29. package/dist/runtime/createModel.js +19 -2
  30. package/dist/scope/plugins/builtin/BatchEditTool.js +5 -2
  31. package/dist/scope/plugins/builtin/DownloadFileTool.js +1 -1
  32. package/dist/scope/plugins/builtin/EditTool.js +6 -1
  33. package/dist/scope/plugins/builtin/ExecTool.js +2 -2
  34. package/dist/scope/plugins/builtin/FindTextTool.js +6 -6
  35. package/dist/scope/plugins/builtin/GenerateImageTool.d.ts +1 -1
  36. package/dist/scope/plugins/builtin/GenerateImageTool.js +7 -7
  37. package/dist/scope/plugins/builtin/GlobTool.js +1 -1
  38. package/dist/scope/plugins/builtin/InspectFileTool.d.ts +2 -2
  39. package/dist/scope/plugins/builtin/InspectFileTool.js +5 -5
  40. package/dist/scope/plugins/builtin/LsTool.js +1 -1
  41. package/dist/scope/plugins/builtin/{ReadFileTool.d.ts → ReadTool.d.ts} +1 -1
  42. package/dist/scope/plugins/builtin/{ReadFileTool.js → ReadTool.js} +3 -3
  43. package/dist/scope/plugins/builtin/{ExecAsyncTool.d.ts → SpawnTool.d.ts} +1 -1
  44. package/dist/scope/plugins/builtin/{ExecAsyncTool.js → SpawnTool.js} +2 -2
  45. package/dist/scope/plugins/builtin/ViewImageTool.d.ts +6 -4
  46. package/dist/scope/plugins/builtin/ViewImageTool.js +43 -59
  47. package/dist/scope/plugins/builtin/{WriteFileTool.d.ts → WriteTool.d.ts} +1 -1
  48. package/dist/scope/plugins/builtin/{WriteFileTool.js → WriteTool.js} +2 -2
  49. package/dist/scope/plugins/builtin/index.d.ts +4 -4
  50. package/dist/scope/plugins/builtin/index.js +7 -7
  51. package/dist/scope/plugins/builtin/outputGuard.d.ts +2 -2
  52. package/dist/scope/plugins/builtin/outputGuard.js +1 -1
  53. package/dist/scope/plugins/mcp/McpScopePlugin.d.ts +1 -1
  54. package/dist/scope/plugins/mcp/McpScopePlugin.js +4 -3
  55. package/dist/scope/plugins/mcp/mcpTools.d.ts +9 -3
  56. package/dist/scope/plugins/mcp/mcpTools.js +196 -18
  57. package/dist/scope/plugins/skills/discover.js +1 -2
  58. package/dist/scope/plugins/skills/skillTools.js +0 -2
  59. package/dist/scope/plugins/subagents/subAgentTools.js +2 -1
  60. package/dist/shared/content-blocks.d.ts +108 -0
  61. package/dist/shared/content-blocks.js +253 -0
  62. package/dist/shared/errors.d.ts +4 -8
  63. package/dist/shared/errors.js +8 -14
  64. package/dist/types/index.d.ts +103 -10
  65. package/package.json +3 -3
  66. package/dist/runtime/TaskMigrationService.d.ts +0 -128
  67. package/dist/runtime/TaskMigrationService.js +0 -211
@@ -14,7 +14,7 @@ export class ExecTool extends SdkCallbackTool {
14
14
  },
15
15
  timeout: {
16
16
  type: "number",
17
- description: "超时时间(毫秒),必填。超时后会终止进程;超过该时长仍未完成即视为超时并终止。建议长时间运行的命令使用 exec_async 异步执行。",
17
+ description: "超时时间(毫秒),必填。超时后会终止进程;超过该时长仍未完成即视为超时并终止。建议长时间运行的命令使用 spawn 异步执行。",
18
18
  },
19
19
  },
20
20
  required: ["command", "timeout"],
@@ -98,7 +98,7 @@ export class ExecTool extends SdkCallbackTool {
98
98
  return JSON.stringify({
99
99
  outputTooLarge: true,
100
100
  warning: `输出过大:stdout + stderr 合计 ${result.stdout.length + result.stderr.length} 字符,` +
101
- `超过上限 ${limit} 字符。完整内容已分文件落盘(stdout.log / stderr.log),可用 readFile 分批读取。`,
101
+ `超过上限 ${limit} 字符。完整内容已分文件落盘(stdout.log / stderr.log),可用 read 分批读取。`,
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 },
@@ -4,13 +4,13 @@ import { SdkCallbackTool } from "./SdkCallbackTool.js";
4
4
  import { DEFAULT_MAX_TOOL_OUTPUT, guardOutput, headPreview } from "./outputGuard.js";
5
5
  /** 默认总匹配数上限 */
6
6
  const DEFAULT_MAX_MATCHES = 200;
7
- /** 默认文件大小上限(字节,与 ReadFileTool 的 300KB 口径一致) */
7
+ /** 默认文件大小上限(字节,与 ReadTool 的 300KB 口径一致) */
8
8
  const DEFAULT_MAX_FILE_SIZE = 300 * 1024;
9
9
  /** 默认行内容/匹配子串截断长度 */
10
10
  const DEFAULT_MAX_LINE_LENGTH = 200;
11
11
  export class FindTextTool extends SdkCallbackTool {
12
12
  function = {
13
- name: "findText",
13
+ name: "find_text",
14
14
  description: "查找文本出现的位置,支持普通文本或正则匹配,返回文件名及具体行号、行内容。为避免结果撑爆上下文,默认限制总匹配数、单文件大小与行内容长度,超限会截断并标记 truncated。",
15
15
  parameters: {
16
16
  type: "object",
@@ -89,12 +89,12 @@ export class FindTextTool extends SdkCallbackTool {
89
89
  break; // 中断:stat 后再次检查
90
90
  if (!stats.isFile())
91
91
  continue;
92
- // 文件过大则跳过(避免撑爆上下文;与 ReadFileTool 300KB 口径一致,但此处为跳过而非报错)
92
+ // 文件过大则跳过(避免撑爆上下文;与 ReadTool 300KB 口径一致,但此处为跳过而非报错)
93
93
  if (stats.size > maxFileSize)
94
94
  continue;
95
95
  const content = await fsp.readFile(fullPath, "utf-8");
96
96
  if (signal?.aborted)
97
- break; // 中断:readFile 后再次检查
97
+ break; // 中断:read 后再次检查
98
98
  const lines = content.split("\n");
99
99
  const matches = [];
100
100
  for (let index = 0; index < lines.length; index++) {
@@ -145,7 +145,7 @@ export class FindTextTool extends SdkCallbackTool {
145
145
  async protect(json) {
146
146
  const limit = this.env.config.maxToolOutput ?? DEFAULT_MAX_TOOL_OUTPUT;
147
147
  return await guardOutput({
148
- tool: "findText",
148
+ tool: "find_text",
149
149
  content: json,
150
150
  isOverLimit: (text) => text.length > limit,
151
151
  dump: (_dir, text) => [{ name: "result.json", content: text }],
@@ -154,7 +154,7 @@ export class FindTextTool extends SdkCallbackTool {
154
154
  return JSON.stringify({
155
155
  outputTooLarge: true,
156
156
  warning: `输出过大:结果共 ${json.length} 字符,超过上限 ${limit} 字符。` +
157
- `完整结果已落盘(result.json),可用 readFile 分批读取。`,
157
+ `完整结果已落盘(result.json),可用 read 分批读取。`,
158
158
  files: ctx.files,
159
159
  chars: json.length,
160
160
  ...preview,
@@ -4,7 +4,7 @@ import type { AgentDefinition, AppConfig, ToolEnv } from "../../../types/index.j
4
4
  /**
5
5
  * 根据文字描述生成图片。**统一返回字符串**(JSON,含图片 URL 列表):
6
6
  * 生成结果如何处理(查看/下载/转发)由模型与上层自由决定——
7
- * 需要让模型看图时,模型可主动调用 viewImage(URL);需要保存时用 downloadFile。
7
+ * 需要让模型看图时,模型可主动调用 view_image(URL);需要保存时用 download_file。
8
8
  *
9
9
  * 依赖 AppConfig 中的 imageModels 和 defaultImageModel 配置(isAvailable 声明),
10
10
  * 未配置时由 buildTools 过滤,不暴露给 Agent。
@@ -3,7 +3,7 @@ import { SdkCallbackTool } from "./SdkCallbackTool.js";
3
3
  /**
4
4
  * 根据文字描述生成图片。**统一返回字符串**(JSON,含图片 URL 列表):
5
5
  * 生成结果如何处理(查看/下载/转发)由模型与上层自由决定——
6
- * 需要让模型看图时,模型可主动调用 viewImage(URL);需要保存时用 downloadFile。
6
+ * 需要让模型看图时,模型可主动调用 view_image(URL);需要保存时用 download_file。
7
7
  *
8
8
  * 依赖 AppConfig 中的 imageModels 和 defaultImageModel 配置(isAvailable 声明),
9
9
  * 未配置时由 buildTools 过滤,不暴露给 Agent。
@@ -14,8 +14,8 @@ export class GenerateImageTool extends SdkCallbackTool {
14
14
  return !!config.defaultImageModel;
15
15
  }
16
16
  function = {
17
- name: "generateImage",
18
- description: "根据文字描述生成图片,返回图片 URL 列表(JSON)。如需查看图片内容,请用 viewImage 工具(传入图片 URL);如需保存到本地,用 downloadFile 下载。",
17
+ name: "generate_image",
18
+ description: "根据文字描述生成图片,返回图片 URL 列表(JSON)。如需查看图片内容,请用 view_image 工具(传入图片 URL);如需保存到本地,用 download_file 下载。",
19
19
  parameters: {
20
20
  type: "object",
21
21
  properties: {
@@ -25,7 +25,7 @@ export class GenerateImageTool extends SdkCallbackTool {
25
25
  },
26
26
  model: {
27
27
  type: "string",
28
- description: "图片模型 ID,不指定则使用配置中的默认图片模型。配置中的图片模型可通过 'aiz config show' 查看。",
28
+ description: "图片模型 ID,不指定则使用配置中的默认图片模型;若指定的模型不存在,本工具会返回当前可用的模型列表。",
29
29
  },
30
30
  size: {
31
31
  type: "string",
@@ -99,15 +99,15 @@ export class GenerateImageTool extends SdkCallbackTool {
99
99
  .map((img) => img.url)
100
100
  .filter((url) => !!url);
101
101
  // 统一返回字符串(JSON):生成结果如何处理(查看/下载/转发)由模型与上层自由决定,
102
- // 不替模型做「看或不看」的假设。需要让模型看图 → 模型可主动调用 viewImage(URL);
103
- // 需要保存 → downloadFile 下载。
102
+ // 不替模型做「看或不看」的假设。需要让模型看图 → 模型可主动调用 view_image(URL);
103
+ // 需要保存 → download_file 下载。
104
104
  return JSON.stringify({
105
105
  success: true,
106
106
  model: imageModel.name,
107
107
  modelId: imageModel.id,
108
108
  created: result.created,
109
109
  images: urls.map((url, i) => ({ index: i, url })),
110
- note: "图片临时链接有效期为30天。如需查看图片内容,可用 viewImage 工具(传入图片 URL);如需保存,可用 downloadFile 下载。",
110
+ note: "图片临时链接有效期为30天。如需查看图片内容,可用 view_image 工具(传入图片 URL);如需保存,可用 download_file 下载。",
111
111
  }, null, 2);
112
112
  }
113
113
  catch (error) {
@@ -69,7 +69,7 @@ export class GlobTool extends SdkCallbackTool {
69
69
  return JSON.stringify({
70
70
  outputTooLarge: true,
71
71
  warning: `输出过大:匹配结果共 ${json.length} 字符,超过上限 ${limit} 字符。` +
72
- `完整结果已落盘(result.json),可用 readFile 分批读取;` +
72
+ `完整结果已落盘(result.json),可用 read 分批读取;` +
73
73
  `也可用更精确的 pattern 或 exclude 缩小范围后重新查找。`,
74
74
  files: ctx.files,
75
75
  chars: json.length,
@@ -4,8 +4,8 @@ import type { AgentNS } from "@ai-zen/agents-core";
4
4
  /**
5
5
  * 勘察文件结构概况(行数、每行列数等),不做内容读取。
6
6
  *
7
- * 定位与 readFile 的 range 配套:大文件先看结构(有多少行、每行多长),
8
- * 再决定 readFile 的读取范围。流式扫描,故不受 readFile 的 300KB 读取阈值限制。
7
+ * 定位与 read 的 range 配套:大文件先看结构(有多少行、每行多长),
8
+ * 再决定 read 的读取范围。流式扫描,故不受 read 的 300KB 读取阈值限制。
9
9
  */
10
10
  export declare class InspectFileTool extends SdkCallbackTool {
11
11
  function: AgentNS.FunctionDefine;
@@ -6,13 +6,13 @@ import { DEFAULT_MAX_TOOL_OUTPUT } from "./outputGuard.js";
6
6
  /**
7
7
  * 勘察文件结构概况(行数、每行列数等),不做内容读取。
8
8
  *
9
- * 定位与 readFile 的 range 配套:大文件先看结构(有多少行、每行多长),
10
- * 再决定 readFile 的读取范围。流式扫描,故不受 readFile 的 300KB 读取阈值限制。
9
+ * 定位与 read 的 range 配套:大文件先看结构(有多少行、每行多长),
10
+ * 再决定 read 的读取范围。流式扫描,故不受 read 的 300KB 读取阈值限制。
11
11
  */
12
12
  export class InspectFileTool extends SdkCallbackTool {
13
13
  function = {
14
- name: "inspectFile",
15
- description: "勘察文件的结构概况:总行数、总字符数、最长行的列数及其行索引、平均列数、行尾类型(LF/CRLF)。可选返回每行的列数映射。适用于在 readFile 之前先了解文件规模,再决定用 range 读取哪一段。仅做流式扫描,不受 readFile 的 300KB 读取阈值限制。",
14
+ name: "inspect_file",
15
+ description: "勘察文件的结构概况:总行数、总字符数、最长行的列数及其行索引、平均列数、行尾类型(LF/CRLF)。可选返回每行的列数映射。适用于在 read 之前先了解文件规模,再决定用 range 读取哪一段。仅做流式扫描,不受 read 的 300KB 读取阈值限制。",
16
16
  parameters: {
17
17
  type: "object",
18
18
  properties: {
@@ -63,7 +63,7 @@ export class InspectFileTool extends SdkCallbackTool {
63
63
  colCountMapOmitted: true,
64
64
  warning: `行长度明细过大:概况与 colCountMap 合计 ${payload.length} 字符,` +
65
65
  `超过上限 ${limit} 字符,未落盘返回。文件规模超出可处理范围,` +
66
- `建议改用 findText 定位后再按 range 分段读取。`,
66
+ `建议改用 find_text 定位后再按 range 分段读取。`,
67
67
  });
68
68
  }
69
69
  return payload;
@@ -36,7 +36,7 @@ export class LsTool extends SdkCallbackTool {
36
36
  return JSON.stringify({
37
37
  outputTooLarge: true,
38
38
  warning: `输出过大:目录条目共 ${entries.length} 项、${json.length} 字符,超过上限 ${limit} 字符。` +
39
- `完整列表已落盘(result.json),可用 readFile 分批读取;` +
39
+ `完整列表已落盘(result.json),可用 read 分批读取;` +
40
40
  `也可改用 glob 按模式筛选。`,
41
41
  files: ctx.files,
42
42
  chars: json.length,
@@ -1,7 +1,7 @@
1
1
  import { SdkCallbackTool } from "./SdkCallbackTool.js";
2
2
  import type { ToolEnv } from "../../../types/index.js";
3
3
  import type { AgentNS } from "@ai-zen/agents-core";
4
- export declare class ReadFileTool extends SdkCallbackTool {
4
+ export declare class ReadTool extends SdkCallbackTool {
5
5
  function: AgentNS.FunctionDefine;
6
6
  constructor(env: ToolEnv);
7
7
  call(input: {
@@ -1,9 +1,9 @@
1
1
  import * as fsp from "fs/promises";
2
2
  import { SdkCallbackTool } from "./SdkCallbackTool.js";
3
3
  import { DEFAULT_MAX_TOOL_OUTPUT, guardOutput } from "./outputGuard.js";
4
- export class ReadFileTool extends SdkCallbackTool {
4
+ export class ReadTool extends SdkCallbackTool {
5
5
  function = {
6
- name: "readFile",
6
+ name: "read",
7
7
  description: "读取文件",
8
8
  parameters: {
9
9
  type: "object",
@@ -41,7 +41,7 @@ export class ReadFileTool extends SdkCallbackTool {
41
41
  // 输出保护:本次返回内容超过上限时只警告、不落盘,并提示用 range 分批读取
42
42
  const limit = this.env.config.maxToolOutput ?? DEFAULT_MAX_TOOL_OUTPUT;
43
43
  return await guardOutput({
44
- tool: "readFile",
44
+ tool: "read",
45
45
  content: output,
46
46
  isOverLimit: (text) => text.length > limit,
47
47
  buildWarning: () => `读取内容过大:本次输出 ${output.length} 字符,超过上限 ${limit} 字符,未落盘。` +
@@ -1,7 +1,7 @@
1
1
  import { SdkCallbackTool } from "./SdkCallbackTool.js";
2
2
  import type { ToolEnv } from "../../../types/index.js";
3
3
  import type { AgentNS } from "@ai-zen/agents-core";
4
- export declare class ExecAsyncTool extends SdkCallbackTool {
4
+ export declare class SpawnTool extends SdkCallbackTool {
5
5
  function: AgentNS.FunctionDefine;
6
6
  constructor(env: ToolEnv);
7
7
  call(input: {
@@ -1,8 +1,8 @@
1
1
  import { spawn } from "child_process";
2
2
  import { SdkCallbackTool } from "./SdkCallbackTool.js";
3
- export class ExecAsyncTool extends SdkCallbackTool {
3
+ export class SpawnTool extends SdkCallbackTool {
4
4
  function = {
5
- name: "exec_async",
5
+ name: "spawn",
6
6
  description: "异步执行命令,启动程序后立即返回,不等待执行结果。适用于启动长期运行的进程(如服务器、监听程序、GUI应用等)或不需要等待结果的命令。注意:该工具本身不捕获命令的输出;若需获取/留存输出,可在命令中使用 shell 重定向写入文件(如 `> /path/file`,支持 `>>` 追加、`|` 管道)。命令经 shell 解析执行。",
7
7
  parameters: {
8
8
  type: "object",
@@ -7,11 +7,13 @@ import type { AgentDefinition, AppConfig, ToolEnv } from "../../../types/index.j
7
7
  * buildTools 阶段按声明过滤——非视觉模型不会拿到该工具,故无需运行时重复校验。
8
8
  *
9
9
  * 输入 `path_or_url`:
10
- * - http(s) URL → 直接以 `image_url` 内容块返回,模型直接查看;
11
- * - 本地路径 → 自动通过 Files API(DeepSeek)上传,以 `file` 内容块(file_id)返回。
10
+ * - http(s) URL → 直接以 `image_url` 内容块返回;
11
+ * - 本地路径 → 交给 `fileToContentSection()` 统一处理:
12
+ * 端点声明 `capabilities.filesApi`(出厂仅 deepseek)→ Files API 上传,`file` 块(file_id);
13
+ * 其余端点 → base64 data URL 内联(`image_url`),不再要求必须配置 deepseek。
12
14
  *
13
- * 不使用 base64 内联(本地图片走 Files API,符合 DeepSeek 推荐与 48 MiB 请求体限制)。
14
- * 依赖配置中的 deepseek 端点(Files API 能力);URL 场景不依赖上传。
15
+ * 端点按**当前模型**反查(`ctx.agent.model`),不硬编码端点 id——`file_id` 是端点维度
16
+ * 资源,传错端点会返回 not found。
15
17
  */
16
18
  export declare class ViewImageTool extends SdkCallbackTool {
17
19
  /** 仅视觉模型可用:从 definition.modelId 解析当前模型,非视觉模型由 buildTools 过滤 */
@@ -1,8 +1,7 @@
1
- import OpenAI, { toFile } from "openai";
2
- import { promises as fs } from "node:fs";
3
1
  import path from "node:path";
2
+ import { promises as fs } from "node:fs";
4
3
  import { SdkCallbackTool } from "./SdkCallbackTool.js";
5
- const SUPPORTED_EXT = [".jpg", ".jpeg", ".png", ".gif", ".webp"];
4
+ import { fileToContentSection, mimeOf, resolveModelAndEndpoint } from "../../../shared/content-blocks.js";
6
5
  /**
7
6
  * 是否为"本地 URL"——指向本地/回环地址,API 服务商无法下载访问。
8
7
  * 覆盖 file:// 等本地协议,以及指向本机的 http(s) 地址(localhost、127.0.0.1、[::1]、0.0.0.0)。
@@ -19,22 +18,24 @@ function isLocalUrl(value) {
19
18
  * buildTools 阶段按声明过滤——非视觉模型不会拿到该工具,故无需运行时重复校验。
20
19
  *
21
20
  * 输入 `path_or_url`:
22
- * - http(s) URL → 直接以 `image_url` 内容块返回,模型直接查看;
23
- * - 本地路径 → 自动通过 Files API(DeepSeek)上传,以 `file` 内容块(file_id)返回。
21
+ * - http(s) URL → 直接以 `image_url` 内容块返回;
22
+ * - 本地路径 → 交给 `fileToContentSection()` 统一处理:
23
+ * 端点声明 `capabilities.filesApi`(出厂仅 deepseek)→ Files API 上传,`file` 块(file_id);
24
+ * 其余端点 → base64 data URL 内联(`image_url`),不再要求必须配置 deepseek。
24
25
  *
25
- * 不使用 base64 内联(本地图片走 Files API,符合 DeepSeek 推荐与 48 MiB 请求体限制)。
26
- * 依赖配置中的 deepseek 端点(Files API 能力);URL 场景不依赖上传。
26
+ * 端点按**当前模型**反查(`ctx.agent.model`),不硬编码端点 id——`file_id` 是端点维度
27
+ * 资源,传错端点会返回 not found。
27
28
  */
28
29
  export class ViewImageTool extends SdkCallbackTool {
29
30
  /** 仅视觉模型可用:从 definition.modelId 解析当前模型,非视觉模型由 buildTools 过滤 */
30
31
  isAvailable(config, definition) {
31
- const modelId = definition.modelId ?? config.defaultModel;
32
+ const modelId = definition.modelId;
32
33
  const model = (config.models ?? []).find((m) => m.id === modelId);
33
34
  return model?.vision === true;
34
35
  }
35
36
  function = {
36
- name: "viewImage",
37
- description: "查看一张图片并分析其内容。传入本地图片路径或 http(s) 图片 URL。本地图片会自动上传(Files API)后引用,网络图片直接引用。不支持本地 URL(如 file:// 协议、指向本机 localhost/127.0.0.1 的 http(s) 地址),本地图片请使用文件路径。适合识别图片文字、描述图片内容、分析图表等。",
37
+ name: "view_image",
38
+ description: "查看一张图片并分析其内容。传入本地图片路径或 http(s) 图片 URL。本地图片优先上传(端点支持 Files API 时)否则以 base64 内联引用,网络图片直接引用。不支持本地 URL(如 file:// 协议、指向本机 localhost/127.0.0.1 的 http(s) 地址),本地图片请使用文件路径。适合识别图片文字、描述图片内容、分析图表等。",
38
39
  parameters: {
39
40
  type: "object",
40
41
  properties: {
@@ -54,63 +55,46 @@ export class ViewImageTool extends SdkCallbackTool {
54
55
  const signal = ctx?.signal;
55
56
  const target = input?.path_or_url;
56
57
  if (!target || !target.trim()) {
57
- return "viewImage 需要提供 path_or_url 参数(本地图片路径或 http(s) URL)。";
58
+ return "view_image 需要提供 path_or_url 参数(本地图片路径或 http(s) URL)。";
58
59
  }
59
60
  const value = target.trim();
60
- // 拒绝服务商无法访问的"本地 URL":file:// 等本地协议,或指向本机回环地址(localhost/127.0.0.1/[::1]/0.0.0.0)的 http(s) 地址
61
+ // 拒绝服务商无法访问的"本地 URL":file:// 等本地协议,或指向本机回环地址的 http(s) 地址
61
62
  if (isLocalUrl(value)) {
62
63
  return "不支持本地 URL(API 服务商无法下载访问)。本地图片请使用文件路径,而不是本地链接。";
63
64
  }
65
+ // 按当前模型定位端点:决定走 Files API 上传还是 base64 内联
66
+ const { model, endpoint } = resolveModelAndEndpoint(this.env.config, ctx?.agent?.model);
64
67
  try {
65
- // 网络图片:直接返回 image_url 内容块(无需上传)
66
- if (/^https?:\/\//i.test(value)) {
67
- return [
68
- {
69
- type: "text",
70
- text: `已获取网络图片:${value}`,
71
- },
72
- { type: "image_url", image_url: { url: value } },
73
- ];
74
- }
75
- // 本地图片:解析路径 + 校验 + Files API 上传
76
- const absPath = path.isAbsolute(value)
77
- ? value
78
- : path.join(this.env.cwd, value);
79
- let stat;
80
- try {
81
- stat = await fs.stat(absPath);
82
- }
83
- catch {
84
- return `本地图片不存在: ${absPath}`;
85
- }
86
- if (!stat.isFile()) {
87
- return `"${absPath}" 不是文件。`;
68
+ const isUrl = /^https?:\/\//i.test(value);
69
+ // 本工具只处理图片:先按扩展名把关,给出比通用入口更贴切的提示
70
+ if (!isUrl) {
71
+ const abs = this.resolve(value);
72
+ // 先 stat 一次,把 ENOENT 变成对人友好的提示(通用入口的报错面向任意文件)
73
+ try {
74
+ await fs.stat(abs);
75
+ }
76
+ catch {
77
+ return `本地图片不存在: ${abs}`;
78
+ }
79
+ const mime = mimeOf(abs);
80
+ if (!mime || !mime.startsWith("image/")) {
81
+ return `不支持的图片格式 "${path.extname(abs) || "(无扩展名)"}"。支持的格式:JPEG、PNG、GIF、WebP。`;
82
+ }
88
83
  }
89
- const ext = path.extname(absPath).toLowerCase();
90
- if (!SUPPORTED_EXT.includes(ext)) {
91
- return `不支持的图片格式 "${ext || "(无扩展名)"}"。支持的格式:JPEG、PNG、GIF、WebP。`;
92
- }
93
- // 上传端点:deepseek(Files API 能力)
94
- const endpoint = (this.env.config.endpoints ?? []).find((e) => e.id === "deepseek");
95
- if (!endpoint) {
96
- return "未配置 deepseek 端点,无法上传本地图片(Files API)。如需查看本地图片,请先配置 deepseek 端点;网络图片 URL 可直接使用。";
97
- }
98
- if (!endpoint.apiKey) {
99
- return `端点 "${endpoint.name}" 的 API Key 未设置,无法上传本地图片。`;
100
- }
101
- const client = new OpenAI({
102
- apiKey: endpoint.apiKey,
103
- baseURL: endpoint.baseUrl,
84
+ const result = await fileToContentSection({
85
+ source: isUrl ? { url: value } : { path: this.resolve(value) },
86
+ endpoint,
87
+ model,
88
+ signal,
104
89
  });
105
- const file = await toFile(await fs.readFile(absPath), path.basename(absPath));
106
- const uploaded = await client.files.create({ file, purpose: "user_data" }, { signal });
107
- return [
108
- {
109
- type: "text",
110
- text: `图片已上传(${path.basename(absPath)}),file_id: ${uploaded.id}`,
111
- },
112
- { type: "file", file_id: uploaded.id },
113
- ];
90
+ const label = isUrl ? `已获取网络图片:${value}` : `已获取本地图片(${path.basename(value)})`;
91
+ const how = result.transport === "files-api"
92
+ ? `,已上传至 Files API(file_id: ${result.section.file_id ?? "?"})`
93
+ : result.transport === "inline"
94
+ ? `,已按 base64 内联(${(result.bytes / 1024).toFixed(0)} KB)`
95
+ : "";
96
+ const extra = result.warning ? `(降级说明:${result.warning})` : "";
97
+ return [{ type: "text", text: `${label}${how}${extra}` }, result.section];
114
98
  }
115
99
  catch (error) {
116
100
  // 中断(abort)时返回明确的中断结果
@@ -1,7 +1,7 @@
1
1
  import { SdkCallbackTool } from "./SdkCallbackTool.js";
2
2
  import type { ToolEnv } from "../../../types/index.js";
3
3
  import type { AgentNS } from "@ai-zen/agents-core";
4
- export declare class WriteFileTool extends SdkCallbackTool {
4
+ export declare class WriteTool extends SdkCallbackTool {
5
5
  function: AgentNS.FunctionDefine;
6
6
  constructor(env: ToolEnv);
7
7
  call(input: {
@@ -1,9 +1,9 @@
1
1
  import * as fsp from "fs/promises";
2
2
  import * as path from "path";
3
3
  import { SdkCallbackTool } from "./SdkCallbackTool.js";
4
- export class WriteFileTool extends SdkCallbackTool {
4
+ export class WriteTool extends SdkCallbackTool {
5
5
  function = {
6
- name: "writeFile",
6
+ name: "write",
7
7
  description: "写入文件",
8
8
  parameters: {
9
9
  type: "object",
@@ -1,9 +1,9 @@
1
1
  import type { SdkCallbackTool } from "./SdkCallbackTool.js";
2
2
  import type { ToolEnv } from "../../../types/index.js";
3
3
  import { CwdTool } from "./CwdTool.js";
4
- import { ReadFileTool } from "./ReadFileTool.js";
4
+ import { ReadTool } from "./ReadTool.js";
5
5
  import { InspectFileTool } from "./InspectFileTool.js";
6
- import { WriteFileTool } from "./WriteFileTool.js";
6
+ import { WriteTool } from "./WriteTool.js";
7
7
  import { ExecTool } from "./ExecTool.js";
8
8
  import { MkdirTool } from "./MkdirTool.js";
9
9
  import { RmTool } from "./RmTool.js";
@@ -16,7 +16,7 @@ import { RenameTool } from "./RenameTool.js";
16
16
  import { CopyTool } from "./CopyTool.js";
17
17
  import { BatchEditTool } from "./BatchEditTool.js";
18
18
  import { EditTool } from "./EditTool.js";
19
- import { ExecAsyncTool } from "./ExecAsyncTool.js";
19
+ import { SpawnTool } from "./SpawnTool.js";
20
20
  import { SleepTool } from "./SleepTool.js";
21
21
  import { ViewImageTool } from "./ViewImageTool.js";
22
22
  import { GenerateImageTool } from "./GenerateImageTool.js";
@@ -29,4 +29,4 @@ import { GenerateImageTool } from "./GenerateImageTool.js";
29
29
  * 如 GenerateImageTool 依赖 defaultImageModel、ViewImageTool 仅视觉模型可用。
30
30
  */
31
31
  export declare const BUILTIN_TOOL_CLASSES: Array<new (env: ToolEnv) => SdkCallbackTool>;
32
- export { CwdTool, ReadFileTool, InspectFileTool, WriteFileTool, ExecTool, MkdirTool, RmTool, GlobTool, LsTool, ExistTool, FindTextTool, DownloadFileTool, RenameTool, CopyTool, BatchEditTool, EditTool, ExecAsyncTool, SleepTool, ViewImageTool, GenerateImageTool, };
32
+ export { CwdTool, ReadTool, InspectFileTool, WriteTool, ExecTool, MkdirTool, RmTool, GlobTool, LsTool, ExistTool, FindTextTool, DownloadFileTool, RenameTool, CopyTool, BatchEditTool, EditTool, SpawnTool, SleepTool, ViewImageTool, GenerateImageTool, };
@@ -1,7 +1,7 @@
1
1
  import { CwdTool } from "./CwdTool.js";
2
- import { ReadFileTool } from "./ReadFileTool.js";
2
+ import { ReadTool } from "./ReadTool.js";
3
3
  import { InspectFileTool } from "./InspectFileTool.js";
4
- import { WriteFileTool } from "./WriteFileTool.js";
4
+ import { WriteTool } from "./WriteTool.js";
5
5
  import { ExecTool } from "./ExecTool.js";
6
6
  import { MkdirTool } from "./MkdirTool.js";
7
7
  import { RmTool } from "./RmTool.js";
@@ -14,7 +14,7 @@ import { RenameTool } from "./RenameTool.js";
14
14
  import { CopyTool } from "./CopyTool.js";
15
15
  import { BatchEditTool } from "./BatchEditTool.js";
16
16
  import { EditTool } from "./EditTool.js";
17
- import { ExecAsyncTool } from "./ExecAsyncTool.js";
17
+ import { SpawnTool } from "./SpawnTool.js";
18
18
  import { SleepTool } from "./SleepTool.js";
19
19
  import { ViewImageTool } from "./ViewImageTool.js";
20
20
  import { GenerateImageTool } from "./GenerateImageTool.js";
@@ -28,9 +28,9 @@ import { GenerateImageTool } from "./GenerateImageTool.js";
28
28
  */
29
29
  export const BUILTIN_TOOL_CLASSES = [
30
30
  CwdTool,
31
- ReadFileTool,
31
+ ReadTool,
32
32
  InspectFileTool,
33
- WriteFileTool,
33
+ WriteTool,
34
34
  ExecTool,
35
35
  MkdirTool,
36
36
  RmTool,
@@ -43,9 +43,9 @@ export const BUILTIN_TOOL_CLASSES = [
43
43
  CopyTool,
44
44
  BatchEditTool,
45
45
  EditTool,
46
- ExecAsyncTool,
46
+ SpawnTool,
47
47
  SleepTool,
48
48
  ViewImageTool,
49
49
  GenerateImageTool,
50
50
  ];
51
- export { CwdTool, ReadFileTool, InspectFileTool, WriteFileTool, ExecTool, MkdirTool, RmTool, GlobTool, LsTool, ExistTool, FindTextTool, DownloadFileTool, RenameTool, CopyTool, BatchEditTool, EditTool, ExecAsyncTool, SleepTool, ViewImageTool, GenerateImageTool, };
51
+ export { CwdTool, ReadTool, InspectFileTool, WriteTool, ExecTool, MkdirTool, RmTool, GlobTool, LsTool, ExistTool, FindTextTool, DownloadFileTool, RenameTool, CopyTool, BatchEditTool, EditTool, SpawnTool, SleepTool, ViewImageTool, GenerateImageTool, };
@@ -28,7 +28,7 @@ export interface OutputGuardOptions<T> {
28
28
  /** 超限判定:上限由工具自行从 config 读取,骨架不传参 */
29
29
  isOverLimit: (content: T) => boolean;
30
30
  /**
31
- * 落盘内容组织(可选)。缺省表示"只警告、不落盘"(如 readFile)。
31
+ * 落盘内容组织(可选)。缺省表示"只警告、不落盘"(如 read)。
32
32
  * 返回的文件按顺序写入本次调用专属的目录。
33
33
  */
34
34
  dump?: (dir: string, content: T) => GuardDumpFile[] | Promise<GuardDumpFile[]>;
@@ -61,7 +61,7 @@ export interface HeadPreview {
61
61
  /**
62
62
  * 生成头部预览:最多 size 字符。
63
63
  *
64
- * 适用于结果按出现顺序排列、尾部参考价值低的工具(如 findText)。
64
+ * 适用于结果按出现顺序排列、尾部参考价值低的工具(如 find_text)。
65
65
  */
66
66
  export declare function headPreview(text: string, size?: number): HeadPreview;
67
67
  /** 头尾预览 */
@@ -42,7 +42,7 @@ export async function guardOutput(options) {
42
42
  /**
43
43
  * 生成头部预览:最多 size 字符。
44
44
  *
45
- * 适用于结果按出现顺序排列、尾部参考价值低的工具(如 findText)。
45
+ * 适用于结果按出现顺序排列、尾部参考价值低的工具(如 find_text)。
46
46
  */
47
47
  export function headPreview(text, size = 1000) {
48
48
  if (text.length <= size)
@@ -24,6 +24,6 @@ export declare class McpScopePlugin implements ScopePlugin {
24
24
  /** 是否配置了 MCP(供外部代码判定,如 UnknownToolHintPlugin) */
25
25
  hasConfig(): boolean;
26
26
  candidates(): ScopeCandidates;
27
- instantiate(_scope: Scope, filtered: FilterOutput): Tool[];
27
+ instantiate(scope: Scope, filtered: FilterOutput): Tool[];
28
28
  dispose(): Promise<void>;
29
29
  }
@@ -37,7 +37,7 @@ export class McpScopePlugin {
37
37
  tools: ["load_mcp", "call_mcp_tool", "read_mcp_resource"],
38
38
  };
39
39
  }
40
- instantiate(_scope, filtered) {
40
+ instantiate(scope, filtered) {
41
41
  const allow = new Set(filtered.tools);
42
42
  const allowedMcps = new Set(filtered.mcps);
43
43
  const filteredMcps = this.servers.filter((s) => allowedMcps.has(s.id));
@@ -47,10 +47,11 @@ export class McpScopePlugin {
47
47
  result.push(createLoadMcpTool(m, filteredMcps));
48
48
  }
49
49
  if (allow.has("call_mcp_tool") && m) {
50
- result.push(createCallMcpTool(m));
50
+ // env 供 call_mcp_tool 读取 maxToolOutput,与内置工具共用同一上限口径
51
+ result.push(createCallMcpTool(m, scope.env));
51
52
  }
52
53
  if (allow.has("read_mcp_resource") && m) {
53
- result.push(createReadMcpResourceTool(m));
54
+ result.push(createReadMcpResourceTool(m, scope.env));
54
55
  }
55
56
  return result;
56
57
  }
@@ -1,6 +1,6 @@
1
1
  import { CallbackTool } from "@ai-zen/agents-core";
2
2
  import type { McpConnectionManager } from "./McpConnectionManager.js";
3
- import type { McpServerConfig } from "../../../types/index.js";
3
+ import type { McpServerConfig, ToolEnv } from "../../../types/index.js";
4
4
  /**
5
5
  * 创建 load_mcp 工具。
6
6
  * server 枚举由 filteredMcps (完整 McpServerConfig[])自动推导,
@@ -10,9 +10,15 @@ export declare function createLoadMcpTool(mcpManager: McpConnectionManager, filt
10
10
  /**
11
11
  * 创建 call_mcp_tool 工具。
12
12
  * 通过官方 Client.callTool() API 调用 MCP 服务器上的工具。
13
+ *
14
+ * 返回形态:
15
+ * - 仅文本 → 纯字符串(与历史行为一致);
16
+ * - 含媒体 → 内容块数组:首块为受保护的文本,其后原样透传 image / file 块。
17
+ *
18
+ * env 仅用于读取 maxToolOutput(文本输出保护上限),与内置工具口径一致。
13
19
  */
14
- export declare function createCallMcpTool(mcpManager: McpConnectionManager): CallbackTool;
20
+ export declare function createCallMcpTool(mcpManager: McpConnectionManager, env: ToolEnv): CallbackTool;
15
21
  /**
16
22
  * 创建 read_mcp_resource 工具。
17
23
  */
18
- export declare function createReadMcpResourceTool(mcpManager: McpConnectionManager): CallbackTool;
24
+ export declare function createReadMcpResourceTool(mcpManager: McpConnectionManager, env: ToolEnv): CallbackTool;