@yeaft/webchat-agent 0.1.1016 → 0.1.1018
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/package.json +1 -1
- package/yeaft/tools/agent.js +67 -11
- package/yeaft/tools/apply-patch.js +15 -2
- package/yeaft/tools/ask-user.js +20 -3
- package/yeaft/tools/bash.js +27 -4
- package/yeaft/tools/close-agent.js +17 -3
- package/yeaft/tools/enter-worktree.js +19 -3
- package/yeaft/tools/exit-worktree.js +22 -4
- package/yeaft/tools/file-edit.js +29 -5
- package/yeaft/tools/file-read.js +25 -4
- package/yeaft/tools/file-write.js +20 -3
- package/yeaft/tools/glob.js +24 -4
- package/yeaft/tools/grep.js +61 -12
- package/yeaft/tools/history-search.js +16 -3
- package/yeaft/tools/image-generation.js +23 -4
- package/yeaft/tools/js-repl.js +26 -4
- package/yeaft/tools/list-agents.js +19 -3
- package/yeaft/tools/list-dir.js +16 -3
- package/yeaft/tools/mcp-tools.js +39 -6
- package/yeaft/tools/notebook-edit.js +32 -6
- package/yeaft/tools/registry.js +19 -32
- package/yeaft/tools/route-forward.js +32 -4
- package/yeaft/tools/send-message.js +22 -3
- package/yeaft/tools/skill.js +34 -6
- package/yeaft/tools/start-plan.js +43 -6
- package/yeaft/tools/todo-write.js +34 -5
- package/yeaft/tools/types.js +2 -2
- package/yeaft/tools/view-image.js +28 -2
- package/yeaft/tools/wait-agent.js +30 -3
- package/yeaft/tools/web-fetch.js +24 -4
- package/yeaft/tools/web-search.js +19 -3
- package/yeaft/tools/localized-descriptions.js +0 -237
package/yeaft/tools/grep.js
CHANGED
|
@@ -142,7 +142,8 @@ async function nodeGrep(pattern, searchPath, options) {
|
|
|
142
142
|
|
|
143
143
|
export default defineTool({
|
|
144
144
|
name: 'Grep',
|
|
145
|
-
description:
|
|
145
|
+
description: {
|
|
146
|
+
en: `Search file contents for a regex pattern.
|
|
146
147
|
|
|
147
148
|
Uses ripgrep (rg) when available for fast searching, with a Node.js fallback.
|
|
148
149
|
|
|
@@ -156,53 +157,101 @@ Guidelines:
|
|
|
156
157
|
- Use glob or type filters to narrow the search
|
|
157
158
|
- Skips binary files and common large directories (node_modules, .git)
|
|
158
159
|
- Results are limited to 500 matches by default`,
|
|
160
|
+
zh: `用正则表达式搜索文件内容。
|
|
161
|
+
|
|
162
|
+
优先使用 ripgrep (rg) 快速搜索,回退到 Node.js 实现。
|
|
163
|
+
|
|
164
|
+
输出模式:
|
|
165
|
+
- "content" — 显示匹配行,含文件路径和行号
|
|
166
|
+
- "files_with_matches" — 仅显示匹配的文件路径(默认)
|
|
167
|
+
- "count" — 显示每个文件的匹配数量
|
|
168
|
+
|
|
169
|
+
使用指南:
|
|
170
|
+
- 使用正则语法(特殊字符需转义:\\.、\\{ 等)
|
|
171
|
+
- 用 glob 或 type 过滤缩小搜索范围
|
|
172
|
+
- 跳过二进制文件和常见大目录(node_modules、.git)
|
|
173
|
+
- 默认结果限制 500 条`
|
|
174
|
+
},
|
|
159
175
|
parameters: {
|
|
160
176
|
type: 'object',
|
|
161
177
|
properties: {
|
|
162
178
|
pattern: {
|
|
163
179
|
type: 'string',
|
|
164
|
-
description:
|
|
180
|
+
description: {
|
|
181
|
+
en: 'Regex pattern to search for',
|
|
182
|
+
zh: '要搜索的正则表达式模式',
|
|
183
|
+
},
|
|
165
184
|
},
|
|
166
185
|
path: {
|
|
167
186
|
type: 'string',
|
|
168
|
-
description:
|
|
187
|
+
description: {
|
|
188
|
+
en: 'File or directory to search (default: cwd)',
|
|
189
|
+
zh: '要搜索的文件或目录(默认当前工作目录)',
|
|
190
|
+
},
|
|
169
191
|
},
|
|
170
192
|
output_mode: {
|
|
171
193
|
type: 'string',
|
|
172
194
|
enum: ['content', 'files_with_matches', 'count'],
|
|
173
|
-
description:
|
|
195
|
+
description: {
|
|
196
|
+
en: 'Output format (default: "files_with_matches")',
|
|
197
|
+
zh: '输出格式(默认 "files_with_matches")',
|
|
198
|
+
},
|
|
174
199
|
},
|
|
175
200
|
glob: {
|
|
176
201
|
type: 'string',
|
|
177
|
-
description:
|
|
202
|
+
description: {
|
|
203
|
+
en: 'Glob filter for file names (e.g. "*.js", "*.{ts,tsx}")',
|
|
204
|
+
zh: '文件名 glob 过滤(如 "*.js"、"*.{ts,tsx}")',
|
|
205
|
+
},
|
|
178
206
|
},
|
|
179
207
|
type: {
|
|
180
208
|
type: 'string',
|
|
181
|
-
description:
|
|
209
|
+
description: {
|
|
210
|
+
en: 'File type filter (e.g. "js", "py", "rust")',
|
|
211
|
+
zh: '文件类型过滤(如 "js"、"py"、"rust")',
|
|
212
|
+
},
|
|
182
213
|
},
|
|
183
214
|
case_insensitive: {
|
|
184
215
|
type: 'boolean',
|
|
185
|
-
description:
|
|
216
|
+
description: {
|
|
217
|
+
en: 'Case-insensitive search (default: false)',
|
|
218
|
+
zh: '不区分大小写搜索(默认 false)',
|
|
219
|
+
},
|
|
186
220
|
},
|
|
187
221
|
context: {
|
|
188
222
|
type: 'number',
|
|
189
|
-
description:
|
|
223
|
+
description: {
|
|
224
|
+
en: 'Lines of context around matches (for "content" mode)',
|
|
225
|
+
zh: '匹配行周围的上下文行数(用于 "content" 模式)',
|
|
226
|
+
},
|
|
190
227
|
},
|
|
191
228
|
before: {
|
|
192
229
|
type: 'number',
|
|
193
|
-
description:
|
|
230
|
+
description: {
|
|
231
|
+
en: 'Lines before each match',
|
|
232
|
+
zh: '每个匹配之前显示的行数',
|
|
233
|
+
},
|
|
194
234
|
},
|
|
195
235
|
after: {
|
|
196
236
|
type: 'number',
|
|
197
|
-
description:
|
|
237
|
+
description: {
|
|
238
|
+
en: 'Lines after each match',
|
|
239
|
+
zh: '每个匹配之后显示的行数',
|
|
240
|
+
},
|
|
198
241
|
},
|
|
199
242
|
multiline: {
|
|
200
243
|
type: 'boolean',
|
|
201
|
-
description:
|
|
244
|
+
description: {
|
|
245
|
+
en: 'Enable multiline matching',
|
|
246
|
+
zh: '启用多行匹配',
|
|
247
|
+
},
|
|
202
248
|
},
|
|
203
249
|
head_limit: {
|
|
204
250
|
type: 'number',
|
|
205
|
-
description:
|
|
251
|
+
description: {
|
|
252
|
+
en: 'Limit output to first N results (default: 250)',
|
|
253
|
+
zh: '限制输出前 N 条结果(默认 250)',
|
|
254
|
+
},
|
|
206
255
|
},
|
|
207
256
|
},
|
|
208
257
|
required: ['pattern'],
|
|
@@ -10,22 +10,35 @@ import { searchMessages } from '../conversation/search.js';
|
|
|
10
10
|
|
|
11
11
|
export default defineTool({
|
|
12
12
|
name: 'HistorySearch',
|
|
13
|
-
description:
|
|
13
|
+
description: {
|
|
14
|
+
en: `Search through past conversation history.
|
|
14
15
|
|
|
15
16
|
Searches for keywords in previously persisted conversation messages.
|
|
16
17
|
Useful for finding previous discussions, decisions, or code snippets.
|
|
17
18
|
|
|
18
19
|
Results are returned newest-first with message role and content.`,
|
|
20
|
+
zh: `搜索历史对话记录。
|
|
21
|
+
|
|
22
|
+
在已持久化的对话消息中按关键词搜索。用于查找之前的讨论、决策或代码片段。
|
|
23
|
+
|
|
24
|
+
结果按最新优先返回,包含消息角色和内容。`
|
|
25
|
+
},
|
|
19
26
|
parameters: {
|
|
20
27
|
type: 'object',
|
|
21
28
|
properties: {
|
|
22
29
|
keyword: {
|
|
23
30
|
type: 'string',
|
|
24
|
-
description:
|
|
31
|
+
description: {
|
|
32
|
+
en: 'Search keyword (case-insensitive)',
|
|
33
|
+
zh: '搜索关键词(不区分大小写)',
|
|
34
|
+
},
|
|
25
35
|
},
|
|
26
36
|
limit: {
|
|
27
37
|
type: 'number',
|
|
28
|
-
description:
|
|
38
|
+
description: {
|
|
39
|
+
en: 'Maximum number of results (default: 20)',
|
|
40
|
+
zh: '最多返回结果数(默认 20)',
|
|
41
|
+
},
|
|
29
42
|
},
|
|
30
43
|
},
|
|
31
44
|
required: ['keyword'],
|
|
@@ -8,7 +8,8 @@ import { defineTool } from './types.js';
|
|
|
8
8
|
|
|
9
9
|
export default defineTool({
|
|
10
10
|
name: 'ImageGeneration',
|
|
11
|
-
description:
|
|
11
|
+
description: {
|
|
12
|
+
en: `Generate an image from a text description.
|
|
12
13
|
|
|
13
14
|
Uses a configured image generation API to create images.
|
|
14
15
|
Requires an image generation API endpoint in config.
|
|
@@ -17,21 +18,39 @@ Guidelines:
|
|
|
17
18
|
- Provide detailed, specific descriptions for best results
|
|
18
19
|
- Specify style, composition, and mood
|
|
19
20
|
- Images are saved to the working directory`,
|
|
21
|
+
zh: `根据文字描述生成图片。
|
|
22
|
+
|
|
23
|
+
使用配置的图片生成 API 创建图片。需要配置中设置图片生成 API 端点。
|
|
24
|
+
|
|
25
|
+
使用指南:
|
|
26
|
+
- 提供详细具体的描述以获得最佳效果
|
|
27
|
+
- 指定风格、构图和氛围
|
|
28
|
+
- 图片保存到工作目录`
|
|
29
|
+
},
|
|
20
30
|
parameters: {
|
|
21
31
|
type: 'object',
|
|
22
32
|
properties: {
|
|
23
33
|
prompt: {
|
|
24
34
|
type: 'string',
|
|
25
|
-
description:
|
|
35
|
+
description: {
|
|
36
|
+
en: 'Text description of the image to generate',
|
|
37
|
+
zh: '要生成图像的文本描述',
|
|
38
|
+
},
|
|
26
39
|
},
|
|
27
40
|
output_path: {
|
|
28
41
|
type: 'string',
|
|
29
|
-
description:
|
|
42
|
+
description: {
|
|
43
|
+
en: 'File path to save the generated image',
|
|
44
|
+
zh: '保存生成图像的文件路径',
|
|
45
|
+
},
|
|
30
46
|
},
|
|
31
47
|
size: {
|
|
32
48
|
type: 'string',
|
|
33
49
|
enum: ['256x256', '512x512', '1024x1024'],
|
|
34
|
-
description:
|
|
50
|
+
description: {
|
|
51
|
+
en: 'Image size (default: "1024x1024")',
|
|
52
|
+
zh: '图像大小(默认 "1024x1024")',
|
|
53
|
+
},
|
|
35
54
|
},
|
|
36
55
|
},
|
|
37
56
|
required: ['prompt'],
|
package/yeaft/tools/js-repl.js
CHANGED
|
@@ -53,7 +53,8 @@ function getContext() {
|
|
|
53
53
|
|
|
54
54
|
export const jsRepl = defineTool({
|
|
55
55
|
name: 'JsRepl',
|
|
56
|
-
description:
|
|
56
|
+
description: {
|
|
57
|
+
en: `Evaluate JavaScript code in a persistent REPL environment.
|
|
57
58
|
|
|
58
59
|
The REPL context persists across calls — variables and functions
|
|
59
60
|
defined in one call are available in subsequent calls.
|
|
@@ -66,16 +67,34 @@ Guidelines:
|
|
|
66
67
|
- console.log output is captured and returned
|
|
67
68
|
- Returns the last expression's value plus any console output
|
|
68
69
|
- No filesystem or network access from within the REPL`,
|
|
70
|
+
zh: `在持久 REPL 环境中执行 JavaScript 代码。
|
|
71
|
+
|
|
72
|
+
REPL 上下文在多次调用间保持——一次调用中定义的变量和函数可在后续调用中使用。
|
|
73
|
+
|
|
74
|
+
使用指南:
|
|
75
|
+
- 用于计算、数据转换和快速实验
|
|
76
|
+
- 状态在调用间保留
|
|
77
|
+
- 传入 reset: true 可在执行前清除所有状态(干净环境)。此模式下 code 可选——只传 reset 即可清除
|
|
78
|
+
- console.log 输出被捕获并返回
|
|
79
|
+
- 返回最后一个表达式的值及所有 console 输出
|
|
80
|
+
- REPL 内不能访问文件系统或网络`
|
|
81
|
+
},
|
|
69
82
|
parameters: {
|
|
70
83
|
type: 'object',
|
|
71
84
|
properties: {
|
|
72
85
|
code: {
|
|
73
86
|
type: 'string',
|
|
74
|
-
description:
|
|
87
|
+
description: {
|
|
88
|
+
en: 'JavaScript code to evaluate. Optional when reset=true and you only want to clear state.',
|
|
89
|
+
zh: '要执行的 JavaScript 代码。当 reset=true 且只想清空状态时可省略。',
|
|
90
|
+
},
|
|
75
91
|
},
|
|
76
92
|
reset: {
|
|
77
93
|
type: 'boolean',
|
|
78
|
-
description:
|
|
94
|
+
description: {
|
|
95
|
+
en: 'When true, reset the REPL context BEFORE evaluating `code`. If `code` is omitted, just resets.',
|
|
96
|
+
zh: '为 true 时,在求值前先重置 REPL 上下文。若省略 code,则仅重置。',
|
|
97
|
+
},
|
|
79
98
|
},
|
|
80
99
|
},
|
|
81
100
|
},
|
|
@@ -136,7 +155,10 @@ function warnJsReplResetDeprecated() {
|
|
|
136
155
|
|
|
137
156
|
export const jsReplReset = defineTool({
|
|
138
157
|
name: 'JsReplReset',
|
|
139
|
-
description:
|
|
158
|
+
description: {
|
|
159
|
+
en: 'DEPRECATED — use JsRepl with `reset: true` instead. Resets the persistent REPL context. Removal target: v0.2.0.',
|
|
160
|
+
zh: '已废弃——请改用带有 `reset: true` 参数的 JsRepl。它会清空持久 REPL 上下文。计划在 v0.2.0 移除。',
|
|
161
|
+
},
|
|
140
162
|
parameters: { type: 'object', properties: {} },
|
|
141
163
|
isConcurrencySafe: () => false,
|
|
142
164
|
isReadOnly: () => false,
|
|
@@ -16,7 +16,8 @@ import { diagnoseAgentLiveness } from '../sub-agent/liveness.js';
|
|
|
16
16
|
|
|
17
17
|
export default defineTool({
|
|
18
18
|
name: 'ListAgents',
|
|
19
|
-
description:
|
|
19
|
+
description: {
|
|
20
|
+
en: `List all sub-agents and their current status.
|
|
20
21
|
|
|
21
22
|
Returns id, name, status, mission/task summary, durable outputFile path,
|
|
22
23
|
liveness counters (toolUseCount, tokenCount, msSinceLastEvent, recentTools),
|
|
@@ -26,16 +27,31 @@ this as the primary non-blocking monitor for async sub-agent work, and Read
|
|
|
26
27
|
|
|
27
28
|
By default only non-closed agents are returned. Pass include_closed=true
|
|
28
29
|
to also list closed/failed/abandoned/completed agents.`,
|
|
30
|
+
zh: `列出所有子 Agent 及其当前状态。
|
|
31
|
+
|
|
32
|
+
返回每个 Agent 的 id、name、status、mission/task 摘要、持久化 outputFile 路径、
|
|
33
|
+
liveness 计数器(toolUseCount、tokenCount、msSinceLastEvent、recentTools)、
|
|
34
|
+
stale/stalled 诊断、result 尾部和消息数量。将此作为异步子 Agent 工作的主要非阻塞监控工具;
|
|
35
|
+
如需查看某个 Agent 的完整时间线,可 Read 其 outputFile。
|
|
36
|
+
|
|
37
|
+
默认只返回未关闭的 Agent。传 include_closed=true 可同时列出 closed/failed/abandoned/completed 的 Agent。`
|
|
38
|
+
},
|
|
29
39
|
parameters: {
|
|
30
40
|
type: 'object',
|
|
31
41
|
properties: {
|
|
32
42
|
include_closed: {
|
|
33
43
|
type: 'boolean',
|
|
34
|
-
description:
|
|
44
|
+
description: {
|
|
45
|
+
en: 'Include closed/failed/abandoned/completed agents in the list (default: false)',
|
|
46
|
+
zh: '在列表中包含已关闭/失败/放弃/完成的 Agent(默认 false)',
|
|
47
|
+
},
|
|
35
48
|
},
|
|
36
49
|
include_terminal: {
|
|
37
50
|
type: 'boolean',
|
|
38
|
-
description:
|
|
51
|
+
description: {
|
|
52
|
+
en: 'Alias for include_closed — include all terminal-status agents in the list',
|
|
53
|
+
zh: 'include_closed 的别名 — 列出所有已终止状态的 Agent',
|
|
54
|
+
},
|
|
39
55
|
},
|
|
40
56
|
},
|
|
41
57
|
},
|
package/yeaft/tools/list-dir.js
CHANGED
|
@@ -17,23 +17,36 @@ const SKIP_DIRS = new Set([
|
|
|
17
17
|
|
|
18
18
|
export default defineTool({
|
|
19
19
|
name: 'ListDir',
|
|
20
|
-
description:
|
|
20
|
+
description: {
|
|
21
|
+
en: `List the contents of a directory.
|
|
21
22
|
|
|
22
23
|
Shows files and subdirectories with their types and sizes.
|
|
23
24
|
Directories are listed first, then files, both sorted alphabetically.
|
|
24
25
|
Common large directories (node_modules, .git) are skipped.
|
|
25
26
|
|
|
26
27
|
This is better than using Bash with 'ls' because it provides structured output.`,
|
|
28
|
+
zh: `列出目录内容。
|
|
29
|
+
|
|
30
|
+
显示文件和子目录,含类型和大小。目录优先列出,文件次之,均按字母排序。常见大目录(node_modules、.git)被跳过。
|
|
31
|
+
|
|
32
|
+
比用 Bash 执行 ls 更好,因为它提供结构化输出。`
|
|
33
|
+
},
|
|
27
34
|
parameters: {
|
|
28
35
|
type: 'object',
|
|
29
36
|
properties: {
|
|
30
37
|
path: {
|
|
31
38
|
type: 'string',
|
|
32
|
-
description:
|
|
39
|
+
description: {
|
|
40
|
+
en: 'Directory path to list (default: cwd)',
|
|
41
|
+
zh: '要列出的目录路径(默认当前工作目录)',
|
|
42
|
+
},
|
|
33
43
|
},
|
|
34
44
|
show_hidden: {
|
|
35
45
|
type: 'boolean',
|
|
36
|
-
description:
|
|
46
|
+
description: {
|
|
47
|
+
en: 'Include hidden files (starting with dot, default: true)',
|
|
48
|
+
zh: '是否包含隐藏文件(以点开头的文件,默认 true)',
|
|
49
|
+
},
|
|
37
50
|
},
|
|
38
51
|
},
|
|
39
52
|
},
|
package/yeaft/tools/mcp-tools.js
CHANGED
|
@@ -135,7 +135,8 @@ export function buildMcpFlattenedTools(mcpManager) {
|
|
|
135
135
|
|
|
136
136
|
export const mcpListTools = defineTool({
|
|
137
137
|
name: 'mcp_list_tools',
|
|
138
|
-
description:
|
|
138
|
+
description: {
|
|
139
|
+
en: `List all tools available from connected MCP (Model Context Protocol) servers.
|
|
139
140
|
|
|
140
141
|
Usage guidelines:
|
|
141
142
|
- Use to discover what MCP tools are available in the current session
|
|
@@ -143,12 +144,24 @@ Usage guidelines:
|
|
|
143
144
|
- Each tool is prefixed with the server name (e.g. "github__list_prs", "slack__send_message")
|
|
144
145
|
- Use this before mcp_call_tool to understand available capabilities
|
|
145
146
|
- MCP servers are configured by the user — if none are connected, returns an empty list`,
|
|
147
|
+
zh: `列出已连接 MCP(Model Context Protocol)服务器提供的所有工具。
|
|
148
|
+
|
|
149
|
+
使用指南:
|
|
150
|
+
- 用于发现当前会话中可用的 MCP 工具
|
|
151
|
+
- 返回所有已连接 MCP 服务器的工具名称、描述和参数 schema
|
|
152
|
+
- 每个工具名称都带服务器名前缀(例如 "github__list_prs"、"slack__send_message")
|
|
153
|
+
- 调用 mcp_call_tool 前先用此工具确认可用能力和参数
|
|
154
|
+
- MCP 服务器由用户配置;如果当前没有连接服务器,将返回空列表`,
|
|
155
|
+
},
|
|
146
156
|
parameters: {
|
|
147
157
|
type: 'object',
|
|
148
158
|
properties: {
|
|
149
159
|
server: {
|
|
150
160
|
type: 'string',
|
|
151
|
-
description:
|
|
161
|
+
description: {
|
|
162
|
+
en: 'Filter tools from a specific MCP server (optional)',
|
|
163
|
+
zh: '按特定 MCP 服务器过滤工具(可选)',
|
|
164
|
+
},
|
|
152
165
|
},
|
|
153
166
|
},
|
|
154
167
|
},
|
|
@@ -181,7 +194,8 @@ Usage guidelines:
|
|
|
181
194
|
|
|
182
195
|
export const mcpCallTool = defineTool({
|
|
183
196
|
name: 'mcp_call_tool',
|
|
184
|
-
description:
|
|
197
|
+
description: {
|
|
198
|
+
en: `Call a tool on a connected MCP (Model Context Protocol) server.
|
|
185
199
|
|
|
186
200
|
Usage guidelines:
|
|
187
201
|
- Use after discovering tools via mcp_list_tools
|
|
@@ -190,20 +204,39 @@ Usage guidelines:
|
|
|
190
204
|
- The tool executes on the MCP server and returns the result
|
|
191
205
|
- Timeouts depend on the MCP server — use timeout_ms if the operation is slow
|
|
192
206
|
- Errors from the MCP server are returned as structured error objects`,
|
|
207
|
+
zh: `调用已连接 MCP(Model Context Protocol)服务器上的某个工具。
|
|
208
|
+
|
|
209
|
+
使用指南:
|
|
210
|
+
- 先通过 mcp_list_tools 发现工具,再调用此工具
|
|
211
|
+
- 提供包含服务器名前缀的完整工具名(例如 "github__create_issue")
|
|
212
|
+
- arguments 必须严格匹配该 MCP 工具的参数 schema
|
|
213
|
+
- 工具会在 MCP 服务器侧执行,并返回执行结果
|
|
214
|
+
- 超时时间取决于 MCP 服务器;慢操作可显式传入 timeout_ms
|
|
215
|
+
- MCP 服务器返回的错误会以结构化错误对象返回`,
|
|
216
|
+
},
|
|
193
217
|
parameters: {
|
|
194
218
|
type: 'object',
|
|
195
219
|
properties: {
|
|
196
220
|
tool_name: {
|
|
197
221
|
type: 'string',
|
|
198
|
-
description:
|
|
222
|
+
description: {
|
|
223
|
+
en: 'Full tool name including server prefix (e.g. "github__list_prs")',
|
|
224
|
+
zh: '完整工具名称,包括服务器前缀(如 "github__list_prs")',
|
|
225
|
+
},
|
|
199
226
|
},
|
|
200
227
|
arguments: {
|
|
201
228
|
type: 'object',
|
|
202
|
-
description:
|
|
229
|
+
description: {
|
|
230
|
+
en: 'Arguments to pass to the MCP tool (must match its schema)',
|
|
231
|
+
zh: '传递给 MCP 工具的参数(必须匹配其 schema)',
|
|
232
|
+
},
|
|
203
233
|
},
|
|
204
234
|
timeout_ms: {
|
|
205
235
|
type: 'number',
|
|
206
|
-
description:
|
|
236
|
+
description: {
|
|
237
|
+
en: 'Timeout in milliseconds (default: 30000)',
|
|
238
|
+
zh: '超时时间,单位毫秒(默认 30000)',
|
|
239
|
+
},
|
|
207
240
|
},
|
|
208
241
|
},
|
|
209
242
|
required: ['tool_name'],
|
|
@@ -11,7 +11,8 @@ import { resolve } from 'path';
|
|
|
11
11
|
|
|
12
12
|
export default defineTool({
|
|
13
13
|
name: 'NotebookEdit',
|
|
14
|
-
description:
|
|
14
|
+
description: {
|
|
15
|
+
en: `Edit a Jupyter notebook (.ipynb file) cell.
|
|
15
16
|
|
|
16
17
|
Actions:
|
|
17
18
|
- "replace" — replace the source of a cell at the given index
|
|
@@ -20,30 +21,55 @@ Actions:
|
|
|
20
21
|
- "read" — read the notebook content (all cells)
|
|
21
22
|
|
|
22
23
|
Cell types: "code" or "markdown"`,
|
|
24
|
+
zh: `编辑 Jupyter notebook(.ipynb 文件)单元格。
|
|
25
|
+
|
|
26
|
+
操作:
|
|
27
|
+
- "replace" — 替换指定索引处单元格的源码
|
|
28
|
+
- "insert" — 在指定索引后插入新单元格
|
|
29
|
+
- "delete" — 删除指定索引处的单元格
|
|
30
|
+
- "read" — 读取 notebook 内容(全部单元格)
|
|
31
|
+
|
|
32
|
+
单元格类型:"code" 或 "markdown"`
|
|
33
|
+
},
|
|
23
34
|
parameters: {
|
|
24
35
|
type: 'object',
|
|
25
36
|
properties: {
|
|
26
37
|
notebook_path: {
|
|
27
38
|
type: 'string',
|
|
28
|
-
description:
|
|
39
|
+
description: {
|
|
40
|
+
en: 'Path to the .ipynb file',
|
|
41
|
+
zh: '.ipynb 文件的路径',
|
|
42
|
+
},
|
|
29
43
|
},
|
|
30
44
|
action: {
|
|
31
45
|
type: 'string',
|
|
32
46
|
enum: ['replace', 'insert', 'delete', 'read'],
|
|
33
|
-
description:
|
|
47
|
+
description: {
|
|
48
|
+
en: 'Operation to perform (default: "replace")',
|
|
49
|
+
zh: '要执行的操作(默认 "replace")',
|
|
50
|
+
},
|
|
34
51
|
},
|
|
35
52
|
cell_index: {
|
|
36
53
|
type: 'number',
|
|
37
|
-
description:
|
|
54
|
+
description: {
|
|
55
|
+
en: 'Cell index (0-based)',
|
|
56
|
+
zh: '单元格索引(从 0 开始)',
|
|
57
|
+
},
|
|
38
58
|
},
|
|
39
59
|
cell_type: {
|
|
40
60
|
type: 'string',
|
|
41
61
|
enum: ['code', 'markdown'],
|
|
42
|
-
description:
|
|
62
|
+
description: {
|
|
63
|
+
en: 'Cell type for insert/replace',
|
|
64
|
+
zh: '插入/替换的单元格类型',
|
|
65
|
+
},
|
|
43
66
|
},
|
|
44
67
|
source: {
|
|
45
68
|
type: 'string',
|
|
46
|
-
description:
|
|
69
|
+
description: {
|
|
70
|
+
en: 'New cell source content',
|
|
71
|
+
zh: '新单元格的源码内容',
|
|
72
|
+
},
|
|
47
73
|
},
|
|
48
74
|
},
|
|
49
75
|
required: ['notebook_path'],
|
package/yeaft/tools/registry.js
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { formatSize } from '../archive/tool-results.js';
|
|
13
|
-
import { getBuiltinToolLocalization } from './localized-descriptions.js';
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* Collaboration tools are mutually exclusive per Yeaft group shape:
|
|
@@ -52,47 +51,36 @@ function normalizeLanguage(language) {
|
|
|
52
51
|
return String(language || '').toLowerCase().startsWith('zh') ? 'zh' : 'en';
|
|
53
52
|
}
|
|
54
53
|
|
|
54
|
+
function isLocalizedTextObject(value) {
|
|
55
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
56
|
+
return ['en', 'zh', 'zh-CN', 'en-US', 'default'].some(key => typeof value[key] === 'string' || typeof value[key] === 'function');
|
|
57
|
+
}
|
|
58
|
+
|
|
55
59
|
function localizeVisibleText(value, language, toolName) {
|
|
56
60
|
const lang = normalizeLanguage(language);
|
|
57
61
|
if (typeof value === 'function') return localizeVisibleText(value(lang), lang, toolName);
|
|
58
|
-
if (value
|
|
62
|
+
if (isLocalizedTextObject(value)) {
|
|
59
63
|
const picked = value[lang] || value[lang === 'zh' ? 'zh-CN' : 'en-US'] || value.en || value.default;
|
|
60
64
|
if (typeof picked === 'string') return picked;
|
|
65
|
+
if (typeof picked === 'function') return localizeVisibleText(picked(lang), lang, toolName);
|
|
61
66
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return [
|
|
66
|
-
`工具说明:${toolName || '该工具'}。请严格按照 schema 调用;工具名、参数名、JSON key 和枚举值保持英文,不要翻译。`,
|
|
67
|
-
`原始协议说明(英文,供精确调用参考):${text}`,
|
|
68
|
-
].join('\n');
|
|
67
|
+
// Legacy single-string descriptions are already authored text. Return them
|
|
68
|
+
// unchanged; do not synthesize zh-prefixed wrappers around English strings.
|
|
69
|
+
return typeof value === 'string' ? value : String(value || '');
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
/**
|
|
72
|
-
* Walk a JSON Schema `parameters` object and localize
|
|
73
|
-
* `description`
|
|
73
|
+
* Walk a JSON Schema `parameters` object and localize human-readable
|
|
74
|
+
* `description` values to the requested language. All other schema bits
|
|
74
75
|
* (`type`, `enum`, `required`, `items`, nested `properties`, etc.) are
|
|
75
76
|
* preserved by value.
|
|
76
77
|
*
|
|
77
78
|
* Critical correctness rule: a JSON Schema can have a *property named*
|
|
78
79
|
* `description` whose value is itself a sub-schema (e.g.
|
|
79
|
-
* `properties: { description: { type: 'string', description:
|
|
80
|
+
* `properties: { description: { type: 'string', description: { en, zh } }}`).
|
|
80
81
|
* In that case the OUTER `description` key holds the sub-schema and must
|
|
81
|
-
* be recursed into; only the INNER `description
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* Previous bug: this walker localized ANY value under a `description`
|
|
85
|
-
* key, including sub-schema objects. `localizeVisibleText` then ran
|
|
86
|
-
* `String(value)` on the object, producing the literal string
|
|
87
|
-
* `'[object Object]'`. GPT-5's strict schema validator rejected the
|
|
88
|
-
* resulting `{ description: '[object Object]' }` with
|
|
89
|
-
* `"'[object Object]' is not of type 'object', 'boolean'"`. This made
|
|
90
|
-
* FeatureCreate (and any tool whose schema contains a property named
|
|
91
|
-
* `description`) unusable in zh locale on strict providers.
|
|
92
|
-
*
|
|
93
|
-
* The fix: only treat a `description` value as localizable text when it
|
|
94
|
-
* is actually a string. Object/array values under a `description` key
|
|
95
|
-
* are sub-schemas and must be recursed into normally.
|
|
82
|
+
* be recursed into; only the INNER bilingual `description` value should
|
|
83
|
+
* be localized.
|
|
96
84
|
*/
|
|
97
85
|
function cloneSchema(value) {
|
|
98
86
|
if (!value || typeof value !== 'object') return value;
|
|
@@ -126,11 +114,11 @@ function applyParameterOverrides(parameters, overrides) {
|
|
|
126
114
|
|
|
127
115
|
function localizeParameters(parameters, language, toolName, parameterOverrides = null) {
|
|
128
116
|
const lang = normalizeLanguage(language);
|
|
129
|
-
if (
|
|
117
|
+
if (!parameters || typeof parameters !== 'object') return parameters;
|
|
130
118
|
if (Array.isArray(parameters)) return parameters.map(v => localizeParameters(v, lang, toolName));
|
|
131
119
|
const out = {};
|
|
132
120
|
for (const [key, value] of Object.entries(parameters)) {
|
|
133
|
-
if (key === 'description' && typeof value === 'string') {
|
|
121
|
+
if (key === 'description' && (typeof value === 'string' || typeof value === 'function' || isLocalizedTextObject(value))) {
|
|
134
122
|
out[key] = localizeVisibleText(value, lang, toolName);
|
|
135
123
|
} else if (value && typeof value === 'object') {
|
|
136
124
|
out[key] = localizeParameters(value, lang, toolName, null);
|
|
@@ -374,11 +362,10 @@ export class ToolRegistry {
|
|
|
374
362
|
return this.getAllTools()
|
|
375
363
|
.filter(t => !isToolHiddenByCollabPolicy(t.name, collabToolPolicy))
|
|
376
364
|
.map(t => {
|
|
377
|
-
const localized = getBuiltinToolLocalization(t.name, lang);
|
|
378
365
|
return {
|
|
379
366
|
name: t.name,
|
|
380
|
-
description:
|
|
381
|
-
parameters: localizeParameters(t.parameters, lang, t.name
|
|
367
|
+
description: localizeVisibleText(t.description, lang, t.name),
|
|
368
|
+
parameters: localizeParameters(t.parameters, lang, t.name),
|
|
382
369
|
};
|
|
383
370
|
});
|
|
384
371
|
}
|