@ai-zen/agents-sdk 1.0.0-alpha.21 → 1.0.0-alpha.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +223 -171
- package/README.md +15 -0
- package/README.zh.md +15 -0
- package/dist/agent-plugins/AutoMigratePlugin.js +1 -1
- package/dist/agent-plugins/UnknownToolHintPlugin.js +4 -4
- package/dist/config/constants.js +29 -24
- package/dist/config/factory-skills.js +56 -56
- package/dist/config/migrate.d.ts +10 -2
- package/dist/config/migrate.js +23 -2
- package/dist/runtime/HandoffGuardPlugin.js +2 -2
- package/dist/runtime/HandoffService.js +22 -22
- package/dist/runtime/createAgent.d.ts +7 -3
- package/dist/runtime/createAgent.js +7 -5
- package/dist/runtime/createModel.js +3 -3
- package/dist/scope/PermissionEvaluator.js +1 -1
- package/dist/scope/Scope.js +1 -1
- package/dist/scope/plugins/builtin/BatchEditTool.js +8 -8
- package/dist/scope/plugins/builtin/CopyTool.js +4 -4
- package/dist/scope/plugins/builtin/CwdTool.js +1 -1
- package/dist/scope/plugins/builtin/DownloadFileTool.js +8 -8
- package/dist/scope/plugins/builtin/EditTool.js +9 -9
- package/dist/scope/plugins/builtin/ExecTool.js +8 -8
- package/dist/scope/plugins/builtin/ExistTool.js +2 -2
- package/dist/scope/plugins/builtin/FindTextTool.js +10 -10
- package/dist/scope/plugins/builtin/GenerateImageTool.js +13 -13
- package/dist/scope/plugins/builtin/GlobTool.js +4 -4
- package/dist/scope/plugins/builtin/InspectFileTool.js +7 -7
- package/dist/scope/plugins/builtin/LsTool.js +5 -5
- package/dist/scope/plugins/builtin/MkdirTool.js +3 -3
- package/dist/scope/plugins/builtin/ReadTool.js +8 -8
- package/dist/scope/plugins/builtin/RenameTool.js +3 -3
- package/dist/scope/plugins/builtin/RmTool.js +3 -3
- package/dist/scope/plugins/builtin/SleepTool.js +6 -6
- package/dist/scope/plugins/builtin/SpawnTool.js +9 -9
- package/dist/scope/plugins/builtin/ViewImageTool.js +12 -12
- package/dist/scope/plugins/builtin/WriteTool.js +3 -3
- package/dist/scope/plugins/mcp/McpConnectionManager.js +7 -7
- package/dist/scope/plugins/mcp/discover.js +1 -1
- package/dist/scope/plugins/mcp/mcpTools.js +29 -29
- package/dist/scope/plugins/skills/discover.js +7 -7
- package/dist/scope/plugins/skills/skillTools.js +17 -17
- package/dist/scope/plugins/subagents/subAgentTools.js +2 -2
- package/dist/scope/plugins/usertools/discover.js +2 -2
- package/dist/shared/content-blocks.js +9 -9
- package/dist/shared/errors.js +3 -3
- package/dist/types/index.d.ts +22 -1
- package/package.json +10 -1
package/README.md
CHANGED
|
@@ -125,6 +125,8 @@ Conditionally injected based on the active model / config:
|
|
|
125
125
|
|
|
126
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).
|
|
127
127
|
|
|
128
|
+
Interrupt strategies live in the config: `AppConfig.abortedMessageStrategy` (`keep` / `drop`, factory `keep` — the model can continue the half-sentence after an interrupt) and `AppConfig.abortedToolRoundStrategy` (`fill` / `drop`, factory `fill` — whole rounds go in or out, because `tool_calls` must pair with results). `createAgent()` reads them from `scope.config` when no explicit option is passed, keeping a single source of truth.
|
|
129
|
+
|
|
128
130
|
## Built-in Plugins
|
|
129
131
|
|
|
130
132
|
| Plugin | Description |
|
|
@@ -134,6 +136,19 @@ Tool output protection: `AppConfig.maxToolOutput` (in characters, default 32768)
|
|
|
134
136
|
| `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 |
|
|
135
137
|
| `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`) |
|
|
136
138
|
|
|
139
|
+
## Language
|
|
140
|
+
|
|
141
|
+
The SDK is **English-only**: every model-facing and user-facing string — tool schema descriptions, factory prompts, runtime-injected guidance, tool results, error messages, factory catalog data — is a fixed English literal. There is no locale field, no message catalog, and no runtime language switch. UI language belongs entirely to each consumer (the CLI's `i18n/`, the Desktop's language setting).
|
|
142
|
+
|
|
143
|
+
This is a performance boundary, not a convenience choice — **SDK behaviour and performance must not vary by the user's language**:
|
|
144
|
+
|
|
145
|
+
- **Thresholds are absolute.** `Model.tokenThresholds.handoff` / `guard` and `AppConfig.maxToolOutput` are absolute token / character counts. A localized tool schema is a different token count, so handoff and context-guard would trip at *different amounts of real content* for the same `config.json` — same config, different behaviour per language.
|
|
146
|
+
- **Schema cost is paid every turn.** Description text is re-sent with every request, so a token delta is a standing cost in request size, context usage and spend rather than a one-off.
|
|
147
|
+
- **Cache prefixes stop being comparable.** Prompt caching matches on the prefix; letting language into the SDK multiplies the prefix shapes of one code path and removes the single variable that benchmarks rely on.
|
|
148
|
+
- **Diagnosis needs one shape.** Performance data, bug reproductions and tool-call success rates have to be comparable across users; once behaviour drifts with locale, every conclusion needs a "what language are you on?" qualifier first.
|
|
149
|
+
|
|
150
|
+
Following the user's language is handled by one language-neutral instruction in the factory system prompt — `Always reply in the same language the user writes in.` — which adapts per **message** rather than per installation, and needs no locale plumbing in the SDK. Full rationale: [`docs/sdk-design-v1.md` §1](docs/sdk-design-v1.md).
|
|
151
|
+
|
|
137
152
|
## Design Principles
|
|
138
153
|
|
|
139
154
|
See the project-root [`PRINCIPLES.md`](../../PRINCIPLES.md):
|
package/README.zh.md
CHANGED
|
@@ -124,6 +124,8 @@ await scope.dispose(); // 断开 MCP、释放插件资源
|
|
|
124
124
|
|
|
125
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)。
|
|
126
126
|
|
|
127
|
+
中断策略进配置:`AppConfig.abortedMessageStrategy`(`keep` / `drop`,出厂 `keep`——中断后模型能接着半截往下说)与 `AppConfig.abortedToolRoundStrategy`(`fill` / `drop`,出厂 `fill`——整轮进出,`tool_calls` 必须与结果配对)。`createAgent()` 不传参数时直接取 `scope.config`,于是策略只有一个真相源。
|
|
128
|
+
|
|
127
129
|
## 内置插件
|
|
128
130
|
|
|
129
131
|
| 插件 | 说明 |
|
|
@@ -133,6 +135,19 @@ await scope.dispose(); // 断开 MCP、释放插件资源
|
|
|
133
135
|
| `ContextGuardPlugin` | 上下文安全护栏 — 每轮发请求前检测上一轮 `usage.prompt_tokens`,超过 `maxTokens × ratio`(默认 1.5,即 +50%)时抛 `ContextOverflowError` 中断对话,防止读入超大文件导致上下文失控 |
|
|
134
136
|
| `UnknownToolHintPlugin` | 未知工具智能提示 — LLM 调用不存在的工具时,根据 MCP 配置引导使用 `call_mcp_tool` / 提示权限问题(调用方显式 `agent.use` 注册) |
|
|
135
137
|
|
|
138
|
+
## 语言
|
|
139
|
+
|
|
140
|
+
SDK 是 **English-only**:所有面向模型与面向用户的文案(工具 schema 描述、出厂提示词、运行期注入语、工具返回值、错误信息、出厂 catalog 数据)都是**固定英文字面量**。没有 locale 字段、没有语言包、没有运行期切换。界面语言完全属于各端(CLI 的 `i18n/`、Desktop 的语言设置)。
|
|
141
|
+
|
|
142
|
+
这是**性能边界**,不是图省事 —— **SDK 的行为与性能不得随用户语言变化**:
|
|
143
|
+
|
|
144
|
+
- **阈值是绝对量。** `Model.tokenThresholds.handoff` / `guard` 与 `AppConfig.maxToolOutput` 都以 token / 字符的绝对值为判据。工具 schema 换个语言就是另一组 token 数 —— 交接与上下文阻断会在**不同的实际内容量**上触发。同一份 `config.json` 在不同语言下跑出不同行为。
|
|
145
|
+
- **schema 成本每轮都付。** 描述文字随每次请求重发,token 差异是摊在每一轮上的持续开销,会改变请求体积、上下文占用与费用,而不是一次性成本。
|
|
146
|
+
- **缓存前缀不再可比。** prompt 缓存按前缀匹配;语言一旦进入 SDK,同一条代码路径就有了多个前缀形态,基准测试失去单一变量。
|
|
147
|
+
- **基准与排查需要单一形态。** 性能数据、bug 复现、工具调用成功率都要能横向比较;行为若随 locale 漂移,任何结论都得先问「你是什么语言」。
|
|
148
|
+
|
|
149
|
+
「模型跟随用户语言」由一条语言中立的 system prompt 指令承担 —— `Always reply in the same language the user writes in.` —— 按**每条消息**自适应,比按安装实例绑定 locale 更准确,也不需要 SDK 参与。完整论述见 [`docs/sdk-design-v1.md` §1](docs/sdk-design-v1.md)。
|
|
150
|
+
|
|
136
151
|
## 设计原则
|
|
137
152
|
|
|
138
153
|
参见项目根 [`PRINCIPLES.md`](../../PRINCIPLES.md):
|
|
@@ -43,7 +43,7 @@ export class AutoMigratePlugin {
|
|
|
43
43
|
await service.migrate({ agent, promptTokens, handoffTokens, mode: mode ?? undefined });
|
|
44
44
|
}
|
|
45
45
|
catch (err) {
|
|
46
|
-
getLogger().error(`[autoMigrate]
|
|
46
|
+
getLogger().error(`[autoMigrate] migration failed: ${err?.message ?? err}`);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -19,16 +19,16 @@ export class UnknownToolHintPlugin {
|
|
|
19
19
|
this.scope = options.scope;
|
|
20
20
|
}
|
|
21
21
|
onUnknownTool(ctx) {
|
|
22
|
-
const toolName = ctx.toolCall.function?.name ?? "
|
|
22
|
+
const toolName = ctx.toolCall.function?.name ?? "unknown";
|
|
23
23
|
const mcpPlugin = this.scope.getPluginById(McpScopePlugin.ID);
|
|
24
24
|
const hasMcpConfig = mcpPlugin?.hasConfig() ?? false;
|
|
25
25
|
const hasCallMcpTool = ctx.availableTools.some((t) => t.function.name === "call_mcp_tool");
|
|
26
26
|
if (hasMcpConfig && !hasCallMcpTool) {
|
|
27
|
-
return
|
|
27
|
+
return `Tool "${toolName}" does not exist. An MCP server is configured, but the call_mcp_tool permission is disabled — adjust permissions if you need to use MCP tools.`;
|
|
28
28
|
}
|
|
29
29
|
if (hasMcpConfig && hasCallMcpTool) {
|
|
30
|
-
return
|
|
30
|
+
return `Tool "${toolName}" does not exist. To call MCP tools, use call_mcp_tool.`;
|
|
31
31
|
}
|
|
32
|
-
return
|
|
32
|
+
return `Tool "${toolName}" does not exist.`;
|
|
33
33
|
}
|
|
34
34
|
}
|
package/dist/config/constants.js
CHANGED
|
@@ -14,15 +14,15 @@ export const DEFAULT_MODEL_ID = "deepseek-flash";
|
|
|
14
14
|
/** 默认 Agent 定义(不含时间戳) */
|
|
15
15
|
export const DEFAULT_AGENT_DEFINITION = {
|
|
16
16
|
id: DEFAULT_AGENT_ID,
|
|
17
|
-
name: "
|
|
18
|
-
description: "
|
|
17
|
+
name: "Default Assistant",
|
|
18
|
+
description: "The default AI assistant, suited for everyday Q&A and task execution.",
|
|
19
19
|
// 模型写在 Agent 定义上,是运行期唯一真相源(无 config.defaultModel 兜底)
|
|
20
20
|
modelId: DEFAULT_MODEL_ID,
|
|
21
21
|
messages: [
|
|
22
|
-
Message.System(
|
|
22
|
+
Message.System(`You are a witty, rigorous and reliable assistant. Always reply in the same language the user writes in. Current operating system: ${process.platform}.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
1. When user input is needed for a decision, ask only one question at a time.
|
|
25
|
+
2. This session runs on Node.js — you can write Node.js scripts to carry out complex tasks and run them via the exec tool.`),
|
|
26
26
|
],
|
|
27
27
|
// 出厂默认 Agent:custom 为 false,SDK 初始化时会同步出厂提示词(用户设为 true 则跳过)
|
|
28
28
|
custom: false,
|
|
@@ -41,10 +41,10 @@ export const DEFAULT_SUBAGENT_ID = "sub-agent-default";
|
|
|
41
41
|
/** 默认 SubAgent 定义(不含时间戳) */
|
|
42
42
|
export const DEFAULT_SUBAGENT_DEFINITION = {
|
|
43
43
|
id: DEFAULT_SUBAGENT_ID,
|
|
44
|
-
name: "
|
|
45
|
-
description: "
|
|
44
|
+
name: "General Assistant",
|
|
45
|
+
description: "A general-purpose sub-agent that is good at completing all kinds of tasks independently.",
|
|
46
46
|
messages: [
|
|
47
|
-
Message.System("
|
|
47
|
+
Message.System("You are a general-purpose assistant sub-agent, delegated by a parent agent to independently complete a specific task. Based on the given task description, proactively call your tools (file read/write, command execution, search, etc.) to analyze and complete the task. When the task is done, return the result directly; there is no need to explain your thinking process.\n\n⚠️ Mandatory requirements:\n1. If anything in the task description is unclear, vague or missing (including but not limited to the specific goal, file paths, constraints, expected deliverables, etc.), you MUST refuse to execute immediately and explicitly list what is unclear or missing, asking the parent agent to provide more complete task context. Do not assume, guess or make up any information on your own.\n2. If the task description contains contradictory information (such as mutually conflicting requirements, inconsistent file paths, contradictory constraints, etc.), you MUST point out the contradiction promptly and require the parent agent to clarify before executing."),
|
|
48
48
|
Message.User("{{task}}"),
|
|
49
49
|
],
|
|
50
50
|
permissions: {
|
|
@@ -55,11 +55,11 @@ export const DEFAULT_SUBAGENT_DEFINITION = {
|
|
|
55
55
|
},
|
|
56
56
|
function: {
|
|
57
57
|
name: "sub_agent_default",
|
|
58
|
-
description: "
|
|
58
|
+
description: "General-purpose sub-agent that can independently complete all kinds of tasks. ⚠️ When delegating a task you must provide complete task context, including all necessary background information, file paths, specific requirements and constraints; any unclear information will cause the sub-agent to refuse execution and ask for more information.",
|
|
59
59
|
parameters: {
|
|
60
60
|
type: "object",
|
|
61
61
|
properties: {
|
|
62
|
-
task: { type: "string", description: "
|
|
62
|
+
task: { type: "string", description: "Complete task description; must include all necessary background, goals, constraints and context. Any unclear information will cause the sub-agent to refuse execution." },
|
|
63
63
|
},
|
|
64
64
|
required: ["task"],
|
|
65
65
|
additionalProperties: false,
|
|
@@ -77,21 +77,21 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
77
77
|
name: "OpenAI",
|
|
78
78
|
apiKey: "",
|
|
79
79
|
baseUrl: "https://api.openai.com/v1",
|
|
80
|
-
description: "OpenAI API
|
|
80
|
+
description: "OpenAI API endpoint",
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
id: "bigmodelcn",
|
|
84
|
-
name: "BigModelCN (
|
|
84
|
+
name: "BigModelCN (Zhipu AI)",
|
|
85
85
|
apiKey: "",
|
|
86
86
|
baseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
87
|
-
description: "
|
|
87
|
+
description: "Zhipu AI large-model endpoint",
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
id: "deepseek",
|
|
91
91
|
name: "DeepSeek",
|
|
92
92
|
apiKey: "",
|
|
93
93
|
baseUrl: "https://api.deepseek.com/v1",
|
|
94
|
-
description: "DeepSeek API
|
|
94
|
+
description: "DeepSeek API endpoint (supports the Files API: local files are uploaded and then referenced by file_id)",
|
|
95
95
|
// 唯一声明了 Files API 能力的出厂端点;其余端点本地文件走 base64 内联。
|
|
96
96
|
// 判断依据是能力位而非端点 id,用户自建端点只要声明即可享受上传。
|
|
97
97
|
capabilities: { filesApi: true },
|
|
@@ -128,7 +128,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
128
128
|
defaultParams: {},
|
|
129
129
|
vision: true,
|
|
130
130
|
reasoningPreset: "openai",
|
|
131
|
-
description: "OpenAI
|
|
131
|
+
description: "OpenAI flagship (2026-09): advanced analysis, software engineering, deep research and scientific work",
|
|
132
132
|
},
|
|
133
133
|
{
|
|
134
134
|
id: "gpt-6.1-sol",
|
|
@@ -139,7 +139,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
139
139
|
defaultParams: {},
|
|
140
140
|
vision: true,
|
|
141
141
|
reasoningPreset: "openai",
|
|
142
|
-
description: "OpenAI
|
|
142
|
+
description: "OpenAI latest flagship (2026-09-29): agentic coding, computer operation and document-intensive professional tasks",
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
id: "gpt-6-luna",
|
|
@@ -150,7 +150,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
150
150
|
defaultParams: {},
|
|
151
151
|
vision: true,
|
|
152
152
|
reasoningPreset: "openai",
|
|
153
|
-
description: "OpenAI GPT-6
|
|
153
|
+
description: "OpenAI GPT-6 series fast, low-cost model: suited for high-throughput, low-latency scenarios",
|
|
154
154
|
},
|
|
155
155
|
// ---------- 智谱 BigModel ----------
|
|
156
156
|
{
|
|
@@ -161,7 +161,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
161
161
|
tokenThresholds: { handoff: 250_000, guard: 375_000 },
|
|
162
162
|
defaultParams: { thinking: { type: "enabled" }, reasoning_effort: "max" },
|
|
163
163
|
reasoningPreset: "zhipu-5.3",
|
|
164
|
-
description: "
|
|
164
|
+
description: "Zhipu latest flagship: complex software engineering and long-horizon agent tasks (thinking always on, 1M context)",
|
|
165
165
|
},
|
|
166
166
|
{
|
|
167
167
|
id: "glm-5.3-flash",
|
|
@@ -172,7 +172,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
172
172
|
defaultParams: { thinking: { type: "enabled" }, reasoning_effort: "max" },
|
|
173
173
|
vision: true,
|
|
174
174
|
reasoningPreset: "zhipu-5.3",
|
|
175
|
-
description: "
|
|
175
|
+
description: "Zhipu native multimodal model (image/video/file): visual coding, 1M context",
|
|
176
176
|
},
|
|
177
177
|
{
|
|
178
178
|
id: "glm-5.3-flashx",
|
|
@@ -183,7 +183,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
183
183
|
defaultParams: { thinking: { type: "enabled" }, reasoning_effort: "max" },
|
|
184
184
|
vision: true,
|
|
185
185
|
reasoningPreset: "zhipu-5.3",
|
|
186
|
-
description: "GLM-5.3-Flash
|
|
186
|
+
description: "GLM-5.3-Flash high-speed edition (200 tokens/s), native multimodal",
|
|
187
187
|
},
|
|
188
188
|
{
|
|
189
189
|
id: "glm-4.7-flash",
|
|
@@ -193,7 +193,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
193
193
|
// 该模型真实窗口仅 200K,阈值下调至 100K(约窗口的 50%),留足 response 空间
|
|
194
194
|
tokenThresholds: { handoff: 100_000, guard: 150_000 },
|
|
195
195
|
defaultParams: {},
|
|
196
|
-
description: "
|
|
196
|
+
description: "Zhipu free text model: 200K context, suited for lightweight tasks",
|
|
197
197
|
},
|
|
198
198
|
// ---------- DeepSeek ----------
|
|
199
199
|
{
|
|
@@ -205,7 +205,7 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
205
205
|
defaultParams: { thinking: { type: "enabled" } },
|
|
206
206
|
vision: true,
|
|
207
207
|
reasoningPreset: "deepseek",
|
|
208
|
-
description: "DeepSeek
|
|
208
|
+
description: "DeepSeek flagship (2026-09-10): 552B MoE asymmetric architecture, native multimodal, 1M context",
|
|
209
209
|
},
|
|
210
210
|
],
|
|
211
211
|
imageModels: [
|
|
@@ -237,6 +237,11 @@ export const DEFAULT_APP_CONFIG = {
|
|
|
237
237
|
defaultMigrationModel: "deepseek-flash",
|
|
238
238
|
// 工具输出上限(字符数),与 DEFAULT_MAX_TOOL_OUTPUT 保持一致
|
|
239
239
|
maxToolOutput: 32_768,
|
|
240
|
+
// 中断策略:出厂取「保留已输出的内容」—— 中断后模型能接着半截往下说,
|
|
241
|
+
// 而不是「失忆重来」(core 的库级默认是 drop,产品层认为那是坑);
|
|
242
|
+
// 工具轮次保持整轮进出(fill),服务端要求 tool_calls 与结果一一配对。
|
|
243
|
+
abortedMessageStrategy: "keep",
|
|
244
|
+
abortedToolRoundStrategy: "fill",
|
|
240
245
|
};
|
|
241
246
|
// ---------------------------------------------------------------------------
|
|
242
247
|
// 默认 MCP 配置(socket-pty 终端 MCP + chrome-devtools 浏览器调试 MCP)
|
|
@@ -255,13 +260,13 @@ export const DEFAULT_MCP_CONFIG = {
|
|
|
255
260
|
type: "stdio",
|
|
256
261
|
command: "npx",
|
|
257
262
|
args: ["-y", "@ai-zen/socket-pty", "mcp"],
|
|
258
|
-
description: "
|
|
263
|
+
description: "Manageable pseudo-terminal (pty): spawn/read/wait/write/resize/status/kill",
|
|
259
264
|
},
|
|
260
265
|
"chrome-devtools": {
|
|
261
266
|
type: "stdio",
|
|
262
267
|
command: "npx",
|
|
263
268
|
args: ["-y", "chrome-devtools-mcp@latest"],
|
|
264
|
-
description: "Chrome DevTools
|
|
269
|
+
description: "Chrome DevTools browser debugging and automation (Google chrome-devtools-mcp): page navigation, performance tracing, network and console inspection, screenshots, etc.",
|
|
265
270
|
},
|
|
266
271
|
},
|
|
267
272
|
};
|
|
@@ -19,94 +19,94 @@ export const HANDOFF_SKILL_ID = "handoff";
|
|
|
19
19
|
* 它是**保险丝**,不是第二真相源:正常路径下模板一律来自 SKILL.md。
|
|
20
20
|
*/
|
|
21
21
|
export const FALLBACK_HANDOFF_TEMPLATE = [
|
|
22
|
-
"#
|
|
22
|
+
"# Handoff Document",
|
|
23
23
|
"",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
24
|
+
"Stop work immediately: implement no further features, do not continue unfinished tasks, and do not ask the user questions.",
|
|
25
|
+
"Your only task is: verify the actual current state → write the handoff document to the path given by the handoff parameters.",
|
|
26
26
|
"",
|
|
27
|
-
"## 💬
|
|
28
|
-
"## 🎯
|
|
29
|
-
"## 📊
|
|
30
|
-
"## ✅
|
|
31
|
-
"## 📋
|
|
32
|
-
"## 🧠
|
|
33
|
-
"## ⚠️
|
|
34
|
-
"## 📁
|
|
35
|
-
"## 🔔
|
|
27
|
+
"## 💬 Conversation Breakpoint",
|
|
28
|
+
"## 🎯 Task Goal",
|
|
29
|
+
"## 📊 Current State",
|
|
30
|
+
"## ✅ Completed",
|
|
31
|
+
"## 📋 Not Completed",
|
|
32
|
+
"## 🧠 Key Decisions",
|
|
33
|
+
"## ⚠️ Constraints and Preferences",
|
|
34
|
+
"## 📁 File Index",
|
|
35
|
+
"## 🔔 First Step for the Next Agent",
|
|
36
36
|
"",
|
|
37
|
-
"
|
|
37
|
+
'For anything you cannot verify, write "⚠️ Unverified"; speculation is forbidden.',
|
|
38
38
|
].join("\n");
|
|
39
39
|
/** 出厂 Skill:交接(handoff) */
|
|
40
40
|
const HANDOFF_SKILL = `---
|
|
41
41
|
name: handoff
|
|
42
|
-
description:
|
|
42
|
+
description: Write a task handoff document — when you stop work and leave the conversation, put the task goal, the actual current state, key decisions and next steps into a document that someone else can pick up directly.
|
|
43
43
|
metadata:
|
|
44
44
|
custom: "false"
|
|
45
45
|
---
|
|
46
46
|
|
|
47
|
-
#
|
|
47
|
+
# Handoff Document Writing Standard
|
|
48
48
|
|
|
49
|
-
##
|
|
49
|
+
## When It Applies
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
When the context is about to overflow and trigger a task migration, when the user explicitly asks you to "write a handoff document", or when you are asked to "hand over".
|
|
52
52
|
|
|
53
|
-
##
|
|
53
|
+
## Iron Rules (violating any of them counts as failure)
|
|
54
54
|
|
|
55
|
-
1.
|
|
56
|
-
2.
|
|
57
|
-
3.
|
|
58
|
-
4.
|
|
59
|
-
|
|
60
|
-
5.
|
|
55
|
+
1. **Stop work immediately**: implement no further features, do not continue unfinished tasks, do not fix bugs in passing, do not refactor.
|
|
56
|
+
2. **Do not ask the user questions**, do not discuss approaches, do not ask for confirmation.
|
|
57
|
+
3. Do only two things: **verify the actual current state** → **write the document**.
|
|
58
|
+
4. Read-only operations are allowed (read / ls / glob / find_text / inspect_file / cwd, plus read-only commands such as git status, git diff, git log).
|
|
59
|
+
Modifying the workspace is forbidden: do not change source code, do not npm install, do not commit, do not push. The only file you are allowed to write is the handoff document itself.
|
|
60
|
+
5. **Write it in one pass**, do not rewrite it over and over; do not read large files; do not paste long code into the document.
|
|
61
61
|
|
|
62
|
-
##
|
|
62
|
+
## Writing the File
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
The path is given by the handoff parameters — **use it verbatim**; do not rename it, do not move it to another directory, do not omit it.
|
|
65
|
+
After writing with the write tool, read it back once with the read tool to confirm the content is complete (skip this step if you have no read capability).
|
|
66
66
|
|
|
67
|
-
##
|
|
67
|
+
## Document Structure (write in this order, keep the headings exactly as they are)
|
|
68
68
|
|
|
69
|
-
## 💬
|
|
70
|
-
|
|
69
|
+
## 💬 Conversation Breakpoint
|
|
70
|
+
What the user last asked for, and what you last replied with or delivered. One paragraph each; do not restate the whole conversation.
|
|
71
71
|
|
|
72
|
-
## 🎯
|
|
73
|
-
|
|
72
|
+
## 🎯 Task Goal
|
|
73
|
+
What the user actually wants (intent and acceptance criteria), not a checklist of the steps you performed.
|
|
74
74
|
|
|
75
|
-
## 📊
|
|
76
|
-
|
|
75
|
+
## 📊 Current State
|
|
76
|
+
Branch and commit, uncommitted changes, whether tests pass, whether any process/port is running, whether dependencies are installed.
|
|
77
77
|
|
|
78
|
-
## ✅
|
|
79
|
-
|
|
78
|
+
## ✅ Completed
|
|
79
|
+
For each item write: what was done + the output path (file/directory) + how to verify that it really took effect.
|
|
80
80
|
|
|
81
|
-
## 📋
|
|
82
|
-
|
|
81
|
+
## 📋 Not Completed
|
|
82
|
+
Order by priority; for each item write "the smallest next action", down to a specific file or runnable command.
|
|
83
83
|
|
|
84
|
-
## 🧠
|
|
85
|
-
|
|
84
|
+
## 🧠 Key Decisions
|
|
85
|
+
Why it was done this way (the trade-off reasoning); and the dead ends you walked into — so the next person does not repeat them.
|
|
86
86
|
|
|
87
|
-
## ⚠️
|
|
88
|
-
|
|
87
|
+
## ⚠️ Constraints and Preferences
|
|
88
|
+
Rules the user stressed, project conventions, parts that must not be changed, styles that must be followed.
|
|
89
89
|
|
|
90
|
-
## 📁
|
|
91
|
-
|
|
90
|
+
## 📁 File Index
|
|
91
|
+
Path + purpose + whether it changed this time (added / modified / read only).
|
|
92
92
|
|
|
93
|
-
## 🔔
|
|
94
|
-
3
|
|
93
|
+
## 🔔 First Step for the Next Agent
|
|
94
|
+
At most 3 commands or actions that can be run directly, so the next person can start working immediately.
|
|
95
95
|
|
|
96
|
-
##
|
|
96
|
+
## State Verification Checklist
|
|
97
97
|
|
|
98
|
-
- \`git status\` / \`git diff --stat
|
|
99
|
-
-
|
|
100
|
-
-
|
|
101
|
-
-
|
|
98
|
+
- \`git status\` / \`git diff --stat\`: current branch, whether there are uncommitted changes
|
|
99
|
+
- The latest test result: if you ran tests, write a summary of the result; if you did not, write "not run" — **do not write "it should pass"**
|
|
100
|
+
- Key files involved in the handoff: use ls / read to confirm they really exist on disk
|
|
101
|
+
- Running services and ports: \`ps\` / \`lsof -i\`, or the project's own start script
|
|
102
102
|
|
|
103
|
-
> ⚠️
|
|
103
|
+
> ⚠️ If the handoff parameters state that tools are unavailable: write "⚠️ Unverified" for every item above; **speculation is forbidden** about specific paths, progress and test status.
|
|
104
104
|
|
|
105
|
-
##
|
|
105
|
+
## Writing Requirements
|
|
106
106
|
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
107
|
+
- Every sentence must let the next person "start working immediately"; empty phrases such as "continue development", "polish the features", "do some optimization" are forbidden.
|
|
108
|
+
- For information you can no longer determine in the current context, write "needs user confirmation on XXX"; do not fabricate it.
|
|
109
|
+
- Keep the whole document within 300 lines.
|
|
110
110
|
`;
|
|
111
111
|
/** 全部出厂 Skill(顺序无意义,按 id 去重) */
|
|
112
112
|
export const FACTORY_SKILLS = [
|
package/dist/config/migrate.d.ts
CHANGED
|
@@ -58,10 +58,18 @@ export declare function migrateModelReasoning(model: Model): {
|
|
|
58
58
|
* 迁移整个配置。只处理对话模型(`models`)——
|
|
59
59
|
* 图片模型(`imageModels`)既没有上下文阈值、也没有思考概念。
|
|
60
60
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
61
|
+
* 同时做两件字段级收敛:
|
|
62
|
+
* - 清除已废弃的 `defaultModel`(模型真相源已改为 `AgentDefinition.modelId`);
|
|
63
|
+
* - 补齐中断策略(`abortedMessageStrategy` / `abortedToolRoundStrategy`):把缺失或
|
|
64
|
+
* 非法值写成出厂默认。这样「配置即事实」——文件里写着的就是运行时用的,不再需要
|
|
65
|
+
* 使用方各自 `?? "keep"` 地兵分两路兜底。
|
|
63
66
|
*/
|
|
64
67
|
export declare function migrateConfig(config: AppConfig): {
|
|
65
68
|
config: AppConfig;
|
|
66
69
|
changed: boolean;
|
|
67
70
|
};
|
|
71
|
+
/** 合法的中断策略取值(与 core 的枚举一致) */
|
|
72
|
+
export declare const ABORTED_MESSAGE_STRATEGIES: readonly ["keep", "drop"];
|
|
73
|
+
export declare const ABORTED_TOOL_ROUND_STRATEGIES: readonly ["fill", "drop"];
|
|
74
|
+
/** 缺失 / 非法 → 出厂默认;返回是否有改动(直接改写传入对象) */
|
|
75
|
+
export declare function normalizeAbortStrategies(config: AppConfig): boolean;
|
package/dist/config/migrate.js
CHANGED
|
@@ -103,8 +103,11 @@ export function migrateModelReasoning(model) {
|
|
|
103
103
|
* 迁移整个配置。只处理对话模型(`models`)——
|
|
104
104
|
* 图片模型(`imageModels`)既没有上下文阈值、也没有思考概念。
|
|
105
105
|
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
106
|
+
* 同时做两件字段级收敛:
|
|
107
|
+
* - 清除已废弃的 `defaultModel`(模型真相源已改为 `AgentDefinition.modelId`);
|
|
108
|
+
* - 补齐中断策略(`abortedMessageStrategy` / `abortedToolRoundStrategy`):把缺失或
|
|
109
|
+
* 非法值写成出厂默认。这样「配置即事实」——文件里写着的就是运行时用的,不再需要
|
|
110
|
+
* 使用方各自 `?? "keep"` 地兵分两路兜底。
|
|
108
111
|
*/
|
|
109
112
|
export function migrateConfig(config) {
|
|
110
113
|
let changed = false;
|
|
@@ -121,7 +124,25 @@ export function migrateConfig(config) {
|
|
|
121
124
|
delete next.defaultModel;
|
|
122
125
|
changed = true;
|
|
123
126
|
}
|
|
127
|
+
if (normalizeAbortStrategies(next))
|
|
128
|
+
changed = true;
|
|
124
129
|
if (!changed)
|
|
125
130
|
return { config, changed };
|
|
126
131
|
return { config: next, changed };
|
|
127
132
|
}
|
|
133
|
+
/** 合法的中断策略取值(与 core 的枚举一致) */
|
|
134
|
+
export const ABORTED_MESSAGE_STRATEGIES = ["keep", "drop"];
|
|
135
|
+
export const ABORTED_TOOL_ROUND_STRATEGIES = ["fill", "drop"];
|
|
136
|
+
/** 缺失 / 非法 → 出厂默认;返回是否有改动(直接改写传入对象) */
|
|
137
|
+
export function normalizeAbortStrategies(config) {
|
|
138
|
+
let changed = false;
|
|
139
|
+
if (!ABORTED_MESSAGE_STRATEGIES.includes(config.abortedMessageStrategy)) {
|
|
140
|
+
config.abortedMessageStrategy = DEFAULT_APP_CONFIG.abortedMessageStrategy;
|
|
141
|
+
changed = true;
|
|
142
|
+
}
|
|
143
|
+
if (!ABORTED_TOOL_ROUND_STRATEGIES.includes(config.abortedToolRoundStrategy)) {
|
|
144
|
+
config.abortedToolRoundStrategy = DEFAULT_APP_CONFIG.abortedToolRoundStrategy;
|
|
145
|
+
changed = true;
|
|
146
|
+
}
|
|
147
|
+
return changed;
|
|
148
|
+
}
|
|
@@ -55,10 +55,10 @@ export class HandoffGuardPlugin {
|
|
|
55
55
|
if (typeof raw === "string" && resolvePath(this.cwd, raw) === handoffPath) {
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
|
-
return
|
|
58
|
+
return `During the handoff round the only allowed write is the handoff document: ${handoffPath} (pass that path to write exactly as given), start writing now.`;
|
|
59
59
|
}
|
|
60
60
|
if (HandoffGuardPlugin.WRITE_TOOLS.has(name)) {
|
|
61
|
-
return
|
|
61
|
+
return `Modifying the workspace is not allowed during the handoff round (${name}): your only task is to write the handoff document to ${handoffPath}.`;
|
|
62
62
|
}
|
|
63
63
|
return;
|
|
64
64
|
}
|