@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.
- package/CHANGELOG.md +368 -0
- package/README.md +15 -14
- package/README.zh.md +15 -14
- package/dist/agent-plugins/AutoMigratePlugin.d.ts +35 -9
- package/dist/agent-plugins/AutoMigratePlugin.js +23 -10
- package/dist/agent-plugins/ContextGuardPlugin.d.ts +14 -16
- package/dist/agent-plugins/ContextGuardPlugin.js +13 -17
- package/dist/config/ConfigManager.d.ts +57 -8
- package/dist/config/ConfigManager.js +166 -25
- package/dist/config/constants.d.ts +7 -0
- package/dist/config/constants.js +38 -13
- package/dist/config/factory-skills.d.ts +33 -0
- package/dist/config/factory-skills.js +126 -0
- package/dist/config/migrate.d.ts +67 -0
- package/dist/config/migrate.js +127 -0
- package/dist/config/reasoning-presets.d.ts +37 -0
- package/dist/config/reasoning-presets.js +64 -0
- package/dist/index.d.ts +11 -5
- package/dist/index.js +10 -2
- package/dist/runtime/HandoffGuardPlugin.d.ts +35 -0
- package/dist/runtime/HandoffGuardPlugin.js +65 -0
- package/dist/runtime/HandoffService.d.ts +245 -0
- package/dist/runtime/HandoffService.js +472 -0
- package/dist/runtime/SdkAgent.d.ts +7 -1
- package/dist/runtime/SdkAgent.js +3 -0
- package/dist/runtime/createAgent.d.ts +16 -1
- package/dist/runtime/createAgent.js +10 -5
- package/dist/runtime/createModel.d.ts +8 -4
- package/dist/runtime/createModel.js +19 -2
- package/dist/scope/plugins/builtin/BatchEditTool.js +5 -2
- package/dist/scope/plugins/builtin/DownloadFileTool.js +1 -1
- package/dist/scope/plugins/builtin/EditTool.js +6 -1
- package/dist/scope/plugins/builtin/ExecTool.js +2 -2
- package/dist/scope/plugins/builtin/FindTextTool.js +6 -6
- package/dist/scope/plugins/builtin/GenerateImageTool.d.ts +1 -1
- package/dist/scope/plugins/builtin/GenerateImageTool.js +7 -7
- package/dist/scope/plugins/builtin/GlobTool.js +1 -1
- package/dist/scope/plugins/builtin/InspectFileTool.d.ts +2 -2
- package/dist/scope/plugins/builtin/InspectFileTool.js +5 -5
- package/dist/scope/plugins/builtin/LsTool.js +1 -1
- package/dist/scope/plugins/builtin/{ReadFileTool.d.ts → ReadTool.d.ts} +1 -1
- package/dist/scope/plugins/builtin/{ReadFileTool.js → ReadTool.js} +3 -3
- package/dist/scope/plugins/builtin/{ExecAsyncTool.d.ts → SpawnTool.d.ts} +1 -1
- package/dist/scope/plugins/builtin/{ExecAsyncTool.js → SpawnTool.js} +2 -2
- package/dist/scope/plugins/builtin/ViewImageTool.d.ts +6 -4
- package/dist/scope/plugins/builtin/ViewImageTool.js +43 -59
- package/dist/scope/plugins/builtin/{WriteFileTool.d.ts → WriteTool.d.ts} +1 -1
- package/dist/scope/plugins/builtin/{WriteFileTool.js → WriteTool.js} +2 -2
- package/dist/scope/plugins/builtin/index.d.ts +4 -4
- package/dist/scope/plugins/builtin/index.js +7 -7
- package/dist/scope/plugins/builtin/outputGuard.d.ts +2 -2
- package/dist/scope/plugins/builtin/outputGuard.js +1 -1
- package/dist/scope/plugins/mcp/McpScopePlugin.d.ts +1 -1
- package/dist/scope/plugins/mcp/McpScopePlugin.js +4 -3
- package/dist/scope/plugins/mcp/mcpTools.d.ts +9 -3
- package/dist/scope/plugins/mcp/mcpTools.js +196 -18
- package/dist/scope/plugins/skills/discover.js +1 -2
- package/dist/scope/plugins/skills/skillTools.js +0 -2
- package/dist/scope/plugins/subagents/subAgentTools.js +2 -1
- package/dist/shared/content-blocks.d.ts +108 -0
- package/dist/shared/content-blocks.js +253 -0
- package/dist/shared/errors.d.ts +4 -8
- package/dist/shared/errors.js +8 -14
- package/dist/types/index.d.ts +103 -10
- package/package.json +3 -3
- package/dist/runtime/TaskMigrationService.d.ts +0 -128
- package/dist/runtime/TaskMigrationService.js +0 -211
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,373 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0-alpha.21] - 2026-10-28
|
|
4
|
+
|
|
5
|
+
### ✨ 新增
|
|
6
|
+
|
|
7
|
+
- **交接/迁移支持取消(`AbortSignal`)** —— 此前 `handoff()` / `migrate()` 一旦开始就没有任何中止手段,宿主按 Ctrl+C 也停不下来:
|
|
8
|
+
- `summary` 模式的独立模型请求根本没有任何取消点;
|
|
9
|
+
- `self` 模式能被 `agent.abort()` 打断,但中止后被误判成「模型没写出文档」,于是重试一轮、再降级 `summary` 再跑一轮——用户以为取消了,实际多烧两轮全量输入。
|
|
10
|
+
|
|
11
|
+
现在 `HandoffParams` 新增 `signal?: AbortSignal`,两个执行者都认它:
|
|
12
|
+
|
|
13
|
+
- `summary`:`signal` 作为 **RequestOptions(第二参数)** 传给 `chat.completions.create`,请求会真的被中断;
|
|
14
|
+
- `self`:每个 `agent.send()` 之后**主动检查** `aborted`(因为 `abort()` 不抛错),取消即抛 `HandoffAbortedError`,**不重试、不降级**;
|
|
15
|
+
- `runHandoff()` 在阶段之间加取消点;`migrate()` 被取消时**不改动会话历史**(不会留下「已迁移」的假象)。
|
|
16
|
+
|
|
17
|
+
- **导出 `HandoffAbortedError`** —— 取消单独成类型,调用方才能把「用户按了 Ctrl+C」与「交接真的失败」分开处理:前者静默收尾,后者才该报错。
|
|
18
|
+
|
|
19
|
+
### 🐛 修复
|
|
20
|
+
|
|
21
|
+
- **取消后清掉写了一半的交接文档** —— 只删本次新建的(同名旧文件不碰)。半成品比没有更糟:下一轮接手方会把它当成真实交接读进去。
|
|
22
|
+
|
|
23
|
+
### ✅ 测试
|
|
24
|
+
|
|
25
|
+
- 全量 **57 文件 / 608 例通过**(6 skipped)。
|
|
26
|
+
- `HandoffService.test.ts` **24 → 30 例**:signal 透传 RequestOptions、summary 取消不落盘、self 取消不重试不降级、半成品清理、migrate 取消不改历史、未取消时不误伤正常路径。
|
|
27
|
+
- 破坏性验证:删掉 `self` 的取消检查 → 「不重试、不降级」用例红。
|
|
28
|
+
|
|
29
|
+
## [1.0.0-alpha.20] - 2026-10-27
|
|
30
|
+
|
|
31
|
+
### ✨ 新增
|
|
32
|
+
|
|
33
|
+
- **`createAgent(scope, agentId, options?)` 新增第三参,把中断相关策略透传出来** —— 此前 SDK 完全没有暴露 core 的 `abortedToolRoundStrategy` / `abortedToolRoundReason`,调用方只能吃默认值。本轮一并接出三个字段(新增导出类型 `CreateAgentOptions`,`SdkAgent` 构造参数同步支持):
|
|
34
|
+
- `abortedToolRoundStrategy?: "drop" | "fill"` —— 工具轮次被中断(或结果不完整)时的归一策略(core 默认 `fill`);
|
|
35
|
+
- `abortedToolRoundReason?: string` —— `fill` 策略下未完成工具结果的填充原因;
|
|
36
|
+
- `abortedMessageStrategy?: "drop" | "keep"` —— **新增能力**:被中断的 assistant 已输出的内容 / 思考是否保留(core 默认 `drop`)。传 `keep` 后,用户中断时那半句话仍会进入下一轮上下文,模型能接着往下说,而不是「失忆重来」;带工具调用的轮次仍由 `abortedToolRoundStrategy` 决定,不受影响。
|
|
37
|
+
|
|
38
|
+
### 🔧 依赖
|
|
39
|
+
|
|
40
|
+
- 升级 `@ai-zen/agents-core` 至 `4.5.3`(新增 `abortedMessageStrategy` 与 `reconcileAbortedMessages()`)。
|
|
41
|
+
|
|
42
|
+
### ✅ 测试
|
|
43
|
+
|
|
44
|
+
- 全量 **57 文件 / 602 例通过**(6 skipped)。
|
|
45
|
+
|
|
46
|
+
## [1.0.0-alpha.19] - 2026-10-27
|
|
47
|
+
|
|
48
|
+
### 🐛 修复
|
|
49
|
+
|
|
50
|
+
- **日志不再绕过 `getLogger()`:`[load_mcp]` 等日志会直接打到宿主终端** —— 4 个模块此前在**模块顶层**执行 `const log = getLogger()`,而 `getLogger()` 当时返回的是 `{ info: console.log, ... }`。模块求值那一刻,`console.log` 的**函数引用**就被冻进了常量;此后宿主调用 `setLogger()` 换掉的是内部 `currentLogger` 变量,**改不动已经抓走的引用**。后果:宿主怎么接管日志都没用 —— CLI 的 Ink TUI 里加载 MCP 时,`load_mcp` 会把整份 tools / resources 清单直接砸在渲染区外面,把界面撕成乱码。
|
|
51
|
+
|
|
52
|
+
现在 4 处全部改为**运行时取 logger**(`getLogger().info(...)` 等),`setLogger()` 立即生效:
|
|
53
|
+
|
|
54
|
+
- `scope/plugins/mcp/mcpTools.ts` —— `[load_mcp]` 两条 info(摘要 + 完整清单);
|
|
55
|
+
- `scope/plugins/skills/discover.ts` —— `[skills]` 一条 warn;
|
|
56
|
+
- `agent-plugins/AutoMigratePlugin.ts` —— `[autoMigrate]` 一条 error;
|
|
57
|
+
- `scope/plugins/skills/skillTools.ts` —— 顶层快照**从未被调用**(死代码),连同 `getLogger` import 一并删除。
|
|
58
|
+
|
|
59
|
+
至此 SDK 内 10 处日志调用全部经由 `getLogger()` 这一个出口,宿主 `setLogger()` 换什么就走什么,不再存在 console 旁路。
|
|
60
|
+
|
|
61
|
+
### ✅ 测试
|
|
62
|
+
|
|
63
|
+
- 全量 **57 文件 / 602 例通过**(6 skipped)。
|
|
64
|
+
- 端到端对照验证:同一段「先求值模块 → 再 `setLogger` → 调用 `load_mcp`」的代码,旧版日志直写 stdout、日志文件为空;新版 stdout 干净、日志完整落盘。
|
|
65
|
+
|
|
66
|
+
## [1.0.0-alpha.18] - 2026-10-22
|
|
67
|
+
|
|
68
|
+
### 🔧 行为变更(不兼容)
|
|
69
|
+
|
|
70
|
+
- **模型的唯一真相源改为 `AgentDefinition.modelId`,`AppConfig.defaultModel` 删除** —— 此前模型有两处来源:Agent 定义里的 `modelId` 优先,缺失时回退 `config.defaultModel`。而 CLI 等客户端还会在**会话存档**里另记一份模型名用于展示。三份值一旦分叉,就会出现「界面写着 DeepSeek、请求却发给 GLM」的静默错位:用户看到 401 却怎么切都切不回来(切换命令按显示层判断「已经是当前模型」直接返回,实际模型从未改变)。现在:
|
|
71
|
+
- `AppConfig` 删除 `defaultModel` 字段,出厂配置同步删除;`migrateConfig` 会把磁盘上的旧字段**清掉**(一次性,写入后不再出现);
|
|
72
|
+
- `createAgent` 只读 `definition.modelId`,**缺失直接报错**,不再有任何隐式回退;
|
|
73
|
+
- `ViewImageTool.isAvailable` / `resolveModelAndEndpoint` 同步去掉 `defaultModel` 兜底,反查不到即视为「无端点」,让调用方走降级路径而不是猜一个模型。
|
|
74
|
+
- **`DEFAULT_AGENT_DEFINITION.modelId = "deepseek-flash"`(新增导出 `DEFAULT_MODEL_ID`)** —— 出厂默认 Agent 自带模型 ID,装完即用,不再依赖配置层的默认值。
|
|
75
|
+
- **升级兼容:`ensureDefaultAgent()` 会为缺 `modelId` 的历史 Agent 补上** —— 优先沿用老的 `config.defaultModel`(若该模型仍在清单里),否则回落到出厂 `deepseek-flash`,因此升级不会改变用户正在用的模型。
|
|
76
|
+
|
|
77
|
+
### ✅ 测试
|
|
78
|
+
|
|
79
|
+
- 全量 **57 文件 / 602 例通过**(6 skipped)。
|
|
80
|
+
- 新增/改写重点:`createAgent` 缺 `modelId` 必须抛错、`ensureDefaultAgent` 补齐并落盘、`migrateConfig` 清除废弃字段且不就地改原对象、`resolveModelAndEndpoint` 未命中返回 `null` 端点、MCP / 图片工具按 `ctx.agent.model` 反查端点(不再靠默认模型兜底)。
|
|
81
|
+
|
|
82
|
+
## [1.0.0-alpha.16] - 2026-10-22
|
|
83
|
+
|
|
84
|
+
### 🔧 行为变更(不兼容,向上修复)
|
|
85
|
+
|
|
86
|
+
- **升级 `@ai-zen/agents-core` 至 `4.5.2`:请求体里的固定字段不再被 `modelParams` 覆盖** —— core 之前把模型参数 spread 在请求体**最后**(`{ model, messages, tools, stream, stream_options, ...modelConfig }`),而 `modelConfig` 来自用户可编辑的 `Model.defaultParams` / `AgentDefinition.modelParams`。一旦其中出现同名键就会静默生效:最惨的是 `stream: false`,它会把流式关掉而调用方仍在按异步流解析,整轮直接失败。现在顺序反过来,固定字段永远胜出,非固定键(`temperature` / `reasoning_effort` / 厂商特有 `thinking` 等)照常透传。
|
|
87
|
+
|
|
88
|
+
对本 SDK 的实际影响:`AgentDefinition.modelParams` 里写 `model` / `messages` / `tools` / `stream` / `stream_options` 将不再有任何效果。正常配置(温度、思考参数)不受影响。
|
|
89
|
+
|
|
90
|
+
### ✅ 测试
|
|
91
|
+
|
|
92
|
+
- 依赖的 core 侧新增 3 例(非固定键透传、固定键被忽略、无 `modelConfig` 时请求体只含框架字段)。SDK 自身 **57 文件 / 600 例通过**(6 skipped)。
|
|
93
|
+
|
|
94
|
+
## [1.0.0-alpha.15] - 2026-10-22
|
|
95
|
+
|
|
96
|
+
### 🔧 行为变更(不兼容)
|
|
97
|
+
|
|
98
|
+
- **SDK 不再「消费」思考档位,只保留表本身** —— alpha.14 把「档位 → 请求参数」的整套解析塞进了 SDK 运行时:`applyReasoningEffort` / `modelLevels` / `resolveReasoningEffort` / `resolveModelParams`,还要 `Scope.reasoningPresets` 注入、Agent 上存档位名、运行时查表拼参数。但 `low` / `high` / `max` / `off` 是**产品语义,不是协议事实**,SDK 唯一客观知道的是「请求参数长什么样」——那本来就由 `defaultParams` 承载。现在:
|
|
99
|
+
- **删除 `shared/reasoning.ts` 的全部 API**(`applyReasoningEffort` / `modelLevels` / `presetOf` / `readReasoningEffort` / `resolveReasoningEffort` / `resolveModelParams`)以及 `Scope.reasoningPresets`;
|
|
100
|
+
- **保留**思考预设的**定义与出厂内容**(`ReasoningPresets` / `ReasoningPreset` / `ReasoningPatch` 类型、`config/reasoning-presets.ts`、`ConfigManager.readReasoningPresets()` / `ensureReasoningPresets()`)—— 它是 CLI / 桌面端等客户端**共享的资产**(「智谱 5.3 只认 low/high/max」这种知识不该被每个客户端各写一遍),但那只是**数据**,SDK 不拿它生成请求参数。
|
|
101
|
+
- **`AgentDefinition.reasoningEffort` → `AgentDefinition.modelParams?: ModelParams`** —— Agent 级覆盖从「档位名」变成「具体参数」。客户端读到表、拿档位补丁,在**写入配置时**就把它展开存进 `modelParams` 里。于是**配置即事实**:运行期不查表、不推导。
|
|
102
|
+
- **`createModel(scope, modelId, overrides?: ModelParams)`** —— 第三参从 `Pick<AgentDefinition, "reasoningEffort">` 变成裸的参数对象,内部只有一行:
|
|
103
|
+
```ts
|
|
104
|
+
modelConfig = { ...model.defaultParams, ...overrides };
|
|
105
|
+
```
|
|
106
|
+
浅合并(嵌套对象整体替换),没有覆盖时原样返回 `defaultParams`。SDK 从此不解释任何键名,也不知道 `thinking` / `reasoning_effort` 是什么。SubAgent 指定独立 `modelId` 时同样应用自己的 `modelParams`。
|
|
107
|
+
- **`Model.reasoningPreset` 保留**,语义改为「引用策略表的键」,供客户端判断该模型能选哪几档;SDK 只保证它能随模型配置一起读写与出厂同步,不消费。
|
|
108
|
+
|
|
109
|
+
### 🐛 修复
|
|
110
|
+
|
|
111
|
+
- **迁移 `Model.reasoning` 旧字段(alpha.13 遗留)** —— 未标 `custom` 的模型会被出厂定义整块覆盖,但**标了 `custom: true` 的会原地保留**,于是留下一个无人认识的 `reasoning`,同时因为缺 `reasoningPreset` 而被客户端判定为「不可调档」。`ConfigManager.read()` 现在会删掉该旧字段,并按同 id 的出厂模型补回 `reasoningPreset`;id 不在出厂清单的(真·自建模型)只删不补。只处理「旧字段存在」这一种情况,用户后续主动删掉 `reasoningPreset` 不会被反复补回来。
|
|
112
|
+
|
|
113
|
+
### ✅ 测试
|
|
114
|
+
|
|
115
|
+
- `src/runtime/createModel.test.ts` 新增 6 例锁住合并语义:无覆盖原样返回、空覆盖等同无覆盖、同名键覆盖而其它键保留、模型无 `defaultParams` 时覆盖即全部、**浅合并不做深合并**(换档位不残留旧子键)、两个 Agent 各配一套互不影响。
|
|
116
|
+
- `src/config/migrate.test.ts` 新增 6 例(19 例合计)。
|
|
117
|
+
- 删除 `src/shared/reasoning.test.ts`(26 例,运行时解析已移除);`src/config/reasoning-presets.test.ts`(11 例)保留 —— 表还在 SDK。
|
|
118
|
+
- 全量 **57 文件 / 600 例通过**(6 skipped)。
|
|
119
|
+
|
|
120
|
+
## [1.0.0-alpha.14] - 2026-10-22
|
|
121
|
+
|
|
122
|
+
### 🔧 行为变更(不兼容)
|
|
123
|
+
|
|
124
|
+
- **思考能力声明从「模型字段」改为「策略表」** —— alpha.13 那版用 `Model.reasoning: { efforts, disable? }` 表达厂商差异,结果三处结构性缺陷:`disable` 一词双义(既指关闭方式又指能力)、`off` 靠拼接而非声明(`disable ? ["off", ...efforts] : efforts`)、用全局 `ReasoningEffort` 枚举假装各家档位等价。现在改为**档位 → 请求参数补丁**的声明式表:
|
|
125
|
+
- 新增 `<basePath>/reasoning-presets.json`(与 `config.json` 同级),出厂内容见 `config/reasoning-presets.ts`;
|
|
126
|
+
- `Model.reasoning?` → **`Model.reasoningPreset?: string`**(引用策略名;缺省 = 不可调档);
|
|
127
|
+
- `AgentDefinition.reasoningEffort` 类型 `ReasoningEffort` → **`string`**(档位名由策略表定义,不再用假枚举假装统一);
|
|
128
|
+
- 删除 `ReasoningEffort` / `ReasoningCapability` 类型与 `capabilityOf` / `modelEfforts` / `modelAllowedEfforts` / `isReasoningEffort` 函数;
|
|
129
|
+
- 函数签名统一以 `(model, presets, …)` 开头:`readReasoningEffort(model, presets, params?)`、`applyReasoningEffort(model, presets, level, params?)`、`resolveReasoningEffort(model, presets, definition?)`、`resolveModelParams(model, presets, definition?)`;
|
|
130
|
+
- `Scope` 新增 `reasoningPresets`(与 `config` 平级注入,不做全局单例)。
|
|
131
|
+
|
|
132
|
+
换来三个结构性保证,全部来自表的形状、代码零分支:① 可选档位 = `Object.keys(levels)`;② 能否关闭思考 = 表里有没有 `off` 键;③ 「哪些参数键属于思考」= 整张表出现过的键的并集(写入前按此剔除旧值,`max` → `off` 不再残留 `reasoning_effort`,模型换策略也不会反刍上一套参数)。
|
|
133
|
+
|
|
134
|
+
- **`applyReasoningEffort` 的档位校验更严** —— 策略里没有该档位直接抛错(旧版对未声明能力的模型给的是「不支持档位」文案),错误信息带上策略名与可选档位列表。
|
|
135
|
+
|
|
136
|
+
### 🚀 新功能
|
|
137
|
+
|
|
138
|
+
- **`ConfigManager.readReasoningPresets()` / `ensureReasoningPresets()`** —— 策略表的读写与出厂同步。同步粒度**到单个策略**,规则与出厂模型同步同构:文件缺失即写种子;`custom: true` 的策略冻结;未标的用出厂版覆盖;用户自建的非出厂策略永远保留。`bootstrap()` 会自动调用。文件缺失或损坏时 `readReasoningPresets()` 回落到出厂种子,不让上层拿到空表。
|
|
139
|
+
- **`presetOf(model, presets)` / `modelLevels(model, presets)`** —— 策略查找与可选档位读取(`modelLevels` 取代旧的 `modelEfforts` + `modelAllowedEfforts` 两个函数:`off` 现在只是表里的一行,没有「档位」与「关闭」的二分)。
|
|
140
|
+
- **包入口补导出 `TokenThresholds` / `ReasoningPatch` / `ReasoningPreset` / `ReasoningPresets`** —— alpha.13 漏了这几个类型的导出(原 `7b10196` 补丁并入本版本)。
|
|
141
|
+
|
|
142
|
+
### ✅ 测试
|
|
143
|
+
|
|
144
|
+
- `src/shared/reasoning.test.ts` 重写(26 例):出厂表协议事实(OpenAI 表无 `thinking` 键、`zhipu-5.3` 表无 `off`、DeepSeek 用 `thinking.type` 关闭)、可调档位 = 表的键、反查取最具体匹配、写入剔除旧键、往返一致、Agent 声明非法回退模型默认、原型链污染名字(`constructor`)不生效。
|
|
145
|
+
- `src/config/reasoning-presets.test.ts` 新增(11 例):落盘位置、首次释放、幂等不写盘、未标 `custom` 被覆盖、标了 `custom` 冻结、用户自建策略保留、损坏/数组内容回落种子、`bootstrap()` 顺带释放。
|
|
146
|
+
|
|
147
|
+
### 📄 文档
|
|
148
|
+
|
|
149
|
+
- `docs/{zh,en}/sdk.md`:「思考强度」章节重写为「思考预设(reasoning presets)」,含策略表结构、出厂策略对照表、三个结构性保证、新 API 表与 `Scope` 注入示例。
|
|
150
|
+
- 顺手修掉 alpha.13 之后就过期的示例:`ContextGuardPlugin({ maxTokens, isSuspended })` → `{ guardTokens }`、`AutoMigratePlugin({ maxTokens })` → `{ handoffTokens }`、「交接回合护栏放宽 × 2」的旧描述。
|
|
151
|
+
|
|
152
|
+
## [1.0.0-alpha.13] - 2026-10-22
|
|
153
|
+
|
|
154
|
+
### 💥 Breaking Changes
|
|
155
|
+
|
|
156
|
+
- **`Model.maxContextTokens` / `maxContextChars` → `Model.tokenThresholds: { handoff, guard }`** —— 旧名字一直在误导(它既不是上下文窗口,也不是单一概念)。现在拆成两条**绝对值**:
|
|
157
|
+
- `handoff`:软线,响应返回后 `prompt_tokens` 超过它触发任务交接(`AutoMigratePlugin`);
|
|
158
|
+
- `guard`:硬线,请求前用量超过它抛 `ContextOverflowError` 中断本轮(`ContextGuardPlugin`)。
|
|
159
|
+
- `maxContextChars` 直接淘汰(字符口径早于 token 口径,两者并存只会让「按哪个判」长期含糊)。
|
|
160
|
+
- 迁移由 `config/migrate.ts` 完成,`ConfigManager.read()` 读到即迁、`ensureDefaultConfig()` 检测差异才写盘:`maxContextTokens` → `handoff`,`maxContextChars` → `floor(/4)`,缺 `guard` 时按 1.5 倍折算一次(保持迁移前行为),旧键随即删除。
|
|
161
|
+
- **`ContextGuardPlugin` 的 `ratio` / `suspendedRatio` / `isSuspended` 全部删除** —— 它们只是「相对阈值」的产物。现在 `new ContextGuardPlugin({ guardTokens })` 一个绝对数就够:想调松就把数字改大。代价是**交接回合不再有 2.0 倍的额外放宽**,若需要该缓冲请把 `guard` 配得更大(出厂取 `handoff × 1.5`,并写成整数)。
|
|
162
|
+
- **`ContextOverflowError(promptTokens, guardTokens)`** —— 去掉 `maxTokens` / `ratio` / `threshold` 三个派生字段,改为直接暴露被越过的 `guardTokens`。
|
|
163
|
+
- **`AutoMigratePlugin` 的 `maxTokens` → `handoffTokens`**,`AutoMigrateTriggerInfo.maxTokens` → `handoffTokens`,`HandoffService` 的 `HandoffParams.maxTokens` / `HandoffContext.maxTokens` → `handoffTokens`(仅字段改名,语义不变:交接阈值)。
|
|
164
|
+
|
|
165
|
+
### ✨ 新增
|
|
166
|
+
|
|
167
|
+
- **思考强度(reasoning effort)能力位** —— 档位此前完全没有建模,CLI 只能手改 `defaultParams`,而各家端点接受的值域并不相同(智谱 GLM-5.3 系对 `medium` 直接报错、对 `thinking.type = "disabled"` 也报错)。现在分三层:
|
|
168
|
+
- `Model.reasoning: { efforts: ReasoningEffort[], disable?: "thinking" | "effort-none" }` —— **物理能力上限**,按厂商官方文档声明(防 400);
|
|
169
|
+
- `AgentDefinition.reasoningEffort?: ReasoningEffort` —— 该 Agent 的实际档位,在 `createModel` 阶段覆盖模型 `defaultParams`。于是同一模型可以给不同 Agent 配不同强度(编程 Agent `max`、闲聊 Agent `low`、闲聊小号 `off`)。
|
|
170
|
+
- 刻意**没有**做 Agent 侧的档位白名单(`reasoningEfforts`):它解决的是伪需求(真正会 400 的是端点能力,那已由模型声明兜住),却引入「差一个 s」的命名陷阱与一层交集规则。
|
|
171
|
+
- **`shared/reasoning.ts`**(公共 API,均从包入口导出):`resolveReasoningEffort()`、`resolveModelParams()`、`applyReasoningEffort()`(按声明写入,非法档位抛错)、`modelEfforts()` / `modelAllowedEfforts()` / `readReasoningEffort()` / `isReasoningEffort()` / `capabilityOf()`,以及 `ReasoningEffort` / `ReasoningCapability` 类型。
|
|
172
|
+
- **写入形态由声明决定**:`disable: "thinking"` → 同时维护 `thinking.type` 与 `reasoning_effort`(DeepSeek);`disable: "effort-none"` → 只写 `reasoning_effort`(OpenAI 没有 `thinking` 参数,写它会被当未知参数);未声明 `disable` → 只写 `reasoning_effort`,`off` 直接抛错(智谱 5.3 系)。
|
|
173
|
+
- **出厂模型按官方文档声明能力**(依据已写入 `constants.ts` 注释):
|
|
174
|
+
- `deepseek-flash` → `low/high/max`,可关闭(`api-docs.deepseek.com/guides/thinking_mode`)
|
|
175
|
+
- `glm-5.3` / `glm-5.3-flash` / `glm-5.3-flashx` → `low/high/max`,**不可关闭**(`docs.bigmodel.cn/cn/guide/capabilities/thinking`)
|
|
176
|
+
- `glm-4.7-flash` → 不可调(强制思考,`reasoning_effort` 仅 5.2+ 支持)
|
|
177
|
+
- `gpt-6-astra` / `gpt-6.1-sol` / `gpt-6-luna` → `minimal/low/medium/high`,可用 `reasoning_effort: "none"` 关闭
|
|
178
|
+
- 未声明的模型一律视为不可调档,不推测
|
|
179
|
+
|
|
180
|
+
### 🔧 变更
|
|
181
|
+
|
|
182
|
+
- **`createModel(scope, modelId)` → `createModel(scope, modelId, overrides?: Pick<AgentDefinition, "reasoningEffort">)`** —— 新增的可选第三参由 `createAgent` 传入 `definition.reasoningEffort`。不传时行为与以前完全一致(原样返回模型 `defaultParams`)。
|
|
183
|
+
|
|
184
|
+
### ✅ 测试
|
|
185
|
+
|
|
186
|
+
- 新增 `shared/reasoning.test.ts` 22 例:能力声明与出厂清单一致性、`off` 只对有 `disable` 的模型开放、GLM-5.3 设 `medium` 与 `off` 均抛错、OpenAI 永不写 `thinking` 键、往返一致性、Agent 覆盖与回退。
|
|
187
|
+
- 新增 `config/migrate.test.ts` 13 例:旧字段迁移(tokens / chars / 两者 / 都无 / 脏值)、`guard` 折算、已是新形状时不迁移、不改入参、部分迁移时未变条目保持同一引用。
|
|
188
|
+
- 全量 **57 文件 / 599 用例** 通过(6 skipped),`tsc --noEmit` 与 `build` 零错误。
|
|
189
|
+
|
|
190
|
+
### 📄 Docs
|
|
191
|
+
|
|
192
|
+
- `docs/{zh,en}/sdk.md` 新增「思考强度(reasoning effort)」小节(三层模型、出厂声明表、写入形态、公共 API),核心实体表补充 `Model.reasoning` 与 Agent 的两个档位字段。
|
|
193
|
+
|
|
194
|
+
## [1.0.0-alpha.12] - 2026-10-22
|
|
195
|
+
|
|
196
|
+
### 💥 Breaking Changes
|
|
197
|
+
|
|
198
|
+
- **`createReadMcpResourceTool(mcpManager)` → `createReadMcpResourceTool(mcpManager, env: ToolEnv)`** —— 与 alpha.11 的 `createCallMcpTool` 对齐:`env` 用于读取 `maxToolOutput` 并解析当前模型绑定的端点。直接调用该工厂的下游需补第二参。
|
|
199
|
+
- **`view_image` 不再依赖 `deepseek` 端点** —— 此前本地图片固定上传到 `id === "deepseek"` 的端点,未配置时直接报「未配置 deepseek 端点,无法上传本地图片」。现在按能力位决定:声明 `capabilities.filesApi` 的端点走上传,其余走 base64 内联,没有 deepseek 也能查看本地图片。返回文案随之变化(含传输方式与降级说明)。
|
|
200
|
+
- **`Endpoint` 新增可选字段 `capabilities`** —— 类型层面非破坏性(可选),但语义重要:缺省视为「无特殊能力」,即不做上传。
|
|
201
|
+
|
|
202
|
+
### ✨ 新增
|
|
203
|
+
|
|
204
|
+
- **「本地文件 → 内容块」统一入口 `shared/content-blocks.ts`**。这段逻辑此前散落三处各自为政(`ViewImageTool` 硬编码端点 id、MCP 媒体块归一、CLI 侧再实现一遍),现在收敛为一处:
|
|
205
|
+
- `fileToContentSection({ source, endpoint, model, signal, maxInlineBytes, deps })` → `{ section, transport, bytes, warning? }`;`source` 支持 `path` / `url` / `data`(`Uint8Array`)/ `base64` 四种来源。
|
|
206
|
+
- 决策链:http(s) URL → `image_url`;端点有 Files API → 上传得 `file_id`(失败**自动降级**为内联);其余端点 + 图片 → `image_url` + base64 data URL;其余端点 + 非图片 → `file` 块 + `file_data`;非视觉模型 + 图片 → 文本提示;内联超限且无法上传 → 抛错(不静默塞进请求体)。
|
|
207
|
+
- 配套:`resolveModelAndEndpoint()`(按发给 API 的模型名反查模型与端点)、`supportsFilesApi()`、`mimeOf()` / `toDataUrl()` / `base64ByteLength()` / `contentHash()`、`clearUploadCache()`、`DEFAULT_MAX_INLINE_BYTES`(8 MiB)。
|
|
208
|
+
- **已从包入口 `src/index.ts` 导出为公共 API**(`fileToContentSection` / `resolveModelAndEndpoint` / `supportsFilesApi` / `mimeOf` / `toDataUrl` / `base64ByteLength` / `contentHash` / `clearUploadCache` / `MIME_BY_EXT` / `DEFAULT_MAX_INLINE_BYTES`,以及 `FileSource` / `FileToSectionInput` / `FileToSectionResult` / `ContentTransport` / `UploadFn` / `EndpointCapabilities` 类型)。上层应用构造媒体消息时直接复用,不要各自再实现一遍「上传 or 内联」。
|
|
209
|
+
- 上传缓存:路径来源用「端点 id + 绝对路径 + mtime + size」,内存来源用内容 sha256(同一张图不重复上传),上限 200 条。
|
|
210
|
+
- `deps.upload` 可注入,便于测试与替换上传实现。
|
|
211
|
+
- **`Endpoint.capabilities.filesApi` 能力位** —— 出厂仅 `deepseek` 标记为 `true`(description 同步说明)。判据从「端点 id 是不是 deepseek」改为能力位,用户自建端点只要声明即可享受上传。
|
|
212
|
+
|
|
213
|
+
### 🔧 修复
|
|
214
|
+
|
|
215
|
+
- **MCP 媒体不再一律 base64 内联**:`call_mcp_tool` 的 `image` / `audio` / `resource.blob` 改走统一入口,端点有 Files API 能力时上传后以 `file_id` 发送,请求体不再随历史反复膨胀;转换失败**不丢块**,退化为一条文本说明,避免整个工具调用因媒体问题失败。
|
|
216
|
+
- **`read_mcp_resource` 的二进制资源被静默丢弃**:此前只拼 `contents[].text`,图片 / PDF 等 `blob` 内容直接消失。新增 `resourceToMessageSection()`——注意 `readResource` 的 contents 形如 `{ uri, mimeType?, text|blob }`,**没有 `type` 判别字段**,不能复用 `toMessageSection`(否则文本资源会被 JSON 化);`blob` 交给统一入口。
|
|
217
|
+
- **`read_mcp_resource` 接入输出保护**:与 `call_mcp_tool` 共用同一套口径——文本超限落盘 `result.txt` 并返回头尾预览,媒体块原位保留;返回形态同样是「仅文本 → 字符串 / 含媒体 → 内容块数组」。
|
|
218
|
+
- **非视觉模型退化**:`Model.vision !== true` 时图片退化为一行文本提示(本地路径来源与 MCP 内存 base64 来源均生效),避免 `image_url` 被服务端 4xx 拒掉。
|
|
219
|
+
- **`edit` 并发护栏**:`edit` / `batch_edit` 工具描述明确「同一文件禁止并行 `edit`,多处修改改用 `batch_edit`,只有不同文件才能并行」——描述层劝阻(不引入运行时锁)。
|
|
220
|
+
|
|
221
|
+
### ✅ 测试
|
|
222
|
+
|
|
223
|
+
- 新增 `shared/content-blocks.test.ts` 16 例;`mcpTools.test.ts` 扩展覆盖媒体统一入口、非视觉退化、`read` 超限落盘与混排保序;`ViewImageTool` / `EditTool` / `BatchEditTool` 断言同步到新语义。
|
|
224
|
+
- 全量 **55 文件 / 569 用例**通过(6 skipped),`tsc --noEmit` 与 `build` 零错误。
|
|
225
|
+
|
|
226
|
+
### 📄 Docs
|
|
227
|
+
|
|
228
|
+
- `docs/{zh,en}/sdk.md` 新增「本地文件 → 内容块(统一入口)」小节、`Endpoint` 能力位与 MCP 媒体 / 退化 / 输出保护说明。
|
|
229
|
+
- `docs/{zh,en}/mcp.md` 同步 `call_mcp_tool` / `read_mcp_resource` 的媒体传输方式与输出保护口径。
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
## [1.0.0-alpha.11] - 2026-10-20
|
|
233
|
+
|
|
234
|
+
### 💥 Breaking Changes
|
|
235
|
+
|
|
236
|
+
- **`createCallMcpTool(mcpManager)` → `createCallMcpTool(mcpManager, env: ToolEnv)`** —— 新增的 `env` 只用于读取 `maxToolOutput`,由 `McpScopePlugin.instantiate` 传入 `scope.env`。直接调用该工厂的下游需补第二参。
|
|
237
|
+
|
|
238
|
+
### ✨ 新增
|
|
239
|
+
|
|
240
|
+
- **`call_mcp_tool` 输出保护**:MCP 工具是最容易一次吐出上百 KB 甚至上 MB JSON 的通道,此前完全裸返回。现在文本部分超过 `maxToolOutput`(默认 32768 字符)时,完整文本落盘为 `result.txt`,只返回警告 + 头尾预览(`outputTooLarge` / `chars` / `head` / `tail` / `files`)。复用内置工具同一套 `outputGuard` 骨架与同一配置口径,不另立阈值。
|
|
241
|
+
- **媒体内容块原样透传**:`image` → `image_url`(`data:<mimeType>;base64,...`)、`audio` → `file`(`file_data` 内联 data URL)、`resource.blob` 按 mimeType 分流到 `image_url` / `file`。媒体**不参与字符计数、不落盘、不丢弃**——只对文本做保护。
|
|
242
|
+
- **返回形态**:仅文本 → 纯字符串(与历史一致);含媒体 → 内容块数组。
|
|
243
|
+
|
|
244
|
+
### 🔧 修复
|
|
245
|
+
|
|
246
|
+
- **非文本内容块被静默丢弃**:旧实现只取 `type === "text"`,`image` / `audio` / `resource` / `resource_link` 全部被扔掉,模型只知道「有个块没给我」。现在媒体透传、`resource.text` 并入正文、`resource_link` 转为 `[资源链接] name · uri · mimeType` 线索、未知类型(如 `tool_use`)退化为 JSON 文本。
|
|
247
|
+
- **内容块顺序**:严格按 MCP `content[]` 原始顺序逐块映射,文本夹在图片中间也保持原位。超限时只保留一个警告块占据原**首个**文本块的位置,其余文本落盘、媒体原位不动——即使文本夹在两图之间,图注与图片的对应关系也不会被打乱。
|
|
248
|
+
- **判定口径**:用全部文本的字符**总数**判超限,不逐块判(否则 N 段各 3 万字符都不超限、合起来却能灌爆)。
|
|
249
|
+
|
|
250
|
+
### ✅ 测试
|
|
251
|
+
|
|
252
|
+
- `mcpTools.test.ts` 38 例(新增内容块映射 8 例、输出保护 4 例、保序 4 例)。全量 **54 文件 / 542 用例**通过,`tsc --noEmit` 与 `build` 零错误。
|
|
253
|
+
|
|
254
|
+
### 📄 Docs
|
|
255
|
+
|
|
256
|
+
- `docs/sdk-design-v1.md` §9 `call_mcp_tool` 补充内容块映射表、输出保护口径与超限保序折叠规则;`../docs/{zh,en}/mcp.md` 及网站镜像同步。
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
## [1.0.0-alpha.10] - 2026-10-20
|
|
260
|
+
|
|
261
|
+
### 💥 Breaking Changes
|
|
262
|
+
|
|
263
|
+
- **`TaskMigrationService` → `HandoffService`,并拆成 `handoff()` / `migrate()` 两层** —— 「交接」(写交接文档)与「迁移」(交接 + 开新会话)此前混在一个方法里,现在概念与 API 一一对应:
|
|
264
|
+
- `handoff(params)`:停工 → 核实 → 写文档 → 校验(失败降级)。**不改动会话**;
|
|
265
|
+
- `migrate(params)`:`handoff()` 之后按 `strategy` 清历史并注入断点消息。
|
|
266
|
+
- 类型随迁:`MigrationContext` → `HandoffContext`(新增 `mode` / `requestedMode` / `handoffPath` / `migrating`),`TaskMigrationServiceOptions` → `HandoffServiceOptions`(新增 `handoffDir` / `mode` / `skillDirs` / `minHandoffBytes`)。
|
|
267
|
+
- 钩子重命名:`onBeforeMigrate` → `onBeforeHandoff`,新增 `onHandoff`(文档写好、清历史之前)。
|
|
268
|
+
- **交接文档改由「原 agent 在自己会话里写」(`mode: "self"`,默认)**,而非另起一次独立的摘要调用。原实现把历史序列化成 `[role]: content` 后交给一个无工具、无原会话上下文的调用,工具名与调用参数在序列化时全部丢失,写文档的一方也无法核实分支/改动/测试状态,只能靠推演——这是文档质量差的根因。现在的 `self` 回合:原 agent 保留全部上下文与工具,可 `read` / `exec` / `git status` 核实后落盘。
|
|
269
|
+
- **`summary`(旧的独立摘要)保留为逃生模式**,并作为 `self` 失败时的**单向**降级目标(无 `write` 工具 / 文件缺失或过小(重试一次)/ 工具或护栏报错)。反向永不发生——那会在上下文最满的时刻再加一次全量请求。
|
|
270
|
+
- **`AutoMigrateOptions` 新增 `resolveMode`**,把「用谁交接」从插件继承转移到回调:返回 `HandoffMode` 或 `null`(跳过本次迁移)。原 CLI 的 `AutoMigrateConfirmPlugin` 子类因此退化为纯回调。
|
|
271
|
+
|
|
272
|
+
### ✨ 新增
|
|
273
|
+
|
|
274
|
+
- **`HandoffGuardPlugin`**:交接回合的一次性工具守卫。只放行 `write` 到交接文档(绝对路径归一后比较),拒绝 `edit` / `batch_edit` / `rm` / `rename` / `mkdir` / `copy`。拒绝返回字符串(软拒绝)而非抛错——抛错会被 core 标记为 Error + `isPreventDefault`,整轮交接直接死。边界如实记录:挡不住 `exec`,它降低误操作概率,不是沙箱。
|
|
275
|
+
- **`ContextGuardPlugin` 新增 `isSuspended` / `suspendedRatio`**:交接回合(`service.busy`)把硬上限从 `× 1.5` 放宽到 `× 2`,既不被误杀又保留熔断。
|
|
276
|
+
- **出厂 Skill 机制**:`ConfigManager.ensureFactorySkills()` 在 `bootstrap()` 中释放 / 同步 `skills/handoff/SKILL.md`。覆盖策略与 `syncManagedModels` 同构——frontmatter 标 `metadata.custom: "true"` 视为用户自有、永不覆盖;否则随版本同步。内容以字符串常量内联在 `config/factory-skills.ts`(`tsc` 直接编译,无需打包非 TS 资源)。
|
|
277
|
+
- **交接模板的唯一真相源改为 Skill**:`HANDOFF_SECTIONS` / `createPrompt()` 已删除,代码只保留「读 Skill → 注入 → 校验文件 → 读不到时用 `FALLBACK_HANDOFF_TEMPLATE`」。Skill 只讲「怎么写」,代码只讲「写到哪、有什么能力」(本体系无模板变量引擎,不发明占位符)。
|
|
278
|
+
- `HandoffService.createHandoffFileName()`(`<YYYYMMDD-HHmmss>-<会话id前8位>.md`)、`createHandoffInstruction()`、`createPostMessages(handoffPath)`(断点**只给路径**,由接手方 `read`)。
|
|
279
|
+
|
|
280
|
+
### 🔧 修复
|
|
281
|
+
|
|
282
|
+
- **交接触发的无限递归**:交接回合本身是一次 `send()`,其 `onAfterSend` 会再次进入 `AutoMigratePlugin`。现以 `service.busy` 让行(`handoff()` / `migrate()` 共用一把锁,顺带拒绝重入)。
|
|
283
|
+
- **交接回合被自己触发/被护栏击杀**:交接恰好发生在上下文最满的时刻。除 `busy` 让行外,交接回合开跑前清空 `agent.lastUsage`(纵深防御),并由调用方在迁移后清空(否则护栏拿交接回合用量误杀新会话第一条消息)。
|
|
284
|
+
|
|
285
|
+
### ✅ 测试
|
|
286
|
+
|
|
287
|
+
- 新增 `HandoffService.test.ts`(24 例)、`HandoffGuardPlugin.test.ts`(7 例)、`factory-skills.test.ts`(7 例);重写 `AutoMigratePlugin.test.ts`、`ContextGuardPlugin.test.ts`(新增 `isSuspended` 4 例)。全量 **54 文件 / 525 用例**通过,`tsc --noEmit` 与 `build` 零错误。
|
|
288
|
+
|
|
289
|
+
### 📄 Docs
|
|
290
|
+
|
|
291
|
+
- `docs/sdk-design-v1.md` §18 重写为「任务交接(HandoffService)」(两层语义、Template 单一真相源、两种执行者、自交接三道保险、接口、断点只给路径);§17 插件章节、目录树与消费示例同步。
|
|
292
|
+
- `README.md` / `README.zh.md`、`../docs/{en,zh}/sdk.md`、`architecture.md`、`getting-started.md` 及网站镜像同步。
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
## [1.0.0-alpha.9] - 2026-10-14
|
|
296
|
+
|
|
297
|
+
### ⚠️ Dependency
|
|
298
|
+
|
|
299
|
+
- **`@ai-zen/agents-core` bumped to `^4.5.1`** — docs-only patch; no SDK source change.
|
|
300
|
+
|
|
301
|
+
### 📄 Docs
|
|
302
|
+
|
|
303
|
+
- Corrected the factory-defaults description in `docs/sdk-design-v1.md`: the preset **model count is 8** (was 7), and `DEFAULT_MCP_CONFIG` ships **two** servers (`socket-pty` + `chrome-devtools`).
|
|
304
|
+
|
|
305
|
+
## [1.0.0-alpha.8] - 2026-10-14
|
|
306
|
+
|
|
307
|
+
### ⚠️ Dependency
|
|
308
|
+
|
|
309
|
+
- **`@ai-zen/agents-core` bumped to `^4.5.0`** — picks up `AgentNS.Message.subAgentUsage` and the `sumUsage()` primitive. The skill sub-agent tool (`call_skill_sub_agent`) is built on `AgentToolLazy`, so it inherits the delegation-boundary stamping with no SDK source change.
|
|
310
|
+
|
|
311
|
+
## [1.0.0-alpha.7] - 2026-10-14
|
|
312
|
+
|
|
313
|
+
### ⚠️ Dependency
|
|
314
|
+
|
|
315
|
+
- **`@ai-zen/agents-core` bumped to `^4.4.0`** — picks up `AgentNS.Message.usage` (per-message token usage, stamped from the final stream chunk). No SDK source change; the `workspace:^` range resolves to `^4.4.0` on publish.
|
|
316
|
+
|
|
317
|
+
## [1.0.0-alpha.6] - 2026-10-13
|
|
318
|
+
|
|
319
|
+
### 💥 Breaking Changes
|
|
320
|
+
|
|
321
|
+
- **`exec_async` renamed to `spawn`** — the tool wraps `child_process.spawn` and returns `{ success, pid, message }`, so the new name matches both the implementation and the "start a process, get its pid" contract. It also pairs cleanly with `exec`: `exec` runs and **waits** (captures output, has a `timeout`), `spawn` starts and **returns immediately**. Permission lists, skill `allowed-tools` and any prompt naming `exec_async` must switch to `spawn`. The behaviour is otherwise unchanged — still shell-parsed, still does **not** capture output (redirect to a file if you need it).
|
|
322
|
+
- **Built-in tool classes renamed to match their new tool names** — `ReadFileTool` → `ReadTool`, `WriteFileTool` → `WriteTool`, `ExecAsyncTool` → `SpawnTool`, with the source files renamed to match. The classes re-exported from `@ai-zen/agents-sdk` change accordingly, so consumers importing them by name must update.
|
|
323
|
+
|
|
324
|
+
## [1.0.0-alpha.5] - 2026-10-13
|
|
325
|
+
|
|
326
|
+
### 💥 Breaking Changes
|
|
327
|
+
|
|
328
|
+
- **All built-in tool names unified on `snake_case`** — the multi-word tools that used camelCase were renamed. A tool name is the API contract the model sees and is matched **verbatim** by the permission system, so any `permissions.allow` / `deny` list, skill `allowed-tools` entry or prompt that names a tool must be updated:
|
|
329
|
+
|
|
330
|
+
| old | new |
|
|
331
|
+
|---|---|
|
|
332
|
+
| `readFile` | `read` |
|
|
333
|
+
| `writeFile` | `write` |
|
|
334
|
+
| `inspectFile` | `inspect_file` |
|
|
335
|
+
| `findText` | `find_text` |
|
|
336
|
+
| `batchEdit` | `batch_edit` |
|
|
337
|
+
| `downloadFile` | `download_file` |
|
|
338
|
+
| `generateImage` | `generate_image` |
|
|
339
|
+
| `viewImage` | `view_image` |
|
|
340
|
+
|
|
341
|
+
Single-word tools (`cwd`, `edit`, `glob`, `ls`, `mkdir`, `rm`, `rename`, `copy`, `exec`, `exist`, `sleep`), the already-`snake_case` `exec_async`, the MCP / skill wrapper tools and core's `indexed_search` are unchanged. Tool descriptions and docs were updated in the same pass.
|
|
342
|
+
|
|
343
|
+
### 🔧 Changed
|
|
344
|
+
|
|
345
|
+
- **Dependency `@ai-zen/agents-core` bumped to `^4.3.2`** (carries the `indexedSearch` → `indexed_search` rename).
|
|
346
|
+
|
|
347
|
+
### 📄 Docs
|
|
348
|
+
|
|
349
|
+
- Current-version references in `docs/zh` and `docs/en` updated to `1.0.0-alpha.5`.
|
|
350
|
+
|
|
351
|
+
## [1.0.0-alpha.4] - 2026-10-13
|
|
352
|
+
|
|
353
|
+
### 🔧 Changed
|
|
354
|
+
|
|
355
|
+
- **Dependency `@ai-zen/agents-core` bumped to `^4.3.1`** (declared as `workspace:^`, expanded to `^4.3.1` at publish time). Pulls in core 4.3.1's fix for interrupted parallel tool rounds — the assistant message carrying `tool_calls` and its results now stay all-in or all-out, so an interrupted parallel tool call no longer leaves orphan `tool` results that the server rejects. `AgentContext` gains `abortedToolRoundStrategy` (default `"fill"`) and `abortedToolRoundReason`; set `agent.abortedToolRoundStrategy = "drop"` to drop the whole round instead
|
|
356
|
+
|
|
357
|
+
### 📄 Docs
|
|
358
|
+
|
|
359
|
+
- Current-version references in `docs/zh` and `docs/en` updated to `1.0.0-alpha.4`.
|
|
360
|
+
|
|
361
|
+
## [1.0.0-alpha.3] - 2026-10-03
|
|
362
|
+
|
|
363
|
+
### 🔧 Fixed
|
|
364
|
+
|
|
365
|
+
- **`generateImage` `model` parameter description no longer points at a host-CLI command** — the description used to read "配置中的图片模型可通过 'aiz config show' 查看" (the configured image models can be viewed via `aiz config show`). Since tool descriptions ship inside every request's `tools` payload, that hint propagated into **every layer** of the agent stack: the model would `exec("aiz config show")`, but the CLI resolves a bare `aiz config show` invocation as stdio mode with the instruction `config show` — i.e. a **brand-new agent** — whose own `generateImage` description repeats the very same hint, producing a self-referential recursion that keeps spawning nested agents. The parameter now reads "若指定的模型不存在,本工具会返回当前可用的模型列表" (if the specified model does not exist, this tool returns the list of currently available models), which matches the actual `!imageModel` branch in `call()` and stops the model from shelling out to the host CLI.
|
|
366
|
+
|
|
367
|
+
### 📄 Docs
|
|
368
|
+
|
|
369
|
+
- Current-version references in `docs/zh` and `docs/en` updated to `1.0.0-alpha.3`.
|
|
370
|
+
|
|
3
371
|
## [1.0.0-alpha.2] - 2026-10-03
|
|
4
372
|
|
|
5
373
|
### 🚀 New Features
|
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ Desktop ──┘ │
|
|
|
21
21
|
types ← pure types, zero business dependencies (incl. ToolEnv)
|
|
22
22
|
config ← ConfigManager + constants: config.json read/write + directory setup + factory defaults
|
|
23
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 /
|
|
24
|
+
runtime ← model factory + Agent assembly + task handoff/migration (cancellable via `AbortSignal`; createModel / createAgent / SdkAgent / HandoffService / HandoffGuardPlugin)
|
|
25
25
|
scope ← Scope orchestration container + ScopePlugin contract + PermissionEvaluator / disclosure + plugins/ (5 self-contained capability sources: discovery + tools + plugin)
|
|
26
26
|
agent-plugins ← Agent plugins (autoMigrate, autoRefreshTools, contextGuard, unknownToolHint)
|
|
27
27
|
shared ← logging, errors
|
|
@@ -68,8 +68,9 @@ const scope = new Scope({
|
|
|
68
68
|
await scope.init();
|
|
69
69
|
|
|
70
70
|
const agent = await createAgent(scope, "my-agent");
|
|
71
|
-
const
|
|
72
|
-
agent.use(new
|
|
71
|
+
const handoffService = new HandoffService({ handoffDir: "/work/.ai-zen/handoffs", skillDirs });
|
|
72
|
+
agent.use(new ContextGuardPlugin({ maxTokens, isSuspended: () => handoffService.busy }));
|
|
73
|
+
agent.use(new AutoMigratePlugin({ service: handoffService, maxTokens }));
|
|
73
74
|
agent.use(new AutoRefreshToolsPlugin());
|
|
74
75
|
agent.use(new UnknownToolHintPlugin({ scope }));
|
|
75
76
|
await agent.init();
|
|
@@ -97,21 +98,21 @@ All built-in tools are classes (extending `SdkCallbackTool`), instantiated by th
|
|
|
97
98
|
| Tool | Description |
|
|
98
99
|
|------|-------------|
|
|
99
100
|
| `cwd` | Get the current working directory |
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
101
|
+
| `read` | Read a file (optional `range` for line/column slicing; oversized output warns and suggests batched reads) |
|
|
102
|
+
| `inspect_file` | Inspect a file's structural overview (line count, column distribution, etc.); streamed scan, not limited by the 300KB read threshold |
|
|
103
|
+
| `write` | Write a file |
|
|
103
104
|
| `exec` | Execute a command (supports `timeout`; oversized stdout+stderr is dumped to `stdout.log` / `stderr.log`) |
|
|
104
|
-
| `
|
|
105
|
+
| `spawn` | Execute a command asynchronously, returns immediately without waiting |
|
|
105
106
|
| `mkdir` | Create a directory |
|
|
106
107
|
| `rm` | Delete a file or directory |
|
|
107
108
|
| `glob` | Scan and find files using glob patterns (oversized output dumped to `result.json`) |
|
|
108
109
|
| `ls` | List directory contents (oversized output dumped to `result.json`) |
|
|
109
110
|
| `exist` | Check whether a file or directory exists |
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
111
|
+
| `find_text` | Search for text or regex in files (oversized output dumped to `result.json`) |
|
|
112
|
+
| `download_file` | Download a file from a URL and save it locally |
|
|
112
113
|
| `rename` | Rename or move a file/directory |
|
|
113
114
|
| `copy` | Copy a file or directory |
|
|
114
|
-
| `
|
|
115
|
+
| `batch_edit` | Batch-edit file text (only unmatched items are echoed back; successful ones are counted) |
|
|
115
116
|
| `edit` | Edit text in a file |
|
|
116
117
|
| `sleep` | Wait for a specified number of milliseconds |
|
|
117
118
|
|
|
@@ -119,16 +120,16 @@ Conditionally injected based on the active model / config:
|
|
|
119
120
|
|
|
120
121
|
| Tool | Injection Condition | Description |
|
|
121
122
|
|------|---------------------|-------------|
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
123
|
+
| `generate_image` | Only when `defaultImageModel` is configured | Generate an image from a text description |
|
|
124
|
+
| `view_image` | 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 |
|
|
124
125
|
|
|
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), `
|
|
126
|
+
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), `find_text` / `glob` / `ls` dump `result.json` (returning a head preview), and `read` 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).
|
|
126
127
|
|
|
127
128
|
## Built-in Plugins
|
|
128
129
|
|
|
129
130
|
| Plugin | Description |
|
|
130
131
|
|--------|-------------|
|
|
131
|
-
| `AutoMigratePlugin` | Automatically triggers task migration when the context overflows; delegates the actual migration to the injected `
|
|
132
|
+
| `AutoMigratePlugin` | Automatically triggers task migration when the context overflows; delegates the actual migration to the injected `HandoffService` (history handled per `strategy`: default `omit` keeps it auditable, optional `prune` drops it). `resolveMode` lets the caller pick the handoff executor or skip the round. Steps aside while `service.busy` to avoid the handoff→migrate→handoff recursion |
|
|
132
133
|
| `AutoRefreshToolsPlugin` | Re-scans the file system before each `send()` to refresh the tool list |
|
|
133
134
|
| `ContextGuardPlugin` | Context safety guard — before each request, throws `ContextOverflowError` (interrupting the conversation) when the previous round's `usage.prompt_tokens` exceeds `maxTokens × ratio` (default 1.5), preventing context runaway from reading oversized files |
|
|
134
135
|
| `UnknownToolHintPlugin` | Smarter unknown-tool hints — when the LLM calls a nonexistent tool, guides it to `call_mcp_tool` / points out permission issues based on MCP config (opt-in via `agent.use`) |
|
package/README.zh.md
CHANGED
|
@@ -20,7 +20,7 @@ Desktop ──┘ │
|
|
|
20
20
|
types ← 纯类型,零业务依赖(含 ToolEnv 工具环境)
|
|
21
21
|
config ← ConfigManager + constants:读写 config.json + 目录初始化 + 出厂默认
|
|
22
22
|
crud ← 能力实体 CRUD(Agent 定义等;会话/草稿已下放给各端自行持久化)
|
|
23
|
-
runtime ← 模型工厂 + Agent 组装 +
|
|
23
|
+
runtime ← 模型工厂 + Agent 组装 + 任务交接/迁移(可 `AbortSignal` 取消;createModel / createAgent / SdkAgent / HandoffService / HandoffGuardPlugin)
|
|
24
24
|
scope ← Scope 编排容器 + ScopePlugin 契约 + PermissionEvaluator / disclosure + plugins/(5 个自包含能力来源:发现 + 工具 + 插件)
|
|
25
25
|
agent-plugins ← Agent 插件(autoMigrate、autoRefreshTools、contextGuard、unknownToolHint)
|
|
26
26
|
shared ← 日志、错误
|
|
@@ -67,8 +67,9 @@ const scope = new Scope({
|
|
|
67
67
|
await scope.init();
|
|
68
68
|
|
|
69
69
|
const agent = await createAgent(scope, "my-agent");
|
|
70
|
-
const
|
|
71
|
-
agent.use(new
|
|
70
|
+
const handoffService = new HandoffService({ handoffDir: "/work/.ai-zen/handoffs", skillDirs });
|
|
71
|
+
agent.use(new ContextGuardPlugin({ maxTokens, isSuspended: () => handoffService.busy }));
|
|
72
|
+
agent.use(new AutoMigratePlugin({ service: handoffService, maxTokens }));
|
|
72
73
|
agent.use(new AutoRefreshToolsPlugin());
|
|
73
74
|
agent.use(new UnknownToolHintPlugin({ scope }));
|
|
74
75
|
await agent.init();
|
|
@@ -96,21 +97,21 @@ await scope.dispose(); // 断开 MCP、释放插件资源
|
|
|
96
97
|
| 工具 | 说明 |
|
|
97
98
|
|------|------|
|
|
98
99
|
| `cwd` | 获取当前工作目录 |
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
100
|
+
| `read` | 读取文件(可选 `range` 按行列范围读取;输出超限仅警告并提示用 `range` 分批) |
|
|
101
|
+
| `inspect_file` | 勘察文件结构概况(行数、列数分布等),流式扫描不受 300KB 限制 |
|
|
102
|
+
| `write` | 写入文件 |
|
|
102
103
|
| `exec` | 执行命令(支持 `timeout` 超时参数;stdout+stderr 超限时分文件落盘 `stdout.log` / `stderr.log`) |
|
|
103
|
-
| `
|
|
104
|
+
| `spawn` | 异步执行命令,启动后立即返回,不等待结果 |
|
|
104
105
|
| `mkdir` | 创建目录 |
|
|
105
106
|
| `rm` | 删除文件或目录 |
|
|
106
107
|
| `glob` | 使用 glob 模式扫描查找文件(输出超限落盘 `result.json`) |
|
|
107
108
|
| `ls` | 列出目录内容(输出超限落盘 `result.json`) |
|
|
108
109
|
| `exist` | 检查文件或目录是否存在 |
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
110
|
+
| `find_text` | 在文件中搜索文本或正则(输出超限落盘 `result.json`) |
|
|
111
|
+
| `download_file` | 从 URL 下载文件并保存到本地 |
|
|
111
112
|
| `rename` | 重命名或移动文件/目录 |
|
|
112
113
|
| `copy` | 复制文件或目录 |
|
|
113
|
-
| `
|
|
114
|
+
| `batch_edit` | 批量编辑文件文本(仅回显未匹配项,成功项只计数) |
|
|
114
115
|
| `edit` | 编辑文件中的文本 |
|
|
115
116
|
| `sleep` | 等待指定毫秒数后继续 |
|
|
116
117
|
|
|
@@ -118,16 +119,16 @@ await scope.dispose(); // 断开 MCP、释放插件资源
|
|
|
118
119
|
|
|
119
120
|
| 工具 | 注入条件 | 说明 |
|
|
120
121
|
|------|----------|------|
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
122
|
+
| `generate_image` | 配置了 `defaultImageModel` 才注册 | 根据文字描述生成图片 |
|
|
123
|
+
| `view_image` | 仅视觉模型可用(Agent 的 `modelId` 解析为 `vision: true` 的模型) | 查看/分析图片:本地图片自动经 Files API 上传,网络 URL 直接引用 |
|
|
123
124
|
|
|
124
|
-
工具输出保护:`AppConfig.maxToolOutput`(字符数,缺省 32768)是工具输出的统一上限,超限时由各工具自行处置——`exec` 分文件落盘 `stdout.log` / `stderr.log`(返回各流头尾预览),`
|
|
125
|
+
工具输出保护:`AppConfig.maxToolOutput`(字符数,缺省 32768)是工具输出的统一上限,超限时由各工具自行处置——`exec` 分文件落盘 `stdout.log` / `stderr.log`(返回各流头尾预览),`find_text` / `glob` / `ls` 落盘 `result.json`(返回头部预览),`read` 仅警告并提示用 `range` 分批读取。落盘目录为 `<tmpdir>/ai-zen/tool-output/<工具名>-<时间戳>-<随机串>/`,每次调用独立、不自清理。详见 [`docs/sdk-design-v1.md` §12](docs/sdk-design-v1.md)。
|
|
125
126
|
|
|
126
127
|
## 内置插件
|
|
127
128
|
|
|
128
129
|
| 插件 | 说明 |
|
|
129
130
|
|------|------|
|
|
130
|
-
| `AutoMigratePlugin` |
|
|
131
|
+
| `AutoMigratePlugin` | 上下文超限时自动触发任务迁移(历史按 `strategy` 处理:默认 `omit` 标记保留可审计,可选 `prune` 物理剔除),委托注入的 `HandoffService`;`resolveMode` 让调用方决定交接执行者或跳过本轮;`service.busy` 期间让行,避免「交接→迁移→交接」递归 |
|
|
131
132
|
| `AutoRefreshToolsPlugin` | 每次 `send()` 前重新扫描文件系统,刷新工具列表 |
|
|
132
133
|
| `ContextGuardPlugin` | 上下文安全护栏 — 每轮发请求前检测上一轮 `usage.prompt_tokens`,超过 `maxTokens × ratio`(默认 1.5,即 +50%)时抛 `ContextOverflowError` 中断对话,防止读入超大文件导致上下文失控 |
|
|
133
134
|
| `UnknownToolHintPlugin` | 未知工具智能提示 — LLM 调用不存在的工具时,根据 MCP 配置引导使用 `call_mcp_tool` / 提示权限问题(调用方显式 `agent.use` 注册) |
|
|
@@ -1,23 +1,49 @@
|
|
|
1
1
|
import type { AgentPlugin, SendContext } from "@ai-zen/agents-core";
|
|
2
|
-
import {
|
|
2
|
+
import type { HandoffMode, HandoffService } from "../runtime/HandoffService.js";
|
|
3
|
+
/** 触发迁移时的用量信息(供执行者选择、确认文案使用) */
|
|
4
|
+
export interface AutoMigrateTriggerInfo {
|
|
5
|
+
/** 当前 prompt token 用量 */
|
|
6
|
+
promptTokens: number;
|
|
7
|
+
/** 交接阈值(当前模型的 tokenThresholds.handoff) */
|
|
8
|
+
handoffTokens: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 迁移执行者解析器:只负责「用什么方式交接」,不负责「何时迁移」。
|
|
12
|
+
*
|
|
13
|
+
* - 返回 `HandoffMode` → 用该执行者迁移;
|
|
14
|
+
* - 返回 `null` → 跳过本次迁移(例如用户拒绝);
|
|
15
|
+
* - 未提供 → 使用 `HandoffService` 构造时的默认执行者。
|
|
16
|
+
*/
|
|
17
|
+
export type AutoMigrateModeResolver = (info: AutoMigrateTriggerInfo) => HandoffMode | null | Promise<HandoffMode | null>;
|
|
3
18
|
export interface AutoMigrateOptions {
|
|
4
|
-
/**
|
|
5
|
-
service:
|
|
19
|
+
/** 交接服务实例(持有交接前后钩子;编排 handoff / migrate 两条链路) */
|
|
20
|
+
service: HandoffService;
|
|
6
21
|
/** 触发迁移的上下文阈值 */
|
|
7
|
-
|
|
22
|
+
handoffTokens: number;
|
|
23
|
+
/**
|
|
24
|
+
* 执行者解析(可选)。上层可在此弹确认框、读用户偏好,
|
|
25
|
+
* 或直接返回 `null` 表达「本次不迁移」——**无需**再继承本插件。
|
|
26
|
+
*/
|
|
27
|
+
resolveMode?: AutoMigrateModeResolver;
|
|
8
28
|
}
|
|
9
29
|
/**
|
|
10
|
-
* 自动迁移插件
|
|
30
|
+
* 自动迁移插件 —— 只负责「何时触发」,实际迁移统一委托给持有的 `HandoffService`。
|
|
11
31
|
*
|
|
12
|
-
* 当
|
|
13
|
-
* 唯一运行时参数,其余(模型、迁移前后钩子)已由服务在构造时持有。
|
|
32
|
+
* 当 `usage.prompt_tokens` 超过 handoffTokens 时触发迁移:
|
|
14
33
|
*
|
|
15
34
|
* ```ts
|
|
16
35
|
* agent.use(new AutoMigratePlugin({
|
|
17
|
-
* service:
|
|
18
|
-
*
|
|
36
|
+
* service: handoffService,
|
|
37
|
+
* handoffTokens: 250_000,
|
|
38
|
+
* resolveMode: async ({ promptTokens, handoffTokens }) =>
|
|
39
|
+
* (await askUser(promptTokens, handoffTokens)) ? "self" : null,
|
|
19
40
|
* }));
|
|
20
41
|
* ```
|
|
42
|
+
*
|
|
43
|
+
* 两层让行(缺一不可):
|
|
44
|
+
* - `service.busy`:交接回合本身就是一次 `send()`,其 `onAfterSend` 会再次到这里;
|
|
45
|
+
* 不跳过就会「交接触发迁移 → 迁移再交接」无限递归;
|
|
46
|
+
* - 交接回合结束后的用量也仍可能超阈值,故 busy 判断必须覆盖整个交接 + 迁移过程。
|
|
21
47
|
*/
|
|
22
48
|
export declare class AutoMigratePlugin implements AgentPlugin {
|
|
23
49
|
private options;
|