@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
@@ -2,23 +2,23 @@ import { CallbackTool, Agent, AgentToolLazy, Message } from "@ai-zen/agents-core
2
2
  import { createDisclosureParam } from "../../disclosure.js";
3
3
  import { readSkill } from "./discover.js";
4
4
  import { promises as fs } from "node:fs";
5
- const EMPTY_HINT = "(当前没有可用的 Skill,请联系用户添加)";
5
+ const EMPTY_HINT = "(No Skills are currently available, please ask the user to add one)";
6
6
  /**
7
7
  * 创建 load_skill 工具。
8
8
  * 枚举所有允许的 skill(不限是否支持子 Agent),由 filteredSkills 提供完整信息。
9
9
  */
10
10
  export function createLoadSkillTool(skillDirs, filteredSkills) {
11
11
  const ids = filteredSkills.map((s) => s.id);
12
- const param = createDisclosureParam(ids, "选择一个 Skill", EMPTY_HINT);
12
+ const param = createDisclosureParam(ids, "Select a Skill", EMPTY_HINT);
13
13
  // 拼接各 skill 的描述供 LLM 参考
14
14
  const skillDescriptions = filteredSkills
15
- .map((s) => ` - ${s.id}: ${s.description || "无描述"}`)
15
+ .map((s) => ` - ${s.id}: ${s.description || "No description"}`)
16
16
  .join("\n");
17
- const skillIdDescription = `${param.description}\n\n各 Skill 说明:\n${skillDescriptions || " 无可用 Skill"}`;
17
+ const skillIdDescription = `${param.description}\n\nSkill descriptions:\n${skillDescriptions || " No Skills available"}`;
18
18
  return new CallbackTool({
19
19
  function: {
20
20
  name: "load_skill",
21
- description: "加载指定 Skill 的完整指导文档。Skill 是预制的专业指导,加载后展开到上下文中。",
21
+ description: "Load the full guidance document of a Skill. A Skill is pre-authored expert guidance that is expanded into the context once loaded.",
22
22
  parameters: {
23
23
  type: "object",
24
24
  properties: {
@@ -36,7 +36,7 @@ export function createLoadSkillTool(skillDirs, filteredSkills) {
36
36
  const skillId = input.skill_id;
37
37
  const skill = await readSkill(skillDirs, skillId);
38
38
  if (!skill) {
39
- return `❌ Skill "${skillId}" 不存在,请确认名称是否正确`;
39
+ return `❌ Skill "${skillId}" not found, please verify the name`;
40
40
  }
41
41
  // 扫描 skill 目录下的文件列表
42
42
  let fileList = "";
@@ -46,18 +46,18 @@ export function createLoadSkillTool(skillDirs, filteredSkills) {
46
46
  const suffix = e.isDirectory() ? "/" : "";
47
47
  return ` - ${e.name}${suffix}`;
48
48
  });
49
- fileList = `\n\nSkill 目录文件列表:\n${files.join("\n")}`;
49
+ fileList = `\n\nSkill directory files:\n${files.join("\n")}`;
50
50
  }
51
51
  catch {
52
52
  fileList = "";
53
53
  }
54
54
  return [
55
- `Skill "${skillId}" 已加载:`,
55
+ `Skill "${skillId}" loaded:`,
56
56
  ``,
57
- `Skill 目录路径: ${skill.dirPath}`,
57
+ `Skill directory path: ${skill.dirPath}`,
58
58
  fileList,
59
59
  ``,
60
- `--- Skill 内容开始 ---`,
60
+ `--- Skill content starts ---`,
61
61
  skill.content,
62
62
  ].join("\n");
63
63
  },
@@ -76,16 +76,16 @@ export function createCallSkillSubAgentTool(skillDirs, filteredSkills) {
76
76
  // 只保留支持子 Agent 模式的 skill
77
77
  const subAgentSkills = filteredSkills.filter((s) => s.subAgent);
78
78
  const ids = subAgentSkills.map((s) => s.id);
79
- const param = createDisclosureParam(ids, "选择一个 Skill(支持子 Agent)", EMPTY_HINT);
79
+ const param = createDisclosureParam(ids, "Select a Skill that supports SubAgent mode", EMPTY_HINT);
80
80
  // 拼接各 skill 的描述供 LLM 参考
81
81
  const skillDescriptions = subAgentSkills
82
- .map((s) => ` - ${s.id}: ${s.description || "无描述"}`)
82
+ .map((s) => ` - ${s.id}: ${s.description || "No description"}`)
83
83
  .join("\n");
84
- const skillIdDescription = `${param.description}\n\n各 Skill 说明:\n${skillDescriptions || " 无可用 Skill"}`;
84
+ const skillIdDescription = `${param.description}\n\nSkill descriptions:\n${skillDescriptions || " No Skills available"}`;
85
85
  return new AgentToolLazy({
86
86
  function: {
87
87
  name: "call_skill_sub_agent",
88
- description: "将任务委派给指定的 Skill 子 Agent,由其独立完成并返回结果。",
88
+ description: "Delegate a task to the specified Skill SubAgent, which completes it independently and returns the result.",
89
89
  parameters: {
90
90
  type: "object",
91
91
  properties: {
@@ -96,7 +96,7 @@ export function createCallSkillSubAgentTool(skillDirs, filteredSkills) {
96
96
  },
97
97
  task: {
98
98
  type: "string",
99
- description: "要委派给子 Agent 完成的任务描述",
99
+ description: "Task description to delegate to the SubAgent",
100
100
  },
101
101
  },
102
102
  required: ["skill_id", "task"],
@@ -110,10 +110,10 @@ export function createCallSkillSubAgentTool(skillDirs, filteredSkills) {
110
110
  const task = parsedArgs.task;
111
111
  const skill = await readSkill(skillDirs, skillId);
112
112
  if (!skill) {
113
- throw new Error(`❌ Skill "${skillId}" 不存在,请确认名称是否正确`);
113
+ throw new Error(`❌ Skill "${skillId}" not found, please verify the name`);
114
114
  }
115
115
  if (!skill.subAgent) {
116
- throw new Error(`Skill "${skillId}" 不支持子 Agent 模式,请使用 load_skill 加载指导后自行处理`);
116
+ throw new Error(`Skill "${skillId}" does not support SubAgent mode, use load_skill to load its guidance and handle the task yourself`);
117
117
  }
118
118
  // 沿用父 Agent 的工具能力(不做权限过滤),剔除自身以防自递归
119
119
  const parentAgent = ctx.agent;
@@ -8,7 +8,7 @@ import { createModel } from "../../../runtime/createModel.js";
8
8
  */
9
9
  export function createSubAgentTool(def, scope) {
10
10
  if (!def.function) {
11
- throw new Error(`Agent "${def.id}" 没有 function 字段,不能作为 SubAgent 工具`);
11
+ throw new Error(`Agent "${def.id}" has no function field, so it cannot be used as a SubAgent tool`);
12
12
  }
13
13
  const selfName = def.function.name;
14
14
  const lazy = new AgentToolLazy({
@@ -33,7 +33,7 @@ export function createSubAgentTool(def, scope) {
33
33
  else {
34
34
  const parent = ctx.agent;
35
35
  if (!parent) {
36
- throw new Error(`SubAgent "${selfName}" 未指定 modelId,且父 Agent 没有可复用的模型`);
36
+ throw new Error(`SubAgent "${selfName}" does not specify a modelId, and the parent Agent has no reusable model`);
37
37
  }
38
38
  client = parent.client;
39
39
  model = parent.model;
@@ -74,7 +74,7 @@ async function loadToolFile(filepath, silent) {
74
74
  }
75
75
  catch (err) {
76
76
  if (!silent) {
77
- getLogger().error(`[usertools] 加载工具文件失败: ${filepath} — ${err?.message ?? err}`);
77
+ getLogger().error(`[usertools] Failed to load tool file: ${filepath} — ${err?.message ?? err}`);
78
78
  }
79
79
  return null;
80
80
  }
@@ -110,7 +110,7 @@ function normalizeToolExport(exported, filepath, silent) {
110
110
  });
111
111
  }
112
112
  if (!silent) {
113
- getLogger().error(`[usertools] 无法识别的工具格式: ${filepath}`);
113
+ getLogger().error(`[usertools] Unrecognized tool format: ${filepath}`);
114
114
  }
115
115
  return null;
116
116
  }
@@ -139,7 +139,7 @@ export async function fileToContentSection(input) {
139
139
  // ---- 1. 网络 URL:直接引用,不经上传/内联 ----
140
140
  if ("url" in source) {
141
141
  if (!isRemoteUrl(source.url)) {
142
- throw new Error(`不支持本地 URL(${source.url}):API 服务商无法访问。本地文件请传文件路径,而不是本地链接。`);
142
+ throw new Error(`Unsupported local URL (${source.url}): the API provider cannot access it. For local files pass a file path, not a local link`);
143
143
  }
144
144
  return {
145
145
  section: { type: "image_url", image_url: { url: source.url } },
@@ -160,10 +160,10 @@ export async function fileToContentSection(input) {
160
160
  absPath = path.isAbsolute(source.path) ? source.path : path.resolve(source.path);
161
161
  const st = await fs.stat(absPath);
162
162
  if (!st.isFile())
163
- throw new Error(`"${absPath}" 不是文件。`);
163
+ throw new Error(`"${absPath}" is not a file`);
164
164
  const inferred = mimeOf(absPath);
165
165
  if (!inferred) {
166
- throw new Error(`不支持的文件类型:"${absPath}"(已知类型:${Object.keys(MIME_BY_EXT).join(" ")})`);
166
+ throw new Error(`Unsupported file type: "${absPath}" (known types: ${Object.keys(MIME_BY_EXT).join(" ")})`);
167
167
  }
168
168
  mime = inferred;
169
169
  stat = { mtimeMs: st.mtimeMs, size: st.size };
@@ -198,10 +198,10 @@ export async function fileToContentSection(input) {
198
198
  if (isImage && model && model.vision !== true) {
199
199
  const label = absPath ?? filename;
200
200
  return {
201
- section: textSection(`[图片] ${label}(当前模型不支持图片输入,如需查看请改用视觉模型)`),
201
+ section: textSection(`[Image] ${label} (the current model does not support image input; use a vision-capable model to view it)`),
202
202
  transport: "text",
203
203
  bytes: byteLength,
204
- warning: "当前模型不支持图片输入,已退化为文本提示",
204
+ warning: "The current model does not support image input; degraded to a text hint",
205
205
  };
206
206
  }
207
207
  // ---- 4. 端点支持 Files API:上传(带缓存),失败降级内联 ----
@@ -231,14 +231,14 @@ export async function fileToContentSection(input) {
231
231
  catch (error) {
232
232
  if (signal?.aborted)
233
233
  throw error;
234
- warning = `上传 Files API 失败,已改为 base64 内联:${error?.message ?? error}`;
234
+ warning = `Files API upload failed, falling back to base64 inline: ${error?.message ?? error}`;
235
235
  }
236
236
  }
237
237
  // ---- 5. 内联(base64 data URL)----
238
238
  if (byteLength > maxInline) {
239
- throw new Error(`文件体积 ${(byteLength / 1024 / 1024).toFixed(1)} MiB 超过内联上限 ` +
240
- `${(maxInline / 1024 / 1024).toFixed(1)} MiB,且端点未声明 Files API 能力` +
241
- `(capabilities.filesApi),无法发送。`);
239
+ throw new Error(`File size ${(byteLength / 1024 / 1024).toFixed(1)} MiB exceeds the inline limit of ` +
240
+ `${(maxInline / 1024 / 1024).toFixed(1)} MiB and the endpoint does not declare the Files API capability ` +
241
+ `(capabilities.filesApi), so it cannot be sent`);
242
242
  }
243
243
  // base64 来源直接透传,不重新编码(保证字节级一致,也省一次编解码)
244
244
  const dataUrl = toDataUrl(base64 ?? Buffer.from(ensureBytes()).toString("base64"), mime);
@@ -24,9 +24,9 @@ export class ContextOverflowError extends Error {
24
24
  /** 被越过的强行阻断阈值(`tokenThresholds.guard`,绝对值) */
25
25
  guardTokens;
26
26
  constructor(promptTokens, guardTokens) {
27
- super(`🛑 上下文严重超限:已用 ${promptTokens} token,超过阻断阈值 ${guardTokens}。` +
28
- `当前对话可能因读入超大文件而失控,已中断,请检查并重新开始。` +
29
- `(如确需更长上下文,请调高 config.json 中该模型的 tokenThresholds.guard。)`);
27
+ super(`🛑 Context critically over the limit: ${promptTokens} tokens used, above the blocking threshold of ${guardTokens}.` +
28
+ ` The conversation may have run away after reading an oversized file, so it has been interrupted — please inspect and start over.` +
29
+ ` (If you really need a longer context, raise tokenThresholds.guard for this model in config.json.)`);
30
30
  this.name = "ContextOverflowError";
31
31
  this.promptTokens = promptTokens;
32
32
  this.guardTokens = guardTokens;
package/package.json CHANGED
@@ -1,10 +1,19 @@
1
1
  {
2
2
  "name": "@ai-zen/agents-sdk",
3
- "version": "1.0.0-alpha.22",
3
+ "version": "1.0.0-alpha.23",
4
4
  "description": "AI-Zen Agents SDK — shared business logic for CLI and Desktop",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "type": "module",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/ai-zen/agents.git",
11
+ "directory": "packages/sdk"
12
+ },
13
+ "homepage": "https://github.com/ai-zen/agents/tree/main/packages/sdk#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/ai-zen/agents/issues"
16
+ },
8
17
  "keywords": [
9
18
  "ai",
10
19
  "agent",