@ai-zen/agents-sdk 0.12.0 → 1.0.0-alpha.1

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 (114) hide show
  1. package/CHANGELOG.md +103 -36
  2. package/README.md +29 -21
  3. package/README.zh.md +29 -21
  4. package/dist/{plugin → agent-plugins}/AutoRefreshToolsPlugin.js +2 -2
  5. package/dist/{plugin → agent-plugins}/UnknownToolHintPlugin.d.ts +7 -5
  6. package/dist/{plugin → agent-plugins}/UnknownToolHintPlugin.js +9 -5
  7. package/dist/config/ConfigManager.d.ts +17 -0
  8. package/dist/config/ConfigManager.js +44 -1
  9. package/dist/config/constants.js +67 -37
  10. package/dist/index.d.ts +37 -29
  11. package/dist/index.js +28 -21
  12. package/dist/runtime/SdkAgent.d.ts +5 -5
  13. package/dist/runtime/SdkAgent.js +4 -4
  14. package/dist/runtime/TaskMigrationService.d.ts +1 -1
  15. package/dist/runtime/TaskMigrationService.js +2 -2
  16. package/dist/runtime/createAgent.d.ts +4 -4
  17. package/dist/runtime/createAgent.js +8 -8
  18. package/dist/runtime/createModel.d.ts +3 -3
  19. package/dist/runtime/createModel.js +3 -3
  20. package/dist/scope/Scope.d.ts +111 -0
  21. package/dist/scope/Scope.js +165 -0
  22. package/dist/scope/ScopePlugin.d.ts +51 -0
  23. package/dist/scope/ScopePlugin.js +1 -0
  24. package/dist/scope/plugins/allInOne.d.ts +18 -0
  25. package/dist/scope/plugins/allInOne.js +23 -0
  26. package/dist/{capabilities/implements → scope/plugins}/builtin/BatchEditTool.d.ts +1 -1
  27. package/dist/{capabilities/implements → scope/plugins}/builtin/BatchEditTool.js +1 -1
  28. package/dist/scope/plugins/builtin/BuiltinToolsScopePlugin.d.ts +20 -0
  29. package/dist/scope/plugins/builtin/BuiltinToolsScopePlugin.js +30 -0
  30. package/dist/{capabilities/implements → scope/plugins}/builtin/CopyTool.d.ts +1 -1
  31. package/dist/{capabilities/implements → scope/plugins}/builtin/CopyTool.js +1 -1
  32. package/dist/{capabilities/implements → scope/plugins}/builtin/CwdTool.d.ts +1 -1
  33. package/dist/{capabilities/implements → scope/plugins}/builtin/CwdTool.js +1 -1
  34. package/dist/{capabilities/implements → scope/plugins}/builtin/DownloadFileTool.d.ts +1 -1
  35. package/dist/{capabilities/implements → scope/plugins}/builtin/DownloadFileTool.js +1 -1
  36. package/dist/{capabilities/implements → scope/plugins}/builtin/EditTool.d.ts +1 -1
  37. package/dist/{capabilities/implements → scope/plugins}/builtin/EditTool.js +1 -1
  38. package/dist/{capabilities/implements → scope/plugins}/builtin/ExecAsyncTool.d.ts +1 -1
  39. package/dist/{capabilities/implements → scope/plugins}/builtin/ExecAsyncTool.js +1 -1
  40. package/dist/{capabilities/implements → scope/plugins}/builtin/ExecTool.d.ts +1 -1
  41. package/dist/{capabilities/implements → scope/plugins}/builtin/ExecTool.js +1 -1
  42. package/dist/{capabilities/implements → scope/plugins}/builtin/ExistTool.d.ts +1 -1
  43. package/dist/{capabilities/implements → scope/plugins}/builtin/ExistTool.js +1 -1
  44. package/dist/{capabilities/implements → scope/plugins}/builtin/FindTextTool.d.ts +1 -1
  45. package/dist/{capabilities/implements → scope/plugins}/builtin/FindTextTool.js +1 -1
  46. package/dist/{capabilities/implements → scope/plugins}/builtin/GenerateImageTool.d.ts +1 -1
  47. package/dist/{capabilities/implements → scope/plugins}/builtin/GenerateImageTool.js +1 -1
  48. package/dist/{capabilities/implements → scope/plugins}/builtin/GlobTool.d.ts +1 -1
  49. package/dist/{capabilities/implements → scope/plugins}/builtin/GlobTool.js +1 -1
  50. package/dist/{capabilities/implements → scope/plugins}/builtin/InspectFileTool.d.ts +1 -1
  51. package/dist/{capabilities/implements → scope/plugins}/builtin/InspectFileTool.js +1 -1
  52. package/dist/{capabilities/implements → scope/plugins}/builtin/LsTool.d.ts +1 -1
  53. package/dist/{capabilities/implements → scope/plugins}/builtin/LsTool.js +1 -1
  54. package/dist/{capabilities/implements → scope/plugins}/builtin/MkdirTool.d.ts +1 -1
  55. package/dist/{capabilities/implements → scope/plugins}/builtin/MkdirTool.js +1 -1
  56. package/dist/{capabilities/implements → scope/plugins}/builtin/ReadFileTool.d.ts +1 -1
  57. package/dist/{capabilities/implements → scope/plugins}/builtin/ReadFileTool.js +1 -1
  58. package/dist/{capabilities/implements → scope/plugins}/builtin/RenameTool.d.ts +1 -1
  59. package/dist/{capabilities/implements → scope/plugins}/builtin/RenameTool.js +1 -1
  60. package/dist/{capabilities/implements → scope/plugins}/builtin/RmTool.d.ts +1 -1
  61. package/dist/{capabilities/implements → scope/plugins}/builtin/RmTool.js +1 -1
  62. package/dist/{runtime → scope/plugins/builtin}/SdkCallbackTool.d.ts +1 -1
  63. package/dist/{capabilities/implements → scope/plugins}/builtin/SleepTool.d.ts +1 -1
  64. package/dist/{capabilities/implements → scope/plugins}/builtin/SleepTool.js +1 -1
  65. package/dist/{capabilities/implements → scope/plugins}/builtin/ViewImageTool.d.ts +1 -1
  66. package/dist/{capabilities/implements → scope/plugins}/builtin/ViewImageTool.js +1 -1
  67. package/dist/{capabilities/implements → scope/plugins}/builtin/WriteFileTool.d.ts +1 -1
  68. package/dist/{capabilities/implements → scope/plugins}/builtin/WriteFileTool.js +1 -1
  69. package/dist/{capabilities/discovery/builtin.d.ts → scope/plugins/builtin/discover.d.ts} +1 -1
  70. package/dist/{capabilities/discovery/builtin.js → scope/plugins/builtin/discover.js} +1 -1
  71. package/dist/{capabilities/implements → scope/plugins}/builtin/index.d.ts +2 -2
  72. package/dist/{capabilities/implements → scope/plugins}/builtin/index.js +1 -1
  73. package/dist/{runtime → scope/plugins/mcp}/McpConnectionManager.d.ts +1 -1
  74. package/dist/{runtime → scope/plugins/mcp}/McpConnectionManager.js +1 -1
  75. package/dist/scope/plugins/mcp/McpScopePlugin.d.ts +29 -0
  76. package/dist/scope/plugins/mcp/McpScopePlugin.js +60 -0
  77. package/dist/{capabilities/discovery/mcp.d.ts → scope/plugins/mcp/discover.d.ts} +1 -1
  78. package/dist/{capabilities/discovery/mcp.js → scope/plugins/mcp/discover.js} +1 -1
  79. package/dist/{capabilities/implements → scope/plugins/mcp}/mcpTools.d.ts +2 -2
  80. package/dist/{capabilities/implements → scope/plugins/mcp}/mcpTools.js +2 -2
  81. package/dist/scope/plugins/skills/SkillsScopePlugin.d.ts +28 -0
  82. package/dist/scope/plugins/skills/SkillsScopePlugin.js +48 -0
  83. package/dist/{capabilities/discovery/skills.js → scope/plugins/skills/discover.js} +1 -1
  84. package/dist/{capabilities/implements → scope/plugins/skills}/skillTools.d.ts +1 -1
  85. package/dist/{capabilities/implements → scope/plugins/skills}/skillTools.js +3 -3
  86. package/dist/scope/plugins/subagents/SubAgentsScopePlugin.d.ts +20 -0
  87. package/dist/scope/plugins/subagents/SubAgentsScopePlugin.js +34 -0
  88. package/dist/{capabilities/discovery/subagents.d.ts → scope/plugins/subagents/discover.d.ts} +1 -1
  89. package/dist/{capabilities/implements → scope/plugins/subagents}/subAgentTools.d.ts +3 -3
  90. package/dist/{capabilities/implements → scope/plugins/subagents}/subAgentTools.js +5 -5
  91. package/dist/scope/plugins/usertools/UserToolsScopePlugin.d.ts +24 -0
  92. package/dist/scope/plugins/usertools/UserToolsScopePlugin.js +28 -0
  93. package/dist/{capabilities/discovery/usertools.js → scope/plugins/usertools/discover.js} +1 -1
  94. package/dist/types/index.d.ts +23 -2
  95. package/package.json +3 -3
  96. package/dist/runtime/Provider.d.ts +0 -119
  97. package/dist/runtime/Provider.js +0 -218
  98. /package/dist/{plugin → agent-plugins}/AutoMigratePlugin.d.ts +0 -0
  99. /package/dist/{plugin → agent-plugins}/AutoMigratePlugin.js +0 -0
  100. /package/dist/{plugin → agent-plugins}/AutoRefreshToolsPlugin.d.ts +0 -0
  101. /package/dist/{plugin → agent-plugins}/ContextGuardPlugin.d.ts +0 -0
  102. /package/dist/{plugin → agent-plugins}/ContextGuardPlugin.js +0 -0
  103. /package/dist/{capabilities → scope}/PermissionEvaluator.d.ts +0 -0
  104. /package/dist/{capabilities → scope}/PermissionEvaluator.js +0 -0
  105. /package/dist/{capabilities → scope}/disclosure.d.ts +0 -0
  106. /package/dist/{capabilities → scope}/disclosure.js +0 -0
  107. /package/dist/{runtime → scope/plugins/builtin}/SdkCallbackTool.js +0 -0
  108. /package/dist/{capabilities/implements → scope/plugins}/builtin/outputGuard.d.ts +0 -0
  109. /package/dist/{capabilities/implements → scope/plugins}/builtin/outputGuard.js +0 -0
  110. /package/dist/{capabilities/implements → scope/plugins}/builtin/test-helpers.d.ts +0 -0
  111. /package/dist/{capabilities/implements → scope/plugins}/builtin/test-helpers.js +0 -0
  112. /package/dist/{capabilities/discovery/skills.d.ts → scope/plugins/skills/discover.d.ts} +0 -0
  113. /package/dist/{capabilities/discovery/subagents.js → scope/plugins/subagents/discover.js} +0 -0
  114. /package/dist/{capabilities/discovery/usertools.d.ts → scope/plugins/usertools/discover.d.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,94 +1,161 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.0-alpha.1] - 2026-10-03
