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
|
@@ -16,7 +16,7 @@ const KNOWN_EVENTS = new Set([
|
|
|
16
16
|
"agent-preset/selected", "agent/inbox/spliced", "approval/asked", "approval/decided", "approval/policy", "assistant/attempt", "assistant/message",
|
|
17
17
|
"command/done", "command/run", "compaction/end", "compaction/prune", "compaction/start", "compaction/summary", "deliverables/presented",
|
|
18
18
|
"developer/message", "feedback/message-delete", "feedback/message-put", "feedback/record", "goal/change", "hook/invoked", "hook/result",
|
|
19
|
-
"image/offload", "llm/retry", "llm/retry-started", "model/selection", "permission/preset", "plan/mode", "request/context", "request/header",
|
|
19
|
+
"image/offload", "job/registered", "job/settled", "llm/retry", "llm/retry-started", "model/selection", "permission/preset", "plan/mode", "request/context", "request/header",
|
|
20
20
|
"sandbox/mode", "schedule/change", "session-log-deepseek/delivery-accepted", "session/end-seed", "session/title", "session/title-llm-request",
|
|
21
21
|
"step/end", "step/start", "subagent/catalog", "subagent/descriptor", "subagent/model-selection-policy", "system/message", "team/member",
|
|
22
22
|
"team/message/delivered", "team/message/queued", "team/task", "todo/write", "tool-workflow/agent-end", "tool-workflow/agent-start",
|
|
@@ -134,6 +134,21 @@ class DshSessionHistoryProvider {
|
|
|
134
134
|
readAt: new Date().toISOString(),
|
|
135
135
|
};
|
|
136
136
|
}
|
|
137
|
+
async readDeliveryState(sessionId) {
|
|
138
|
+
if (typeof sessionId !== "string" || !sessionId.trim() || sessionId.length > 512)
|
|
139
|
+
throw new Error("DSH 会话标识无效");
|
|
140
|
+
const root = await this.realRoot();
|
|
141
|
+
if (typeof root !== "string")
|
|
142
|
+
throw new Error(root.reason);
|
|
143
|
+
const discovered = await this.discover(root);
|
|
144
|
+
const entry = discovered.entries.find(item => item.id === sessionId);
|
|
145
|
+
if (!entry)
|
|
146
|
+
throw new Error("DSH 会话尚无可读取的持久化日志");
|
|
147
|
+
const parsed = await this.readEntry(root, entry);
|
|
148
|
+
if (!parsed.complete || !parsed.cwd)
|
|
149
|
+
throw new Error(parsed.reasons.join(";") || "DSH 会话工作目录无法验证");
|
|
150
|
+
return { cwd: parsed.cwd, sequence: parsed.sequence, presented: parsed.presented };
|
|
151
|
+
}
|
|
137
152
|
async realRoot() {
|
|
138
153
|
try {
|
|
139
154
|
const resolved = await (0, promises_1.realpath)(this.root);
|
|
@@ -251,7 +266,7 @@ class DshSessionHistoryProvider {
|
|
|
251
266
|
}
|
|
252
267
|
async readEntry(root, entry) {
|
|
253
268
|
const reasons = [];
|
|
254
|
-
const fail = (reason) => ({ session: { id: entry.id, title: entry.id }, turns: [], complete: false, reasons: [...new Set([...reasons, reason])] });
|
|
269
|
+
const fail = (reason) => ({ session: { id: entry.id, title: entry.id }, turns: [], complete: false, reasons: [...new Set([...reasons, reason])], sequence: -1, presented: [] });
|
|
255
270
|
try {
|
|
256
271
|
if (!await this.inside(root, entry.path))
|
|
257
272
|
return fail("DSH 会话日志路径越界或为符号链接");
|
|
@@ -285,7 +300,9 @@ class DshSessionHistoryProvider {
|
|
|
285
300
|
if (header.type !== "session" || header.version !== 4 || header.id !== entry.id || !validTime(header.createdAt) || typeof header.isSeeded !== "boolean" || !Number.isSafeInteger(header.delegationDepth) || header.delegationDepth < 0)
|
|
286
301
|
return fail("DSH 会话头格式或身份无法验证");
|
|
287
302
|
const turns = [];
|
|
303
|
+
const presented = [];
|
|
288
304
|
let complete = true;
|
|
305
|
+
let sequence = -1;
|
|
289
306
|
for (let index = 1; index < rows.length; index++) {
|
|
290
307
|
let event;
|
|
291
308
|
try {
|
|
@@ -296,11 +313,22 @@ class DshSessionHistoryProvider {
|
|
|
296
313
|
}
|
|
297
314
|
if (!event || event.seq !== index - 1 || typeof event.type !== "string" || !validTime(event.time) || typeof event.data !== "object" || event.data === null)
|
|
298
315
|
return fail("DSH 会话事件序号、时间或结构无法验证");
|
|
316
|
+
sequence = event.seq;
|
|
299
317
|
if (!KNOWN_EVENTS.has(event.type) && event.ignorable !== true) {
|
|
300
318
|
complete = false;
|
|
301
319
|
reasons.push(`存在未知且不可安全忽略的 DSH 事件:${event.type}`);
|
|
302
320
|
continue;
|
|
303
321
|
}
|
|
322
|
+
if (event.type === "deliverables/presented") {
|
|
323
|
+
const files = event.data.files;
|
|
324
|
+
if (!Array.isArray(files) || files.some((file) => !file || typeof file.path !== "string" || !file.path.trim())) {
|
|
325
|
+
complete = false;
|
|
326
|
+
reasons.push("DSH present 事件缺少有效文件路径");
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
presented.push({ sequence, paths: files.map((file) => file.path) });
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
304
332
|
if (event.type !== "user/message" && event.type !== "assistant/message")
|
|
305
333
|
continue;
|
|
306
334
|
const declaredRole = event.type === "user/message" ? event.data.role : event.data.message?.role;
|
|
@@ -335,7 +363,7 @@ class DshSessionHistoryProvider {
|
|
|
335
363
|
if (message)
|
|
336
364
|
turns.push({ id: `${entry.id}:${event.seq}`, role: event.type === "user/message" ? "user" : "assistant", text: message, time: new Date(event.time).toISOString() });
|
|
337
365
|
}
|
|
338
|
-
return { session: { id: entry.id, title: entry.id, updatedAt: new Date(before.mtimeMs).toISOString() }, turns, complete, reasons: [...new Set(reasons)] };
|
|
366
|
+
return { session: { id: entry.id, title: entry.id, updatedAt: new Date(before.mtimeMs).toISOString() }, turns, complete, reasons: [...new Set(reasons)], ...(typeof header.cwd === "string" ? { cwd: header.cwd } : {}), sequence, presented };
|
|
339
367
|
}
|
|
340
368
|
catch (error) {
|
|
341
369
|
return fail(error instanceof Error && /maximum output length|larger than/.test(error.message) ? "DSH 解压日志超过读取上限" : "DSH 会话日志无法按官方 v4 格式完整解析");
|
|
@@ -5,7 +5,7 @@ const promises_1 = require("node:fs/promises");
|
|
|
5
5
|
const node_path_1 = require("node:path");
|
|
6
6
|
const qrcode_1 = require("qrcode");
|
|
7
7
|
const client_1 = require("./client");
|
|
8
|
-
const manifest = { id: "feishu", version: "1.0.
|
|
8
|
+
const manifest = { id: "feishu", version: "1.0.8", type: "channel", displayName: "飞书通道", entry: "dist/src-ts/plugins/feishu/index.js", apiVersion: "1", process: "in-process", capabilities: ["channel", "channel.bot.setup", "channel.receive", "channel.send", "health", "cli"], configSchema: "config.schema.json", managementPage: "/abweb/channels/feishu" };
|
|
9
9
|
const newSessionCommand = /^\/new(?:[ \t]+[^\r\n]+)?$/;
|
|
10
10
|
const commands = [
|
|
11
11
|
{ name: "status", summary: "查看飞书通道状态", usage: "mab feishu status" },
|
|
@@ -6,6 +6,7 @@ const node_child_process_1 = require("node:child_process");
|
|
|
6
6
|
const node_fs_1 = require("node:fs");
|
|
7
7
|
const promises_1 = require("node:fs/promises");
|
|
8
8
|
const node_path_1 = require("node:path");
|
|
9
|
+
const node_os_1 = require("node:os");
|
|
9
10
|
const node_crypto_1 = require("node:crypto");
|
|
10
11
|
const nativeId = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
|
|
11
12
|
const maximumOutput = 4 * 1024 * 1024;
|
|
@@ -22,6 +23,21 @@ function diagnostic(value) {
|
|
|
22
23
|
} })
|
|
23
24
|
.slice(0, 2000);
|
|
24
25
|
}
|
|
26
|
+
function modelDiagnostic(error) {
|
|
27
|
+
const message = String(error);
|
|
28
|
+
const code = /CLI 退出码 (?:\d+|[A-Z][A-Z0-9_]+)/.exec(message)?.[0];
|
|
29
|
+
if (code)
|
|
30
|
+
return `${code}:请检查 CLI 登录、权限和网络`;
|
|
31
|
+
if (message.includes("CLI 调用超时"))
|
|
32
|
+
return "CLI 模型调用超时,请检查登录、网络或权限审批";
|
|
33
|
+
if (message.includes("CLI 请求已取消"))
|
|
34
|
+
return "CLI 模型请求已取消";
|
|
35
|
+
if (message.includes("插件已停止"))
|
|
36
|
+
return "插件已停止,当前 CLI 模型任务已终止";
|
|
37
|
+
if (message.includes("CLI 输出超过"))
|
|
38
|
+
return "CLI 模型输出超过 4MB 限制";
|
|
39
|
+
return "CLI 模型调用或结构化输出失败,请检查 CLI 状态";
|
|
40
|
+
}
|
|
25
41
|
async function resolveWindowsCliShim(file, provider) {
|
|
26
42
|
const modules = (0, node_path_1.basename)((0, node_path_1.dirname)(file)) === ".bin" ? (0, node_path_1.dirname)((0, node_path_1.dirname)(file)) : (0, node_path_1.join)((0, node_path_1.dirname)(file), "node_modules");
|
|
27
43
|
const root = (0, node_path_1.join)(modules, ...(provider === "codex" ? ["@openai", "codex"] : ["@anthropic-ai", "claude-code"]));
|
|
@@ -76,6 +92,8 @@ class LocalCliAgent {
|
|
|
76
92
|
ready = false;
|
|
77
93
|
message = "插件尚未启动";
|
|
78
94
|
version = "";
|
|
95
|
+
modelModeSupported = false;
|
|
96
|
+
get modelInput() { return this.ready && this.modelModeSupported; }
|
|
79
97
|
sessions = new Map();
|
|
80
98
|
writes = Promise.resolve();
|
|
81
99
|
pending = new Map();
|
|
@@ -108,6 +126,9 @@ class LocalCliAgent {
|
|
|
108
126
|
const help = (await this.run(this.provider === "codex" ? ["exec", "--help"] : ["--help"], "", 10000)).stdout;
|
|
109
127
|
if (this.provider === "codex" ? !help.includes("--json") || !help.includes("resume") : !help.includes("--output-format") || !help.includes("--resume"))
|
|
110
128
|
throw new Error("CLI 版本不支持所需的结构化输出和会话恢复,请升级 CLI");
|
|
129
|
+
this.modelModeSupported = this.provider === "claude"
|
|
130
|
+
? ["--json-schema", "--tools", "--strict-mcp-config", "--mcp-config", "--no-session-persistence", "--settings", "--setting-sources"].every(flag => help.includes(flag))
|
|
131
|
+
: ["--output-schema", "--ephemeral", "--sandbox", "--disable", "--config", "--ignore-rules"].every(flag => help.includes(flag));
|
|
111
132
|
this.sessions = new Map();
|
|
112
133
|
try {
|
|
113
134
|
const data = JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(context.dataDir, "cli-sessions.json"), "utf8"));
|
|
@@ -128,7 +149,9 @@ class LocalCliAgent {
|
|
|
128
149
|
throw new Error(this.message);
|
|
129
150
|
}
|
|
130
151
|
}
|
|
131
|
-
run(args, input, timeout) {
|
|
152
|
+
run(args, input, timeout, signal) {
|
|
153
|
+
if (signal?.aborted)
|
|
154
|
+
return Promise.reject(new Error("CLI 请求已取消"));
|
|
132
155
|
const command = this.command;
|
|
133
156
|
let child;
|
|
134
157
|
let stop;
|
|
@@ -153,8 +176,10 @@ class LocalCliAgent {
|
|
|
153
176
|
}
|
|
154
177
|
};
|
|
155
178
|
stop = () => terminate("插件已停止,当前 CLI 任务已终止");
|
|
179
|
+
const abort = () => terminate("CLI 请求已取消");
|
|
180
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
156
181
|
const timer = setTimeout(() => terminate(`CLI 调用超时(${timeout}ms),请检查登录、网络或权限审批`), timeout);
|
|
157
|
-
const cleanup = () => { clearTimeout(timer); this.children.delete(child); };
|
|
182
|
+
const cleanup = () => { clearTimeout(timer); signal?.removeEventListener("abort", abort); this.children.delete(child); };
|
|
158
183
|
child.stdout.setEncoding("utf8");
|
|
159
184
|
child.stderr.setEncoding("utf8");
|
|
160
185
|
const collect = (value, error) => {
|
|
@@ -227,35 +252,70 @@ class LocalCliAgent {
|
|
|
227
252
|
throw new Error(`${this.manifest.displayName} 本期不支持附件输入`);
|
|
228
253
|
if (!request.task?.trim())
|
|
229
254
|
throw new Error("CLI 任务文本不能为空");
|
|
255
|
+
return this.executeCli(request, request.modelResponseSchema);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 模型来源的独立入口。调用方只提供最小模型契约,不能通过普通 Agent handle 进入管理路径。
|
|
259
|
+
*/
|
|
260
|
+
async completeStructuredModel(request, responseSchema) {
|
|
261
|
+
if (!request.prompt?.trim())
|
|
262
|
+
throw new Error("模型请求文本不能为空");
|
|
263
|
+
if (!request.sessionId?.trim())
|
|
264
|
+
throw new Error("模型请求会话标识不能为空");
|
|
265
|
+
const response = await this.executeCli({ task: request.prompt, sessionId: request.sessionId, timeoutMs: request.timeoutMs, signal: request.signal }, responseSchema);
|
|
266
|
+
return { text: response.text };
|
|
267
|
+
}
|
|
268
|
+
async executeCli(request, modelSchema) {
|
|
269
|
+
const modelMode = modelSchema !== undefined;
|
|
270
|
+
if (modelMode && !this.modelModeSupported)
|
|
271
|
+
throw new Error("当前 CLI 不支持无工具结构化模型请求,请升级 CLI");
|
|
272
|
+
const schema = modelMode ? JSON.stringify(modelSchema) : undefined;
|
|
273
|
+
if (modelMode && (!modelSchema || typeof modelSchema !== "object" || Array.isArray(modelSchema) || !schema || Buffer.byteLength(schema) > 64_000))
|
|
274
|
+
throw new Error("模型输出 Schema 无效或过大");
|
|
230
275
|
const logical = request.sessionId || (0, node_crypto_1.randomUUID)();
|
|
231
276
|
return this.serial(logical, async () => {
|
|
232
277
|
this.requireReady();
|
|
233
|
-
const id = request.newSession ? undefined : this.sessions.get(logical);
|
|
278
|
+
const id = modelMode || request.newSession ? undefined : this.sessions.get(logical);
|
|
279
|
+
let schemaFile;
|
|
280
|
+
let schemaDirectory;
|
|
234
281
|
const args = this.provider === "codex"
|
|
235
282
|
? ["exec", ...(id ? ["resume", id] : []), "--json", "-"]
|
|
236
283
|
: ["--print", "--output-format", "json", ...(id ? ["--resume", id] : [])];
|
|
284
|
+
if (modelMode && this.provider === "claude")
|
|
285
|
+
args.push("--json-schema", schema, "--tools", "", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--settings", '{"disableAllHooks":true}', "--setting-sources", "user", "--no-session-persistence");
|
|
237
286
|
const timeout = request.timeoutMs ?? this.timeoutMs;
|
|
238
287
|
if (!Number.isInteger(timeout) || timeout <= 0 || timeout > 2147483647)
|
|
239
288
|
throw new Error("任务超时必须是有效的正整数毫秒数");
|
|
240
289
|
const started = Date.now();
|
|
241
290
|
this.context.logger.info("本地 CLI 任务开始", { plugin: this.manifest.id, sessionId: logical, resume: Boolean(id) });
|
|
242
291
|
try {
|
|
243
|
-
|
|
244
|
-
|
|
292
|
+
if (modelMode && this.provider === "codex") {
|
|
293
|
+
schemaDirectory = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "mab-codex-model-"));
|
|
294
|
+
schemaFile = (0, node_path_1.join)(schemaDirectory, "response-schema.json");
|
|
295
|
+
await (0, promises_1.writeFile)(schemaFile, schema, { flag: "wx", mode: 0o600 });
|
|
296
|
+
args.splice(-1, 0, "--ephemeral", "--sandbox", "read-only", "--output-schema", schemaFile, "--disable", "shell_tool", "--disable", "apps", "--disable", "plugins", "--disable", "hooks", "--ignore-rules", "--config", "mcp_servers={}");
|
|
297
|
+
}
|
|
298
|
+
const output = await this.run(args, request.task, timeout, request.signal);
|
|
299
|
+
const response = this.parse(output.stdout, modelMode);
|
|
245
300
|
if (id && response.id !== id)
|
|
246
301
|
throw new Error("CLI 恢复返回的会话 ID 与指定会话不一致,保留原会话绑定");
|
|
247
|
-
|
|
302
|
+
if (!modelMode)
|
|
303
|
+
await this.save(logical, response.id);
|
|
248
304
|
this.context.logger.info("本地 CLI 任务完成", { plugin: this.manifest.id, sessionId: logical, durationMs: Date.now() - started, exitCode: 0 });
|
|
249
|
-
return { text: response.text, sessionId: logical };
|
|
305
|
+
return { text: response.text, ...(!modelMode ? { sessionId: logical } : {}) };
|
|
250
306
|
}
|
|
251
307
|
catch (error) {
|
|
252
|
-
const detail = diagnostic(String(error));
|
|
308
|
+
const detail = modelMode ? modelDiagnostic(error) : diagnostic(String(error));
|
|
253
309
|
this.context.logger.error("本地 CLI 任务失败", { plugin: this.manifest.id, sessionId: logical, durationMs: Date.now() - started, error: detail });
|
|
254
310
|
throw new Error(`${this.manifest.displayName}:${detail}`);
|
|
255
311
|
}
|
|
312
|
+
finally {
|
|
313
|
+
if (schemaFile && schemaDirectory)
|
|
314
|
+
await (0, promises_1.rm)(schemaDirectory, { recursive: true, force: true });
|
|
315
|
+
}
|
|
256
316
|
});
|
|
257
317
|
}
|
|
258
|
-
parse(stdout) {
|
|
318
|
+
parse(stdout, modelMode = false) {
|
|
259
319
|
let id = "", text = "";
|
|
260
320
|
try {
|
|
261
321
|
if (this.provider === "codex") {
|
|
@@ -287,6 +347,11 @@ class LocalCliAgent {
|
|
|
287
347
|
throw new Error(JSON.stringify(event.errors ?? event));
|
|
288
348
|
id = event.session_id;
|
|
289
349
|
text = event.result;
|
|
350
|
+
if (modelMode) {
|
|
351
|
+
if (!event.structured_output || typeof event.structured_output !== "object" || Array.isArray(event.structured_output))
|
|
352
|
+
throw new Error("缺少有效结构化模型输出");
|
|
353
|
+
text = JSON.stringify(event.structured_output);
|
|
354
|
+
}
|
|
290
355
|
}
|
|
291
356
|
if (typeof id !== "string" || !nativeId.test(id))
|
|
292
357
|
throw new Error("缺少有效原生会话 ID");
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.responseSchema = void 0;
|
|
4
|
+
exports.claudeCliCompletion = claudeCliCompletion;
|
|
5
|
+
const tools_1 = require("./tools");
|
|
6
|
+
exports.responseSchema = {
|
|
7
|
+
type: "object", additionalProperties: false, required: ["role", "content", "tool_calls"],
|
|
8
|
+
properties: {
|
|
9
|
+
role: { type: "string", enum: ["assistant"] },
|
|
10
|
+
content: { type: ["string", "null"], maxLength: 32_000 },
|
|
11
|
+
tool_calls: { type: "array", maxItems: 8, items: {
|
|
12
|
+
type: "object", additionalProperties: false, required: ["id", "type", "function"],
|
|
13
|
+
properties: {
|
|
14
|
+
id: { type: "string", minLength: 1, maxLength: 128 }, type: { type: "string", enum: ["function"] },
|
|
15
|
+
function: { type: "object", additionalProperties: false, required: ["name", "arguments"], properties: {
|
|
16
|
+
name: { type: "string", enum: [...tools_1.TOOL_NAMES] }, arguments: { type: "string", maxLength: 64_000 },
|
|
17
|
+
} },
|
|
18
|
+
},
|
|
19
|
+
} },
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
function claudeCliCompletion(callPlugin, timeoutMs) {
|
|
23
|
+
return async (messages, signal) => {
|
|
24
|
+
const response = await callPlugin("claude-cli", {
|
|
25
|
+
task: JSON.stringify({ instructions: "你是个人助理的模型推理器。按照 messages 中的系统要求处理最后一条用户消息;tools 仅描述宿主提供的管理工具。需要查询或执行任务时返回 tool_calls,待工具结果返回后再回答;不要编造结果。每次调用使用不同 id。最终回答时 tool_calls 为空数组。仅返回 Schema 规定的 JSON。", messages, tools: (0, tools_1.toolDefinitions)() }),
|
|
26
|
+
modelResponseSchema: exports.responseSchema, timeoutMs, signal,
|
|
27
|
+
});
|
|
28
|
+
return response.text;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createCoordinator = createCoordinator;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const node_util_1 = require("node:util");
|
|
6
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
7
|
+
const isAid = (value) => typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{2,255}$/.test(value);
|
|
8
|
+
const isAgentId = (value) => typeof value === "string" && /^[a-z0-9][a-z0-9-]{0,63}$/.test(value);
|
|
9
|
+
const isThreadId = (value) => typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/.test(value);
|
|
10
|
+
function createCoordinator(options) {
|
|
11
|
+
if (!isAid(options.selfAid))
|
|
12
|
+
throw new Error("selfAid 无效");
|
|
13
|
+
const allowed = new Map();
|
|
14
|
+
for (const agent of options.allowedAgents) {
|
|
15
|
+
if (!isAgentId(agent.id) || !Array.isArray(agent.aids) || agent.aids.some((aid) => !isAid(aid)))
|
|
16
|
+
throw new Error("Agent 白名单无效");
|
|
17
|
+
allowed.set(agent.id, new Set(agent.aids));
|
|
18
|
+
}
|
|
19
|
+
const run = options.execFile ?? (async (file, args) => execFileAsync(file, args, { timeout: 30_000, maxBuffer: 1024 * 1024, encoding: "utf8", windowsHide: true }));
|
|
20
|
+
const command = async (args) => {
|
|
21
|
+
const { stdout } = await run(options.ecPath ?? "ec", args);
|
|
22
|
+
let value;
|
|
23
|
+
try {
|
|
24
|
+
value = JSON.parse(stdout);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new Error("EvolCore 返回了无效 JSON");
|
|
28
|
+
}
|
|
29
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || value.ok === false)
|
|
30
|
+
throw new Error("EvolCore 命令失败");
|
|
31
|
+
return value;
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
async dispatch({ agentId, aid, task }) {
|
|
35
|
+
if (!isAgentId(agentId) || !isAid(aid) || !allowed.get(agentId)?.has(aid))
|
|
36
|
+
throw new Error("目标 Agent/AID 不在白名单中");
|
|
37
|
+
if (typeof task !== "string" || !task.trim() || task.length > 20_000)
|
|
38
|
+
throw new Error("任务长度无效");
|
|
39
|
+
const thread = await command(["thread", "create", aid, "--name", `个人助理派活-${agentId}`, "--format", "json"]);
|
|
40
|
+
const threadId = thread.thread_id ?? thread.threadId ?? thread.id;
|
|
41
|
+
if (!isThreadId(threadId))
|
|
42
|
+
throw new Error("EvolCore 未返回有效 threadId");
|
|
43
|
+
await command(["msg", "send", options.selfAid, aid, "--text", task, "--thread", threadId, "--return", "required", "--format", "json"]);
|
|
44
|
+
return { status: "pending", agentId, aid, threadId };
|
|
45
|
+
},
|
|
46
|
+
async getStatus({ aid, threadId }) {
|
|
47
|
+
if (!isAid(aid))
|
|
48
|
+
throw new Error("AID 无效");
|
|
49
|
+
if (!isThreadId(threadId))
|
|
50
|
+
throw new Error("threadId 无效");
|
|
51
|
+
const history = await command(["thread", "history", threadId, "--format", "json"]);
|
|
52
|
+
const state = history.status ?? history.state;
|
|
53
|
+
const status = state === "completed" || state === "failed" || state === "cancelled" ? state : "pending";
|
|
54
|
+
return { status, aid, threadId, history };
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PersonalAssistantPlugin = exports.personalAssistantManifest = void 0;
|
|
4
|
+
const attachments_1 = require("../../attachments");
|
|
5
|
+
const agent_1 = require("../../agent");
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
const model_client_1 = require("./model-client");
|
|
8
|
+
const tools_1 = require("./tools");
|
|
9
|
+
const remote_ability_1 = require("./remote-ability");
|
|
10
|
+
const owner_context_1 = require("./owner-context");
|
|
11
|
+
const claude_cli_model_1 = require("./claude-cli-model");
|
|
12
|
+
const session_memory_1 = require("./session-memory");
|
|
13
|
+
exports.personalAssistantManifest = {
|
|
14
|
+
id: "personal-assistant",
|
|
15
|
+
version: "1.0.7",
|
|
16
|
+
type: "agent",
|
|
17
|
+
displayName: "个人助理",
|
|
18
|
+
entry: "dist/src-ts/plugins/personal-assistant/index.js",
|
|
19
|
+
apiVersion: "1",
|
|
20
|
+
process: "in-process",
|
|
21
|
+
capabilities: ["chat", "management", "health"],
|
|
22
|
+
configSchema: "config.schema.json",
|
|
23
|
+
};
|
|
24
|
+
class PersonalAssistantPlugin {
|
|
25
|
+
manifest = exports.personalAssistantManifest;
|
|
26
|
+
sessionOperations = ["new"];
|
|
27
|
+
context;
|
|
28
|
+
client;
|
|
29
|
+
state = "failed";
|
|
30
|
+
stateMessage = "插件尚未启动";
|
|
31
|
+
fetcher;
|
|
32
|
+
ownerContext;
|
|
33
|
+
threads = new Map();
|
|
34
|
+
constructor(options = {}) { this.fetcher = options.fetch; }
|
|
35
|
+
async activate(context) {
|
|
36
|
+
this.context = context;
|
|
37
|
+
const config = context.config;
|
|
38
|
+
try {
|
|
39
|
+
const endpoint = validEndpoint(config.endpoint);
|
|
40
|
+
if (!Array.isArray(config.allowedPeers) || !Array.isArray(config.allowedAgents) || !config.allowedPeers.every(value => value === "*" || isAid(value)) || !config.allowedAgents.every(value => value === "*" || isPluginId(value)))
|
|
41
|
+
throw new Error("allowedPeers 或 allowedAgents 配置无效");
|
|
42
|
+
if (config.modelProvider !== undefined && !["openai", "claude-cli", "plugin"].includes(config.modelProvider))
|
|
43
|
+
throw new Error("不支持该模型来源");
|
|
44
|
+
const claudeMode = config.modelProvider === "claude-cli";
|
|
45
|
+
const pluginMode = config.modelProvider === "plugin";
|
|
46
|
+
const modelPlugin = pluginMode && isPluginId(config.modelPlugin) ? config.modelPlugin : undefined;
|
|
47
|
+
if (claudeMode && context.modelInputGuard !== true)
|
|
48
|
+
throw new Error("当前核心不支持安全的 Claude 模型调用,请升级核心");
|
|
49
|
+
if (pluginMode && !modelPlugin)
|
|
50
|
+
throw new Error("请选择有效的模型来源插件");
|
|
51
|
+
if (pluginMode && typeof context.callModelProvider !== "function")
|
|
52
|
+
throw new Error("当前核心不支持通用模型来源,请升级核心");
|
|
53
|
+
const modelConfigured = claudeMode || pluginMode || Boolean(config.endpoint || config.model || config.apiKey);
|
|
54
|
+
if (!claudeMode && !pluginMode && modelConfigured && (!endpoint || !config.model?.trim()))
|
|
55
|
+
throw new Error("请配置有效的模型 endpoint 和 model");
|
|
56
|
+
const selfAid = isAid(config.selfAid) ? config.selfAid : undefined;
|
|
57
|
+
const abilityProviders = Array.isArray(config.abilityProviders) && config.abilityProviders.every(isAid) ? config.abilityProviders : [];
|
|
58
|
+
const remoteAbility = selfAid && abilityProviders.length ? (0, remote_ability_1.createRemoteAbilityClient)({ selfAid, ecPath: validEcPath(config.ecPath) }) : undefined;
|
|
59
|
+
this.ownerContext = isAid(config.ownerAid) && typeof config.ownerContextPath === "string"
|
|
60
|
+
? (0, owner_context_1.createOwnerContextStore)({ ownerAid: config.ownerAid, path: config.ownerContextPath, ttlMs: validContextTtl(config.ownerContextTtlMs), ecPath: validEcPath(config.ecPath) })
|
|
61
|
+
: undefined;
|
|
62
|
+
this.threads.clear();
|
|
63
|
+
const callModelProvider = context.callModelProvider;
|
|
64
|
+
const pluginCompletion = pluginMode && modelPlugin && callModelProvider ? async (messages, signal, sessionId) => (await callModelProvider(modelPlugin, {
|
|
65
|
+
prompt: JSON.stringify({ instructions: "你是个人助理的模型推理器。依据 messages 处理本轮请求。需要查询或执行已列出的管理工具时,只返回 assistant JSON 中的 tool_calls;收到工具结果后再回答。最终回答只写入 content,tool_calls 为空数组。只返回符合 responseSchema 的 JSON 对象,禁止 Markdown 或额外文字。messages 中要求只回复某个词或标记时,只决定 content,不得省略 JSON 包装。", messages, tools: (0, tools_1.toolDefinitions)(), responseSchema: claude_cli_model_1.responseSchema }),
|
|
66
|
+
sessionId,
|
|
67
|
+
timeoutMs: validTimeout(config.timeoutMs),
|
|
68
|
+
signal,
|
|
69
|
+
})).text : undefined;
|
|
70
|
+
const sessionMemory = modelConfigured && typeof context.dataDir === "string" && context.dataDir
|
|
71
|
+
? new session_memory_1.PersonalAssistantSessionMemory(context.dataDir, context.logger?.warn?.bind(context.logger))
|
|
72
|
+
: undefined;
|
|
73
|
+
this.client = modelConfigured ? new model_client_1.PersonalAssistantModelClient({ endpoint, model: config.model?.trim(), apiKey: config.apiKey, cliCompletion: claudeMode ? (0, claude_cli_model_1.claudeCliCompletion)(context.callPlugin, validTimeout(config.timeoutMs)) : undefined, pluginCompletion, timeoutMs: validTimeout(config.timeoutMs), fetch: this.fetcher ?? fetch, allowedAgents: config.allowedAgents, sessionMemory, toolRuntime: { selfAid, ecPath: validEcPath(config.ecPath), allowedProviders: abilityProviders, remoteAbility, ownerContext: this.ownerContext, threads: this.threads } }) : undefined;
|
|
74
|
+
this.context = { config: context.config, logger: context.logger, callModelProvider };
|
|
75
|
+
this.state = "ready";
|
|
76
|
+
this.stateMessage = "";
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
this.client = undefined;
|
|
80
|
+
this.state = "degraded";
|
|
81
|
+
this.stateMessage = error instanceof Error ? error.message : "模型配置无效";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async deactivate(_reason) {
|
|
85
|
+
this.client?.close();
|
|
86
|
+
this.client = undefined;
|
|
87
|
+
this.ownerContext = undefined;
|
|
88
|
+
this.threads.clear();
|
|
89
|
+
this.context = undefined;
|
|
90
|
+
this.state = "failed";
|
|
91
|
+
this.stateMessage = "插件已停止";
|
|
92
|
+
}
|
|
93
|
+
async health() {
|
|
94
|
+
return { status: this.state, ...(this.stateMessage ? { message: this.stateMessage } : {}), details: { modelConfigured: Boolean(this.client) } };
|
|
95
|
+
}
|
|
96
|
+
async controlSession(request) {
|
|
97
|
+
if (request.operation !== "new")
|
|
98
|
+
throw new Error("个人助理只支持新建会话");
|
|
99
|
+
return { text: "个人助理已创建新会话", sessionId: (0, node_crypto_1.randomUUID)() };
|
|
100
|
+
}
|
|
101
|
+
async handle(request) {
|
|
102
|
+
const management = request.management;
|
|
103
|
+
if (!management)
|
|
104
|
+
return { text: "请通过已授权的 AUN 身份访问管理能力。" };
|
|
105
|
+
const command = (0, tools_1.parseExplicitCommand)(request.task);
|
|
106
|
+
if (command?.kind === "invalid")
|
|
107
|
+
return { text: "不支持该管理命令,请检查命令格式后重试。" };
|
|
108
|
+
if (command?.kind === "sessions") {
|
|
109
|
+
try {
|
|
110
|
+
return { text: await management.runSessionCommand(command.command) };
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return { text: "会话管理命令执行失败,请检查权限和命令格式。" };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (command?.kind === "context-refresh") {
|
|
117
|
+
if (!this.ownerContext)
|
|
118
|
+
return { text: "Owner 共享上下文尚未配置。" };
|
|
119
|
+
try {
|
|
120
|
+
const refreshed = await this.ownerContext.refresh();
|
|
121
|
+
return { text: `Owner 共享上下文已刷新(${refreshed.content.length} 字符)。` };
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return { text: "Owner 共享上下文刷新失败,已保留之前成功读取的缓存。" };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (command?.kind === "agent-call") {
|
|
128
|
+
try {
|
|
129
|
+
const result = await management.callAgent(command.agentId, command.task);
|
|
130
|
+
return { ...result, sessionId: request.sessionId };
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return { text: `调用 Agent ${command.agentId} 失败,请检查白名单和目标状态。` };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (command?.kind === "create") {
|
|
137
|
+
try {
|
|
138
|
+
return { text: safeJson(await management.createAunIdentity({ aid: command.aid, ...(command.ownerAid ? { ownerAid: command.ownerAid } : {}) })) };
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return { text: "AUN 身份创建未能确认完成;身份可能已创建,请先查询 AID 列表确认状态,不要重复创建。" };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (command?.kind === "bind") {
|
|
145
|
+
try {
|
|
146
|
+
return { text: safeJson(await management.bindAunIdentity({ aid: command.aid, agentId: command.agentId, replace: command.replace })) };
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return { text: "AUN 身份绑定失败,请检查身份、目标和权限。" };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (!this.client || this.state !== "ready")
|
|
153
|
+
return { text: "个人助理模型尚未配置,暂时无法处理请求。" };
|
|
154
|
+
const attachments = [];
|
|
155
|
+
const scopedManagement = { ...management, callAgent: async (id, task) => {
|
|
156
|
+
const result = await management.callAgent(id, task);
|
|
157
|
+
const items = result.attachments?.length ? result.attachments : (0, attachments_1.asAttachments)(result.images);
|
|
158
|
+
(0, attachments_1.validateAttachments)([...attachments, ...items]);
|
|
159
|
+
attachments.push(...items);
|
|
160
|
+
return result;
|
|
161
|
+
} };
|
|
162
|
+
const requestId = (0, node_crypto_1.randomUUID)();
|
|
163
|
+
const startedAt = Date.now();
|
|
164
|
+
const provider = this.context?.config.modelProvider === "plugin"
|
|
165
|
+
? String(this.context.config.modelPlugin ?? "未指定插件")
|
|
166
|
+
: String(this.context?.config.modelProvider ?? "openai");
|
|
167
|
+
const sessionKey = (0, node_crypto_1.createHash)("sha256").update(`${this.manifest.id}\0${provider}\0${request.sessionId ?? (0, node_crypto_1.randomUUID)()}`).digest("hex");
|
|
168
|
+
try {
|
|
169
|
+
let context;
|
|
170
|
+
try {
|
|
171
|
+
const ownerContext = await this.ownerContext?.get();
|
|
172
|
+
if (ownerContext)
|
|
173
|
+
context = `读取状态:${ownerContext.status};缓存年龄:${ownerContext.ageMs} 毫秒。\n${ownerContext.content}`;
|
|
174
|
+
}
|
|
175
|
+
catch { /* 共享资料不可用时保留常规助理能力 */ }
|
|
176
|
+
const modelSessionId = sessionKey;
|
|
177
|
+
const result = await this.client.complete(request.task, scopedManagement, request.signal, requestTimeout(request.timeoutMs), context, modelSessionId);
|
|
178
|
+
this.context?.logger?.info("个人助理请求完成", { requestId, provider, durationMs: Date.now() - startedAt });
|
|
179
|
+
return { text: result, ...(attachments.length ? { attachments } : {}) };
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
const detail = modelFailure(error, provider);
|
|
183
|
+
this.context?.logger?.warn("个人助理请求失败", { requestId, provider, session: sessionKey, stage: detail.stage, code: detail.code, durationMs: Date.now() - startedAt, ...(detail.message ? { error: detail.message } : {}) });
|
|
184
|
+
return { text: detail.text, ...(attachments.length ? { attachments } : {}) };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
exports.PersonalAssistantPlugin = PersonalAssistantPlugin;
|
|
189
|
+
function validEndpoint(value) {
|
|
190
|
+
if (typeof value !== "string" || !value.trim())
|
|
191
|
+
return undefined;
|
|
192
|
+
try {
|
|
193
|
+
const url = new URL(value);
|
|
194
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.hash || url.search || !url.pathname.endsWith("/chat/completions"))
|
|
195
|
+
return undefined;
|
|
196
|
+
return url.toString();
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function isAid(value) { return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{2,255}$/.test(value); }
|
|
203
|
+
function isPluginId(value) { return typeof value === "string" && /^[a-z0-9][a-z0-9-]{0,63}$/.test(value); }
|
|
204
|
+
function validTimeout(value) { return typeof value === "number" && Number.isInteger(value) && value >= 1000 && value <= 120_000 ? value : 120_000; }
|
|
205
|
+
function requestTimeout(value) { return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 120_000; }
|
|
206
|
+
function validEcPath(value) { return typeof value === "string" && value.trim() ? value.trim() : undefined; }
|
|
207
|
+
function validContextTtl(value) { return typeof value === "number" && Number.isSafeInteger(value) && value >= 1_000 && value <= 300_000 ? value : 60_000; }
|
|
208
|
+
function safeJson(value) { try {
|
|
209
|
+
return JSON.stringify(value);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return "操作完成。";
|
|
213
|
+
} }
|
|
214
|
+
function modelFailure(error, provider) {
|
|
215
|
+
const operation = error instanceof agent_1.AgentOperationError ? error : undefined;
|
|
216
|
+
const stage = operation?.stage ?? "模型调用";
|
|
217
|
+
const code = operation?.code ?? "PA_REQUEST_FAILED";
|
|
218
|
+
if (code === "PA_REQUEST_CANCELLED")
|
|
219
|
+
return { text: `个人助理请求已取消(来源 ${provider},阶段:${stage})。`, stage, code };
|
|
220
|
+
if (code === "PA_REQUEST_TIMEOUT")
|
|
221
|
+
return { text: `个人助理请求超时(来源 ${provider},阶段:${stage})。`, stage, code };
|
|
222
|
+
const message = safeFailureMessage(operation?.cause ?? error);
|
|
223
|
+
return { text: `个人助理请求失败(来源 ${provider},阶段:${stage},错误码:${errorCode(error) ?? code})${message ? `:${message}` : "。"}`, stage, code, ...(message ? { message } : {}) };
|
|
224
|
+
}
|
|
225
|
+
function errorCode(error) {
|
|
226
|
+
let result;
|
|
227
|
+
for (let current = error, count = 0; current && count++ < 5;) {
|
|
228
|
+
const code = current.code;
|
|
229
|
+
if (typeof code === "string" && /^[A-Z][A-Z0-9_]{2,31}$/.test(code))
|
|
230
|
+
result = code;
|
|
231
|
+
current = current.cause;
|
|
232
|
+
}
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
function safeFailureMessage(error) {
|
|
236
|
+
// 只回显宿主定义的诊断,插件任意异常可能含提示正文、凭据或本地路径。
|
|
237
|
+
const message = error instanceof Error ? error.message : "";
|
|
238
|
+
if (/^模型(?:服务请求失败:HTTP \d{3}|服务不允许重定向|响应超过大小限制|响应不是有效的工具决策 JSON|响应格式无效|未返回有效回复|返回了空回复|工具调用次数超过限制|返回的工具调用无效)$/.test(message))
|
|
239
|
+
return message;
|
|
240
|
+
if (/^(?:插件 [a-z0-9-]+ 未声明模型来源能力|模型来源插件 [a-z0-9-]+ 未安装)$/.test(message))
|
|
241
|
+
return message;
|
|
242
|
+
if (/^(?:Claude|Codex) CLI 模型请求(?:必须是包含 responseSchema 的 JSON 对象|缺少有效 responseSchema)$/.test(message))
|
|
243
|
+
return message;
|
|
244
|
+
const unavailable = /^模型来源插件“[^”\r\n]{1,64}”未就绪:([\s\S]+)$/.exec(message);
|
|
245
|
+
const reason = unavailable?.[1] ?? message;
|
|
246
|
+
if (/^(?:ready|degraded|failed|disabled|installed|discovered|starting|stopping|插件未实现模型来源接口)$/.test(reason))
|
|
247
|
+
return `模型来源未就绪:${reason}`;
|
|
248
|
+
const cdp = /(?:无法连接)?(千问办公|WorkBuddy|ChatGPT|豆包) CDP (?:端口 )?(\d{1,5})\b/.exec(reason);
|
|
249
|
+
if (cdp)
|
|
250
|
+
return `无法连接${cdp[1]} CDP 端口 ${cdp[2]}`;
|
|
251
|
+
const desktop = /(?:千问(?:办公)?|WorkBuddy|ChatGPT|豆包) ?(?:发送按钮(?:未就绪|不可用)|输入框(?:未就绪|未找到|含有未发送内容)|当前有未发送内容或正在生成回复,不能(?:新建|切换)会话)/.exec(reason);
|
|
252
|
+
if (desktop)
|
|
253
|
+
return desktop[0];
|
|
254
|
+
if (/^(?:WorkBuddy|ChatGPT|豆包) CDP (?:未连接|已断开),模型请求不会启动或重启桌面$/.test(reason))
|
|
255
|
+
return reason;
|
|
256
|
+
if (reason === "千问办公未连接或不支持隔离模型会话,模型请求不会启动或重启桌面" || reason === "DSH 插件未就绪")
|
|
257
|
+
return reason;
|
|
258
|
+
if (reason === "WorkBuddy 当前有用户草稿、待确认操作或正在生成回复,模型请求未切换会话")
|
|
259
|
+
return reason;
|
|
260
|
+
const code = errorCode(error);
|
|
261
|
+
return code ? `底层错误码 ${code}` : undefined;
|
|
262
|
+
}
|
|
263
|
+
exports.default = PersonalAssistantPlugin;
|