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.
- package/README.md +17 -10
- package/dist/src-ts/agent.js +35 -0
- package/dist/src-ts/channel-sessions.js +3 -2
- package/dist/src-ts/cli.js +67 -37
- package/dist/src-ts/control-server.js +12 -30
- package/dist/src-ts/core-updater.js +141 -33
- package/dist/src-ts/daemon-process.js +2 -1
- package/dist/src-ts/dsh-agent.js +31 -13
- package/dist/src-ts/http-agent.js +36 -0
- package/dist/src-ts/plugin-catalog.js +11 -2
- package/dist/src-ts/plugin-manager.js +351 -50
- package/dist/src-ts/plugins/abweb/index.js +1 -1
- package/dist/src-ts/plugins/abweb/public/app.js +73 -5
- package/dist/src-ts/plugins/abweb/public/index.html +15 -0
- package/dist/src-ts/plugins/abweb/public/style.css +4 -0
- package/dist/src-ts/plugins/aun/attachments.js +3 -21
- package/dist/src-ts/plugins/aun/index.js +155 -21
- package/dist/src-ts/plugins/aun/messages.js +4 -1
- package/dist/src-ts/plugins/aun/public/app.js +2 -1
- package/dist/src-ts/plugins/aun/public/index.html +1 -1
- package/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
- package/dist/src-ts/plugins/chatgpt/index.js +80 -19
- package/dist/src-ts/plugins/claude-cli/index.js +19 -2
- package/dist/src-ts/plugins/codex-cli/index.js +19 -2
- package/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/dist/src-ts/plugins/doubao/cdp-client.js +234 -45
- package/dist/src-ts/plugins/doubao/index.js +152 -8
- package/dist/src-ts/plugins/doubao/native-task.js +134 -0
- package/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
- package/dist/src-ts/plugins/dsh/index.js +172 -21
- package/dist/src-ts/plugins/dsh/session-history.js +31 -3
- package/dist/src-ts/plugins/feishu/index.js +1 -1
- package/dist/src-ts/plugins/local-cli/agent.js +74 -9
- package/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
- package/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
- package/dist/src-ts/plugins/personal-assistant/index.js +263 -0
- package/dist/src-ts/plugins/personal-assistant/model-client.js +181 -0
- package/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
- package/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
- package/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
- package/dist/src-ts/plugins/personal-assistant/tools.js +167 -0
- package/dist/src-ts/plugins/qwenwork/cdp-client.js +90 -18
- package/dist/src-ts/plugins/qwenwork/index.js +139 -24
- package/dist/src-ts/plugins/workbuddy/cdp-client.js +353 -82
- package/dist/src-ts/plugins/workbuddy/index.js +124 -41
- package/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
- package/dist/src-ts/plugins/zcode/index.js +97 -0
- package/dist/src-ts/session-management.js +132 -14
- package/dist/src-ts/version-manager.js +6 -3
- package/dist/tests-ts/abweb-cli.test.js +10 -2
- package/dist/tests-ts/abweb-model-config.test.js +73 -0
- package/dist/tests-ts/agent.test.js +27 -0
- package/dist/tests-ts/aun-attachments.test.js +19 -17
- package/dist/tests-ts/aun-default-owner-page.test.js +2 -1
- package/dist/tests-ts/aun-messages.test.js +20 -0
- package/dist/tests-ts/aun-plugin.test.js +292 -19
- package/dist/tests-ts/bundled-package.test.js +12 -2
- package/dist/tests-ts/cdp-draft-recovery.test.js +182 -3
- package/dist/tests-ts/channel-sessions.test.js +27 -0
- package/dist/tests-ts/chatgpt-plugin.test.js +118 -0
- package/dist/tests-ts/control-server.test.js +56 -0
- package/dist/tests-ts/core-updater.test.js +215 -46
- package/dist/tests-ts/daemon-process.test.js +126 -1
- package/dist/tests-ts/desktop-launch.test.js +73 -0
- package/dist/tests-ts/doubao-native-task.test.js +237 -0
- package/dist/tests-ts/doubao-plugin.test.js +773 -12
- package/dist/tests-ts/dsh-agent.test.js +157 -8
- package/dist/tests-ts/dsh-cdp.test.js +148 -0
- package/dist/tests-ts/dsh-history.test.js +37 -0
- package/dist/tests-ts/feishu-plugin.test.js +1 -1
- package/dist/tests-ts/http-agent.test.js +18 -0
- package/dist/tests-ts/installer-assets.test.js +1 -1
- package/dist/tests-ts/local-cli.test.js +146 -1
- package/dist/tests-ts/model-provider-cancellation.test.js +152 -0
- package/dist/tests-ts/native-session-management.test.js +8 -7
- package/dist/tests-ts/personal-assistant-coordination.test.js +92 -0
- package/dist/tests-ts/personal-assistant-extensions.test.js +35 -0
- package/dist/tests-ts/personal-assistant-memory.test.js +117 -0
- package/dist/tests-ts/personal-assistant-owner-context.test.js +107 -0
- package/dist/tests-ts/personal-assistant-remote-ability.test.js +123 -0
- package/dist/tests-ts/personal-assistant.test.js +663 -0
- package/dist/tests-ts/platform-integration.test.js +183 -1
- package/dist/tests-ts/plugin-catalog.test.js +12 -2
- package/dist/tests-ts/plugin-manager.test.js +356 -4
- package/dist/tests-ts/plugin-upgrade-deadline.test.js +354 -0
- package/dist/tests-ts/publish-packages.test.js +42 -0
- package/dist/tests-ts/qwenwork-plugin.test.js +135 -1
- package/dist/tests-ts/qwenwork-reconnect.test.js +195 -0
- package/dist/tests-ts/qwenwork-sse.test.js +33 -0
- package/dist/tests-ts/session-access-defaults.test.js +112 -0
- package/dist/tests-ts/session-management.test.js +159 -4
- package/dist/tests-ts/version-manager.test.js +40 -0
- package/dist/tests-ts/workbuddy-cdp-recovery.test.js +311 -0
- package/dist/tests-ts/workbuddy-plugin.test.js +499 -4
- package/dist/tests-ts/zcode-cdp.test.js +894 -0
- package/dist/tests-ts/zcode-plugin.test.js +396 -0
- package/package.json +1 -1
- package/plugins/abweb/dist/src-ts/agent.js +35 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +73 -5
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +57 -5
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +15 -0
- package/plugins/abweb/dist/src-ts/plugins/abweb/public/style.css +4 -0
- package/plugins/abweb/package.json +1 -1
- package/plugins/abweb/plugin.json +1 -1
- package/plugins/aun/config.schema.json +1 -1
- package/plugins/aun/dist/src-ts/agent.js +35 -0
- package/plugins/aun/dist/src-ts/plugins/aun/attachments.js +3 -21
- package/plugins/aun/dist/src-ts/plugins/aun/index.js +155 -21
- package/plugins/aun/dist/src-ts/plugins/aun/messages.js +4 -1
- package/plugins/aun/dist/src-ts/plugins/aun/public/app.js +2 -1
- package/plugins/aun/dist/src-ts/plugins/aun/public/index.html +1 -1
- package/plugins/aun/package.json +1 -1
- package/plugins/aun/plugin.json +1 -1
- package/plugins/chatgpt/dist/src-ts/agent.js +35 -0
- package/plugins/chatgpt/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +80 -19
- package/plugins/chatgpt/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/chatgpt/package.json +1 -1
- package/plugins/chatgpt/plugin.json +1 -1
- package/plugins/claude-cli/dist/src-ts/agent.js +35 -0
- package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +19 -2
- package/plugins/claude-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
- package/plugins/claude-cli/package.json +1 -1
- package/plugins/claude-cli/plugin.json +3 -2
- package/plugins/codex-cli/dist/src-ts/agent.js +35 -0
- package/plugins/codex-cli/dist/src-ts/plugins/codex-cli/index.js +19 -2
- package/plugins/codex-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
- package/plugins/codex-cli/package.json +1 -1
- package/plugins/codex-cli/plugin.json +3 -2
- package/plugins/doubao/config.schema.json +2 -1
- package/plugins/doubao/dist/src-ts/agent.js +35 -0
- package/plugins/doubao/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +234 -45
- package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +152 -8
- package/plugins/doubao/dist/src-ts/plugins/doubao/native-task.js +134 -0
- package/plugins/doubao/package.json +1 -1
- package/plugins/doubao/plugin.json +3 -2
- package/plugins/dsh/config.schema.json +5 -2
- package/plugins/dsh/dist/src-ts/agent.js +35 -0
- package/plugins/dsh/dist/src-ts/dsh-agent.js +31 -13
- package/plugins/dsh/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/dsh/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
- package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +172 -21
- package/plugins/dsh/dist/src-ts/plugins/dsh/session-history.js +31 -3
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/plugin.json +3 -2
- package/plugins/feishu/dist/src-ts/agent.js +35 -0
- package/plugins/feishu/dist/src-ts/plugins/feishu/index.js +1 -1
- package/plugins/feishu/package.json +1 -1
- package/plugins/feishu/plugin.json +1 -1
- package/plugins/personal-assistant/config.schema.json +23 -0
- package/plugins/personal-assistant/dist/src-ts/agent.js +69 -0
- package/plugins/personal-assistant/dist/src-ts/attachments.js +62 -0
- package/plugins/personal-assistant/dist/src-ts/plugin.js +13 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +263 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +181 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
- package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/tools.js +167 -0
- package/plugins/personal-assistant/package.json +39 -0
- package/plugins/personal-assistant/plugin.json +15 -0
- package/plugins/qwenwork/dist/src-ts/agent.js +35 -0
- package/plugins/qwenwork/dist/src-ts/plugins/cdp-draft.js +5 -2
- package/plugins/qwenwork/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +90 -18
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +139 -24
- package/plugins/qwenwork/package.json +1 -1
- package/plugins/qwenwork/plugin.json +3 -2
- package/plugins/workbuddy/config.schema.json +1 -1
- package/plugins/workbuddy/dist/src-ts/agent.js +35 -0
- package/plugins/workbuddy/dist/src-ts/plugins/desktop-launch.js +44 -3
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +353 -82
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +124 -41
- package/plugins/workbuddy/package.json +1 -1
- package/plugins/workbuddy/plugin.json +3 -2
- package/plugins/zcode/config.schema.json +13 -0
- package/plugins/zcode/dist/src-ts/agent.js +69 -0
- package/plugins/zcode/dist/src-ts/attachments.js +62 -0
- package/plugins/zcode/dist/src-ts/plugin.js +13 -0
- package/plugins/zcode/dist/src-ts/plugins/desktop-launch.js +238 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/cdp-client.js +896 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/cli-client.js +39 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/desktop-launch.js +10 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/index.js +567 -0
- package/plugins/zcode/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
- package/plugins/zcode/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
- package/plugins/zcode/dist/src-ts/plugins/zcode/index.js +97 -0
- package/plugins/zcode/package.json +16 -0
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
529
|
-
|
|
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);
|