4
+
5
+ ### 💥 Breaking Changes
6
+
7
+ - **Factory model catalog refreshed (verified against vendor sources on 2026-10-03)** — every stale or retired model ID in `DEFAULT_APP_CONFIG.models` is replaced:
8
+ - **OpenAI**: `gpt-5.5` → `gpt-6-astra` / `gpt-6.1-sol` / `gpt-6-luna`
9
+ - **Zhipu**: `glm-5.1` / `glm-5v-turbo` → `glm-5.3` / `glm-5.3-flash` / `glm-5.3-flashx`
10
+ - **DeepSeek**: `deepseek-v4-pro` / `deepseek-v4-flash` / `deepseek-v4-flash-vision-exp` → `deepseek-flash`. The two V4-Flash variants were retired by the vendor, and V4-Pro requests are routed to V4.1-Flash.
11
+ - Defaults: `defaultModel` / `defaultMigrationModel` move from `deepseek-v4-flash` to `deepseek-flash`.
12
+ - **`Model.maxContextTokens` semantics clarified — it is the task-migration (handoff) trigger threshold, not the model context window** — `AutoMigratePlugin` does not estimate or learn: it only compares `usage.prompt_tokens` from the API response against this value. The previous comment read "上下文窗口 token 上限" ("context window token limit"), which is why values had been filled in with raw window sizes. Values are now ~25% of the real window (250K for ≥1M-window models), except `glm-4.7-flash` (200K window) which uses 100K.
13
+
14
+ ### 🚀 New Features
15
+
16
+ - **Managed model catalog sync (`custom` opt-out)** — `ConfigManager.ensureDefaultConfig()` now reconciles the factory catalog on every `bootstrap()`. Entries in `models` / `imageModels` that do **not** carry `custom: true` are dropped and replaced by the latest `DEFAULT_APP_CONFIG` definitions; entries marked `custom: true` are treated as user-owned and kept verbatim (user wins on id collision, no duplicates). A dangling `defaultModel` / `defaultImageModel` / `defaultMigrationModel` falls back to the factory default, so no reference is ever left pointing at a removed model. `endpoints` are never managed. The sync is idempotent — it only writes when something actually changed. Implemented in `ConfigManager.syncManagedModels`.
17
+ - **`Model` / `ImageModel` gained an optional `custom?: boolean` field** — marks an entry as user-owned so the factory sync leaves it untouched.
18
+
19
+ ### ✅ Tests
20
+
21
+ - Added coverage for the model catalog sync: stale non-custom entries replaced, `custom: true` entries preserved, user version winning on id collision without duplication, image models reconciled, dangling default IDs falling back, and idempotency across repeated calls.
22
+ - E2E model IDs updated to the current vendor names (`deepseek-flash`).
23
+
24
+ ### 📄 Docs
25
+
26
+ - Current-version references in `docs/zh` and `docs/en` updated to `1.0.0-alpha.1`.
27
+ - `DEFAULT_APP_CONFIG` row in `sdk-design-v0.md` updated to the new model catalog.
28
+
29
+ ## [1.0.0-alpha.0] - 2026-09-30
30
+
31
+ ### 💥 Breaking Changes
32
+
33
+ - **`Provider` renamed to `Scope` (fully removed, no compatibility alias)** — `runtime/Provider.ts` is migrated to `scope/Scope.ts`. All `provider` fields / params / variables are renamed to `scope`: `SdkAgent.provider` → `SdkAgent.scope`, `createAgent(scope, …)`, `createModel(scope, …)`, `createSubAgentTool(def, scope)`. `FilterOutput` / `ExcludeOptions` / `FilterOptions` move from `runtime/Provider` to `scope/Scope` accordingly.
34
+ - **Capability sources "delegated" to `ScopePlugin` (new capability-plugin contract)** — `Scope` degrades to a pure orchestration container: it only holds `config` / `cwd` / `env` / `agentsDir` and orchestrates the three-stage pipeline (discover → filter → instantiate). The four capability sources previously hard-coded in `Provider.refresh/filter/instantiate` are split into **5 standard plugins**, each self-containing its discovery state and instantiation logic:
35
+ - `BuiltinToolsScopePlugin` (`id: "builtin"`) — 20 built-in tool classes
36
+ - `UserToolsScopePlugin` (`id: "user-tools"`) — `tools/*.js`
37
+ - `SkillsScopePlugin` (`id: "skills"`) — `SKILL.md` + `load_skill` / `call_skill_sub_agent`
38
+ - `McpScopePlugin` (`id: "mcp"`) — `mcp.json` + a private `McpConnectionManager` + `load_mcp` / `call_mcp_tool` / `read_mcp_resource`
39
+ - `SubAgentsScopePlugin` (`id: "subagents"`) — `sub-agents/*.json`
40
+ - **`Scope` no longer accepts capability-source paths, nor exposes an aggregated candidate set** — `new Provider({ config, cwd, agentsDir, skillsPaths, toolsPaths, subAgentsPaths, mcpPaths })` → `new Scope({ config, cwd, agentsDir })`. Paths are now held by the plugins (pass absolute paths, or pass relative ones that each plugin joins against `scope.cwd` during `discover()`). `Scope` no longer has `builtinTools` / `userTools` / `subagents` / `skills` / `mcps` / `mcpManager` fields.
41
+ - **`Provider.create()` removed** — replaced by explicit assembly: `new Scope({...}).use(...plugins)` → `await scope.init()`. (The static `create` shortcut is removed for now, to be revisited later.)
42
+ - **Plugins must be assembled explicitly** — `Scope` registers no default plugins itself; without assembly it has "empty capabilities". The convenience factory `allInOne({ skillsPaths, toolsPaths, subAgentsPaths, mcpPaths })` always returns the 5 standard plugins for one-shot assembly.
43
+
44
+ ### 🚀 New Features
45
+
46
+ - **New `ScopePlugin` extension point** — contract `{ id; discover?; candidates; instantiate; dispose? }`, an extension point independent of core's `AgentPlugin` (Agent runtime hooks):
47
+ - `discover?(scope, { silent })` — stage 1 · discovery (may be async, re-scans internally)
48
+ - `candidates(scope, definition)` — stage 2 · contribute four-dimension candidate names (including availability checks, e.g. a synchronous `isAvailable`)
49
+ - `instantiate(scope, filtered)` — stage 3 · produce `Tool[]` from the allowed names after filtering
50
+ - `dispose?()` — release (e.g. disconnect MCP)
51
+ - **New `Scope` API**: `use(...plugins)` (chainable, variadic), `getPluginById(id)` (for external code / plugins to query plugin state), `plugins` (read-only view), `init()` / `refresh({ silent })` / `filter()` / `instantiate()` / `buildTools()` / `dispose()`. Calling `use()` after `init()` throws.
52
+ - **`dispose()` symmetric with `init()`** — calls each plugin's `dispose()` in reverse order; `McpScopePlugin.dispose()` → `mcpManager.disconnectAll()`, so MCP long-lived connections no longer rely solely on an idle timeout.
53
+ - **`allInOne()` convenience factory** — always returns the 5 standard plugins; order is priority (`builtin → user-tools → skills → mcp → subagents`; on dedupe, later registrations override earlier ones, so user tools can still override same-named built-in tools).
54
+ - **`McpScopePlugin.hasConfig()`** — lets `UnknownToolHintPlugin` determine "whether MCP config exists" (replacing a direct read of `provider.mcpPaths`).
55
+
56
+ ### 🛠 Optimized
57
+
58
+ - **`UnknownToolHintPlugin` constructor now takes `{ scope }`** — it learns the MCP config via `scope.getPluginById(McpScopePlugin.ID)?.hasConfig()`; behavior semantics are unchanged (config present but `call_mcp_tool` denied → hint about permission; available → guide usage; no config → just hint that it doesn't exist).
59
+ - **`AutoRefreshToolsPlugin`** — now goes through `agent.scope.refresh()` / `agent.scope.buildTools()`.
60
+ - **Progressive-disclosure (lazy-loading) design unchanged** — the schema of `load_skill` / `call_skill_sub_agent` / `load_mcp` / `call_mcp_tool` / `read_mcp_resource`, enum disclosure (`createDisclosureParam`), the `include_manifest` switch, `touch` renewal, and `call_skill_sub_agent` reusing the parent tool set while excluding itself to prevent recursion — all **preserved verbatim**, merely moved from `Provider.instantiate()` to the `instantiate()` of `SkillsScopePlugin` / `McpScopePlugin`. Permission semantics are unchanged (denying `load_*` on the `tools` dimension cuts off the disclosure channel; denied skills/mcps never enter an enum).
61
+
62
+ ### 📄 Docs
63
+
64
+ - Added [`docs/sdk-design-v1.md`](docs/sdk-design-v1.md) (the v1 design source of truth, covering the Scope / ScopePlugin architecture, the five plugins, and a migration guide); the old doc is renamed to [`docs/sdk-design-v0.md`](docs/sdk-design-v0.md).
65
+
66
+ ### ✅ Tests
67
+
68
+ - All tests migrated to Scope + ScopePlugin: `capabilities.test.ts` rewritten to be organized by plugin dimension (built-in / SubAgent / Skill / MCP / user tools + lifecycle), with `createAgent` / `createModel` / `SdkAgent` / `UnknownToolHint` / `AutoRefreshTools` / `subAgentTools` / `integration` / `e2e-*` updated accordingly. SDK suite: **498 passed / 2 skipped** (including chat / view-image e2e against the real DeepSeek API).
69
+
3
70
  ## [0.12.0] - 2026-09-29
4
71
 
5
72
  ### 🚀 New Features
6
73
 
7
- - **统一工具输出保护(`maxToolOutput`)** — `AppConfig` 新增 `maxToolOutput?: number`(字符数,缺省 32768;出厂 `DEFAULT_APP_CONFIG` 已写入该值)。新增输出保护骨架 `guardOutput()`(`capabilities/implements/builtin/outputGuard.ts`):工具以三个回调自陈策略——`isOverLimit(content)`(工具自读 config 判定,含合计口径)、`dump(dir, content)`(落盘文件清单,缺省即"只警告不落盘")、`buildWarning(ctx)`(返回体,含预览与统计);骨架只负责"判定 → 未超限原样返回 / 超限时落盘"两件事。落盘目录为 `<os.tmpdir()>/ai-zen/tool-output/<工具名>-<时间戳>-<随机串>/`,每次调用独立互不覆盖,不做自清理(依赖系统对临时目录的策略)。已接入:
8
- - **`exec`** — `stdout + stderr` 合计超限即分文件落盘 `stdout.log` / `stderr.log`,返回警告 + 各流头尾预览(各 1000 字符)+ 字符数/行数统计,原 `exitCode` / `killed` / `terminated` 字段保留;
9
- - **`findText`** — 落盘 `result.json`,返回警告 + 头部预览(1000 字符);既有 `maxMatches` / `maxFileSize` / `maxLineLength` 参数保持不变;
10
- - **`glob` / `ls`** — 落盘 `result.json`,返回警告 + 头部预览,并提示缩小范围的方式(更精确的 `pattern` / `exclude`,或改用 `glob`);
11
- - **`readFile`** — 超限**不落盘**,仅返回警告并提示使用 `range` 分批读取。
12
- - 另导出 `headPreview()` / `headTailPreview()` 供各工具按需组织预览(预览文本由工具自行生成)。
13
- - **`readFile` 新增 `range` 参数(可选)** — `range: [起始行, 起始列, 结束行, 结束列]`,行列均 0-based、`-1` 表示末位;如 `[0, 0, 100, -1]` 表示"第 0 行第 0 列读到第 100 行最后一列"。300KB 读取阈值保持不变——读取阈值(能否读)与输出阈值(是否落盘)互相独立。
14
- - **新增 `inspectFile` 工具(`InspectFileTool`)** — 勘察文件结构概况,不做内容读取,与 `readFile` 的 `range` 配套(先看结构,再决定读取范围)。返回 `path` / `bytes` / `lines` / `chars` / `maxCol` / `lineIndexOfMaxCol`(0-based)/ `avgCol` / `lineEnding`(`LF` / `CRLF` / `mixed`);可选 `withColCountMap: true` 返回"行索引 → 列数"对象映射。流式扫描,**不受 300KB 读取阈值限制**;列数按字符数计、不含行尾换行符;明细超限时不落盘,仅返回概况 + 提示"文件规模超出可处理范围"。内置工具类由 19 个增至 20 个。
15
- - **`batchEdit` 输出精简** — 成功项不再回显原文与新文,全部成功时返回 `{ result: "success", replacedCount }`;存在未匹配项时返回 `{ result: "partial", replacedCount, failed: [{ oldText, newText, reason }] }`,仅回显有问题的原文与新文,避免大段文本回显撑大输出。
16
- - **`AgentDefinition` 新增 `custom` 字段** — 为 `true` 时 SDK 初始化不触碰该 Agent 的内置内容(如默认提示词)。出厂 `DEFAULT_AGENT_DEFINITION` 携带 `custom: false`:`ensureDefaultAgent()` 在 `custom !== true` 时**仅同步出厂提示词**(`name` / `permissions` / `modelId` 等用户改动保留),提示词一致时不写盘、不动 `updatedAt`(比较时忽略随机生成的 message `id`,避免时间戳漂移)。
17
- - **默认 Agent 提示词调整** — 去掉原有法则,改为两条:① 需要用户决策时,一次只问一个问题;② 当前对话基于 Node.js 驱动,可编写 Node.js 脚本执行复杂任务并通过 `exec` 工具运行。
74
+ - **Unified tool output protection (`maxToolOutput`)** — `AppConfig` gains `maxToolOutput?: number` (in characters, default 32768; the factory `DEFAULT_APP_CONFIG` already writes this value). New output-protection skeleton `guardOutput()` (`capabilities/implements/builtin/outputGuard.ts`): tools declare their policy via three callbacks — `isOverLimit(content)` (the tool reads config itself to decide, including a combined measure), `dump(dir, content)` (the dumped-file list; the default is "warn only, no dump"), `buildWarning(ctx)` (the return body, with preview and stats); the skeleton only does two things: "decide → return as-is when under the limit / dump when over". The dump directory is `<os.tmpdir()>/ai-zen/tool-output/<tool>-<timestamp>-<random>/`, isolated and non-overwriting per call, with no self-cleanup (relying on the OS's policy for temp directories). Wired into:
75
+ - **`exec`** — when `stdout + stderr` combined exceed the limit, they are dumped to separate files `stdout.log` / `stderr.log`, returning a warning + head/tail preview per stream (1000 chars each) + char/line counts; the original `exitCode` / `killed` / `terminated` fields are preserved;
76
+ - **`findText`** — dumps `result.json`, returns a warning + head preview (1000 chars); the existing `maxMatches` / `maxFileSize` / `maxLineLength` params are unchanged;
77
+ - **`glob` / `ls`** — dumps `result.json`, returns a warning + head preview, and hints how to narrow the scope (a more precise `pattern` / `exclude`, or switching to `glob`);
78
+ - **`readFile`** — does **not** dump on overflow, only returns a warning and suggests batched reads via `range`.
79
+ - Also exports `headPreview()` / `headTailPreview()` for tools to build previews as needed (the preview text is generated by each tool itself).
80
+ - **`readFile` gains a `range` param (optional)** — `range: [startLine, startCol, endLine, endCol]`, all 0-based, with `-1` meaning the last index; e.g. `[0, 0, 100, -1]` means "read from line 0 col 0 through line 100's last column". The 300KB read threshold is unchanged — the read threshold (whether a file can be read) and the output threshold (whether to dump) are independent.
81
+ - **New `inspectFile` tool (`InspectFileTool`)** — surveys a file's structural overview without reading content, complementing `readFile`'s `range` (look at the structure first, then decide the read range). Returns `path` / `bytes` / `lines` / `chars` / `maxCol` / `lineIndexOfMaxCol` (0-based) / `avgCol` / `lineEnding` (`LF` / `CRLF` / `mixed`); an optional `withColCountMap: true` returns a "line index → column count" object map. Streamed scan, **not limited by the 300KB read threshold**; columns are counted as characters, excluding the line-ending newline; on overflow the detail is not dumped, only the overview plus a hint that "the file scale exceeds the processable range". Built-in tool classes increased from 19 to 20.
82
+ - **`batchEdit` output slimmed down** — successful items no longer echo the old and new text; when all succeed it returns `{ result: "success", replacedCount }`; when there are unmatched items it returns `{ result: "partial", replacedCount, failed: [{ oldText, newText, reason }] }`, echoing only the problematic old and new text, so large blocks aren't echoed back to inflate the output.
83
+ - **`AgentDefinition` gains a `custom` field** — when `true`, SDK init does not touch the Agent's built-in content (e.g. the default prompt). The factory `DEFAULT_AGENT_DEFINITION` carries `custom: false`: `ensureDefaultAgent()` **syncs only the factory prompt** when `custom !== true` (user changes to `name` / `permissions` / `modelId` etc. are preserved), and when the prompt is identical it writes nothing and leaves `updatedAt` untouched (randomly generated message `id`s are ignored in the comparison to avoid timestamp drift).
84
+ - **Default Agent prompt adjusted** — the original rules are replaced by two: ① when a user decision is needed, ask only one question at a time; ② the current conversation is Node.js-driven, so you can write Node.js scripts to perform complex tasks and run them via the `exec` tool.
18
85
 
19
86
  ### ✅ Tests
20
87
 
21
- - 新增 `outputGuard.test.ts`(骨架:未超限原样返回 / 超限落盘并返回警告 / 无 `dump` 时只警告 / 目录隔离 / `headPreview` 与 `headTailPreview`)、`InspectFileTool.test.ts`(概况字段、`colCountMap` 映射、CRLF 与 mixed、末行无换行、空文件、300KB 以上文件、明细超限降级、目录与不存在路径)及各工具的超限/范围读取用例;`ConfigManager.bootstrap.test.ts` 覆盖 `custom: true` 跳过、仅同步提示词、一致时不写盘。SDK 全量 **499 passed / 6 skipped**
88
+ - Added `outputGuard.test.ts` (skeleton: return as-is under the limit / dump + warning over the limit / warn-only without `dump` / directory isolation / `headPreview` and `headTailPreview`), `InspectFileTool.test.ts` (overview fields, the `colCountMap` map, CRLF and mixed, no trailing newline on the last line, empty files, files over 300KB, detail-overflow degradation, directories and non-existent paths), and overflow/range-read cases for each tool; `ConfigManager.bootstrap.test.ts` covers `custom: true` skipping, syncing only the prompt, and not writing when identical. SDK suite: **499 passed / 6 skipped**
22
89
 
23
90
  ## [0.11.0] - 2026-09-28
24
91
 
25
92
  ### 💥 Breaking Changes
26
93
 
27
- - **`createCallSkillSubAgentTool` 移除第三参 `provider`** — Skill 子 Agent 不再经 `provider.buildTools(parent.definition, …)` 重新解析工具集,改为**直接沿用父 Agent 的工具集**(`ctx.agent.tools`),仅剔除 `call_skill_sub_agent` 自身:Skill 子 Agent 作为一次性对话分身,沿用调用者的能力而不再走第二遍权限过滤,同时避免 skill 链式自递归。传入 `Provider` 的调用方需删除该实参
94
+ - **`createCallSkillSubAgentTool` drops the third param `provider`** — the Skill sub-agent no longer re-resolves its tool set via `provider.buildTools(parent.definition, …)`; instead it **reuses the parent Agent's tool set directly** (`ctx.agent.tools`), excluding only `call_skill_sub_agent` itself: as a one-shot conversational clone, the Skill sub-agent reuses the caller's capabilities without a second permission pass, while also preventing chained skill self-recursion. Callers that passed a `Provider` must drop that argument
28
95
 
29
96
  ### 🚀 New Features
30
97
 
31
- - **Skill 子 Agent 纳入统一委派边界** — `call_skill_sub_agent` 由 `AgentToolLazy` 承载(原为就地实现的 `CallbackTool`),因此与常规 SubAgent 工具共享同一套 `onSubAgentStart` / `onSubAgentEnd` 插件钩子与 `sub-agent-start` / `sub-agent-end` 事件。将护栏装配到 `ctx.subAgent` 的消费方(如上下文护栏)与渲染子 Agent 流式输出的消费方(如 CLI),自此均覆盖该路径
98
+ - **Skill sub-agent brought into the unified delegation boundary** — `call_skill_sub_agent` is now backed by `AgentToolLazy` (previously an in-place `CallbackTool`), so it shares the same `onSubAgentStart` / `onSubAgentEnd` plugin hooks and `sub-agent-start` / `sub-agent-end` events as regular SubAgent tools. Consumers that plug guards into `ctx.subAgent` (e.g. a context guard) and those that render sub-agent streaming output (e.g. the CLI) now cover this path
32
99
 
33
100
  ### 🛠 Optimized
34
101
 
35
- - **Skill 子 Agent 的构建收敛到 `buildAgent`** — 子 Agent 的初始消息(`System(SKILL.md 内容)` + `User(task)`)改由 `buildAgent` 决定(依托 core 4.3.0 的 `messages` 可选),原先手写的 abort 联动与返回值处理一并删除,交由工具基类统一实现
36
- - **校验失败改为抛错** — `skill_id` 不存在、或 Skill 未声明 `sub-agent: true` 时抛出错误(原为返回提示文本);正常路径由 `skill_id` 枚举限定,不触发
37
- - **依赖 `@ai-zen/agents-core` 升至 `^4.3.0`** — 依赖声明保持 `workspace:^`,由 pnpm 在发布时展开为 `^4.3.0`。core 4.3.0 使 `AgentToolLazy.messages` 变为可选
102
+ - **Skill sub-agent construction consolidated into `buildAgent`** — the sub-agent's initial messages (`System(SKILL.md content)` + `User(task)`) are now decided by `buildAgent` (leveraging core 4.3.0's optional `messages`); the previously hand-written abort linkage and return-value handling are deleted, delegated to the tool base class for a unified implementation
103
+ - **Validation failures now throw** — a missing `skill_id`, or a Skill that doesn't declare `sub-agent: true`, now throws an error (previously it returned a hint text); the normal path is constrained by the `skill_id` enum, so it isn't triggered
104
+ - **Dependency `@ai-zen/agents-core` bumped to `^4.3.0`** — the dependency declaration stays `workspace:^`, expanded to `^4.3.0` by pnpm at publish time. core 4.3.0 makes `AgentToolLazy.messages` optional
38
105
 
39
106
  ### ✅ Tests
40
107
 
41
- - `skillTools.test.ts` 中 `createCallSkillSubAgentTool` 用例重写为围绕 `AgentToolLazy`:工具契约不变、未知 Skill 抛错、非子 Agent Skill 抛错、委派边界(`onSubAgentStart` 与 `sub-agent-start` 分发、子 Agent 沿用父工具集且剔除自身、初始消息取自 SKILL.md 与 `task`);SDK 全量 **463 passed / 6 skipped**
108
+ - The `createCallSkillSubAgentTool` cases in `skillTools.test.ts` are rewritten around `AgentToolLazy`: tool contract unchanged, unknown Skill throws, non-sub-agent Skill throws, delegation boundary (`onSubAgentStart` and `sub-agent-start` dispatch, the child reusing the parent tool set while excluding itself, initial messages drawn from SKILL.md and `task`); SDK suite: **463 passed / 6 skipped**
42
109
 
43
110
  ## [0.10.0] - 2026-09-28
44
111
 
45
112
  ### 🚀 New Features
46
113
 
47
- - **`load_mcp` 新增 `include_manifest` 开关(可选布尔,默认 `true`)** — 默认行为逐字不变(返回 `{ tools, resources }` 完整清单,tools 含完整 `inputSchema`);显式传 `false` 时仅建立连接并返回摘要 `{ server, connected, tools: 数量, resources: 数量 }`,不返回任何工具/资源定义。用于「进程重启后连接已失效、但上文中已存在该清单」的场景:只重建连接,不重复向上下文灌入数十 KB 的工具定义,节省 token。完整清单仍留在 `McpConnectionManager` 内,`call_mcp_tool` / `read_mcp_resource` 行为不变;`server` 参数枚举、工具名表与 `DYNAMIC_TOOL_NAMES` 均未变动(前缀缓存结构不变)。失败路径与已连接路径的语义在两种模式下一致;`docs/sdk-design.md` §9 同步更新
114
+ - **`load_mcp` gains an `include_manifest` switch (optional boolean, default `true`)** — the default behavior is unchanged verbatim (returns the full `{ tools, resources }` manifest, with tools carrying the full `inputSchema`); passing `false` explicitly only establishes the connection and returns a summary `{ server, connected, tools: count, resources: count }`, without any tool/resource definitions. It serves the "connection has gone stale after a process restart, but the manifest already exists in context" scenario: rebuild the connection only, without re-injecting tens of KB of tool definitions into the context, saving tokens. The full manifest stays inside `McpConnectionManager`, and `call_mcp_tool` / `read_mcp_resource` behavior is unchanged; the `server` param enum, the tool-name table, and `DYNAMIC_TOOL_NAMES` are all unchanged (the prefix-cache structure is unaffected). The failure-path and already-connected-path semantics are identical in both modes; `docs/sdk-design.md` §9 updated accordingly
48
115
 
49
116
  ### 🛠 Optimized
50
117
 
51
- - **依赖 `@ai-zen/agents-core` 升至 `^4.2.0`** — 依赖声明保持 `workspace:^`,由 pnpm 在发布时展开为 `^4.2.0`。core 4.2.0 新增子 Agent 委派阻塞钩子 `onSubAgentStart` / `onSubAgentEnd`,并将原有事件 `sub-agent` 更名为 `sub-agent-start`(core 侧破坏性变更);SDK 内部不监听该事件,源码无需适配。监听该事件的消费方需同步更名
118
+ - **Dependency `@ai-zen/agents-core` bumped to `^4.2.0`** — the dependency declaration stays `workspace:^`, expanded to `^4.2.0` by pnpm at publish time. core 4.2.0 adds the sub-agent delegation blocking hooks `onSubAgentStart` / `onSubAgentEnd` and renames the existing `sub-agent` event to `sub-agent-start` (a core-side breaking change); the SDK doesn't listen to that event internally, so no source adaptation is needed. Consumers listening to that event must rename accordingly
52
119
 
53
120
  ### ✅ Tests
54
121
 
55
- - `mcpTools.test.ts` 新增 4 个用例:静默 + 未连接、静默 + 已连接、显式 `true`、静默 + 连接失败;SDK 全量 **461 passed / 6 skipped**
122
+ - `mcpTools.test.ts` gains 4 cases: silent + not connected, silent + connected, explicit `true`, silent + connection failure; SDK suite: **461 passed / 6 skipped**
56
123
 
57
124
  ## [0.9.4] - 2026-09-20
58
125
 
59
126
  ### 🔧 Fixed
60
127
 
61
- - **MCP `sse` transport 改为真正的 SSE 连接(`SSEClientTransport`)** — 此前 `http` 与 `sse` 合并处理,统一使用 `StreamableHTTPClientTransport`(Streamable HTTP:POST 发送消息 + 可选 SSE 流),使配置声明 `type: "sse"` 的旧版 HTTP+SSE 服务器(GET 建立 SSE 流、POST 到 endpoint 发送消息)实际运行在不匹配的协议栈上。现按 transport 拆分:`http` → `StreamableHTTPClientTransport`,`sse` → `SSEClientTransport`(均为官方 SDK 内置实现)。`headers` 仍经 `requestInit` 透传,由 SDK 统一应用于 SSE 建流(GET)与消息发送(POST)请求;缺少 `url` 时的报错行为保持不变。`McpServerConfig.transport` 注释与 `docs/sdk-design.md` §10/§11 同步更新
128
+ - **MCP `sse` transport switched to a real SSE connection (`SSEClientTransport`)** — previously `http` and `sse` were handled together, both using `StreamableHTTPClientTransport` (Streamable HTTP: POST to send messages + an optional SSE stream), which made legacy HTTP+SSE servers declared as `type: "sse"` (GET to open the SSE stream, POST to the endpoint to send messages) actually run on a mismatched protocol stack. Now they are split by transport: `http` → `StreamableHTTPClientTransport`, `sse` → `SSEClientTransport` (both built-in official SDK implementations). `headers` are still passed through via `requestInit`, applied uniformly by the SDK to the SSE stream setup (GET) and message-sending (POST) requests; the error behavior when `url` is missing is unchanged. The `McpServerConfig.transport` comment and `docs/sdk-design.md` §10/§11 updated accordingly
62
129
 
63
130
  ### ✅ Tests
64
131
 
65
- - 针对真实 MCP 服务器(`packages/test-project/mcp-servers/sse-server.mjs`,旧版 HTTP+SSE 协议)验证通过:建立连接、工具发现(`echo` / `add`)、工具调用、断开重连;并确认运行时 transport 实例为 `SSEClientTransport`
132
+ - Verified against a real MCP server (`packages/test-project/mcp-servers/sse-server.mjs`, legacy HTTP+SSE protocol): connection setup, tool discovery (`echo` / `add`), tool calls, and disconnect/reconnect; and confirmed the runtime transport instance is `SSEClientTransport`
66
133
 
67
134
  ## [0.9.3] - 2026-09-01
68
135
 
69
136
  ### 🔧 Fixed
70
137
 
71
- - **`createAgent` 不再污染 Agent 定义模板(`definition.messages` 引用隔离)** — 创建 Agent 时对 `definition.messages` 做模板快照(`[...(definition.messages ?? [])]`)而非直接引用。此前把定义数组直接传给 `AgentContext.messages`(mutable 运行时数组),对话期间 `append()` 会反向把完整对话历史写入 `definition.messages`,导致:
72
- - 任务迁移 `prune` 分支展开的是被污染的"完整历史"而非模板 → 旧消息既未被物理删除、也未被标记 `omit`,却能看到新注入的断点消息;
73
- - `omit` 分支的 `preserveCount` 因 `definition.messages` 与 `agent.messages` 指向同一数组而等于全长 → 历史消息无法被标记 `omit`;
74
- - `/new` 基于被污染的模板拷贝,新对话会继承旧历史。
75
- - 顺带修复 Agent 定义缺少 `messages` 字段(`undefined`)时创建 Agent 的崩溃问题(`?? []` 兜底)。
138
+ - **`createAgent` no longer pollutes the Agent definition template (`definition.messages` reference isolation)** — when creating an Agent, `definition.messages` is now snapshot (`[...(definition.messages ?? [])]`) instead of being referenced directly. Previously the definition array was passed straight to `AgentContext.messages` (a mutable runtime array), so during a conversation `append()` would write the full conversation history back into `definition.messages`, causing:
139
+ - the task-migration `prune` branch to expand the polluted "full history" instead of the template → old messages were neither physically deleted nor marked `omit`, yet the newly injected breakpoint message was visible;
140
+ - the `omit` branch's `preserveCount` to equal the full length because `definition.messages` and `agent.messages` pointed to the same array → history messages could not be marked `omit`;
141
+ - `/new` to copy from the polluted template, so a new conversation inherited the old history.
142
+ - Also fixes a crash when creating an Agent whose definition lacks a `messages` field (`undefined`) (with a `?? []` fallback).
76
143
 
77
144
  ### ✅ Tests
78
145
 
79
- - `createAgent.test.ts` 新增:`append` 不污染 `definition.messages`(引用隔离);Agent 定义缺少 `messages` 字段时正常创建
146
+ - `createAgent.test.ts` gains: `append` does not pollute `definition.messages` (reference isolation); an Agent whose definition lacks a `messages` field is created normally
80
147
 
81
148
  ## [0.9.2] - 2026-08-31
82
149
 
83
150
  ### 🎯 Optimized
84
151
 
85
- - **默认 Agent 提示词重构(`DEFAULT_AGENT_DEFINITION`)** — 精简并统一风格:
86
- - 人格设定调整为"幽默风趣、严谨可靠"
87
- - 原有 8 条原则压缩为一条核心规则:遇到任何不明确、不确定、有矛盾或可能影响结果的状况,立即停下并直接询问用户,而不是自行脑补、猜测或硬干
88
- - 新增"惜字如金"原则:只交代结果与必要说明,执行过程(工具调用、读写、搜索等)用户均已可见,不必再复述,以节省 token
89
- - 保留护栏:主动调用工具、如实汇报、并只在你被指定的工作范围内行事
90
- - 注入"当前操作系统"信息,动态使用 `process.platform`,适配不同平台
91
- - **`ViewImageTool` 增加本地 URL 校验** — 新增 `isLocalUrl()`,拒绝 `file://` 协议及指向本机回环地址(`localhost`/`127.0.0.1`/`[::1]`/`0.0.0.0`)的 http(s) URL(API 服务商无法下载访问),在调用早期直接报错;`function.description` 与 `path_or_url` 参数说明同步补充"不支持本地 URL,本地图片请使用文件路径"
152
+ - **Default Agent prompt refactored (`DEFAULT_AGENT_DEFINITION`)** — streamlined and stylistically unified:
153
+ - the persona is set to "witty and humorous, rigorous and reliable"
154
+ - the original 8 principles are compressed into one core rule: whenever you hit any situation that is unclear, uncertain, contradictory, or that might affect the result, stop immediately and ask the user directly, rather than filling in the gaps yourself, guessing, or forcing ahead
155
+ - a new "terse" principle: state only the result and necessary explanation; the execution process (tool calls, reads/writes, searches, etc.) is already visible to the user, so don't restate it, to save tokens
156
+ - guards retained: proactively call tools, report faithfully, and act only within the scope you've been assigned
157
+ - injects "current operating system" info, dynamically using `process.platform` to adapt to different platforms
158
+ - **`ViewImageTool` gains a local-URL check** — a new `isLocalUrl()` rejects the `file://` protocol and http(s) URLs pointing to local loopback addresses (`localhost`/`127.0.0.1`/`[::1]`/`0.0.0.0`) (API providers cannot download them), erroring early in the call; `function.description` and the `path_or_url` param description now add "local URLs are not supported; use a file path for local images"
92
159
 
93
160
  ## [0.9.1] - 2026-08-28
94
161
 
package/README.md CHANGED
@@ -4,7 +4,7 @@ AI-Zen SDK — shared business logic layer providing a unified Agent runtime for
4
4
 
5
5
  ## Source of Truth
6
6
 
7
- **[`docs/sdk-design.md`](./docs/sdk-design.md)** is the single source of truth for this package's design. All implementations must stay consistent with the document.
7
+ **[`docs/sdk-design-v1.md`](./docs/sdk-design-v1.md)** is the single source of truth for this package's design. All implementations must stay consistent with the document. (Pre-refactor design is kept in [`docs/sdk-design-v0.md`](./docs/sdk-design-v0.md).)
8
8
 
9
9
  ## Architecture
10
10
 
@@ -18,23 +18,24 @@ Desktop ──┘ │
18
18
  ## Module Layering
19
19
 
20
20
  ```
21
- types ← pure types, zero business dependencies (incl. ToolEnv)
22
- config ← ConfigManager + constants: config.json read/write + directory setup + factory defaults
23
- crud ← capability-entity CRUD (Agent definitions, etc.; conversations/drafts are persisted by each consumer)
24
- capabilities ← capability discovery & assembly (built-in + user + MCP + Skill + SubAgent)
25
- runtime ← Provider + model factory + Agent assembly + MCP connection management + task migration + SdkCallbackTool base
26
- plugin ← Agent plugins (autoMigrate, autoRefreshTools, contextGuard, unknownToolHint)
27
- shared ← logging, errors
21
+ types ← pure types, zero business dependencies (incl. ToolEnv)
22
+ config ← ConfigManager + constants: config.json read/write + directory setup + factory defaults
23
+ crud ← capability-entity CRUD (Agent definitions; conversations/drafts are persisted by each consumer)
24
+ runtime ← model factory + Agent assembly + task migration (createModel / createAgent / SdkAgent / TaskMigrationService)
25
+ scope ← Scope orchestration container + ScopePlugin contract + PermissionEvaluator / disclosure + plugins/ (5 self-contained capability sources: discovery + tools + plugin)
26
+ agent-plugins ← Agent plugins (autoMigrate, autoRefreshTools, contextGuard, unknownToolHint)
27
+ shared ← logging, errors
28
28
  ```
29
29
 
30
- Dependency direction: `plugin → runtime → capabilities → crud → config → types`. Upper layers depend on lower layers, never the other way around.
30
+ Dependency direction: `agent-plugins → runtime → scope`; `runtime → scope / crud / shared / types`, `scope → types / shared`. Upper layers depend on lower layers.
31
31
 
32
32
  ## Core Concepts
33
33
 
34
34
  | Entity | Description |
35
35
  |--------|-------------|
36
- | **Provider** | Global context + capability registry, holding config, paths (incl. `cwd`), model factory, and MCP manager; integrates discovery → filtering → instantiation |
37
- | **ToolEnv** | Tool environment `{ cwd, config }`; injected when the Provider instantiates built-in tools, serving as the base for relative path resolution and config reads |
36
+ | **Scope** | Orchestration container: global context (`config` / `cwd` / `agentsDir`) + the three-phase capability pipeline (discover → filter → instantiate) + plugin registry. Explicitly assembled via `scope.use(...)` |
37
+ | **ScopePlugin** | Capability plugin (the v1 extension point): self-owns one capability source's discovery state, candidate names, and instantiation (`discover` / `candidates` / `instantiate` / `dispose`). 5 standard plugins ship out of the box, assembled via `allInOne()` |
38
+ | **ToolEnv** | Tool environment `{ cwd, config }`; injected when the built-in-tools plugin (`BuiltinToolsScopePlugin`) instantiates tools, serving as the base for relative path resolution and config reads |
38
39
  | **SdkCallbackTool** | Abstract base for built-in tools: `env` constructor injection + subclass `call()` + `resolve()` relative path resolution |
39
40
  | **SdkAgent** | Extends the Core Agent, carries SDK metadata, supports `use()` plugin registration |
40
41
  | **AgentPlugin** | Plugin interface (`onInit`, `onBeforeSend`, `onAfterSend`, `onInnerLoopStart`, `onInnerLoopEnd`, `onInnerLoopsStart`, `onInnerLoopsEnd`, `onToolCall`, `onUnknownTool`, `onSubAgentStart`, `onSubAgentEnd`) |
@@ -57,17 +58,24 @@ Agent.permissions
57
58
  ## Consumption
58
59
 
59
60
  ```typescript
60
- const provider = await Provider.create({
61
+ const scope = new Scope({
61
62
  config,
62
- cwd: "/path/to/workspace", // one working directory per Provider; parallel sessions don't interfere
63
- ...paths,
64
- });
65
- const agent = await createAgent(provider, "my-agent");
63
+ cwd: "/path/to/workspace", // one working directory per Scope; parallel sessions don't interfere
64
+ agentsDir,
65
+ }).use(
66
+ ...allInOne({ skillsPaths, toolsPaths, subAgentsPaths, mcpPaths }),
67
+ );
68
+ await scope.init();
69
+
70
+ const agent = await createAgent(scope, "my-agent");
66
71
  const migrationService = new TaskMigrationService({ onMigrated }); // migrates via the agent's own model client
67
72
  agent.use(new AutoMigratePlugin({ service: migrationService, maxTokens }));
68
73
  agent.use(new AutoRefreshToolsPlugin());
74
+ agent.use(new UnknownToolHintPlugin({ scope }));
69
75
  await agent.init();
70
76
  await agent.send("Hello");
77
+
78
+ await scope.dispose(); // disconnect MCP, release plugin resources
71
79
  ```
72
80
 
73
81
  ## Development Status
@@ -77,14 +85,14 @@ await agent.send("Hello");
77
85
  | `types` | ✅ Implemented — core entities, permission model, MCP types complete |
78
86
  | `config` | ✅ Implemented — ConfigManager + factory defaults + one-shot bootstrap |
79
87
  | `crud` | ✅ Implemented — capability-entity CRUD for Agents, etc. (conversations/drafts persisted by each consumer) |
80
- | `capabilities` | ✅ Implemented — discovery + permission filtering + safe pre-filtering + enumeration disclosure |
81
- | `runtime` | ✅ Implemented — Provider, createAgent, MCP connection management, task migration |
82
- | `plugin` | ✅ Implemented — AutoMigratePlugin / AutoRefreshToolsPlugin / ContextGuardPlugin / UnknownToolHintPlugin |
88
+ | `scope` | ✅ Implemented — Scope + ScopePlugin + 5 standard plugins (builtin / user-tools / skills / mcp / subagents) + allInOne |
89
+ | `runtime` | ✅ Implemented — createAgent, MCP connection management, task migration |
90
+ | `agent-plugins` | ✅ Implemented — AutoMigratePlugin / AutoRefreshToolsPlugin / ContextGuardPlugin / UnknownToolHintPlugin |
83
91
  | `shared` | ✅ Implemented — SdkError + injectable Logger |
84
92
 
85
93
  ## Built-in Tools
86
94
 
87
- All built-in tools are classes (extending `SdkCallbackTool`), instantiated by the Provider with a `ToolEnv` — one set of instances per Provider, with `cwd` injected and relative paths resolved against `Provider.cwd`, never depending on the global `process.cwd()`.
95
+ All built-in tools are classes (extending `SdkCallbackTool`), instantiated by the built-in-tools plugin (`BuiltinToolsScopePlugin`) with a `ToolEnv` — one set of instances per Scope, with `cwd` injected and relative paths resolved against `Scope.cwd`, never depending on the global `process.cwd()`.
88
96
 
89
97
  | Tool | Description |
90
98
  |------|-------------|
@@ -114,7 +122,7 @@ Conditionally injected based on the active model / config:
114
122
  | `generateImage` | Only when `defaultImageModel` is configured | Generate an image from a text description |
115
123
  | `viewImage` | Only for vision models (the agent's `modelId` resolves to a model with `vision: true`) | View / analyze an image: local images are auto-uploaded via the Files API, network URLs are referenced directly |
116
124
 
117
- Tool output protection: `AppConfig.maxToolOutput` (in characters, default 32768) is the unified ceiling for tool output. Each tool handles overflow itself — `exec` dumps `stdout.log` / `stderr.log` separately (returning head/tail previews per stream), `findText` / `glob` / `ls` dump `result.json` (returning a head preview), and `readFile` only warns and suggests batched reads via `range`. Dumps go to `<tmpdir>/ai-zen/tool-output/<tool>-<timestamp>-<random>/`, one directory per call, with no self-cleanup. See [`docs/sdk-design.md` §7](docs/sdk-design.md).
125
+ Tool output protection: `AppConfig.maxToolOutput` (in characters, default 32768) is the unified ceiling for tool output. Each tool handles overflow itself — `exec` dumps `stdout.log` / `stderr.log` separately (returning head/tail previews per stream), `findText` / `glob` / `ls` dump `result.json` (returning a head preview), and `readFile` only warns and suggests batched reads via `range`. Dumps go to `<tmpdir>/ai-zen/tool-output/<tool>-<timestamp>-<random>/`, one directory per call, with no self-cleanup. See [`docs/sdk-design-v1.md` §12](docs/sdk-design-v1.md).
118
126
 
119
127
  ## Built-in Plugins
120
128
 
package/README.zh.md CHANGED
@@ -4,7 +4,7 @@ AI-Zen SDK — 共享业务逻辑层,为 CLI 和 Desktop 提供统一的 Agent
4
4
 
5
5
  ## 真相源
6
6
 
7
- **[`docs/sdk-design.md`](./docs/sdk-design.md)** 是本包的唯一设计真相源。所有实现必须与文档一致。
7
+ **[`docs/sdk-design-v1.md`](./docs/sdk-design-v1.md)** 是本包的唯一设计真相源。所有实现必须与文档一致。(重构前的设计保留在 [`docs/sdk-design-v0.md`](./docs/sdk-design-v0.md)。)
8
8
  ## 架构
9
9
 
10
10
  ```
@@ -17,23 +17,24 @@ Desktop ──┘ │
17
17
  ## 模块分层
18
18
 
19
19
  ```
20
- types ← 纯类型,零业务依赖(含 ToolEnv 工具环境)
21
- config ← ConfigManager + constants:读写 config.json + 目录初始化 + 出厂默认
22
- crud ← 能力实体 CRUD(Agent 定义等;会话/草稿已下放给各端自行持久化)
23
- capabilities ← 能力发现与装配(内置 + 用户 + MCP + Skill + SubAgent)
24
- runtime ← Provider + 模型工厂 + Agent 组装 + MCP 连接管理 + 任务迁移 + SdkCallbackTool 工具基类
25
- plugin ← Agent 插件(autoMigrate、autoRefreshTools、contextGuard、unknownToolHint)
26
- shared ← 日志、错误
20
+ types ← 纯类型,零业务依赖(含 ToolEnv 工具环境)
21
+ config ← ConfigManager + constants:读写 config.json + 目录初始化 + 出厂默认
22
+ crud ← 能力实体 CRUD(Agent 定义等;会话/草稿已下放给各端自行持久化)
23
+ runtime ← 模型工厂 + Agent 组装 + 任务迁移(createModel / createAgent / SdkAgent / TaskMigrationService)
24
+ scope ← Scope 编排容器 + ScopePlugin 契约 + PermissionEvaluator / disclosure + plugins/(5 个自包含能力来源:发现 + 工具 + 插件)
25
+ agent-plugins ← Agent 插件(autoMigrate、autoRefreshTools、contextGuard、unknownToolHint)
26
+ shared ← 日志、错误
27
27
  ```
28
28
 
29
- 依赖方向:`plugin → runtime → capabilities → crud → config → types`,上层依赖下层,反之不行。
29
+ 依赖方向:`agent-plugins → runtime → scope`;`runtime → scope / crud / shared / types`,`scope → types / shared`。上层依赖下层,反之不行。
30
30
 
31
31
  ## 核心概念
32
32
 
33
33
  | 实体 | 说明 |
34
34
  |------|------|
35
- | **Provider** | 全局上下文 + 能力注册表,持有配置、路径(含 `cwd`)、模型工厂、MCP 管理器,整合发现 → 过滤 → 实例化 |
36
- | **ToolEnv** | 工具环境 `{ cwd, config }`,Provider 实例化内置工具时注入,作为相对路径解析与配置读取的基准 |
35
+ | **Scope** | 编排容器:全局上下文(`config` / `cwd` / `agentsDir`)+ 三阶段能力管线(发现 → 过滤 → 实例化)+ 插件注册表。经 `scope.use(...)` 显式装配 |
36
+ | **ScopePlugin** | 能力插件(v1 扩展点):自持一类能力的发现状态、候选名与实例化(`discover` / `candidates` / `instantiate` / `dispose`)。内置 5 个标准插件,经 `allInOne()` 一键装配 |
37
+ | **ToolEnv** | 工具环境 `{ cwd, config }`,内置工具插件(`BuiltinToolsScopePlugin`)实例化工具时注入,作为相对路径解析与配置读取的基准 |
37
38
  | **SdkCallbackTool** | 内置工具抽象基类:`env` 构造注入 + 子类实现 `call()` + `resolve()` 相对路径解析 |
38
39
  | **SdkAgent** | 继承 Core Agent,携带 SDK 元数据,支持 `use()` 插件注册 |
39
40
  | **AgentPlugin** | 插件接口(`onInit`, `onBeforeSend`, `onAfterSend`, `onInnerLoopStart`, `onInnerLoopEnd`, `onInnerLoopsStart`, `onInnerLoopsEnd`, `onToolCall`, `onUnknownTool`, `onSubAgentStart`, `onSubAgentEnd`) |
@@ -56,17 +57,24 @@ Agent.permissions
56
57
  ## 消费模式
57
58
 
58
59
  ```typescript
59
- const provider = await Provider.create({
60
+ const scope = new Scope({
60
61
  config,
61
- cwd: "/path/to/workspace", // 每个 Provider 一个工作目录,多会话并行互不干扰
62
- ...paths,
63
- });
64
- const agent = await createAgent(provider, "my-agent");
62
+ cwd: "/path/to/workspace", // 每个 Scope 一个工作目录,多会话并行互不干扰
63
+ agentsDir,
64
+ }).use(
65
+ ...allInOne({ skillsPaths, toolsPaths, subAgentsPaths, mcpPaths }),
66
+ );
67
+ await scope.init();
68
+
69
+ const agent = await createAgent(scope, "my-agent");
65
70
  const migrationService = new TaskMigrationService({ onMigrated }); // 迁移复用传入 agent 自身的模型调用
66
71
  agent.use(new AutoMigratePlugin({ service: migrationService, maxTokens }));
67
72
  agent.use(new AutoRefreshToolsPlugin());
73
+ agent.use(new UnknownToolHintPlugin({ scope }));
68
74
  await agent.init();
69
75
  await agent.send("你好");
76
+
77
+ await scope.dispose(); // 断开 MCP、释放插件资源
70
78
  ```
71
79
 
72
80
  ## 开发状态
@@ -76,14 +84,14 @@ await agent.send("你好");
76
84
  | `types` | ✅ 已实现 — 核心实体、权限模型、MCP 类型完整 |
77
85
  | `config` | ✅ 已实现 — ConfigManager + 出厂默认配置 + 一键 bootstrap |
78
86
  | `crud` | ✅ 已实现 — Agent 等能力实体 CRUD(会话/草稿由各端自行持久化) |
79
- | `capabilities` | ✅ 已实现 — 发现 + 权限过滤 + 安全预过滤 + 枚举披露 |
80
- | `runtime` | ✅ 已实现 — Provider、createAgent、MCP 连接管理、任务迁移 |
81
- | `plugin` | ✅ 已实现 — AutoMigratePlugin / AutoRefreshToolsPlugin / ContextGuardPlugin / UnknownToolHintPlugin |
87
+ | `scope` | ✅ 已实现 — Scope + ScopePlugin + 5 个标准插件(builtin / user-tools / skills / mcp / subagents)+ allInOne |
88
+ | `runtime` | ✅ 已实现 — createAgent、MCP 连接管理、任务迁移 |
89
+ | `agent-plugins` | ✅ 已实现 — AutoMigratePlugin / AutoRefreshToolsPlugin / ContextGuardPlugin / UnknownToolHintPlugin |
82
90
  | `shared` | ✅ 已实现 — SdkError + 可注入 Logger |
83
91
 
84
92
  ## 内置工具
85
93
 
86
- 内置工具全部类化(继承 `SdkCallbackTool`),由 Provider 用 `ToolEnv` 实例化——每个 Provider 一套实例,`cwd` 注入,相对路径以 `Provider.cwd` 为基准,不依赖全局 `process.cwd()`。
94
+ 内置工具全部类化(继承 `SdkCallbackTool`),由内置工具插件(`BuiltinToolsScopePlugin`)用 `ToolEnv` 实例化——每个 Scope 一套实例,`cwd` 注入,相对路径以 `Scope.cwd` 为基准,不依赖全局 `process.cwd()`。
87
95
 
88
96
  | 工具 | 说明 |
89
97
  |------|------|
@@ -113,7 +121,7 @@ await agent.send("你好");
113
121
  | `generateImage` | 配置了 `defaultImageModel` 才注册 | 根据文字描述生成图片 |
114
122
  | `viewImage` | 仅视觉模型可用(Agent 的 `modelId` 解析为 `vision: true` 的模型) | 查看/分析图片:本地图片自动经 Files API 上传,网络 URL 直接引用 |
115
123
 
116
- 工具输出保护:`AppConfig.maxToolOutput`(字符数,缺省 32768)是工具输出的统一上限,超限时由各工具自行处置——`exec` 分文件落盘 `stdout.log` / `stderr.log`(返回各流头尾预览),`findText` / `glob` / `ls` 落盘 `result.json`(返回头部预览),`readFile` 仅警告并提示用 `range` 分批读取。落盘目录为 `<tmpdir>/ai-zen/tool-output/<工具名>-<时间戳>-<随机串>/`,每次调用独立、不自清理。详见 [`docs/sdk-design.md` §7](docs/sdk-design.md)。
124
+ 工具输出保护:`AppConfig.maxToolOutput`(字符数,缺省 32768)是工具输出的统一上限,超限时由各工具自行处置——`exec` 分文件落盘 `stdout.log` / `stderr.log`(返回各流头尾预览),`findText` / `glob` / `ls` 落盘 `result.json`(返回头部预览),`readFile` 仅警告并提示用 `range` 分批读取。落盘目录为 `<tmpdir>/ai-zen/tool-output/<工具名>-<时间戳>-<随机串>/`,每次调用独立、不自清理。详见 [`docs/sdk-design-v1.md` §12](docs/sdk-design-v1.md)。
117
125
 
118
126
  ## 内置插件
119
127
 
@@ -9,8 +9,8 @@
9
9
  export class AutoRefreshToolsPlugin {
10
10
  async onBeforeSend(ctx) {
11
11
  const agent = ctx.agent;
12
- await agent.provider.refresh({ silent: true });
13
- agent.tools = agent.provider.buildTools(agent.definition, {
12
+ await agent.scope.refresh({ silent: true });
13
+ agent.tools = agent.scope.buildTools(agent.definition, {
14
14
  exclude: {
15
15
  subagents: agent.definition.function?.name
16
16
  ? [agent.definition.function.name]
@@ -1,5 +1,5 @@
1
1
  import type { AgentPlugin, UnknownToolContext } from "@ai-zen/agents-core";
2
- import type { Provider } from "../runtime/Provider.js";
2
+ import type { Scope } from "../scope/Scope.js";
3
3
  /**
4
4
  * 未知工具智能提示插件 — MCP 场景引导。
5
5
  *
@@ -8,14 +8,16 @@ import type { Provider } from "../runtime/Provider.js";
8
8
  * - 有 MCP 配置且 `call_mcp_tool` 可用 → 提示使用 `call_mcp_tool`
9
9
  * - 否则 → 仅提示工具不存在
10
10
  *
11
- * 由调用方显式注册(`agent.use(new UnknownToolHintPlugin({ provider }))`)。
11
+ * 由调用方显式注册(`agent.use(new UnknownToolHintPlugin({ scope }))`)。
12
12
  * 未注册时,Agent 回落到底层的简单文本默认提示(core 内建)。
13
+ *
14
+ * 是否"有 MCP 配置"经 `scope.getPluginById(McpScopePlugin.ID)` 查询 MCP 插件获得。
13
15
  */
14
16
  export declare class UnknownToolHintPlugin implements AgentPlugin {
15
- /** 全局 Provider 实例 */
16
- private readonly provider;
17
+ /** 全局 Scope 实例 */
18
+ private readonly scope;
17
19
  constructor(options: {
18
- provider: Provider;
20
+ scope: Scope;
19
21
  });
20
22
  onUnknownTool(ctx: UnknownToolContext): string | undefined;
21
23
  }
@@ -1,3 +1,4 @@
1
+ import { McpScopePlugin } from "../scope/plugins/mcp/McpScopePlugin.js";
1
2
  /**
2
3
  * 未知工具智能提示插件 — MCP 场景引导。
3
4
  *
@@ -6,18 +7,21 @@
6
7
  * - 有 MCP 配置且 `call_mcp_tool` 可用 → 提示使用 `call_mcp_tool`
7
8
  * - 否则 → 仅提示工具不存在
8
9
  *
9
- * 由调用方显式注册(`agent.use(new UnknownToolHintPlugin({ provider }))`)。
10
+ * 由调用方显式注册(`agent.use(new UnknownToolHintPlugin({ scope }))`)。
10
11
  * 未注册时,Agent 回落到底层的简单文本默认提示(core 内建)。
12
+ *
13
+ * 是否"有 MCP 配置"经 `scope.getPluginById(McpScopePlugin.ID)` 查询 MCP 插件获得。
11
14
  */
12
15
  export class UnknownToolHintPlugin {
13
- /** 全局 Provider 实例 */
14
- provider;
16
+ /** 全局 Scope 实例 */
17
+ scope;
15
18
  constructor(options) {
16
- this.provider = options.provider;
19
+ this.scope = options.scope;
17
20
  }
18
21
  onUnknownTool(ctx) {
19
22
  const toolName = ctx.toolCall.function?.name ?? "未知";
20
- const hasMcpConfig = this.provider.mcpPaths.length > 0;
23
+ const mcpPlugin = this.scope.getPluginById(McpScopePlugin.ID);
24
+ const hasMcpConfig = mcpPlugin?.hasConfig() ?? false;
21
25
  const hasCallMcpTool = ctx.availableTools.some((t) => t.function.name === "call_mcp_tool");
22
26
  if (hasMcpConfig && !hasCallMcpTool) {
23
27
  return `工具 "${toolName}" 不存在。当前有 MCP 服务器配置,但 call_mcp_tool 权限已被禁用,如需使用 MCP 工具请调整权限。`;
@@ -18,6 +18,11 @@ export declare class ConfigManager {
18
18
  ensureDirs(): Promise<void>;
19
19
  /**
20
20
  * 确保 config.json 存在。不存在时写入出厂默认配置 DSL。
21
+ *
22
+ * 已存在时执行「出厂模型清单同步」(见 syncManagedModels):
23
+ * models / imageModels 中凡是**没有** `custom: true` 的条目一律视为出厂托管,
24
+ * 会被移除并替换为 DEFAULT_APP_CONFIG 中的最新定义;标了 `custom: true` 的
25
+ * 条目视为用户自有,原样保留。同步结果与磁盘内容一致时不写盘。
21
26
  */
22
27
  ensureDefaultConfig(): Promise<AppConfig>;
23
28
  /**
@@ -70,4 +75,16 @@ export declare class ConfigManager {
70
75
  /** 原子写 JSON 文件(先写 .tmp 再 rename,避免半成品)。 */
71
76
  private writeJson;
72
77
  }
78
+ /**
79
+ * 同步出厂托管的模型清单(与默认 Agent 的 custom 机制一致)。
80
+ *
81
+ * 规则:
82
+ * - 未标 `custom: true` 的条目 → 由 SDK 托管,从结果中移除,改用 defaults 中的最新定义;
83
+ * - 标了 `custom: true` 的条目 → 用户自有,原样保留;与出厂定义同 id 时用户版本优先;
84
+ * - 默认模型 ID(defaultModel / defaultImageModel / defaultMigrationModel)
85
+ * 在合并后若已不存在 → 回退到出厂默认,避免悬空引用。
86
+ *
87
+ * 端点(endpoints)不参与托管,始终保持用户配置。
88
+ */
89
+ export declare function syncManagedModels(config: AppConfig): AppConfig;
73
90
  export { DEFAULT_AGENT_ID, DEFAULT_AGENT_DEFINITION, DEFAULT_SUBAGENT_ID, DEFAULT_SUBAGENT_DEFINITION, DEFAULT_APP_CONFIG, DEFAULT_MCP_CONFIG, CONFIG_SUB_DIRS, } from "./constants.js";