aws-runtime-bridge 1.7.50 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/adapter/AcodeSdkAdapter.d.ts +30 -3
  2. package/dist/adapter/AcodeSdkAdapter.d.ts.map +1 -1
  3. package/dist/adapter/AcodeSdkAdapter.js +461 -105
  4. package/dist/adapter/AcodeSdkAdapter.test.js +1282 -93
  5. package/dist/adapter/ClaudeSdkAdapter.test.js +31 -8
  6. package/dist/adapter/CodexSdkAdapter.d.ts.map +1 -1
  7. package/dist/adapter/CodexSdkAdapter.js +2 -0
  8. package/dist/adapter/OpencodeSdkAdapter.d.ts.map +1 -1
  9. package/dist/adapter/OpencodeSdkAdapter.js +2 -0
  10. package/dist/adapter/SdkProviderSpi.d.ts.map +1 -1
  11. package/dist/adapter/adapter.test.js +2 -2
  12. package/dist/adapter/idle-poll-loop.d.ts +4 -4
  13. package/dist/adapter/idle-poll-loop.d.ts.map +1 -1
  14. package/dist/adapter/idle-poll-loop.js +3 -3
  15. package/dist/adapter/idle-poll-loop.test.js +15 -1
  16. package/dist/adapter/types.d.ts +25 -6
  17. package/dist/adapter/types.d.ts.map +1 -1
  18. package/dist/adapter/types.js +226 -111
  19. package/dist/adapter/types.test.js +10 -3
  20. package/dist/routes/ai-sources.js +5 -1
  21. package/dist/routes/ai-sources.test.js +21 -0
  22. package/dist/routes/instance.d.ts.map +1 -1
  23. package/dist/routes/instance.js +6 -7
  24. package/dist/routes/runtime-binding.js +2 -2
  25. package/dist/routes/terminal.d.ts +22 -5
  26. package/dist/routes/terminal.d.ts.map +1 -1
  27. package/dist/routes/terminal.js +522 -170
  28. package/dist/routes/terminal.test.js +497 -208
  29. package/dist/services/mcp-launch-binding-queue.d.ts +1 -0
  30. package/dist/services/mcp-launch-binding-queue.d.ts.map +1 -1
  31. package/dist/services/mcp-launch-binding-queue.js +8 -2
  32. package/dist/services/mcp-launch-binding-queue.test.js +47 -4
  33. package/dist/services/session-output.d.ts +14 -6
  34. package/dist/services/session-output.d.ts.map +1 -1
  35. package/dist/services/session-output.js +29 -14
  36. package/dist/services/session-output.test.js +93 -39
  37. package/package/acode/dist/built-in-file-tools.d.ts +1 -1
  38. package/package/acode/dist/built-in-file-tools.d.ts.map +1 -1
  39. package/package/acode/dist/built-in-file-tools.js +388 -50
  40. package/package/acode/dist/runtime.d.ts +11 -3
  41. package/package/acode/dist/runtime.d.ts.map +1 -1
  42. package/package/acode/dist/runtime.js +902 -190
  43. package/package/acode/dist/types.d.ts +18 -5
  44. package/package/acode/dist/types.d.ts.map +1 -1
  45. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
  46. package/package/aws-client-agent-mcp/dist/mcp-server.js +3 -2
  47. package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
  48. package/package/aws-client-agent-mcp/dist/mcp-server.test.js +11 -9
  49. package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -1
  50. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts.map +1 -1
  51. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +1 -0
  52. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js.map +1 -1
  53. package/package/aws-client-agent-mcp/dist/status-reporter.d.ts +2 -0
  54. package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +1 -1
  55. package/package/aws-client-agent-mcp/dist/status-reporter.js +31 -3
  56. package/package/aws-client-agent-mcp/dist/status-reporter.js.map +1 -1
  57. package/package/aws-client-agent-mcp/dist/types.d.ts +1 -1
  58. package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -1
  59. package/package/aws-client-agent-mcp/dist/types.js.map +1 -1
  60. package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -1
  61. package/package/aws-client-agent-mcp/dist/websocket-client.js +2 -0
  62. package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -1
  63. package/package/aws-client-agent-mcp/dist/websocket-client.test.js +43 -0
  64. package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +1 -1
  65. package/package.json +1 -1
@@ -4,8 +4,8 @@
4
4
  *
5
5
  * 参考自 SpectrAI 项目的 adapter 设计,用于支持多种 AI Provider
6
6
  */
