mu-agent-bridge 1.0.71 → 1.0.78

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 (195) hide show
  1. package/README.md +17 -10
  2. package/dist/src-ts/agent.js +35 -0
  3. package/dist/src-ts/channel-sessions.js +3 -2
  4. package/dist/src-ts/cli.js +67 -37
  5. package/dist/src-ts/control-server.js +12 -30
  6. package/dist/src-ts/core-updater.js +141 -33
  7. package/dist/src-ts/daemon-process.js +2 -1
  8. package/dist/src-ts/dsh-agent.js +31 -13
  9. package/dist/src-ts/http-agent.js +36 -0
  10. package/dist/src-ts/plugin-catalog.js +11 -2
  11. package/dist/src-ts/plugin-manager.js +351 -50
  12. package/dist/src-ts/plugins/abweb/index.js +1 -1
  13. package/dist/src-ts/plugins/abweb/public/app.js +73 -5
  14. package/dist/src-ts/plugins/abweb/public/index.html +15 -0
  15. package/dist/src-ts/plugins/abweb/public/style.css +4 -0
  16. package/dist/src-ts/plugins/aun/attachments.js +3 -21
  17. package/dist/src-ts/plugins/aun/index.js +155 -21
  18. package/dist/src-ts/plugins/aun/messages.js +4 -1
  19. package/dist/src-ts/plugins/aun/public/app.js +2 -1
  20. package/dist/src-ts/plugins/aun/public/index.html +1 -1
  21. package/dist/src-ts/plugins/cdp-draft.js +5 -2
  22. package/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
  23. package/dist/src-ts/plugins/chatgpt/index.js +80 -19
  24. package/dist/src-ts/plugins/claude-cli/index.js +19 -2
  25. package/dist/src-ts/plugins/codex-cli/index.js +19 -2
  26. package/dist/src-ts/plugins/desktop-launch.js +44 -3
  27. package/dist/src-ts/plugins/doubao/cdp-client.js +234 -45
  28. package/dist/src-ts/plugins/doubao/index.js +152 -8
  29. package/dist/src-ts/plugins/doubao/native-task.js +134 -0
  30. package/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
  31. package/dist/src-ts/plugins/dsh/index.js +172 -21
  32. package/dist/src-ts/plugins/dsh/session-history.js +31 -3
  33. package/dist/src-ts/plugins/feishu/index.js +1 -1
  34. package/dist/src-ts/plugins/local-cli/agent.js +74 -9
  35. package/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
  36. package/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
  37. package/dist/src-ts/plugins/personal-assistant/index.js +263 -0
  38. package/dist/src-ts/plugins/personal-assistant/model-client.js +181 -0
  39. package/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
  40. package/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
  41. package/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
  42. package/dist/src-ts/plugins/personal-assistant/tools.js +167 -0
  43. package/dist/src-ts/plugins/qwenwork/cdp-client.js +90 -18
  44. package/dist/src-ts/plugins/qwenwork/index.js +139 -24
  45. package/dist/src-ts/plugins/workbuddy/cdp-client.js +353 -82
  46. package/dist/src-ts/plugins/workbuddy/index.js +124 -41
  47. package/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
  48. package/dist/src-ts/plugins/zcode/index.js +97 -0
  49. package/dist/src-ts/session-management.js +132 -14
  50. package/dist/src-ts/version-manager.js +6 -3
  51. package/dist/tests-ts/abweb-cli.test.js +10 -2
  52. package/dist/tests-ts/abweb-model-config.test.js +73 -0
  53. package/dist/tests-ts/agent.test.js +27 -0
  54. package/dist/tests-ts/aun-attachments.test.js +19 -17
  55. package/dist/tests-ts/aun-default-owner-page.test.js +2 -1
  56. package/dist/tests-ts/aun-messages.test.js +20 -0
  57. package/dist/tests-ts/aun-plugin.test.js +292 -19
  58. package/dist/tests-ts/bundled-package.test.js +12 -2
  59. package/dist/tests-ts/cdp-draft-recovery.test.js +182 -3
  60. package/dist/tests-ts/channel-sessions.test.js +27 -0
  61. package/dist/tests-ts/chatgpt-plugin.test.js +118 -0
  62. package/dist/tests-ts/control-server.test.js +56 -0
  63. package/dist/tests-ts/core-updater.test.js +215 -46
  64. package/dist/tests-ts/daemon-process.test.js +126 -1
  65. package/dist/tests-ts/desktop-launch.test.js +73 -0
  66. package/dist/tests-ts/doubao-native-task.test.js +237 -0
  67. package/dist/tests-ts/doubao-plugin.test.js +773 -12
  68. package/dist/tests-ts/dsh-agent.test.js +157 -8
  69. package/dist/tests-ts/dsh-cdp.test.js +148 -0
  70. package/dist/tests-ts/dsh-history.test.js +37 -0
  71. package/dist/tests-ts/feishu-plugin.test.js +1 -1
  72. package/dist/tests-ts/http-agent.test.js +18 -0
  73. package/dist/tests-ts/installer-assets.test.js +1 -1
  74. package/dist/tests-ts/local-cli.test.js +146 -1
  75. package/dist/tests-ts/model-provider-cancellation.test.js +152 -0
  76. package/dist/tests-ts/native-session-management.test.js +8 -7
  77. package/dist/tests-ts/personal-assistant-coordination.test.js +92 -0
  78. package/dist/tests-ts/personal-assistant-extensions.test.js +35 -0
  79. package/dist/tests-ts/personal-assistant-memory.test.js +117 -0
  80. package/dist/tests-ts/personal-assistant-owner-context.test.js +107 -0
  81. package/dist/tests-ts/personal-assistant-remote-ability.test.js +123 -0
  82. package/dist/tests-ts/personal-assistant.test.js +663 -0
  83. package/dist/tests-ts/platform-integration.test.js +183 -1
  84. package/dist/tests-ts/plugin-catalog.test.js +12 -2
  85. package/dist/tests-ts/plugin-manager.test.js +356 -4
  86. package/dist/tests-ts/plugin-upgrade-deadline.test.js +354 -0
  87. package/dist/tests-ts/publish-packages.test.js +42 -0
  88. package/dist/tests-ts/qwenwork-plugin.test.js +135 -1
  89. package/dist/tests-ts/qwenwork-reconnect.test.js +195 -0
  90. package/dist/tests-ts/qwenwork-sse.test.js +33 -0
  91. package/dist/tests-ts/session-access-defaults.test.js +112 -0
  92. package/dist/tests-ts/session-management.test.js +159 -4
  93. package/dist/tests-ts/version-manager.test.js +40 -0
  94. package/dist/tests-ts/workbuddy-cdp-recovery.test.js +311 -0
  95. package/dist/tests-ts/workbuddy-plugin.test.js +499 -4
  96. package/dist/tests-ts/zcode-cdp.test.js +894 -0
  97. package/dist/tests-ts/zcode-plugin.test.js +396 -0
  98. package/package.json +1 -1
  99. package/plugins/abweb/dist/src-ts/agent.js +35 -0
  100. package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
  101. package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +73 -5
  102. package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +57 -5
  103. package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +15 -0
  104. package/plugins/abweb/dist/src-ts/plugins/abweb/public/style.css +4 -0
  105. package/plugins/abweb/package.json +1 -1
  106. package/plugins/abweb/plugin.json +1 -1
  107. package/plugins/aun/config.schema.json +1 -1
  108. package/plugins/aun/dist/src-ts/agent.js +35 -0
  109. package/plugins/aun/dist/src-ts/plugins/aun/attachments.js +3 -21
  110. package/plugins/aun/dist/src-ts/plugins/aun/index.js +155 -21
  111. package/plugins/aun/dist/src-ts/plugins/aun/messages.js +4 -1
  112. package/plugins/aun/dist/src-ts/plugins/aun/public/app.js +2 -1
  113. package/plugins/aun/dist/src-ts/plugins/aun/public/index.html +1 -1
  114. package/plugins/aun/package.json +1 -1
  115. package/plugins/aun/plugin.json +1 -1
  116. package/plugins/chatgpt/dist/src-ts/agent.js +35 -0
  117. package/plugins/chatgpt/dist/src-ts/plugins/cdp-draft.js +5 -2
  118. package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
  119. package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +80 -19
  120. package/plugins/chatgpt/dist/src-ts/plugins/desktop-launch.js +44 -3
  121. package/plugins/chatgpt/package.json +1 -1
  122. package/plugins/chatgpt/plugin.json +1 -1
  123. package/plugins/claude-cli/dist/src-ts/agent.js +35 -0
  124. package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +19 -2
  125. package/plugins/claude-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
  126. package/plugins/claude-cli/package.json +1 -1
  127. package/plugins/claude-cli/plugin.json +3 -2
  128. package/plugins/codex-cli/dist/src-ts/agent.js +35 -0
  129. package/plugins/codex-cli/dist/src-ts/plugins/codex-cli/index.js +19 -2
  130. package/plugins/codex-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
  131. package/plugins/codex-cli/package.json +1 -1
  132. package/plugins/codex-cli/plugin.json +3 -2
  133. package/plugins/doubao/config.schema.json +2 -1
  134. package/plugins/doubao/dist/src-ts/agent.js +35 -0
  135. package/plugins/doubao/dist/src-ts/plugins/desktop-launch.js +44 -3
  136. package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +234 -45
  137. package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +152 -8
  138. package/plugins/doubao/dist/src-ts/plugins/doubao/native-task.js +134 -0
  139. package/plugins/doubao/package.json +1 -1
  140. package/plugins/doubao/plugin.json +3 -2
  141. package/plugins/dsh/config.schema.json +5 -2
  142. package/plugins/dsh/dist/src-ts/agent.js +35 -0
  143. package/plugins/dsh/dist/src-ts/dsh-agent.js +31 -13
  144. package/plugins/dsh/dist/src-ts/plugins/desktop-launch.js +44 -3
  145. package/plugins/dsh/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
  146. package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +172 -21
  147. package/plugins/dsh/dist/src-ts/plugins/dsh/session-history.js +31 -3
  148. package/plugins/dsh/package.json +1 -1
  149. package/plugins/dsh/plugin.json +3 -2
  150. package/plugins/feishu/dist/src-ts/agent.js +35 -0
  151. package/plugins/feishu/dist/src-ts/plugins/feishu/index.js +1 -1
  152. package/plugins/feishu/package.json +1 -1
  153. package/plugins/feishu/plugin.json +1 -1
  154. package/plugins/personal-assistant/config.schema.json +23 -0
  155. package/plugins/personal-assistant/dist/src-ts/agent.js +69 -0
  156. package/plugins/personal-assistant/dist/src-ts/attachments.js +62 -0
  157. package/plugins/personal-assistant/dist/src-ts/plugin.js +13 -0
  158. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
  159. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
  160. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +263 -0
  161. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +181 -0
  162. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
  163. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
  164. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
  165. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/tools.js +167 -0
  166. package/plugins/personal-assistant/package.json +39 -0
  167. package/plugins/personal-assistant/plugin.json +15 -0
  168. package/plugins/qwenwork/dist/src-ts/agent.js +35 -0
  169. package/plugins/qwenwork/dist/src-ts/plugins/cdp-draft.js +5 -2
  170. package/plugins/qwenwork/dist/src-ts/plugins/desktop-launch.js +44 -3
  171. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +90 -18
  172. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +139 -24
  173. package/plugins/qwenwork/package.json +1 -1
  174. package/plugins/qwenwork/plugin.json +3 -2
  175. package/plugins/workbuddy/config.schema.json +1 -1
  176. package/plugins/workbuddy/dist/src-ts/agent.js +35 -0
  177. package/plugins/workbuddy/dist/src-ts/plugins/desktop-launch.js +44 -3
  178. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +353 -82
  179. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +124 -41
  180. package/plugins/workbuddy/package.json +1 -1
  181. package/plugins/workbuddy/plugin.json +3 -2
  182. package/plugins/zcode/config.schema.json +13 -0
  183. package/plugins/zcode/dist/src-ts/agent.js +69 -0
  184. package/plugins/zcode/dist/src-ts/attachments.js +62 -0
  185. package/plugins/zcode/dist/src-ts/plugin.js +13 -0
  186. package/plugins/zcode/dist/src-ts/plugins/desktop-launch.js +238 -0
  187. package/plugins/zcode/dist/src-ts/plugins/workbuddy/cdp-client.js +896 -0
  188. package/plugins/zcode/dist/src-ts/plugins/workbuddy/cli-client.js +39 -0
  189. package/plugins/zcode/dist/src-ts/plugins/workbuddy/desktop-launch.js +10 -0
  190. package/plugins/zcode/dist/src-ts/plugins/workbuddy/index.js +567 -0
  191. package/plugins/zcode/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
  192. package/plugins/zcode/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
  193. package/plugins/zcode/dist/src-ts/plugins/zcode/index.js +97 -0
  194. package/plugins/zcode/package.json +16 -0
  195. package/plugins/zcode/plugin.json +11 -0
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TOOL_NAMES = void 0;
7
+ exports.toolDefinitions = toolDefinitions;
8
+ exports.parseToolArguments = parseToolArguments;
9
+ exports.executeTool = executeTool;
10
+ exports.parseExplicitCommand = parseExplicitCommand;
11
+ exports.stringifyToolResult = stringifyToolResult;
12
+ const ajv_1 = __importDefault(require("ajv"));
13
+ const coordination_1 = require("./coordination");
14
+ exports.TOOL_NAMES = ["agents_list", "agents_call", "aun_identity_list", "sessions_command", "sessions_today", "self_status", "ability_discover", "ability_call", "ability_request_get", "coordination_dispatch", "coordination_status", "owner_context_read"];
15
+ const ajv = new ajv_1.default({ allErrors: false, strict: false });
16
+ const schemas = {
17
+ agents_list: ajv.compile({ type: "object", additionalProperties: false, properties: {} }),
18
+ agents_call: ajv.compile({ type: "object", additionalProperties: false, required: ["id", "task"], properties: { id: { type: "string", minLength: 1, maxLength: 128 }, task: { type: "string", minLength: 1, maxLength: 20_000 } } }),
19
+ aun_identity_list: ajv.compile({ type: "object", additionalProperties: false, properties: {} }),
20
+ sessions_command: ajv.compile({ type: "object", additionalProperties: false, required: ["command"], properties: { command: { type: "string", minLength: 1, maxLength: 512 } } }),
21
+ sessions_today: ajv.compile({ type: "object", additionalProperties: false, properties: {} }),
22
+ self_status: ajv.compile({ type: "object", additionalProperties: false, properties: {} }),
23
+ ability_discover: ajv.compile({ type: "object", additionalProperties: false, required: ["providerAid"], properties: { providerAid: { type: "string", minLength: 3, maxLength: 256 } } }),
24
+ ability_call: ajv.compile({ type: "object", additionalProperties: false, required: ["providerAid", "path", "params"], properties: { providerAid: { type: "string", minLength: 3, maxLength: 256 }, path: { type: "string", minLength: 1, maxLength: 256 }, params: { type: "object" } } }),
25
+ ability_request_get: ajv.compile({ type: "object", additionalProperties: false, required: ["providerAid", "requestId"], properties: { providerAid: { type: "string", minLength: 3, maxLength: 256 }, requestId: { type: "string", minLength: 1, maxLength: 256 } } }),
26
+ coordination_dispatch: ajv.compile({ type: "object", additionalProperties: false, required: ["agentId", "aid", "task"], properties: { agentId: { type: "string", minLength: 1, maxLength: 64 }, aid: { type: "string", minLength: 3, maxLength: 256 }, task: { type: "string", minLength: 1, maxLength: 20000 } } }),
27
+ coordination_status: ajv.compile({ type: "object", additionalProperties: false, required: ["aid", "threadId"], properties: { aid: { type: "string", minLength: 3, maxLength: 256 }, threadId: { type: "string", minLength: 1, maxLength: 256 } } }),
28
+ owner_context_read: ajv.compile({ type: "object", additionalProperties: false, properties: {} }),
29
+ };
30
+ const toolDescriptions = {
31
+ agents_list: { name: "agents_list", description: "列出当前可调用的 Agent 及其 AID。", parameters: { type: "object", additionalProperties: false, properties: {} } },
32
+ agents_call: { name: "agents_call", description: "向白名单中的 Agent 发送一项独立任务。", parameters: { type: "object", additionalProperties: false, required: ["id", "task"], properties: { id: { type: "string", minLength: 1, maxLength: 128 }, task: { type: "string", minLength: 1, maxLength: 20_000 } } } },
33
+ aun_identity_list: { name: "aun_identity_list", description: "列出本机已知 AUN 身份及绑定状态。", parameters: { type: "object", additionalProperties: false, properties: {} } },
34
+ sessions_command: { name: "sessions_command", description: "执行只读的 /sessions 管理命令。", parameters: { type: "object", additionalProperties: false, required: ["command"], properties: { command: { type: "string", minLength: 1, maxLength: 512 } } } },
35
+ sessions_today: { name: "sessions_today", description: "生成今天工作的小结。", parameters: { type: "object", additionalProperties: false, properties: {} } },
36
+ self_status: { name: "self_status", description: "报告个人助理自身状态。", parameters: { type: "object", additionalProperties: false, properties: {} } },
37
+ ability_discover: { name: "ability_discover", description: "发现已配置 Provider 的只读 AUN Ability。", parameters: { type: "object", additionalProperties: false, required: ["providerAid"], properties: { providerAid: { type: "string", minLength: 3, maxLength: 256 } } } },
38
+ ability_call: { name: "ability_call", description: "调用无需认证和确认的纯只读 AUN Ability。", parameters: { type: "object", additionalProperties: false, required: ["providerAid", "path", "params"], properties: { providerAid: { type: "string", minLength: 3, maxLength: 256 }, path: { type: "string", minLength: 1, maxLength: 256 }, params: { type: "object" } } } },
39
+ ability_request_get: { name: "ability_request_get", description: "查询已发送 AUN Ability 的请求状态,不重新发送。", parameters: { type: "object", additionalProperties: false, required: ["providerAid", "requestId"], properties: { providerAid: { type: "string", minLength: 3, maxLength: 256 }, requestId: { type: "string", minLength: 1, maxLength: 256 } } } },
40
+ coordination_dispatch: { name: "coordination_dispatch", description: "显式向白名单 Agent 派发任务并创建独立 thread。", parameters: { type: "object", additionalProperties: false, required: ["agentId", "aid", "task"], properties: { agentId: { type: "string", minLength: 1, maxLength: 64 }, aid: { type: "string", minLength: 3, maxLength: 256 }, task: { type: "string", minLength: 1, maxLength: 20000 } } } },
41
+ coordination_status: { name: "coordination_status", description: "读取派发任务对应 thread 的当前状态和历史。", parameters: { type: "object", additionalProperties: false, required: ["aid", "threadId"], properties: { aid: { type: "string", minLength: 3, maxLength: 256 }, threadId: { type: "string", minLength: 1, maxLength: 256 } } } },
42
+ owner_context_read: { name: "owner_context_read", description: "读取管理员配置的 Owner 共享资料,仅作为参考。", parameters: { type: "object", additionalProperties: false, properties: {} } },
43
+ };
44
+ function toolDefinitions() {
45
+ return exports.TOOL_NAMES.map((name) => ({ type: "function", function: { ...toolDescriptions[name], parameters: { ...toolDescriptions[name].parameters } } }));
46
+ }
47
+ function parseToolArguments(name, raw) {
48
+ if (!exports.TOOL_NAMES.includes(name))
49
+ throw new Error("未知的管理工具");
50
+ let value;
51
+ try {
52
+ value = JSON.parse(raw);
53
+ }
54
+ catch {
55
+ throw new Error("管理工具参数不是有效 JSON");
56
+ }
57
+ const validate = schemas[name];
58
+ if (!validate(value))
59
+ throw new Error("管理工具参数不符合要求");
60
+ return value;
61
+ }
62
+ async function executeTool(name, args, management, allowedAgents, runtime = {}) {
63
+ if (!exports.TOOL_NAMES.includes(name))
64
+ throw new Error("未知的管理工具");
65
+ if (!schemas[name](args))
66
+ throw new Error("管理工具参数不符合要求");
67
+ switch (name) {
68
+ case "agents_list": {
69
+ const agents = await management.listAgents();
70
+ return allowedAgents?.length && !allowedAgents.includes("*") ? agents.map((agent) => allowedAgents.includes(agent.id) ? agent : { ...agent, callable: false, reason: "未加入个人助理 Agent 白名单" }) : agents;
71
+ }
72
+ case "agents_call": {
73
+ if (allowedAgents?.length && !allowedAgents.includes("*") && !allowedAgents.includes(String(args.id)))
74
+ throw new Error("目标 Agent 未加入个人助理白名单");
75
+ const result = await management.callAgent(String(args.id), String(args.task));
76
+ return {
77
+ text: result.text,
78
+ ...((result.attachments?.length || result.images?.length) ? { attachments: (result.attachments?.length ? result.attachments : result.images).map(({ filename, contentType }) => ({ filename, contentType })), attachmentDelivery: "附件将随最终回复自动回传,由通道确认发送结果,无需模型另行读取或发送。" } : {}),
79
+ ...(result.sessionId ? { sessionId: result.sessionId } : {}),
80
+ };
81
+ }
82
+ case "aun_identity_list": return management.listAunIdentities();
83
+ case "sessions_command": {
84
+ const command = String(args.command).trim();
85
+ if (!/^\/sessions(?:\s|$)/.test(command) || /[\r\n]/.test(command))
86
+ throw new Error("只支持单行 /sessions 只读命令");
87
+ return management.runSessionCommand(command);
88
+ }
89
+ case "sessions_today": return management.summarizeToday();
90
+ case "self_status": return { status: "ready", capabilities: exports.TOOL_NAMES };
91
+ case "ability_discover": {
92
+ const providerAid = String(args.providerAid);
93
+ if (!runtime.allowedProviders?.includes(providerAid) || !runtime.remoteAbility)
94
+ throw new Error("Provider 未加入远程 Ability 白名单");
95
+ return runtime.remoteAbility.discover(providerAid);
96
+ }
97
+ case "ability_call": {
98
+ const providerAid = String(args.providerAid);
99
+ if (!runtime.allowedProviders?.includes(providerAid) || !runtime.remoteAbility)
100
+ throw new Error("Provider 未加入远程 Ability 白名单");
101
+ return runtime.remoteAbility.call(providerAid, String(args.path), args.params);
102
+ }
103
+ case "ability_request_get": {
104
+ const providerAid = String(args.providerAid);
105
+ if (!runtime.allowedProviders?.includes(providerAid) || !runtime.remoteAbility)
106
+ throw new Error("Provider 未加入远程 Ability 白名单");
107
+ return runtime.remoteAbility.getRequest(providerAid, String(args.requestId));
108
+ }
109
+ case "coordination_dispatch": {
110
+ if (!allowedAgents || (allowedAgents.length > 0 && !allowedAgents.includes("*") && !allowedAgents.includes(String(args.agentId))) || !runtime.selfAid)
111
+ throw new Error("目标 Agent 未加入个人助理白名单");
112
+ const abilities = await management.listAgents();
113
+ const agent = abilities.find((item) => item.id === args.agentId && item.callable && item.aids.includes(String(args.aid)));
114
+ if (!agent)
115
+ throw new Error("目标 Agent/AID 当前不可派活");
116
+ const coordinator = (0, coordination_1.createCoordinator)({ selfAid: runtime.selfAid, ecPath: runtime.ecPath, allowedAgents: [{ id: agent.id, aids: agent.aids }] });
117
+ const result = await coordinator.dispatch({ agentId: agent.id, aid: String(args.aid), task: String(args.task) });
118
+ if (runtime.threads && runtime.threads.size >= 128)
119
+ runtime.threads.delete(runtime.threads.keys().next().value);
120
+ runtime.threads?.set(result.threadId, result.aid);
121
+ return result;
122
+ }
123
+ case "coordination_status": {
124
+ if (!runtime.selfAid)
125
+ throw new Error("个人助理 AID 未配置");
126
+ if (runtime.threads?.get(String(args.threadId)) !== String(args.aid))
127
+ throw new Error("thread 不属于本个人助理已派发的任务");
128
+ const coordinator = (0, coordination_1.createCoordinator)({ selfAid: runtime.selfAid, ecPath: runtime.ecPath, allowedAgents: [] });
129
+ return coordinator.getStatus({ aid: String(args.aid), threadId: String(args.threadId) });
130
+ }
131
+ case "owner_context_read": {
132
+ if (!runtime.ownerContext)
133
+ throw new Error("Owner 共享上下文尚未配置");
134
+ return runtime.ownerContext.get();
135
+ }
136
+ }
137
+ }
138
+ function parseExplicitCommand(task) {
139
+ if (!task.startsWith("/assistant ") && !task.startsWith("/sessions"))
140
+ return undefined;
141
+ if (/[\r\n]/.test(task))
142
+ return { kind: "invalid" };
143
+ if (task.startsWith("/sessions"))
144
+ return /^\/sessions(?:\s|$)/.test(task) ? { kind: "sessions", command: task } : { kind: "invalid" };
145
+ if (task === "/assistant context refresh")
146
+ return { kind: "context-refresh" };
147
+ const agentCall = task.match(/^\/assistant agent call ([a-z0-9][a-z0-9-]{0,63}) ([\s\S]{1,20000})$/);
148
+ if (agentCall)
149
+ return { kind: "agent-call", agentId: agentCall[1], task: agentCall[2] };
150
+ const create = task.match(/^\/assistant aid create ([A-Za-z0-9][A-Za-z0-9._-]{2,255})(?: --owner ([A-Za-z0-9][A-Za-z0-9._-]{2,255}))?$/);
151
+ if (create)
152
+ return { kind: "create", aid: create[1], ...(create[2] ? { ownerAid: create[2] } : {}) };
153
+ const bind = task.match(/^\/assistant aid bind ([A-Za-z0-9][A-Za-z0-9._-]{2,255}) ([A-Za-z0-9][A-Za-z0-9._-]{0,127})( --replace)?$/);
154
+ if (bind)
155
+ return { kind: "bind", aid: bind[1], agentId: bind[2], replace: Boolean(bind[3]) };
156
+ return { kind: "invalid" };
157
+ }
158
+ function stringifyToolResult(value) {
159
+ let text;
160
+ try {
161
+ text = typeof value === "string" ? value : JSON.stringify(value);
162
+ }
163
+ catch {
164
+ text = "工具返回结果不可序列化";
165
+ }
166
+ return text.length > 64_000 ? `${text.slice(0, 64_000)}\n[结果已截断]` : text;
167
+ }
@@ -127,19 +127,40 @@ class QwenCdpClient {
127
127
  const reply = await this.direct(`这是后台健康检测。请只回复 ${token},不要添加任何其它文字。`, 45_000, undefined, token);
128
128
  verifyQwenHeartbeatReply(token, reply);
129
129
  }
