@ai-zen/agents-sdk 1.0.0-alpha.1 → 1.0.0-alpha.11
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 +154 -0
- package/README.md +15 -14
- package/README.zh.md +15 -14
- package/dist/agent-plugins/AutoMigratePlugin.d.ts +33 -7
- package/dist/agent-plugins/AutoMigratePlugin.js +20 -6
- package/dist/agent-plugins/ContextGuardPlugin.d.ts +13 -3
- package/dist/agent-plugins/ContextGuardPlugin.js +9 -6
- package/dist/config/ConfigManager.d.ts +12 -1
- package/dist/config/ConfigManager.js +40 -1
- package/dist/config/constants.d.ts +3 -1
- package/dist/config/constants.js +10 -2
- package/dist/config/factory-skills.d.ts +33 -0
- package/dist/config/factory-skills.js +126 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +5 -2
- package/dist/runtime/HandoffGuardPlugin.d.ts +35 -0
- package/dist/runtime/HandoffGuardPlugin.js +65 -0
- package/dist/runtime/HandoffService.d.ts +199 -0
- package/dist/runtime/HandoffService.js +372 -0
- package/dist/scope/plugins/builtin/BatchEditTool.js +1 -1
- package/dist/scope/plugins/builtin/DownloadFileTool.js +1 -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.js +2 -2
- 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 +3 -2
- package/dist/scope/plugins/mcp/mcpTools.d.ts +8 -2
- package/dist/scope/plugins/mcp/mcpTools.js +129 -9
- package/dist/types/index.d.ts +3 -3
- package/package.json +3 -3
- package/dist/runtime/TaskMigrationService.d.ts +0 -128
- package/dist/runtime/TaskMigrationService.js +0 -211
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SdkCallbackTool } from "./SdkCallbackTool.js";
|
|
2
2
|
import type { ToolEnv } from "../../../types/index.js";
|
|
3
3
|
import type { AgentNS } from "@ai-zen/agents-core";
|
|
4
|
-
export declare class
|
|
4
|
+
export declare class WriteTool extends SdkCallbackTool {
|
|
5
5
|
function: AgentNS.FunctionDefine;
|
|
6
6
|
constructor(env: ToolEnv);
|
|
7
7
|
call(input: {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as fsp from "fs/promises";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { SdkCallbackTool } from "./SdkCallbackTool.js";
|
|
4
|
-
export class
|
|
4
|
+
export class WriteTool extends SdkCallbackTool {
|
|
5
5
|
function = {
|
|
6
|
-
name: "
|
|
6
|
+
name: "write",
|
|
7
7
|
description: "写入文件",
|
|
8
8
|
parameters: {
|
|
9
9
|
type: "object",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { SdkCallbackTool } from "./SdkCallbackTool.js";
|
|
2
2
|
import type { ToolEnv } from "../../../types/index.js";
|
|
3
3
|
import { CwdTool } from "./CwdTool.js";
|
|
4
|
-
import {
|
|
4
|
+
import { ReadTool } from "./ReadTool.js";
|
|
5
5
|
import { InspectFileTool } from "./InspectFileTool.js";
|
|
6
|
-
import {
|
|
6
|
+
import { WriteTool } from "./WriteTool.js";
|
|
7
7
|
import { ExecTool } from "./ExecTool.js";
|
|
8
8
|
import { MkdirTool } from "./MkdirTool.js";
|
|
9
9
|
import { RmTool } from "./RmTool.js";
|
|
@@ -16,7 +16,7 @@ import { RenameTool } from "./RenameTool.js";
|
|
|
16
16
|
import { CopyTool } from "./CopyTool.js";
|
|
17
17
|
import { BatchEditTool } from "./BatchEditTool.js";
|
|
18
18
|
import { EditTool } from "./EditTool.js";
|
|
19
|
-
import {
|
|
19
|
+
import { SpawnTool } from "./SpawnTool.js";
|
|
20
20
|
import { SleepTool } from "./SleepTool.js";
|
|
21
21
|
import { ViewImageTool } from "./ViewImageTool.js";
|
|
22
22
|
import { GenerateImageTool } from "./GenerateImageTool.js";
|
|
@@ -29,4 +29,4 @@ import { GenerateImageTool } from "./GenerateImageTool.js";
|
|
|
29
29
|
* 如 GenerateImageTool 依赖 defaultImageModel、ViewImageTool 仅视觉模型可用。
|
|
30
30
|
*/
|
|
31
31
|
export declare const BUILTIN_TOOL_CLASSES: Array<new (env: ToolEnv) => SdkCallbackTool>;
|
|
32
|
-
export { CwdTool,
|
|
32
|
+
export { CwdTool, ReadTool, InspectFileTool, WriteTool, ExecTool, MkdirTool, RmTool, GlobTool, LsTool, ExistTool, FindTextTool, DownloadFileTool, RenameTool, CopyTool, BatchEditTool, EditTool, SpawnTool, SleepTool, ViewImageTool, GenerateImageTool, };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CwdTool } from "./CwdTool.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ReadTool } from "./ReadTool.js";
|
|
3
3
|
import { InspectFileTool } from "./InspectFileTool.js";
|
|
4
|
-
import {
|
|
4
|
+
import { WriteTool } from "./WriteTool.js";
|
|
5
5
|
import { ExecTool } from "./ExecTool.js";
|
|
6
6
|
import { MkdirTool } from "./MkdirTool.js";
|
|
7
7
|
import { RmTool } from "./RmTool.js";
|
|
@@ -14,7 +14,7 @@ import { RenameTool } from "./RenameTool.js";
|
|
|
14
14
|
import { CopyTool } from "./CopyTool.js";
|
|
15
15
|
import { BatchEditTool } from "./BatchEditTool.js";
|
|
16
16
|
import { EditTool } from "./EditTool.js";
|
|
17
|
-
import {
|
|
17
|
+
import { SpawnTool } from "./SpawnTool.js";
|
|
18
18
|
import { SleepTool } from "./SleepTool.js";
|
|
19
19
|
import { ViewImageTool } from "./ViewImageTool.js";
|
|
20
20
|
import { GenerateImageTool } from "./GenerateImageTool.js";
|
|
@@ -28,9 +28,9 @@ import { GenerateImageTool } from "./GenerateImageTool.js";
|
|
|
28
28
|
*/
|
|
29
29
|
export const BUILTIN_TOOL_CLASSES = [
|
|
30
30
|
CwdTool,
|
|
31
|
-
|
|
31
|
+
ReadTool,
|
|
32
32
|
InspectFileTool,
|
|
33
|
-
|
|
33
|
+
WriteTool,
|
|
34
34
|
ExecTool,
|
|
35
35
|
MkdirTool,
|
|
36
36
|
RmTool,
|
|
@@ -43,9 +43,9 @@ export const BUILTIN_TOOL_CLASSES = [
|
|
|
43
43
|
CopyTool,
|
|
44
44
|
BatchEditTool,
|
|
45
45
|
EditTool,
|
|
46
|
-
|
|
46
|
+
SpawnTool,
|
|
47
47
|
SleepTool,
|
|
48
48
|
ViewImageTool,
|
|
49
49
|
GenerateImageTool,
|
|
50
50
|
];
|
|
51
|
-
export { CwdTool,
|
|
51
|
+
export { CwdTool, ReadTool, InspectFileTool, WriteTool, ExecTool, MkdirTool, RmTool, GlobTool, LsTool, ExistTool, FindTextTool, DownloadFileTool, RenameTool, CopyTool, BatchEditTool, EditTool, SpawnTool, SleepTool, ViewImageTool, GenerateImageTool, };
|
|
@@ -28,7 +28,7 @@ export interface OutputGuardOptions<T> {
|
|
|
28
28
|
/** 超限判定:上限由工具自行从 config 读取,骨架不传参 */
|
|
29
29
|
isOverLimit: (content: T) => boolean;
|
|
30
30
|
/**
|
|
31
|
-
* 落盘内容组织(可选)。缺省表示"只警告、不落盘"(如
|
|
31
|
+
* 落盘内容组织(可选)。缺省表示"只警告、不落盘"(如 read)。
|
|
32
32
|
* 返回的文件按顺序写入本次调用专属的目录。
|
|
33
33
|
*/
|
|
34
34
|
dump?: (dir: string, content: T) => GuardDumpFile[] | Promise<GuardDumpFile[]>;
|
|
@@ -61,7 +61,7 @@ export interface HeadPreview {
|
|
|
61
61
|
/**
|
|
62
62
|
* 生成头部预览:最多 size 字符。
|
|
63
63
|
*
|
|
64
|
-
* 适用于结果按出现顺序排列、尾部参考价值低的工具(如
|
|
64
|
+
* 适用于结果按出现顺序排列、尾部参考价值低的工具(如 find_text)。
|
|
65
65
|
*/
|
|
66
66
|
export declare function headPreview(text: string, size?: number): HeadPreview;
|
|
67
67
|
/** 头尾预览 */
|
|
@@ -24,6 +24,6 @@ export declare class McpScopePlugin implements ScopePlugin {
|
|
|
24
24
|
/** 是否配置了 MCP(供外部代码判定,如 UnknownToolHintPlugin) */
|
|
25
25
|
hasConfig(): boolean;
|
|
26
26
|
candidates(): ScopeCandidates;
|
|
27
|
-
instantiate(
|
|
27
|
+
instantiate(scope: Scope, filtered: FilterOutput): Tool[];
|
|
28
28
|
dispose(): Promise<void>;
|
|
29
29
|
}
|
|
@@ -37,7 +37,7 @@ export class McpScopePlugin {
|
|
|
37
37
|
tools: ["load_mcp", "call_mcp_tool", "read_mcp_resource"],
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
instantiate(
|
|
40
|
+
instantiate(scope, filtered) {
|
|
41
41
|
const allow = new Set(filtered.tools);
|
|
42
42
|
const allowedMcps = new Set(filtered.mcps);
|
|
43
43
|
const filteredMcps = this.servers.filter((s) => allowedMcps.has(s.id));
|
|
@@ -47,7 +47,8 @@ export class McpScopePlugin {
|
|
|
47
47
|
result.push(createLoadMcpTool(m, filteredMcps));
|
|
48
48
|
}
|
|
49
49
|
if (allow.has("call_mcp_tool") && m) {
|
|
50
|
-
|
|
50
|
+
// env 供 call_mcp_tool 读取 maxToolOutput,与内置工具共用同一上限口径
|
|
51
|
+
result.push(createCallMcpTool(m, scope.env));
|
|
51
52
|
}
|
|
52
53
|
if (allow.has("read_mcp_resource") && m) {
|
|
53
54
|
result.push(createReadMcpResourceTool(m));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CallbackTool } from "@ai-zen/agents-core";
|
|
2
2
|
import type { McpConnectionManager } from "./McpConnectionManager.js";
|
|
3
|
-
import type { McpServerConfig } from "../../../types/index.js";
|
|
3
|
+
import type { McpServerConfig, ToolEnv } from "../../../types/index.js";
|
|
4
4
|
/**
|
|
5
5
|
* 创建 load_mcp 工具。
|
|
6
6
|
* server 枚举由 filteredMcps (完整 McpServerConfig[])自动推导,
|
|
@@ -10,8 +10,14 @@ export declare function createLoadMcpTool(mcpManager: McpConnectionManager, filt
|
|
|
10
10
|
/**
|
|
11
11
|
* 创建 call_mcp_tool 工具。
|
|
12
12
|
* 通过官方 Client.callTool() API 调用 MCP 服务器上的工具。
|
|
13
|
+
*
|
|
14
|
+
* 返回形态:
|
|
15
|
+
* - 仅文本 → 纯字符串(与历史行为一致);
|
|
16
|
+
* - 含媒体 → 内容块数组:首块为受保护的文本,其后原样透传 image / file 块。
|
|
17
|
+
*
|
|
18
|
+
* env 仅用于读取 maxToolOutput(文本输出保护上限),与内置工具口径一致。
|
|
13
19
|
*/
|
|
14
|
-
export declare function createCallMcpTool(mcpManager: McpConnectionManager): CallbackTool;
|
|
20
|
+
export declare function createCallMcpTool(mcpManager: McpConnectionManager, env: ToolEnv): CallbackTool;
|
|
15
21
|
/**
|
|
16
22
|
* 创建 read_mcp_resource 工具。
|
|
17
23
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CallbackTool } from "@ai-zen/agents-core";
|
|
2
2
|
import { createDisclosureParam } from "../../disclosure.js";
|
|
3
|
+
import { DEFAULT_MAX_TOOL_OUTPUT, guardOutput, headTailPreview } from "../builtin/outputGuard.js";
|
|
3
4
|
import { getLogger } from "../../../shared/logger.js";
|
|
4
5
|
const EMPTY_HINT = "(当前没有可用的 MCP 服务器,请联系用户添加)";
|
|
5
6
|
const log = getLogger();
|
|
@@ -81,15 +82,130 @@ export function createLoadMcpTool(mcpManager, filteredMcps) {
|
|
|
81
82
|
},
|
|
82
83
|
});
|
|
83
84
|
}
|
|
85
|
+
/** base64 二进制转 data URL(image_url / file_data 均接受) */
|
|
86
|
+
function toDataUrl(base64, mimeType) {
|
|
87
|
+
return `data:${mimeType};base64,${base64}`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* MCP content 块 → SDK 内容块。
|
|
91
|
+
*
|
|
92
|
+
* 文本类(text / resource.text / resource_link / 未知类型)归一为 `text` 块,
|
|
93
|
+
* 由调用方拼接成正文后进入输出保护;
|
|
94
|
+
* 媒体类(image / audio / resource.blob)归一为 `image_url` / `file` 块,
|
|
95
|
+
* 原样透传给模型——图片不进字符闸门,也不丢弃。
|
|
96
|
+
*/
|
|
97
|
+
function toMessageSection(block) {
|
|
98
|
+
switch (block?.type) {
|
|
99
|
+
case "image":
|
|
100
|
+
return {
|
|
101
|
+
type: "image_url",
|
|
102
|
+
image_url: { url: toDataUrl(block.data, block.mimeType ?? "image/png") },
|
|
103
|
+
};
|
|
104
|
+
case "audio":
|
|
105
|
+
// AgentNS 无独立音频块,以 Files API 的 file_data(base64 data URL)内联承载
|
|
106
|
+
return {
|
|
107
|
+
type: "file",
|
|
108
|
+
file_data: toDataUrl(block.data, block.mimeType ?? "audio/wav"),
|
|
109
|
+
};
|
|
110
|
+
case "resource": {
|
|
111
|
+
const r = block.resource ?? {};
|
|
112
|
+
// 内嵌文本资源:本质是纯文本,并入正文(同样受输出保护)
|
|
113
|
+
if (typeof r.text === "string")
|
|
114
|
+
return { type: "text", text: r.text };
|
|
115
|
+
// 内嵌二进制资源:按 mimeType 选块类型,不丢弃
|
|
116
|
+
if (typeof r.blob === "string") {
|
|
117
|
+
const mime = r.mimeType ?? "application/octet-stream";
|
|
118
|
+
return mime.startsWith("image/")
|
|
119
|
+
? { type: "image_url", image_url: { url: toDataUrl(r.blob, mime) } }
|
|
120
|
+
: { type: "file", file_data: toDataUrl(r.blob, mime) };
|
|
121
|
+
}
|
|
122
|
+
return { type: "text", text: `[资源] ${r.uri ?? "(未知 URI)"}` };
|
|
123
|
+
}
|
|
124
|
+
case "resource_link": {
|
|
125
|
+
const parts = [block.name, block.uri, block.mimeType].filter(Boolean);
|
|
126
|
+
return { type: "text", text: `[资源链接] ${parts.join(" · ")}` };
|
|
127
|
+
}
|
|
128
|
+
case "text":
|
|
129
|
+
return { type: "text", text: block.text ?? "" };
|
|
130
|
+
default:
|
|
131
|
+
// 未知类型(如 tool_use)退化为 JSON 文本,仍受输出保护
|
|
132
|
+
return { type: "text", text: JSON.stringify(block) };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* 文本输出保护(保序):
|
|
137
|
+
*
|
|
138
|
+
* - **未超限**:原样返回映射后的块数组,不重排、不合并、不丢块;
|
|
139
|
+
* 纯文本时退化为单个字符串(历史行为)。
|
|
140
|
+
* - **超限**:全部文本落盘为 result.txt,只保留**一个**警告文本块占据
|
|
141
|
+
* 原首个文本块的位置,其余文本块丢弃(内容已落盘),媒体块原位不动。
|
|
142
|
+
* 这样即使文本夹在中间,媒体与位置的对应关系也不会被打乱。
|
|
143
|
+
*
|
|
144
|
+
* 判定用全部文本的字符总数——不能逐块判,否则 N 段各 3 万字符都不超限、合起来却能灌爆。
|
|
145
|
+
* 落盘只写文本正文,不写原始结果——后者可能含 image 的 base64,
|
|
146
|
+
* 落盘等于把几 MB 二进制再抄一遍磁盘。
|
|
147
|
+
*/
|
|
148
|
+
async function protectCallOutput(output, env) {
|
|
149
|
+
const limit = env.config.maxToolOutput ?? DEFAULT_MAX_TOOL_OUTPUT;
|
|
150
|
+
const { sections, fullText, chars, isError } = output;
|
|
151
|
+
const guarded = await guardOutput({
|
|
152
|
+
tool: "call_mcp_tool",
|
|
153
|
+
content: output,
|
|
154
|
+
isOverLimit: (o) => o.chars > limit,
|
|
155
|
+
dump: (_dir, o) => [{ name: "result.txt", content: o.fullText }],
|
|
156
|
+
buildWarning: (ctx) => {
|
|
157
|
+
const preview = headTailPreview(fullText);
|
|
158
|
+
return JSON.stringify({
|
|
159
|
+
outputTooLarge: true,
|
|
160
|
+
isError: isError || undefined,
|
|
161
|
+
warning: `文本输出过大:共 ${chars} 字符,超过上限 ${limit} 字符。` +
|
|
162
|
+
`完整文本已落盘(result.txt),可用 read 分批读取。`,
|
|
163
|
+
files: ctx.files,
|
|
164
|
+
chars,
|
|
165
|
+
...preview,
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
// 超限:警告占据首个文本块的位置,其余文本块合并到它身上(内容已落盘),媒体原位
|
|
170
|
+
if (typeof guarded === "string") {
|
|
171
|
+
const collapsed = [];
|
|
172
|
+
let placed = false;
|
|
173
|
+
for (const section of sections) {
|
|
174
|
+
if (section.type === "text") {
|
|
175
|
+
if (!placed) {
|
|
176
|
+
collapsed.push({ type: "text", text: guarded });
|
|
177
|
+
placed = true;
|
|
178
|
+
}
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
collapsed.push(section);
|
|
182
|
+
}
|
|
183
|
+
// 仅一个文本块(无媒体):退化为字符串,与历史行为一致
|
|
184
|
+
return collapsed.length === 1 ? collapsed[0].text : collapsed;
|
|
185
|
+
}
|
|
186
|
+
// 未超限:无媒体 → 纯字符串;有媒体 → 原序块数组
|
|
187
|
+
const hasMedia = sections.some((s) => s.type !== "text");
|
|
188
|
+
if (!hasMedia)
|
|
189
|
+
return isError ? `❌ 工具执行出错:\n${fullText}` : fullText;
|
|
190
|
+
return isError
|
|
191
|
+
? [{ type: "text", text: "❌ 工具执行出错:" }, ...sections]
|
|
192
|
+
: sections;
|
|
193
|
+
}
|
|
84
194
|
/**
|
|
85
195
|
* 创建 call_mcp_tool 工具。
|
|
86
196
|
* 通过官方 Client.callTool() API 调用 MCP 服务器上的工具。
|
|
197
|
+
*
|
|
198
|
+
* 返回形态:
|
|
199
|
+
* - 仅文本 → 纯字符串(与历史行为一致);
|
|
200
|
+
* - 含媒体 → 内容块数组:首块为受保护的文本,其后原样透传 image / file 块。
|
|
201
|
+
*
|
|
202
|
+
* env 仅用于读取 maxToolOutput(文本输出保护上限),与内置工具口径一致。
|
|
87
203
|
*/
|
|
88
|
-
export function createCallMcpTool(mcpManager) {
|
|
204
|
+
export function createCallMcpTool(mcpManager, env) {
|
|
89
205
|
return new CallbackTool({
|
|
90
206
|
function: {
|
|
91
207
|
name: "call_mcp_tool",
|
|
92
|
-
description: "调用已连接 MCP 服务器上的指定工具。需先通过 load_mcp
|
|
208
|
+
description: "调用已连接 MCP 服务器上的指定工具。需先通过 load_mcp 连接服务器。文本输出过大时会落盘并只返回头尾预览;图片、音频等媒体原样返回。",
|
|
93
209
|
parameters: {
|
|
94
210
|
type: "object",
|
|
95
211
|
properties: {
|
|
@@ -117,15 +233,19 @@ export function createCallMcpTool(mcpManager) {
|
|
|
117
233
|
name: input.tool,
|
|
118
234
|
arguments: input.arguments,
|
|
119
235
|
}, undefined, { signal: ctx?.signal });
|
|
236
|
+
// 按 MCP content 原始顺序映射,文本与媒体的相对位置完整保留
|
|
120
237
|
const contents = result.content ?? [];
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return `❌ 工具执行出错:\n${text || JSON.stringify(result)}`;
|
|
238
|
+
const isError = Boolean(result.isError);
|
|
239
|
+
// 没有 content 时退回原始 JSON(既有兜底)
|
|
240
|
+
if (contents.length === 0) {
|
|
241
|
+
const json = JSON.stringify(result);
|
|
242
|
+
return await protectCallOutput({ sections: [{ type: "text", text: json }], fullText: json, chars: json.length, isError }, env);
|
|
127
243
|
}
|
|
128
|
-
|
|
244
|
+
const sections = contents.map(toMessageSection);
|
|
245
|
+
const textSections = sections.filter((s) => s.type === "text");
|
|
246
|
+
const fullText = textSections.map((s) => s.text).join("\n");
|
|
247
|
+
const chars = textSections.reduce((n, s) => n + s.text.length, 0);
|
|
248
|
+
return await protectCallOutput({ sections, fullText, chars, isError }, env);
|
|
129
249
|
}
|
|
130
250
|
catch (error) {
|
|
131
251
|
return `❌ 调用 "${input.tool}" 失败: ${error?.message ?? error}`;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export interface Model {
|
|
|
36
36
|
/** 旧版字符数阈值(兼容迁移) */
|
|
37
37
|
maxContextChars?: number;
|
|
38
38
|
defaultParams?: Record<string, unknown>;
|
|
39
|
-
/** 是否支持图片输入(视觉模型)。看图工具(
|
|
39
|
+
/** 是否支持图片输入(视觉模型)。看图工具(view_image)仅对视觉模型启用;generate_image 是否返回图片内容块也据此判断 */
|
|
40
40
|
vision?: boolean;
|
|
41
41
|
description?: string;
|
|
42
42
|
version?: number;
|
|
@@ -104,7 +104,7 @@ export interface AppConfig {
|
|
|
104
104
|
*
|
|
105
105
|
* 工具本次输出超过该值时,由工具自身的保护策略处置(落盘或仅警告),
|
|
106
106
|
* 避免超长输出撑爆上下文。仅作用于"输出",与工具的读取阈值无关
|
|
107
|
-
* (如
|
|
107
|
+
* (如 read/find_text 对 300KB 文件的读取上限)。
|
|
108
108
|
*/
|
|
109
109
|
maxToolOutput?: number;
|
|
110
110
|
}
|
|
@@ -112,7 +112,7 @@ export interface AppConfig {
|
|
|
112
112
|
* 工具环境 — Scope 的能力插件在实例化内置工具时注入的环境信息。
|
|
113
113
|
*
|
|
114
114
|
* cwd 是相对路径解析的基准(每个 Scope 对应一个工作目录);
|
|
115
|
-
* config 供需要模型/端点配置的工具(如
|
|
115
|
+
* config 供需要模型/端点配置的工具(如 generate_image)使用。
|
|
116
116
|
*/
|
|
117
117
|
export interface ToolEnv {
|
|
118
118
|
/** 当前工作目录 */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-zen/agents-sdk",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.11",
|
|
4
4
|
"description": "AI-Zen Agents SDK — shared business logic for CLI and Desktop",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"author": "LZQCN",
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"dependencies": {
|
|
19
|
+
"@ai-zen/agents-core": "^4.5.1",
|
|
19
20
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
20
21
|
"dayjs": "^1.11.21",
|
|
21
|
-
"openai": "^7.5.0"
|
|
22
|
-
"@ai-zen/agents-core": "^4.3.0"
|
|
22
|
+
"openai": "^7.5.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^24.12.2",
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { AgentNS } from "@ai-zen/agents-core";
|
|
2
|
-
import type OpenAI from "openai";
|
|
3
|
-
import { type Logger } from "../shared/logger.js";
|
|
4
|
-
import type { SdkAgent } from "./SdkAgent.js";
|
|
5
|
-
/**
|
|
6
|
-
* 迁移上下文 — 贯穿「迁移前 → 生成交接文档 → 迁移后」的结构化数据。
|
|
7
|
-
*
|
|
8
|
-
* 供 `TaskMigrationService.migrate` 与钩子(`onBeforeMigrate` / `onMigrated`)使用。
|
|
9
|
-
* 手动迁移与自动迁移共用同一上下文结构,保证上层(CLI)行为一致。
|
|
10
|
-
*/
|
|
11
|
-
export interface MigrationContext {
|
|
12
|
-
/** 当前对话 Agent(引用不变,仅替换消息列表) */
|
|
13
|
-
agent: SdkAgent;
|
|
14
|
-
/** 生成交接文档所用模型名 */
|
|
15
|
-
model: string;
|
|
16
|
-
/** 当前 prompt token 用量(自动迁移时由 AutoMigratePlugin 提供;手动迁移可省略) */
|
|
17
|
-
promptTokens?: number;
|
|
18
|
-
/** 上下文阈值(自动迁移时由 AutoMigratePlugin 提供;手动迁移可省略) */
|
|
19
|
-
maxTokens?: number;
|
|
20
|
-
/** 序列化后的完整对话历史(迁移输入) */
|
|
21
|
-
historyText: string;
|
|
22
|
-
/** 迁移前消息总数 */
|
|
23
|
-
messageCountBefore: number;
|
|
24
|
-
/** 生成的交接文档(迁移完成后填充) */
|
|
25
|
-
handoffDoc?: string;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* 迁移后的历史处理策略。
|
|
29
|
-
*
|
|
30
|
-
* - `omit`(默认):将历史消息标记 `omit: true`,仍保留在 `agent.messages` 可审计/回放,但不再发送给模型;
|
|
31
|
-
* - `prune`:物理剔除历史,只保留 `definition.messages`(系统提示等模板)+ 追加的对话断点。
|
|
32
|
-
*/
|
|
33
|
-
export type MigrationStrategy = "omit" | "prune";
|
|
34
|
-
/**
|
|
35
|
-
* 任务迁移服务构造参数。
|
|
36
|
-
*/
|
|
37
|
-
export interface TaskMigrationServiceOptions {
|
|
38
|
-
/** 生成交接文档所用客户端(可选;未传则 migrate 时回退到 agent.client) */
|
|
39
|
-
client?: OpenAI;
|
|
40
|
-
/** 生成交接文档所用模型名(可选;未传则 migrate 时回退到 agent.model) */
|
|
41
|
-
model?: string;
|
|
42
|
-
/** 生成交接文档所用模型参数(可选;未传则 migrate 时回退到 agent.modelConfig) */
|
|
43
|
-
modelConfig?: Record<string, unknown>;
|
|
44
|
-
/** 迁移后历史处理策略,默认 `omit`(标记历史保留可审计)。可选 `prune`(物理剔除)。 */
|
|
45
|
-
strategy?: MigrationStrategy;
|
|
46
|
-
/** 迁移前钩子(服务持有)。迁移开始前触发,此时 agent.messages 仍为完整旧历史 */
|
|
47
|
-
onBeforeMigrate?: (ctx: MigrationContext) => void | Promise<void>;
|
|
48
|
-
/** 迁移后钩子(服务持有)。迁移完成后触发,交接文档已注入 agent.messages */
|
|
49
|
-
onMigrated?: (ctx: MigrationContext) => void | Promise<void>;
|
|
50
|
-
/** 可注入日志(可选,默认使用全局单例) */
|
|
51
|
-
logger?: Logger;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* 任务迁移服务。
|
|
55
|
-
*
|
|
56
|
-
* 一个服务实例对应一次迁移上下文。它**不持有**任何模型调用——生成交接文档时
|
|
57
|
-
* 默认复用传入 `agent`(`SdkAgent`)自身的 `client` / `model` / `modelConfig`,
|
|
58
|
-
* 因此无需 Scope、无需独立「迁移 Agent」对象,也无需自建 OpenAI 客户端。
|
|
59
|
-
* 如需用不同模型生成交接文档,可在构造时显式传入 `client` / `model` / `modelConfig`,
|
|
60
|
-
* 未传则 migrate 时回退到 agent 自带的配置。
|
|
61
|
-
*
|
|
62
|
-
* 职责:
|
|
63
|
-
* 1. 生成交接文档的提示词 / 注入格式(静态纯工具,供上层使用)
|
|
64
|
-
* 2. 执行完整迁移(串行化 → 模型生成 → 按策略处理历史 + 追加对话断点),并暴露迁移前后钩子(实例方法)
|
|
65
|
-
*
|
|
66
|
-
* 设计上,`AutoMigratePlugin` 只负责「何时触发」(检测 token 超限),
|
|
67
|
-
* 实际迁移逻辑统一收敛到本服务,从而让手动迁移与自动迁移复用同一条链路。
|
|
68
|
-
*/
|
|
69
|
-
export declare class TaskMigrationService {
|
|
70
|
-
static readonly HANDOFF_SECTIONS: {
|
|
71
|
-
readonly breakpoint: "## 💬 对话断点";
|
|
72
|
-
readonly completed: "## ✅ 已完成的任务";
|
|
73
|
-
readonly pending: "## 📋 未完成的任务";
|
|
74
|
-
readonly memory: "## 🧠 重要记忆";
|
|
75
|
-
readonly files: "## 📁 文件索引";
|
|
76
|
-
readonly instructions: "## 🔔 接手指令";
|
|
77
|
-
};
|
|
78
|
-
private onBeforeMigrate?;
|
|
79
|
-
private onMigrated?;
|
|
80
|
-
private client?;
|
|
81
|
-
private model?;
|
|
82
|
-
private modelConfig?;
|
|
83
|
-
private strategy;
|
|
84
|
-
private logger;
|
|
85
|
-
constructor(options?: TaskMigrationServiceOptions);
|
|
86
|
-
static createPrompt(): string;
|
|
87
|
-
/**
|
|
88
|
-
* 生成迁移后追加的「对话断点」消息(role=user):交接文档 + 接手指令。
|
|
89
|
-
* 该消息是 omit 迁移方案中新上下文的起点,模型看到的序列为
|
|
90
|
-
* `[definition.messages, 断点消息]`(历史消息已被标记 omit,不再发送)。
|
|
91
|
-
*/
|
|
92
|
-
static createPostMessages(handoffDoc: string): AgentNS.Message[];
|
|
93
|
-
/**
|
|
94
|
-
* 序列化消息为 `[role]: content` 文本,供模型理解完整对话历史。
|
|
95
|
-
*/
|
|
96
|
-
static serializeMessages(messages: AgentNS.Message[]): string;
|
|
97
|
-
/**
|
|
98
|
-
* 生成交接文档:使用指定的 client / model / modelConfig 带交接 prompt 生成结构化文档。
|
|
99
|
-
*/
|
|
100
|
-
private generateHandoff;
|
|
101
|
-
/**
|
|
102
|
-
* 执行完整迁移:序列化历史 → 模型生成交接文档 → 按策略处理历史并追加对话断点 → 触发钩子。
|
|
103
|
-
*
|
|
104
|
-
* 策略(`strategy`,构造函数默认 / 本参数可覆盖):
|
|
105
|
-
* - `omit`(默认):`agent.definition.messages`(模板)保留,其余既有历史标记 `omit: true`(仍存于
|
|
106
|
-
* `agent.messages` 可审计/回放但不再发送给模型),末尾追加「对话断点」消息。因此 `formatHistory()`
|
|
107
|
-
* 发送给模型的是 `[definition.messages, 断点消息]` —— 历史被压缩但并不删除。
|
|
108
|
-
* - `prune`:物理剔除历史,只保留 `definition.messages` + 断点消息(即原先的替换行为)。
|
|
109
|
-
*
|
|
110
|
-
* 设计约束:
|
|
111
|
-
* - 不重建 Agent,保留所有引用与插件绑定(仅重建消息列表)
|
|
112
|
-
* - 钩子(onBeforeMigrate / onMigrated)抛错会被捕获并记录,**不中断**迁移流程
|
|
113
|
-
* - 生成失败 / 未取到交接文档时抛错,此时 `agent.messages` 保持原样(不会被部分修改)
|
|
114
|
-
*
|
|
115
|
-
* @param params.agent 当前对话 Agent
|
|
116
|
-
* @param params.promptTokens 当前 prompt token 用量(可选)
|
|
117
|
-
* @param params.maxTokens 上下文阈值(可选)
|
|
118
|
-
* @param params.strategy 本次迁移的历史处理策略(可选,覆盖构造时的默认值)
|
|
119
|
-
* @returns 完整的迁移上下文(含生成的交接文档)
|
|
120
|
-
*/
|
|
121
|
-
migrate(params: {
|
|
122
|
-
agent: SdkAgent;
|
|
123
|
-
promptTokens?: number;
|
|
124
|
-
maxTokens?: number;
|
|
125
|
-
/** 本次迁移的历史处理策略(可选,覆盖构造时的默认值) */
|
|
126
|
-
strategy?: MigrationStrategy;
|
|
127
|
-
}): Promise<MigrationContext>;
|
|
128
|
-
}
|