7
- import { EventEmitter } from 'node:events';
8
- const TEMP_CLIPBOARD_PASTE_PLACEHOLDER = '[temp clipboard paste file]';
7
+ import { EventEmitter } from "node:events";
8
+ const TEMP_CLIPBOARD_PASTE_PLACEHOLDER = "[temp clipboard paste file]";
9
9
  const WINDOWS_TEMP_CLIPBOARD_PASTE_PATH = /[A-Z]:(?:\\\\|\\)[^\r\n"'`]*?(?:\\\\|\\)mini-term-clipboard(?:\\\\|\\)paste-\d+\.txt/gi;
10
10
  const POSIX_TEMP_CLIPBOARD_PASTE_PATH = /\/[^\r\n"'`]*?\/mini-term-clipboard\/paste-\d+\.txt/gi;
11
11
  const BARE_TEMP_CLIPBOARD_PASTE_PATH = /mini-term-clipboard[\\/]paste-\d+\.txt/gi;
@@ -24,8 +24,12 @@ export function redactTransientClipboardPastePaths(text) {
24
24
  * 主流程:剥离包裹引号 -> 要求整串匹配,避免误拦截显式读取该文件的正常命令。
25
25
  */
26
26
  export function isTransientClipboardPasteFilePath(text) {
27
- const trimmed = text.trim().replace(/^(["'])(.*)\1$/, '$2');
28
- return [WINDOWS_TEMP_CLIPBOARD_PASTE_PATH, POSIX_TEMP_CLIPBOARD_PASTE_PATH, BARE_TEMP_CLIPBOARD_PASTE_PATH].some((pattern) => {
27
+ const trimmed = text.trim().replace(/^(["'])(.*)\1$/, "$2");
28
+ return [
29
+ WINDOWS_TEMP_CLIPBOARD_PASTE_PATH,
30
+ POSIX_TEMP_CLIPBOARD_PASTE_PATH,
31
+ BARE_TEMP_CLIPBOARD_PASTE_PATH,
32
+ ].some((pattern) => {
29
33
  pattern.lastIndex = 0;
30
34
  const match = pattern.exec(trimmed);
31
35
  return Boolean(match && match[0] === trimmed);
@@ -35,13 +39,13 @@ export function isTransientClipboardPasteFilePath(text) {
35
39
  * 递归脱敏工具展示参数,保留真实执行输入不变,仅用于日志/状态/时间线展示。
36
40
  */
37
41
  export function redactTransientClipboardPastePathsInValue(value) {
38
- if (typeof value === 'string') {
42
+ if (typeof value === "string") {
39
43
  return redactTransientClipboardPastePaths(value);
40
44
  }
41
45
  if (Array.isArray(value)) {
42
46
  return value.map((item) => redactTransientClipboardPastePathsInValue(item));
43
47
  }
44
- if (value && typeof value === 'object') {
48
+ if (value && typeof value === "object") {
45
49
  return Object.fromEntries(Object.entries(value).map(([key, item]) => [
46
50
  key,
47
51
  redactTransientClipboardPastePathsInValue(item),
@@ -57,23 +61,63 @@ export function redactTransientClipboardPastePathsInValue(value) {
57
61
  */
58
62
  const KNOWN_MCP_TOOLS = {
59
63
  // aws-mcp 工具
60
- 'get_colleague': 'aws-mcp',
61
- 'get_group_rooms': 'aws-mcp',
62
- 'get_profile': 'aws-mcp',
63
- 'get_status': 'aws-mcp',
64
- 'hire_colleague': 'aws-mcp',
65
- 'hire_colleagues_batch': 'aws-mcp',
66
- 'send_group': 'aws-mcp',
67
- 'send_private': 'aws-mcp',
68
- 'poll_message': 'aws-mcp',
69
- 'get_message': 'aws-mcp',
70
- 'get_dm_history': 'aws-mcp',
71
- 'get_group_history': 'aws-mcp',
72
- 'my_task': 'aws-mcp',
73
- 'submit_task_result': 'aws-mcp',
74
- 'reject_task': 'aws-mcp',
75
- 'terminate_task': 'aws-mcp',
64
+ get_colleague: "aws-mcp",
65
+ get_group_rooms: "aws-mcp",
66
+ get_profile: "aws-mcp",
67
+ get_status: "aws-mcp",
68
+ hire_colleague: "aws-mcp",
69
+ hire_colleagues_batch: "aws-mcp",
70
+ send_group: "aws-mcp",
71
+ send_private: "aws-mcp",
72
+ poll_message: "aws-mcp",
73
+ get_message: "aws-mcp",
74
+ get_dm_history: "aws-mcp",
75
+ get_group_history: "aws-mcp",
76
+ my_task: "aws-mcp",
77
+ submit_task_result: "aws-mcp",
78
+ reject_task: "aws-mcp",
79
+ terminate_task: "aws-mcp",
76
80
  };
81
+ const AGENTSWORK_MCP_SERVER_ALIASES = new Set([
82
+ "aws-mcp",
83
+ "agents-work-studio",
84
+ ]);
85
+ function canonicalizeMcpServer(serverName, toolName) {
86
+ const knownMcpServer = KNOWN_MCP_TOOLS[toolName.toLowerCase()];
87
+ if (knownMcpServer && AGENTSWORK_MCP_SERVER_ALIASES.has(serverName.toLowerCase())) {
88
+ return knownMcpServer;
89
+ }
90
+ return serverName;
91
+ }
92
+ export function getCanonicalMcpDetail(rawToolName) {
93
+ const normalizedRawToolName = rawToolName.trim();
94
+ if (!normalizedRawToolName)
95
+ return normalizedRawToolName;
96
+ if (normalizedRawToolName.includes("__")) {
97
+ const parts = normalizedRawToolName.split("__").filter(Boolean);
98
+ if (parts.length >= 3 && parts[0].toLowerCase() === "mcp") {
99
+ const serverName = parts[1];
100
+ const toolName = parts.slice(2).join("__");
101
+ return `${canonicalizeMcpServer(serverName, toolName)}/${toolName}`;
102
+ }
103
+ if (parts.length >= 2) {
104
+ const serverName = parts[0];
105
+ const toolName = parts.slice(1).join("__");
106
+ return `${canonicalizeMcpServer(serverName, toolName)}/${toolName}`;
107
+ }
108
+ }
109
+ const knownMcpServer = KNOWN_MCP_TOOLS[normalizedRawToolName.toLowerCase()];
110
+ return knownMcpServer
111
+ ? `${knownMcpServer}/${normalizedRawToolName}`
112
+ : normalizedRawToolName;
113
+ }
114
+ function formatCanonicalMcpDetail(serverName, toolName) {
115
+ if (!serverName)
116
+ return toolName;
117
+ if (!toolName)
118
+ return serverName;
119
+ return `${canonicalizeMcpServer(serverName, toolName)}/${toolName}`;
120
+ }
77
121
  /**
78
122
  * 根据工具名称获取动作类型信息
79
123
  * @param toolName 工具名称
@@ -82,32 +126,36 @@ const KNOWN_MCP_TOOLS = {
82
126
  */
83
127
  export function getToolActionInfo(toolName, toolInput, metadata) {
84
128
  if (!toolName) {
85
- return { actionType: 'other', actionLabel: '调用工具' };
129
+ return { actionType: "other", actionLabel: "调用工具" };
86
130
  }
87
131
  const name = toolName.toLowerCase();
88
- const normalizedName = name.replace(/[_-]/g, '');
89
- const semanticToolName = name.startsWith('mcp__')
90
- ? name.split('__').slice(2).join('__')
91
- : name.includes('-mcp__')
92
- ? name.slice(name.indexOf('-mcp__') + 6)
93
- : name;
94
- const normalizedSemanticToolName = semanticToolName.replace(/[_-]/g, '');
132
+ const normalizedName = name.replace(/[_-]/g, "");
133
+ const semanticToolName = name.startsWith("mcp__")
134
+ ? name.split("__").slice(2).join("__")
135
+ : name.startsWith("builtin__")
136
+ ? name.split("__").slice(1).join("__")
137
+ : name.includes("-mcp__")
138
+ ? name.slice(name.indexOf("-mcp__") + 6)
139
+ : name.includes("/")
140
+ ? name.split("/").filter(Boolean).at(-1) || name
141
+ : name;
142
+ const normalizedSemanticToolName = semanticToolName.replace(/[_-]/g, "");
95
143
  const normalizeSkillList = (value) => {
96
144
  if (!Array.isArray(value)) {
97
145
  return [];
98
146
  }
99
147
  return value
100
- .map((item) => String(item ?? '').trim())
148
+ .map((item) => String(item ?? "").trim())
101
149
  .filter((item) => item.length > 0);
102
150
  };
103
151
  const firstString = (...values) => {
104
152
  for (const value of values) {
105
- if (typeof value === 'string' && value.trim()) {
153
+ if (typeof value === "string" && value.trim()) {
106
154
  return redactTransientClipboardPastePaths(value);
107
155
  }
108
156
  if (Array.isArray(value) && value.length > 0) {
109
157
  const first = value[0];
110
- if (typeof first === 'string' && first.trim()) {
158
+ if (typeof first === "string" && first.trim()) {
111
159
  return redactTransientClipboardPastePaths(first);
112
160
  }
113
161
  }
@@ -115,17 +163,16 @@ export function getToolActionInfo(toolName, toolInput, metadata) {
115
163
  return undefined;
116
164
  };
117
165
  const matchesToolName = (...toolNames) => {
118
- const normalizedToolNames = toolNames.map((item) => item.replace(/[_-]/g, ''));
119
- return normalizedToolNames.includes(normalizedName) || normalizedToolNames.includes(normalizedSemanticToolName);
166
+ const normalizedToolNames = toolNames.map((item) => item.replace(/[_-]/g, ""));
167
+ return (normalizedToolNames.includes(normalizedName) ||
168
+ normalizedToolNames.includes(normalizedSemanticToolName));
120
169
  };
121
- // 调试日志:记录工具名
122
- console.log(`[getToolActionInfo] toolName="${toolName}", name="${name}"`);
123
170
  const loadSkills = normalizeSkillList(toolInput?.load_skills);
124
- if ((name === 'task' || name === 'task()') && loadSkills.length > 0) {
171
+ if ((name === "task" || name === "task()") && loadSkills.length > 0) {
125
172
  return {
126
- actionType: 'skill',
127
- actionLabel: '加载技能',
128
- actionDetail: loadSkills.join(', '),
173
+ actionType: "skill",
174
+ actionLabel: "加载技能",
175
+ actionDetail: loadSkills.join(", "),
129
176
  };
130
177
  }
131
178
  // Skill 调用
@@ -136,62 +183,94 @@ export function getToolActionInfo(toolName, toolInput, metadata) {
136
183
  //
137
184
  // 注意:SDK 发送的 tool_name 可能是 'Skill'(大写)或 'skill'(小写)
138
185
  // 使用 toLowerCase() 统一处理
139
- const isSkillTool = name === 'skill' ||
140
- name === 'load_skill' ||
141
- name === 'disclosecontext' ||
142
- (name.includes('skill') && !name.startsWith('mcp__') && !name.includes('mcp_'));
143
- console.log(`[getToolActionInfo] Skill detection: name="${name}", isSkillTool=${isSkillTool}`);
186
+ const isSkillTool = name === "skill" ||
187
+ name === "load_skill" ||
188
+ name === "disclosecontext" ||
189
+ (name.includes("skill") &&
190
+ !name.startsWith("mcp__") &&
191
+ !name.includes("mcp_"));
144
192
  if (isSkillTool) {
145
193
  // Skill 参数可能有多种格式:
146
194
  // 1. { skill: "skillName" }
147
195
  // 2. { name: "skillName" }
148
196
  // 3. { args: { name: "skillName", ... } } // task() 格式
149
197
  // 4. { load_skills: ["skill1", "skill2"] } // task() 加载技能格式
150
- let skillName = '';
198
+ let skillName = "";
151
199
  // 检查 load_skills 格式(task 工具)
152
- if (Array.isArray(toolInput?.load_skills) && toolInput.load_skills.length > 0) {
153
- skillName = toolInput.load_skills.join(', ');
200
+ if (Array.isArray(toolInput?.load_skills) &&
201
+ toolInput.load_skills.length > 0) {
202
+ skillName = toolInput.load_skills.join(", ");
154
203
  }
155
204
  // 检查 skill 参数
156
- else if (typeof toolInput?.skill === 'string') {
205
+ else if (typeof toolInput?.skill === "string") {
157
206
  skillName = toolInput.skill;
158
207
  }
159
208
  // 检查 name 参数
160
- else if (typeof toolInput?.name === 'string') {
209
+ else if (typeof toolInput?.name === "string") {
161
210
  skillName = toolInput.name;
162
211
  }
163
212
  // 检查嵌套的 args.name
164
- else if (toolInput?.args && typeof toolInput.args === 'object') {
213
+ else if (toolInput?.args && typeof toolInput.args === "object") {
165
214
  const args = toolInput.args;
166
- if (typeof args.name === 'string') {
215
+ if (typeof args.name === "string") {
167
216
  skillName = args.name;
168
217
  }
169
- else if (typeof args.skill === 'string') {
218
+ else if (typeof args.skill === "string") {
170
219
  skillName = args.skill;
171
220
  }
172
221
  }
173
- console.log(`[getToolActionInfo] Detected Skill tool: skillName="${skillName}", toolInput=${JSON.stringify(toolInput)}`);
174
- return { actionType: 'skill', actionLabel: '使用技能', actionDetail: skillName || undefined };
222
+ return {
223
+ actionType: "skill",
224
+ actionLabel: "使用技能",
225
+ actionDetail: skillName || undefined,
226
+ };
175
227
  }
176
228
  // 文件/命令工具即使走 MCP 命名,也优先按语义显示为读取/编辑/命令执行。
177
- if (matchesToolName('edit', 'editfile', 'strreplace', 'editcode', 'apply_patch', 'multiedit', 'replace')) {
229
+ if (matchesToolName("edit", "editfile", "strreplace", "editcode", "apply_patch", "multiedit", "replace")) {
178
230
  const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.target_file, toolInput?.targetFile);
179
- return { actionType: 'edit_file', actionLabel: '编辑文件', actionDetail: filePath };
231
+ return {
232
+ actionType: "edit_file",
233
+ actionLabel: "编辑文件",
234
+ actionDetail: filePath,
235
+ };
180
236
  }
181
- if (matchesToolName('write', 'writefile', 'fswrite')) {
237
+ if (matchesToolName("write", "writefile", "fswrite")) {
182
238
  const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.target_file, toolInput?.targetFile);
183
- return { actionType: 'write_file', actionLabel: '写入文件', actionDetail: filePath };
239
+ return {
240
+ actionType: "write_file",
241
+ actionLabel: "写入文件",
242
+ actionDetail: filePath,
243
+ };
184
244
  }
185
- if (matchesToolName('read', 'readfile', 'readcode', 'readmultiplefiles', 'read_file')) {
245
+ if (matchesToolName("read", "readfile", "readcode", "readmultiplefiles", "read_file")) {
186
246
  if (Array.isArray(toolInput?.paths)) {
187
- return { actionType: 'read_file', actionLabel: '读取文件', actionDetail: `${toolInput.paths.length}个文件` };
247
+ return {
248
+ actionType: "read_file",
249
+ actionLabel: "读取文件",
250
+ actionDetail: `${toolInput.paths.length}个文件`,
251
+ };
188
252
  }
189
253
  const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.paths, toolInput?.target_file, toolInput?.targetFile);
190
- return { actionType: 'read_file', actionLabel: '读取文件', actionDetail: filePath };
254
+ return {
255
+ actionType: "read_file",
256
+ actionLabel: "读取文件",
257
+ actionDetail: filePath,
258
+ };
191
259
  }
192
- if (matchesToolName('bash', 'shell', 'command', 'executepwsh', 'controlpwshprocess', 'execute', 'run_command', 'execute_command')) {
260
+ if (matchesToolName("bash", "shell", "command", "executepwsh", "controlpwshprocess", "execute", "run_command", "execute_command")) {
193
261
  const command = firstString(toolInput?.command, toolInput?.cmd, toolInput?.argv);
194
- return { actionType: 'bash', actionLabel: '执行命令', actionDetail: command?.slice(0, 50) };
262
+ return {
263
+ actionType: "bash",
264
+ actionLabel: "执行命令",
265
+ actionDetail: command?.slice(0, 50),
266
+ };
267
+ }
268
+ if (matchesToolName("poll_message", "get_message")) {
269
+ return {
270
+ actionType: "idle",
271
+ actionLabel: "监听消息",
272
+ actionDetail: getCanonicalMcpDetail(toolName),
273
+ };
195
274
  }
196
275
  // MCP 工具调用
197
276
  // 优先使用 metadata 中的 mcpServer 信息(SDK 直接提供)
@@ -202,101 +281,137 @@ export function getToolActionInfo(toolName, toolInput, metadata) {
202
281
  // 4. xxx-mcp__tool (自定义格式,如 aws-mcp__get_profile)
203
282
  // 5. 包含 'mcp_' 的工具名
204
283
  // 6. Kiro 的 kiroPowers
205
- //
284
+ //
206
285
  // actionDetail 格式: server/tool(包含 MCP server 名称,便于用户识别工具来源)
207
286
  // 优先检查 metadata 中的 MCP server 信息
208
287
  if (metadata?.mcpServer) {
209
288
  const mcpServer = metadata.mcpServer;
210
- const mcpTool = toolName || '';
211
- const detail = mcpTool ? `${mcpServer}/${mcpTool}` : mcpServer;
212
- console.log(`[getToolActionInfo] MCP from metadata: server="${mcpServer}", tool="${mcpTool}"`);
213
- return { actionType: 'mcp', actionLabel: '调用MCP', actionDetail: detail };
289
+ const mcpTool = toolName || "";
290
+ const detail = formatCanonicalMcpDetail(mcpServer, mcpTool);
291
+ return { actionType: "mcp", actionLabel: "调用MCP", actionDetail: detail };
214
292
  }
215
293
  // 检查是否在已知 MCP 工具列表中
216
294
  const knownMcpServer = KNOWN_MCP_TOOLS[toolName];
217
295
  if (knownMcpServer) {
218
296
  const detail = `${knownMcpServer}/${toolName}`;
219
- console.log(`[getToolActionInfo] MCP from known list: server="${knownMcpServer}", tool="${toolName}"`);
220
- return { actionType: 'mcp', actionLabel: '调用MCP', actionDetail: detail };
297
+ return { actionType: "mcp", actionLabel: "调用MCP", actionDetail: detail };
221
298
  }
222
- const isMcpTool = name.startsWith('mcp__') ||
223
- name.includes('-mcp__') ||
224
- (name.includes('mcp_') && !name.includes('skill')) ||
225
- name === 'kiropowers';
299
+ const isMcpTool = name.startsWith("mcp__") ||
300
+ name.includes("-mcp__") ||
301
+ (name.includes("mcp_") && !name.includes("skill")) ||
302
+ name === "kiropowers";
226
303
  if (isMcpTool) {
227
- let mcpServer = '';
228
- let mcpTool = '';
229
- if (name.startsWith('mcp__')) {
304
+ let mcpServer = "";
305
+ let mcpTool = "";
306
+ if (name.startsWith("mcp__")) {
230
307
  // 标准格式: mcp__server__tool
231
- const parts = toolName.split('__');
232
- mcpServer = parts[1] || '';
233
- mcpTool = parts.slice(2).join('__') || '';
308
+ const parts = toolName.split("__");
309
+ mcpServer = parts[1] || "";
310
+ mcpTool = parts.slice(2).join("__") || "";
234
311
  }
235
- else if (name.includes('-mcp__')) {
312
+ else if (name.includes("-mcp__")) {
236
313
  // 自定义格式: xxx-mcp__tool (如 aws-mcp__get_profile)
237
- const idx = name.indexOf('-mcp__');
314
+ const idx = name.indexOf("-mcp__");
238
315
  mcpServer = name.substring(0, idx + 4); // "xxx-mcp"(保留完整 server 名)
239
316
  mcpTool = name.substring(idx + 6); // 去掉 "-mcp__" 前缀,得到工具名
240
317
  }
241
- else if (name === 'kiropowers') {
242
- mcpServer = 'kiro';
243
- mcpTool = (toolInput?.powerName || toolInput?.action) || '';
318
+ else if (name === "kiropowers") {
319
+ mcpServer = "kiro";
320
+ mcpTool = (toolInput?.powerName || toolInput?.action) || "";
244
321
  }
245
322
  else {
246
323
  // 其他包含 mcp_ 的格式
247
- mcpServer = 'mcp';
324
+ mcpServer = "mcp";
248
325
  mcpTool = toolName;
249
326
  }
250
327
  // 格式: server/tool(便于用户识别 MCP server 来源)
251
- const detail = mcpServer && mcpTool ? `${mcpServer}/${mcpTool}` : (mcpTool || toolName);
252
- return { actionType: 'mcp', actionLabel: '调用MCP', actionDetail: detail };
253
- }
254
- if (matchesToolName('poll_message', 'get_message')) {
255
- return { actionType: 'idle', actionLabel: '监听消息', actionDetail: toolName };
328
+ const detail = formatCanonicalMcpDetail(mcpServer, mcpTool) || toolName;
329
+ return { actionType: "mcp", actionLabel: "调用MCP", actionDetail: detail };
256
330
  }
257
331
  // 文件编辑 (Kiro: strReplace, editCode)
258
- if (matchesToolName('edit', 'editfile', 'strreplace', 'editcode', 'apply_patch', 'multiedit', 'replace')) {
332
+ if (matchesToolName("edit", "editfile", "strreplace", "editcode", "apply_patch", "multiedit", "replace")) {
259
333
  const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.target_file, toolInput?.targetFile);
260
- return { actionType: 'edit_file', actionLabel: '编辑文件', actionDetail: filePath };
334
+ return {
335
+ actionType: "edit_file",
336
+ actionLabel: "编辑文件",
337
+ actionDetail: filePath,
338
+ };
261
339
  }
262
340
  // 文件写入 (Kiro: fsWrite)
263
- if (matchesToolName('write', 'writefile', 'fswrite')) {
341
+ if (matchesToolName("write", "writefile", "fswrite")) {
264
342
  const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.target_file, toolInput?.targetFile);
265
- return { actionType: 'write_file', actionLabel: '写入文件', actionDetail: filePath };
343
+ return {
344
+ actionType: "write_file",
345
+ actionLabel: "写入文件",
346
+ actionDetail: filePath,
347
+ };
266
348
  }
267
349
  // 文件读取 (Kiro: readFile, readCode, readMultipleFiles)
268
- if (matchesToolName('read', 'readfile', 'readcode', 'readmultiplefiles', 'read_file')) {
350
+ if (matchesToolName("read", "readfile", "readcode", "readmultiplefiles", "read_file")) {
269
351
  if (Array.isArray(toolInput?.paths)) {
270
- return { actionType: 'read_file', actionLabel: '读取文件', actionDetail: `${toolInput.paths.length}个文件` };
352
+ return {
353
+ actionType: "read_file",
354
+ actionLabel: "读取文件",
355
+ actionDetail: `${toolInput.paths.length}个文件`,
356
+ };
271
357
  }
272
358
  const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.paths, toolInput?.target_file, toolInput?.targetFile);
273
- return { actionType: 'read_file', actionLabel: '读取文件', actionDetail: filePath };
359
+ return {
360
+ actionType: "read_file",
361
+ actionLabel: "读取文件",
362
+ actionDetail: filePath,
363
+ };
274
364
  }
275
365
  // Bash 命令 (Kiro: executePwsh, controlPwshProcess)
276
- if (matchesToolName('bash', 'shell', 'command', 'executepwsh', 'controlpwshprocess', 'execute', 'run_command', 'execute_command')) {
366
+ if (matchesToolName("bash", "shell", "command", "executepwsh", "controlpwshprocess", "execute", "run_command", "execute_command")) {
277
367
  const command = firstString(toolInput?.command, toolInput?.cmd, toolInput?.argv);
278
- return { actionType: 'bash', actionLabel: '执行命令', actionDetail: command?.slice(0, 50) };
368
+ return {
369
+ actionType: "bash",
370
+ actionLabel: "执行命令",
371
+ actionDetail: command?.slice(0, 50),
372
+ };
279
373
  }
280
374
  // 文件搜索 (Kiro: grepSearch, fileSearch)
281
- if (name === 'glob' || name === 'grep' || name === 'find' || name === 'search' || name === 'grepsearch' || name === 'filesearch') {
375
+ if (name === "glob" ||
376
+ name === "grep" ||
377
+ name === "find" ||
378
+ name === "search" ||
379
+ name === "grepsearch" ||
380
+ name === "filesearch") {
282
381
  const query = (toolInput?.query || toolInput?.pattern);
283
- return { actionType: 'search', actionLabel: '搜索文件', actionDetail: query?.slice(0, 30) };
382
+ return {
383
+ actionType: "search",
384
+ actionLabel: "搜索文件",
385
+ actionDetail: query?.slice(0, 30),
386
+ };
284
387
  }
285
388
  // 网络操作 (Kiro: webFetch, remote_web_search)
286
- if (name === 'webfetch' || name === 'websearch' || name === 'fetch' || name === 'http' || name === 'remote_web_search') {
389
+ if (name === "webfetch" ||
390
+ name === "websearch" ||
391
+ name === "fetch" ||
392
+ name === "http" ||
393
+ name === "remote_web_search") {
287
394
  const url = (toolInput?.url || toolInput?.query);
288
- return { actionType: 'web', actionLabel: '网络请求', actionDetail: url?.slice(0, 50) };
395
+ return {
396
+ actionType: "web",
397
+ actionLabel: "网络请求",
398
+ actionDetail: url?.slice(0, 50),
399
+ };
289
400
  }
290
401
  // LSP 操作
291
- if (name.startsWith('lsp') || name.includes('language_server')) {
292
- return { actionType: 'lsp', actionLabel: 'LSP操作' };
402
+ if (name.startsWith("lsp") || name.includes("language_server")) {
403
+ return { actionType: "lsp", actionLabel: "LSP操作" };
293
404
  }
294
405
  // Notebook 编辑
295
- if (name === 'notebookedit' || name.includes('notebook')) {
296
- return { actionType: 'notebook', actionLabel: '编辑Notebook' };
406
+ if (name === "notebookedit" || name.includes("notebook")) {
407
+ return { actionType: "notebook", actionLabel: "编辑Notebook" };
297
408
  }
298
409
  // 其他工具
299
- return { actionType: 'other', actionLabel: '调用工具', actionDetail: toolName };
410
+ return {
411
+ actionType: "other",
412
+ actionLabel: "调用工具",
413
+ actionDetail: toolName,
414
+ };
300
415
  }
301
416
  // ============ Adapter 基类 ============
302
417
  /**
@@ -1,10 +1,17 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import { getToolActionInfo, isTransientClipboardPasteFilePath, redactTransientClipboardPastePaths, redactTransientClipboardPastePathsInValue, } from './types.js';
3
3
  describe('getToolActionInfo', () => {
4
- it('reports poll_message as a normal MCP tool call', () => {
4
+ it('reports poll_message as an idle MCP listening tool call', () => {
5
5
  expect(getToolActionInfo('poll_message')).toEqual({
6
- actionType: 'mcp',
7
- actionLabel: '调用MCP',
6
+ actionType: 'idle',
7
+ actionLabel: '监听消息',
8
+ actionDetail: 'aws-mcp/poll_message',
9
+ });
10
+ });
11
+ it('reports prefixed poll_message as a canonical idle MCP listening tool call', () => {
12
+ expect(getToolActionInfo('aws-mcp__poll_message')).toEqual({
13
+ actionType: 'idle',
14
+ actionLabel: '监听消息',
8
15
  actionDetail: 'aws-mcp/poll_message',
9
16
  });
10
17
  });
@@ -59,7 +59,7 @@ async function fetchRemoteModelList(baseUrl, apiKey) {
59
59
  return normalizeListedModels(anthropicResponse.data);
60
60
  }
61
61
  aiSourcesRouter.post('/cc-switch/apply-ai-sources', validateToken, async (req, res) => {
62
- const { agentId, template, ccSwitchEnabledTools, acodeTemplateId, claudecodeTemplateId, codexTemplateId, opencodeTemplateId, acode, claudecode, codex, opencode, } = req.body || {};
62
+ const { agentId, template, ccSwitchEnabledTools, acodeTemplateId, claudecodeTemplateId, codexTemplateId, opencodeTemplateId, acodeModelEffort, claudecodeModelEffort, codexModelEffort, opencodeModelEffort, acode, claudecode, codex, opencode, } = req.body || {};
63
63
  if (!agentId) {
64
64
  res.status(400).json({ error: 'agentId is required' });
65
65
  return;
@@ -81,6 +81,10 @@ aiSourcesRouter.post('/cc-switch/apply-ai-sources', validateToken, async (req, r
81
81
  claudecodeTemplateId: String(claudecodeTemplateId || ''),
82
82
  codexTemplateId: String(codexTemplateId || ''),
83
83
  opencodeTemplateId: String(opencodeTemplateId || ''),
84
+ acodeModelEffort: String(acodeModelEffort || ''),
85
+ claudecodeModelEffort: String(claudecodeModelEffort || ''),
86
+ codexModelEffort: String(codexModelEffort || ''),
87
+ opencodeModelEffort: String(opencodeModelEffort || ''),
84
88
  generatedFromUnifiedTemplate: appliedSummary.generatedFromUnifiedTemplate,
85
89
  applied: appliedSummary.applied,
86
90
  skippedTools: appliedSummary.skippedTools,
@@ -76,6 +76,14 @@ function buildModelListCandidates(baseUrl, protocol) {
76
76
  : [`${normalizedBaseUrl}/models`, `${normalizedBaseUrl}/v1/models`];
77
77
  return [...openAiCandidates, `${normalizedBaseUrl}/v1/models`];
78
78
  }
79
+ function buildApplyAiSourcesResponse(body) {
80
+ return {
81
+ acodeModelEffort: String(body.acodeModelEffort || ''),
82
+ claudecodeModelEffort: String(body.claudecodeModelEffort || ''),
83
+ codexModelEffort: String(body.codexModelEffort || ''),
84
+ opencodeModelEffort: String(body.opencodeModelEffort || ''),
85
+ };
86
+ }
79
87
  describe('ai source unified config generation', () => {
80
88
  it('builds tool-specific configs from unified template fields', () => {
81
89
  assert.deepEqual(buildUnifiedToolConfig({
@@ -145,4 +153,17 @@ describe('ai source unified config generation', () => {
145
153
  model: 'gpt-test',
146
154
  }).opencode.provider.default.options.baseURL).toBe('https://octopus.721579.xyz/v1');
147
155
  });
156
+ it('echoes selected model effort metadata in apply-ai-sources responses', () => {
157
+ expect(buildApplyAiSourcesResponse({
158
+ acodeModelEffort: 'xhigh',
159
+ claudecodeModelEffort: 'high',
160
+ codexModelEffort: 'max',
161
+ opencodeModelEffort: 'high',
162
+ })).toEqual({
163
+ acodeModelEffort: 'xhigh',
164
+ claudecodeModelEffort: 'high',
165
+ codexModelEffort: 'max',
166
+ opencodeModelEffort: 'high',
167
+ });
168
+ });
148
169
  });
@@ -1 +1 @@
1
- {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/routes/instance.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAoBrD,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAC/D,IAAI,CAEN;AAED,eAAO,MAAM,cAAc,4CAAW,CAAC;AAsJvC,wBAAgB,4BAA4B,CAAC,gBAAgB,EAAE,MAAM,GAAG;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QACJ,EAAE,EAAE,OAAO,CAAC;QACZ,aAAa,CAAC,EAAE,SAAS,CAAC;QAC1B,oBAAoB,EAAE,OAAO,CAAC;QAC9B,qBAAqB,EAAE,OAAO,CAAC;QAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAqCA;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,SAAS,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,iCAAiC;IACzC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,8BAA8B,CAAC;IACtC,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,SAAS,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,KAAK,EACZ,0BAA0B,EAAE,MAAM,GACjC,4BAA4B,CAiB9B;AAoDD;;;GAGG;AACH,wBAAgB,sCAAsC,CACpD,UAAU,EAAE,MAAM,EAAE,GACnB,iCAAiC,CAUnC;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAQhF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,cAAc,EAAE,MAAM,EAAE,EACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC5C,MAAM,CAaR;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC5C,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,SAAS,GAAG,YAAY,GAAG,OAAO,CAAC,CAAC,CAY3F"}
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/routes/instance.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqBrD,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAC/D,IAAI,CAEN;AAED,eAAO,MAAM,cAAc,4CAAW,CAAC;AAsJvC,wBAAgB,4BAA4B,CAAC,gBAAgB,EAAE,MAAM,GAAG;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QACJ,EAAE,EAAE,OAAO,CAAC;QACZ,aAAa,CAAC,EAAE,SAAS,CAAC;QAC1B,oBAAoB,EAAE,OAAO,CAAC;QAC9B,qBAAqB,EAAE,OAAO,CAAC;QAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAqCA;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,SAAS,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,iCAAiC;IACzC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,8BAA8B,CAAC;IACtC,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,SAAS,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,KAAK,EACZ,0BAA0B,EAAE,MAAM,GACjC,4BAA4B,CAiB9B;AAoDD;;;GAGG;AACH,wBAAgB,sCAAsC,CACpD,UAAU,EAAE,MAAM,EAAE,GACnB,iCAAiC,CAUnC;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAQhF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,cAAc,EAAE,MAAM,EAAE,EACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC5C,MAAM,CAaR;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC5C,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,SAAS,GAAG,YAAY,GAAG,OAAO,CAAC,CAAC,CAY3F"}
@@ -2,16 +2,15 @@ import { Router } from "express";
2
2
  import axios from "axios";
3
3
  import { spawn } from "node:child_process";
4
4
  import { createHash, timingSafeEqual } from "node:crypto";
5
- import { validateToken } from "../middleware/auth.js";
6
5
  import { resolveSchedulerBaseUrlFrom } from "../config.js";
7
- import { getRuntimeAccessToken } from "../services/runtime-binding.js";
8
- import { loadInstanceState, saveInstanceState, } from "../services/instance-state.js";
9
- import { initInstance } from "../services/instance-init-service.js";
10
- import { discoverCcSwitchConfiguredItems, loadCcSwitchSdk } from "../services/cc-switch-sdk.js";
11
- import { syncLegacyStateFromSdk } from "../services/instance-init-service.js";
12
- import { detectToolStatuses, ensureToolsInstalled, SUPPORTED_INSTALLABLE_TOOLS, SUPPORTED_UNINSTALLABLE_TOOLS, uninstallTools, } from "../services/tool-installer.js";
6
+ import { validateToken } from "../middleware/auth.js";
13
7
  import { getConfiguredConnectionKeys, requestRuntimeAccessTokenRefreshForServer } from "../services/auto-register.js";
8
+ import { discoverCcSwitchConfiguredItems, loadCcSwitchSdk } from "../services/cc-switch-sdk.js";
9
+ import { initInstance, syncLegacyStateFromSdk } from "../services/instance-init-service.js";
14
10
  import { createBridgeLifecycleOperation, deliverPendingBridgeLifecycleNotifications, upsertBridgeLifecycleOperation, } from "../services/lifecycle-state.js";
11
+ import { loadInstanceState, saveInstanceState, } from "../services/instance-state.js";
12
+ import { getRuntimeAccessToken } from "../services/runtime-binding.js";
13
+ import { detectToolStatuses, ensureToolsInstalled, SUPPORTED_INSTALLABLE_TOOLS, SUPPORTED_UNINSTALLABLE_TOOLS, uninstallTools, } from "../services/tool-installer.js";
15
14
  import { createLogger } from "../utils/logger.js";
16
15
  const log = createLogger("instance");
17
16
  const PANEL_TOOL_STATUS_KEYS = ["acode", "claude", "opencode", "codex"];
@@ -102,7 +102,7 @@ runtimeBindingRouter.post("/binding/claim-agent", (req, res) => {
102
102
  res.status(403).json({ ok: false, error: "loopback request required" });
103
103
  return;
104
104
  }
105
- const { workspacePath, serverUrl } = req.body || {};
105
+ const { agentId, workspacePath, serverUrl } = req.body || {};
106
106
  if (!workspacePath || !String(workspacePath).trim()) {
107
107
  res.status(400).json({ ok: false, error: "workspacePath is required" });
108
108
  return;
@@ -111,7 +111,7 @@ runtimeBindingRouter.post("/binding/claim-agent", (req, res) => {
111
111
  res.status(400).json({ ok: false, error: "serverUrl is required" });
112
112
  return;
113
113
  }
114
- const claimed = claimMcpLaunchBinding({ workspacePath, serverUrl });
114
+ const claimed = claimMcpLaunchBinding({ agentId, workspacePath, serverUrl });
115
115
  if (!claimed) {
116
116
  res.status(404).json({
117
117
  ok: false,