130
- async createSession(_name) {
130
+ async createSession(_name, forceNew = false, preserveDraft = false) {
131
131
  await this.ensureVisible();
132
- await this.recoverDraft();
132
+ if (!preserveDraft)
133
+ await this.recoverDraft();
133
134
  const snapshot = `(() => ({ url: location.href, messages: document.querySelectorAll('[data-message-id]').length, input: document.querySelector(${JSON.stringify(exports.inputSelector)})?.innerText ?? null, blank: Boolean(document.querySelector('.new-task-model-selector')) }))()`;
134
135
  const before = await this.evaluate(snapshot);
135
136
  if (before.input?.trim())
136
137
  throw new Error("千问输入框含有未发送内容,不能新建会话");
137
- if (before.input !== null && before.messages === 0 && before.blank)
138
- return;
139
- const clicked = await this.evaluate(`(() => { const button = document.querySelector('button[aria-label="新任务"]'); if (!button || button.disabled || button.getAttribute('aria-disabled') === 'true') return false; button.click(); return true; })()`);
138
+ if (!forceNew && before.input !== null && before.messages === 0 && before.blank)
139
+ return this.currentSessionId();
140
+ const clicked = await this.evaluate(`(() => { if (document.querySelector(${JSON.stringify(exports.inputSelector)})?.innerText.trim() || document.querySelector('button[aria-label="停止"],button[aria-label="停止生成"],button[aria-label="Stop generating"]')) throw new Error('千问当前有未发送内容或正在生成回复,不能新建会话'); const button = document.querySelector('button[aria-label="新任务"]'); if (!button || button.disabled || button.getAttribute('aria-disabled') === 'true') return false; button.click(); return true; })()`);
140
141
  if (clicked !== true)
141
142
  throw new Error("千问新任务入口未找到或不可用");
142
143
  await this.waitFor(`(() => { const state = ${snapshot}; return state.input !== null && state.input.trim() === '' && state.messages === 0 && state.url !== ${JSON.stringify(before.url)}; })()`, "千问新会话状态校验失败");
144
+ return this.currentSessionId();
145
+ }
146
+ async currentSessionId() {
147
+ const href = String(await this.evaluate("location.href"));
148
+ try {
149
+ return new URL(href).searchParams.get("chat") ?? undefined;
150
+ }
151
+ catch {
152
+ return undefined;
153
+ }
154
+ }
155
+ async activateSession(sessionId) {
156
+ if (!sessionId.trim() || sessionId.length > 512 || /[\u0000-\u001f]/.test(sessionId))
157
+ throw new Error("千问模型会话标识无效");
158
+ await this.ensureVisible();
159
+ const target = new URL(String(await this.evaluate("location.href")));
160
+ target.searchParams.set("chat", sessionId);
161
+ if (target.href !== String(await this.evaluate("location.href")))
162
+ await this.evaluate(`(() => { if (document.querySelector(${JSON.stringify(exports.inputSelector)})?.innerText.trim() || document.querySelector('button[aria-label="停止"],button[aria-label="停止生成"],button[aria-label="Stop generating"]')) throw new Error('千问当前有未发送内容或正在生成回复,不能切换会话'); location.href = ${JSON.stringify(target.href)}; })()`);
163
+ await this.waitFor(`location.href === ${JSON.stringify(target.href)} && Boolean(document.querySelector(${JSON.stringify(exports.inputSelector)}))`, "千问个人助理模型会话恢复失败");
143
164
  }
144
165
  async connect() {
145
166
  if (this.connected())
@@ -204,7 +225,7 @@ class QwenCdpClient {
204
225
  async direct(text, timeoutMs = 240_000, signal, expectedReply) {
205
226
  return (await this.complete(text, timeoutMs, signal, expectedReply)).text;
206
227
  }
207
- async complete(text, timeoutMs = 240_000, signal, expectedReply, attachments, onMessage) {
228
+ async complete(text, timeoutMs = 240_000, signal, expectedReply, attachments, onMessage, preserveDraft = false) {
208
229
  let stage = "准备千问办公桌面任务";
209
230
  let code = "QWEN_TASK_FAILED";
210
231
  try {
@@ -215,7 +236,8 @@ class QwenCdpClient {
215
236
  throw new Error("千问输入框有用户草稿,跳过后台健康检测");
216
237
  }
217
238
  await this.ensureVisible();
218
- await this.recoverDraft();
239
+ if (!preserveDraft)
240
+ await this.recoverDraft();
219
241
  const ready = await this.evaluate(`(() => { const input = document.querySelector(${JSON.stringify(exports.inputSelector)}); return Boolean(input && !input.innerText.trim() && !document.querySelector('button[aria-label="停止"],button[aria-label="停止生成"],button[aria-label="Stop generating"]')); })()`);
220
242
  if (!ready)
221
243
  throw new Error("千问输入框含有未发送内容或正在生成回复,未发送新消息");
@@ -234,15 +256,45 @@ class QwenCdpClient {
234
256
  const addedIds = attachments?.length ? await this.injectAttachments(attachments) : [];
235
257
  stage = "向千问办公提交任务(尚未确认发送)";
236
258
  code = "QWEN_SUBMIT_FAILED";
259
+ let submitUrl = "";
260
+ let inserted = false;
261
+ let submitAttempted = false;
237
262
  try {
263
+ submitUrl = String(await this.evaluate("location.href"));
264
+ const readyToSubmit = `(() => {
265
+ const input = document.querySelector(${JSON.stringify(exports.inputSelector)});
266
+ const button = document.querySelector('button[aria-label="发送消息"]');
267
+ const generating = document.querySelector('button[aria-label="停止"],button[aria-label="停止生成"],button[aria-label="Stop generating"]');
268
+ return Boolean(!document.hidden && location.href === ${JSON.stringify(submitUrl)} && input && input.innerText.trim() === ${JSON.stringify(text.trim())}
269
+ && button && !button.disabled && button.getAttribute('aria-disabled') !== 'true' && !generating);
270
+ })()`;
238
271
  await this.cdp("Input.insertText", { text });
239
- const sent = await this.evaluate(`(() => { const input = document.querySelector(${JSON.stringify(exports.inputSelector)}); const button = document.querySelector('button[aria-label="发送消息"]'); if (!input || input.innerText.trim() !== ${JSON.stringify(text.trim())} || !button || button.disabled || button.getAttribute('aria-disabled') === 'true') return false; button.click(); return true; })()`);
240
- if (sent !== true)
241
- throw new Error("千问发送按钮未就绪或输入校验失败,未发送消息");
272
+ inserted = true;
273
+ // 只有复查确认尚未点击时才再试一次,不能重发提交结果未知的任务。
274
+ for (let attempt = 0; attempt < 2; attempt++) {
275
+ await this.waitFor(readyToSubmit, "千问发送按钮未就绪或输入校验失败,未发送消息");
276
+ submitAttempted = true;
277
+ const sent = await this.evaluate(`(() => {
278
+ if (!${readyToSubmit}) return false;
279
+ document.querySelector('button[aria-label="发送消息"]').click();
280
+ return true;
281
+ })()`);
282
+ if (sent === true)
283
+ break;
284
+ submitAttempted = false;
285
+ if (attempt === 1)
286
+ throw new Error("千问发送按钮未就绪或输入校验失败,未发送消息");
287
+ }
242
288
  await this.waitFor(`(() => { const input = document.querySelector(${JSON.stringify(exports.inputSelector)}); return Boolean(input && !input.innerText.trim()); })()`, "千问未确认消息已提交");
243
289
  }
244
290
  catch (error) {
245
291
  // CDP 断开时清理也可能失败,保留最初的发送错误。
292
+ if (inserted && !submitAttempted) {
293
+ try {
294
+ await this.recoverDraft({ text, url: submitUrl });
295
+ }
296
+ catch { /* 清理失败不能覆盖原错。 */ }
297
+ }
246
298
  try {
247
299
  await this.cleanupAttachments(addedIds);
248
300
  }
@@ -254,23 +306,38 @@ class QwenCdpClient {
254
306
  code = "QWEN_REPLY_FAILED";
255
307
  let previousReply = "";
256
308
  let stablePolls = 0;
309
+ let seenGenerating = false;
310
+ let stoppedPolls = 0;
257
311
  const forwarded = new Set();
258
312
  const deadline = Date.now() + timeoutMs;
259
- while (Date.now() < deadline) {
260
- await wait(2_000, signal);
313
+ while (Date.now() < deadline || signal?.aborted) {
314
+ // 上游取消不停止桌面生成;确认结束前保留插件队列。
315
+ await wait(2_000);
316
+ await this.restoreVisible();
261
317
  const messagesJson = await this.evaluate(`JSON.stringify([...document.querySelectorAll(${JSON.stringify(exports.assistantMessageSelector)})].map(element => ({ id: element.getAttribute('data-message-id'), text: element.innerText })))`);
262
- const fresh = freshAssistantMessages(beforeIds, JSON.parse(messagesJson || "[]"));
318
+ const messages = JSON.parse(messagesJson || "[]");
319
+ const fresh = freshAssistantMessages(beforeIds, messages);
263
320
  const reply = fresh.at(-1)?.text.trim() ?? "";
264
321
  stablePolls = reply && reply === previousReply ? stablePolls + 1 : 0;
265
322
  previousReply = reply;
266
323
  if (reply && stablePolls >= 3 && isQwenApiUnavailableReply(reply))
267
324
  throw new Error("QWEN_REMOTE_ERROR:千问生成接口不可用;桌面仅返回通用错误,未提供服务端错误码;CDP 仍连接,未重启或重新提交任务");
268
325
  const generating = await this.evaluate(`Boolean(document.querySelector('button[aria-label="停止"],button[aria-label="停止生成"],button[aria-label="Stop generating"]'))`);
326
+ seenGenerating ||= generating === true;
327
+ stoppedPolls = seenGenerating && generating === false ? stoppedPolls + 1 : 0;
328
+ if (signal?.aborted && !reply && stoppedPolls >= 2 && messages.some(message => message.id && !beforeIds.has(message.id))) {
329
+ const attachments = expectedReply ? [] : await this.readArtifacts(beforeIds, sessionUrl);
330
+ if (attachments.length)
331
+ return { text: "", sessionId: new URL(sessionUrl).searchParams.get("chat") ?? undefined, attachments };
332
+ code = "QWEN_REQUEST_CANCELLED";
333
+ throw new Error("请求已取消,已确认本轮桌面生成结束,未返回正文或附件");
334
+ }
269
335
  if (!generating && (expectedReply ? isQwenHeartbeatReplyComplete(reply, expectedReply) && stablePolls >= 1 : reply && stablePolls >= 3)) {
270
336
  stage = "读取千问办公已完成任务的产物(尚未上传 AUN FS)";
271
337
  code = "QWEN_ARTIFACT_READ_FAILED";
272
338
  const attachments = expectedReply ? [] : await this.readArtifacts(beforeIds, sessionUrl);
273
- return { text: reply, ...(attachments.length ? { attachments } : {}) };
339
+ const sessionId = new URL(sessionUrl).searchParams.get("chat") ?? undefined;
340
+ return { text: reply, ...(sessionId ? { sessionId } : {}), ...(attachments.length ? { attachments } : {}) };
274
341
  }
275
342
  if (onMessage && generating && reply && stablePolls >= 1 && !forwarded.has(reply) && !isQwenApiUnavailableReply(reply)) {
276
343
  await onMessage(reply);
@@ -510,9 +577,8 @@ class QwenCdpClient {
510
577
  try {
511
578
  await this.evaluate(`(async () => {
512
579
  if (document.querySelector('button[aria-label="停止"],button[aria-label="停止生成"],button[aria-label="Stop generating"]')) throw new Error('千问桌面端正在生成回复');
513
- // 隐藏窗口会暂停消息渲染,使桌面端的提交前检查一直等不到确认。
514
- if (document.hidden && typeof window.desktopApi?.focusMainWindow === 'function') await window.desktopApi.focusMainWindow();
515
580
  })()`);
581
+ await this.restoreVisible();
516
582
  }
517
583
  catch (error) {
518
584
  let permission;
@@ -525,8 +591,13 @@ class QwenCdpClient {
525
591
  throw error;
526
592
  }
527
593
  }
528
- recoverDraft() {
529
- return (0, cdp_draft_1.recoverCdpDraft)(this.evaluate.bind(this), exports.inputSelector, 'button[aria-label="停止"],button[aria-label="停止生成"],button[aria-label="Stop generating"]', this.options.dataDir);
594
+ async restoreVisible() {
595
+ await this.evaluate(`(async () => {
596
+ if (document.hidden && typeof window.desktopApi?.focusMainWindow === 'function') await window.desktopApi.focusMainWindow();
597
+ })()`);
598
+ }
599
+ recoverDraft(expected) {
600
+ return (0, cdp_draft_1.recoverCdpDraft)(this.evaluate.bind(this), exports.inputSelector, 'button[aria-label="停止"],button[aria-label="停止生成"],button[aria-label="Stop generating"]', this.options.dataDir, expected);
530
601
  }
531
602
  async cdp(method, params = {}, timeoutMs = this.options.timeoutMs) {
532
603
  if (!this.socket)
@@ -588,6 +659,7 @@ class QwenCdpClient {
588
659
  async waitFor(expression, message) {
589
660
  const deadline = Date.now() + this.options.timeoutMs;
590
661
  while (Date.now() < deadline) {
662
+ await this.restoreVisible();
591
663
  if (await this.evaluate(expression))
592
664
  return;
593
665
  await wait(200);