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,663 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
40
|
+
const node_test_1 = require("node:test");
|
|
41
|
+
const promises_1 = require("node:fs/promises");
|
|
42
|
+
const node_os_1 = require("node:os");
|
|
43
|
+
const node_path_1 = require("node:path");
|
|
44
|
+
const channel_sessions_1 = require("../src-ts/channel-sessions");
|
|
45
|
+
const agent_1 = require("../src-ts/agent");
|
|
46
|
+
const personal_assistant_1 = require("../src-ts/plugins/personal-assistant");
|
|
47
|
+
const tools_1 = require("../src-ts/plugins/personal-assistant/tools");
|
|
48
|
+
const management = {
|
|
49
|
+
listAgents: async () => [{ id: "qwenwork", displayName: "千问办公", status: "ready", available: true, reason: "", capabilities: ["chat"], aids: ["qwen.agentid.cn"], callable: true }],
|
|
50
|
+
callAgent: async (_id, task) => ({ text: `已处理:${task}` }),
|
|
51
|
+
listAunIdentities: async () => ({ identities: [] }),
|
|
52
|
+
createAunIdentity: async () => ({ status: "created" }),
|
|
53
|
+
bindAunIdentity: async () => ({ status: "bound" }),
|
|
54
|
+
runSessionCommand: async (command) => `执行:${command}`,
|
|
55
|
+
summarizeToday: async () => ({ supported: true, text: "今天完成了任务。", sources: [], complete: true }),
|
|
56
|
+
};
|
|
57
|
+
(0, node_test_1.test)("个人助理 Agent 通配符贯通模型工具,不把全部允许误判为名单外", async () => {
|
|
58
|
+
let round = 0;
|
|
59
|
+
let listed = false;
|
|
60
|
+
let called = false;
|
|
61
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async (_input, init) => {
|
|
62
|
+
const messages = JSON.parse(String(init?.body)).messages;
|
|
63
|
+
if (round++) {
|
|
64
|
+
listed = messages.some((message) => message.role === "tool" && message.content.includes('"callable":true'));
|
|
65
|
+
called = messages.some((message) => message.role === "tool" && message.content.includes("已处理:通配任务"));
|
|
66
|
+
}
|
|
67
|
+
return new Response(JSON.stringify({ choices: [{ message: round === 1 ? { role: "assistant", tool_calls: [
|
|
68
|
+
{ id: "list", type: "function", function: { name: "agents_list", arguments: "{}" } },
|
|
69
|
+
{ id: "call", type: "function", function: { name: "agents_call", arguments: JSON.stringify({ id: "qwenwork", task: "通配任务" }) } },
|
|
70
|
+
] } : { role: "assistant", content: "已完成" } }] }));
|
|
71
|
+
} });
|
|
72
|
+
try {
|
|
73
|
+
await plugin.activate({ config: { endpoint: "https://example.test/v1/chat/completions", model: "test", allowedPeers: ["*"], allowedAgents: ["*"] } });
|
|
74
|
+
strict_1.default.equal((await plugin.health()).status, "ready");
|
|
75
|
+
strict_1.default.equal((await plugin.handle({ task: "调用千问", management })).text, "已完成");
|
|
76
|
+
strict_1.default.ok(listed && called);
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
await plugin.deactivate("stop");
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
(0, node_test_1.test)("个人助理经真实通道会话入口执行 /new 并隔离旧上下文", async () => {
|
|
83
|
+
const dataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "pa-channel-new-"));
|
|
84
|
+
const prompts = [];
|
|
85
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async (_input, init) => {
|
|
86
|
+
prompts.push(JSON.parse(String(init?.body)).messages);
|
|
87
|
+
return new Response(JSON.stringify({ choices: [{ message: { role: "assistant", content: "已处理" } }] }));
|
|
88
|
+
} });
|
|
89
|
+
try {
|
|
90
|
+
await plugin.activate({ dataDir, config: { endpoint: "https://example.test/v1/chat/completions", model: "test", allowedPeers: ["*"], allowedAgents: [] } });
|
|
91
|
+
const p = plugin;
|
|
92
|
+
strict_1.default.ok(p.sessionOperations?.includes("new"));
|
|
93
|
+
const sessions = new channel_sessions_1.ChannelSessions(dataDir);
|
|
94
|
+
const send = (text) => sessions.run({ channel: "aun", recipientId: "pa.agentid.cn", peerId: "devproj.agentid.cn", text, reply: async () => { } }, "personal-assistant", (sessionId, newSession) => plugin.handle({ task: text, sessionId, newSession, management }), request => p.controlSession(request));
|
|
95
|
+
const first = await send("旧记号");
|
|
96
|
+
strict_1.default.match((await send("/new")).text, /新会话/);
|
|
97
|
+
const second = await send("新消息");
|
|
98
|
+
strict_1.default.notEqual(first.logicalSessionId, second.logicalSessionId);
|
|
99
|
+
strict_1.default.equal(prompts[1].length, 2);
|
|
100
|
+
strict_1.default.doesNotMatch(JSON.stringify(prompts[1]), /旧记号/);
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
await plugin.deactivate("stop");
|
|
104
|
+
await (0, promises_1.rm)(dataDir, { recursive: true, force: true });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
(0, node_test_1.test)("后续模型请求保留上轮管理工具调用及结果且失败轮次不写入历史", async () => {
|
|
108
|
+
const dataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "pa-tool-continuity-"));
|
|
109
|
+
const prompts = [];
|
|
110
|
+
let fail = false;
|
|
111
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async (_input, init) => {
|
|
112
|
+
prompts.push(JSON.parse(String(init?.body)).messages);
|
|
113
|
+
if (fail)
|
|
114
|
+
throw new Error("请求失败");
|
|
115
|
+
const message = prompts.length === 1
|
|
116
|
+
? { role: "assistant", content: null, tool_calls: [{ id: "call-1", type: "function", function: { name: "agents_call", arguments: '{"id":"workbuddy","task":"记住 PA_TOOL"}' } }] }
|
|
117
|
+
: { role: "assistant", content: "已完成" };
|
|
118
|
+
return new Response(JSON.stringify({ choices: [{ message }] }));
|
|
119
|
+
} });
|
|
120
|
+
try {
|
|
121
|
+
await plugin.activate({ dataDir, config: { endpoint: "https://example.test/v1/chat/completions", model: "test", allowedPeers: ["*"], allowedAgents: ["workbuddy"] } });
|
|
122
|
+
await plugin.handle({ task: "调用 WorkBuddy 记住 PA_TOOL", sessionId: "one", management });
|
|
123
|
+
await plugin.handle({ task: "接着做", sessionId: "one", management });
|
|
124
|
+
strict_1.default.deepEqual(prompts[2].map((m) => m.role), ["system", "user", "assistant", "tool", "assistant", "user"]);
|
|
125
|
+
strict_1.default.match(prompts[2][3].content, /PA_TOOL/);
|
|
126
|
+
fail = true;
|
|
127
|
+
await plugin.handle({ task: "失败输入", sessionId: "one", management });
|
|
128
|
+
fail = false;
|
|
129
|
+
await plugin.handle({ task: "再次续聊", sessionId: "one", management });
|
|
130
|
+
strict_1.default.doesNotMatch(JSON.stringify(prompts.at(-1)), /失败输入/);
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
await plugin.deactivate("stop");
|
|
134
|
+
await (0, promises_1.rm)(dataDir, { recursive: true, force: true });
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
(0, node_test_1.test)("Claude 临时模型调用沿用个人助理历史,不依赖 CLI 持久会话", async () => {
|
|
138
|
+
const dataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "pa-claude-continuity-"));
|
|
139
|
+
const prompts = [];
|
|
140
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
141
|
+
try {
|
|
142
|
+
await plugin.activate({ dataDir, config: { modelProvider: "claude-cli", allowedPeers: ["*"], allowedAgents: [] }, modelInputGuard: true, callPlugin: async (_id, request) => {
|
|
143
|
+
prompts.push(JSON.parse(request.task).messages);
|
|
144
|
+
return { text: JSON.stringify({ role: "assistant", content: "记号 PA_CLAUDE", tool_calls: [] }) };
|
|
145
|
+
} });
|
|
146
|
+
await plugin.handle({ task: "记住 PA_CLAUDE", sessionId: "one", management });
|
|
147
|
+
await plugin.handle({ task: "继续", sessionId: "one", management });
|
|
148
|
+
strict_1.default.deepEqual(prompts[1].map((m) => m.role), ["system", "user", "assistant", "user"]);
|
|
149
|
+
strict_1.default.equal(prompts[1][1].content, "记住 PA_CLAUDE");
|
|
150
|
+
await plugin.handle({ task: "另一位 Peer", sessionId: "two", management });
|
|
151
|
+
strict_1.default.equal(prompts[2].length, 2);
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
await plugin.deactivate("stop");
|
|
155
|
+
await (0, promises_1.rm)(dataDir, { recursive: true, force: true });
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
(0, node_test_1.test)("个人助理在同一 AUN 会话中恢复模型历史并隔离新会话", async () => {
|
|
159
|
+
const dataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "pa-session-memory-"));
|
|
160
|
+
const requests = [];
|
|
161
|
+
let answer = 0;
|
|
162
|
+
const fetcher = async (_input, init) => {
|
|
163
|
+
const body = JSON.parse(String(init?.body));
|
|
164
|
+
requests.push(body);
|
|
165
|
+
answer++;
|
|
166
|
+
return new Response(JSON.stringify({ choices: [{ message: { role: "assistant", content: answer === 1 ? "第一条记号 PA_FIRST" : answer === 2 ? "第二条已记住 PA_FIRST" : "新会话没有旧记号", tool_calls: [] } }] }), { status: 200 });
|
|
167
|
+
};
|
|
168
|
+
const context = { dataDir, config: { endpoint: "https://example.test/v1/chat/completions", model: "test", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, http: { register: () => undefined, unregister() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready" };
|
|
169
|
+
try {
|
|
170
|
+
const first = new personal_assistant_1.PersonalAssistantPlugin({ fetch: fetcher });
|
|
171
|
+
await first.activate(context);
|
|
172
|
+
strict_1.default.equal((await first.handle({ task: "记住 PA_FIRST", sessionId: "peer-thread", management })).text, "第一条记号 PA_FIRST");
|
|
173
|
+
await first.deactivate("reload");
|
|
174
|
+
const resumed = new personal_assistant_1.PersonalAssistantPlugin({ fetch: fetcher });
|
|
175
|
+
await resumed.activate(context);
|
|
176
|
+
strict_1.default.equal((await resumed.handle({ task: "请引用刚才的记号", sessionId: "peer-thread", management })).text, "第二条已记住 PA_FIRST");
|
|
177
|
+
strict_1.default.deepEqual(requests[1].messages.slice(-3).map(message => [message.role, message.content]), [["user", "记住 PA_FIRST"], ["assistant", "第一条记号 PA_FIRST"], ["user", "请引用刚才的记号"]]);
|
|
178
|
+
strict_1.default.equal((await resumed.handle({ task: "新会话测试", sessionId: "peer-thread-new", management })).text, "新会话没有旧记号");
|
|
179
|
+
strict_1.default.deepEqual(requests[2].messages.slice(-2).map(message => [message.role, message.content]), [["system", requests[2].messages[0].content], ["user", "新会话测试"]]);
|
|
180
|
+
await resumed.deactivate("stop");
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
await (0, promises_1.rm)(dataDir, { recursive: true, force: true });
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
(0, node_test_1.test)("个人助理保留工具调用历史并返回最终文本", async () => {
|
|
187
|
+
const requests = [];
|
|
188
|
+
const fetcher = async (_input, init) => {
|
|
189
|
+
requests.push(JSON.parse(String(init?.body)));
|
|
190
|
+
const round = requests.length;
|
|
191
|
+
const body = round === 1
|
|
192
|
+
? { choices: [{ message: { role: "assistant", content: null, tool_calls: [{ id: "call-1", type: "function", function: { name: "agents_list", arguments: "{}" } }] }, finish_reason: "tool_calls" }] }
|
|
193
|
+
: { choices: [{ message: { role: "assistant", content: "可用 Agent:千问办公" }, finish_reason: "stop" }] };
|
|
194
|
+
return new Response(JSON.stringify(body), { status: 200, headers: { "content-type": "application/json" } });
|
|
195
|
+
};
|
|
196
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: fetcher });
|
|
197
|
+
await plugin.activate({ dataDir: "/tmp", config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, http: { register: () => undefined, unregister() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready" });
|
|
198
|
+
const result = await plugin.handle({ task: "列出 Agent", management });
|
|
199
|
+
strict_1.default.equal(result.text, "可用 Agent:千问办公");
|
|
200
|
+
strict_1.default.equal(requests.length, 2);
|
|
201
|
+
const second = requests[1];
|
|
202
|
+
strict_1.default.deepEqual(second.messages.slice(-2).map((message) => message.role), ["assistant", "tool"]);
|
|
203
|
+
strict_1.default.equal(second.messages.at(-1)?.tool_call_id, "call-1");
|
|
204
|
+
});
|
|
205
|
+
(0, node_test_1.test)("个人助理复用 Claude CLI 默认模型并由管理接口执行自然语言工具调用", async () => {
|
|
206
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async () => { throw new Error("不应请求 HTTP 模型"); } });
|
|
207
|
+
const requests = [];
|
|
208
|
+
let executed = 0;
|
|
209
|
+
await plugin.activate({ modelInputGuard: true, config: { modelProvider: "claude-cli", allowedPeers: ["devproj.agentid.cn"], allowedAgents: ["workbuddy"] }, logger: { info() { }, warn() { }, error() { } }, callPlugin: async (id, request) => {
|
|
210
|
+
strict_1.default.equal(id, "claude-cli");
|
|
211
|
+
strict_1.default.ok(request.modelResponseSchema);
|
|
212
|
+
strict_1.default.equal(request.management, undefined);
|
|
213
|
+
strict_1.default.equal(request.signal.aborted, false);
|
|
214
|
+
const input = JSON.parse(request.task);
|
|
215
|
+
requests.push(input);
|
|
216
|
+
return { text: JSON.stringify(requests.length === 1
|
|
217
|
+
? { role: "assistant", content: null, tool_calls: [{ id: "claude-1", type: "function", function: { name: "agents_call", arguments: '{"id":"workbuddy","task":"测试任务"}' } }] }
|
|
218
|
+
: { role: "assistant", content: "WorkBuddy 已完成测试任务", tool_calls: [] }) };
|
|
219
|
+
} });
|
|
220
|
+
strict_1.default.equal((await plugin.health()).status, "ready");
|
|
221
|
+
strict_1.default.equal((await plugin.health()).details?.modelConfigured, true);
|
|
222
|
+
const result = await plugin.handle({ task: "请调用 WorkBuddy 处理测试任务", management: { ...management, callAgent: async (id, task) => { executed++; strict_1.default.equal(id, "workbuddy"); strict_1.default.equal(task, "测试任务"); return { text: "真实执行结果" }; } } });
|
|
223
|
+
strict_1.default.equal(result.text, "WorkBuddy 已完成测试任务");
|
|
224
|
+
strict_1.default.equal(executed, 1);
|
|
225
|
+
strict_1.default.equal(requests.length, 2);
|
|
226
|
+
strict_1.default.match(requests[1].messages.at(-1).content, /真实执行结果/);
|
|
227
|
+
strict_1.default.equal(requests[0].tools.find((tool) => tool.function.name === "aun_identity_create"), undefined);
|
|
228
|
+
await plugin.deactivate("stop");
|
|
229
|
+
});
|
|
230
|
+
(0, node_test_1.test)("个人助理通过显式桌面模型来源生成并校验管理决策", async () => {
|
|
231
|
+
const calls = [];
|
|
232
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
233
|
+
await plugin.activate({ config: { modelProvider: "plugin", modelPlugin: "qwenwork", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, callPlugin: async () => { throw new Error("不得退回普通 Agent 调用"); }, callModelProvider: async (id, request) => {
|
|
234
|
+
calls.push({ id, request });
|
|
235
|
+
return { text: JSON.stringify({ role: "assistant", content: "来自千问的模型回复", tool_calls: [] }) };
|
|
236
|
+
} });
|
|
237
|
+
const result = await plugin.handle({ task: "只回答测试", sessionId: "aun-peer-thread", management });
|
|
238
|
+
strict_1.default.equal(result.text, "来自千问的模型回复");
|
|
239
|
+
strict_1.default.equal(calls.length, 1);
|
|
240
|
+
strict_1.default.equal(calls[0].id, "qwenwork");
|
|
241
|
+
const firstPrompt = JSON.parse(calls[0].request.prompt);
|
|
242
|
+
strict_1.default.equal(calls[0].request.prompt.includes("agents_call"), true);
|
|
243
|
+
strict_1.default.match(firstPrompt.instructions, /只决定 content/);
|
|
244
|
+
strict_1.default.match(firstPrompt.instructions, /responseSchema/);
|
|
245
|
+
strict_1.default.match(calls[0].request.sessionId, /^[a-f0-9]{64}$/);
|
|
246
|
+
strict_1.default.equal(firstPrompt.responseSchema.required.includes("tool_calls"), true);
|
|
247
|
+
strict_1.default.equal(firstPrompt.messages.at(-1).content, "只回答测试");
|
|
248
|
+
strict_1.default.equal("management" in calls[0].request, false);
|
|
249
|
+
strict_1.default.equal("onMessage" in calls[0].request, false);
|
|
250
|
+
});
|
|
251
|
+
(0, node_test_1.test)("通用模型来源返回纯文本时作为最终回复,不执行管理工具", async () => {
|
|
252
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
253
|
+
let executed = 0;
|
|
254
|
+
await plugin.activate({ config: { modelProvider: "plugin", modelPlugin: "workbuddy", allowedPeers: [], allowedAgents: [] }, callModelProvider: async () => ({ text: "桌面模型的普通回答" }) });
|
|
255
|
+
const result = await plugin.handle({ task: "只回答测试", management: { ...management, listAgents: async () => { executed++; return []; } } });
|
|
256
|
+
strict_1.default.equal(result.text, "桌面模型的普通回答");
|
|
257
|
+
strict_1.default.equal(executed, 0);
|
|
258
|
+
});
|
|
259
|
+
(0, node_test_1.test)("CLI 模型请求格式失败在回复和日志中说明具体原因", async () => {
|
|
260
|
+
for (const provider of ["claude-cli", "codex-cli"]) {
|
|
261
|
+
const reason = `${provider === "claude-cli" ? "Claude" : "Codex"} CLI 模型请求必须是包含 responseSchema 的 JSON 对象`;
|
|
262
|
+
const logs = [];
|
|
263
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
264
|
+
await plugin.activate({ config: { modelProvider: "plugin", modelPlugin: provider, allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn: (_message, fields) => logs.push(fields), error() { } }, callModelProvider: async () => { throw new Error(reason); } });
|
|
265
|
+
try {
|
|
266
|
+
const result = await plugin.handle({ task: "测试", management });
|
|
267
|
+
strict_1.default.ok(result.text.includes(reason));
|
|
268
|
+
strict_1.default.ok(JSON.stringify(logs).includes(reason));
|
|
269
|
+
}
|
|
270
|
+
finally {
|
|
271
|
+
await plugin.deactivate("stop");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
(0, node_test_1.test)("本机 OpenAI 兼容模型允许无密钥调用", async () => {
|
|
276
|
+
let authorization = "未检查";
|
|
277
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async (_input, init) => {
|
|
278
|
+
authorization = new Headers(init?.headers).get("authorization");
|
|
279
|
+
return new Response(JSON.stringify({ choices: [{ message: { role: "assistant", content: "本地模型已回复" } }] }), { status: 200 });
|
|
280
|
+
} });
|
|
281
|
+
await plugin.activate({ config: { endpoint: "http://127.0.0.1:11434/v1/chat/completions", model: "local", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } } });
|
|
282
|
+
strict_1.default.equal((await plugin.handle({ task: "测试", management })).text, "本地模型已回复");
|
|
283
|
+
strict_1.default.equal(authorization, null);
|
|
284
|
+
});
|
|
285
|
+
(0, node_test_1.test)("插件模型失败显示来源、阶段和底层错误码,日志不包含提示和凭据", async () => {
|
|
286
|
+
const records = [];
|
|
287
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
288
|
+
await plugin.activate({ config: { modelProvider: "plugin", modelPlugin: "qwenwork", allowedPeers: [], allowedAgents: [] }, logger: { info: (_message, fields) => records.push(fields), warn: (_message, fields) => records.push(fields), error() { } }, callModelProvider: async (_id, request) => {
|
|
289
|
+
throw new Error(`连接失败 token=secret-value ${request.prompt}`, { cause: Object.assign(new Error("连接被拒绝"), { code: "ECONNREFUSED" }) });
|
|
290
|
+
} });
|
|
291
|
+
try {
|
|
292
|
+
const result = await plugin.handle({ task: "此处是不能写入日志的用户任务", sessionId: "private-peer-thread", management });
|
|
293
|
+
strict_1.default.match(result.text, /qwenwork/);
|
|
294
|
+
strict_1.default.match(result.text, /模型调用/);
|
|
295
|
+
strict_1.default.match(result.text, /ECONNREFUSED/);
|
|
296
|
+
strict_1.default.doesNotMatch(result.text, /secret-value|此处是不能写入日志的用户任务|private-peer-thread/);
|
|
297
|
+
const log = JSON.stringify(records);
|
|
298
|
+
strict_1.default.match(log, /requestId/);
|
|
299
|
+
strict_1.default.match(log, /durationMs/);
|
|
300
|
+
strict_1.default.doesNotMatch(log, /secret-value|此处是不能写入日志的用户任务|private-peer-thread/);
|
|
301
|
+
}
|
|
302
|
+
finally {
|
|
303
|
+
await plugin.deactivate("stop");
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
(0, node_test_1.test)("插件模型的无效 JSON 决策在执行任一工具前被拒绝", async () => {
|
|
307
|
+
let executed = 0;
|
|
308
|
+
for (const raw of [JSON.stringify({ role: "assistant", content: {}, tool_calls: [] }), JSON.stringify({ role: "assistant", content: null, tool_calls: [
|
|
309
|
+
{ id: "good", type: "function", function: { name: "agents_list", arguments: "{}" } },
|
|
310
|
+
{ id: "bad", type: "function", function: { name: "agents_list", arguments: "{bad" } },
|
|
311
|
+
] })]) {
|
|
312
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
313
|
+
await plugin.activate({ config: { modelProvider: "plugin", modelPlugin: "doubao", allowedPeers: [], allowedAgents: [] }, callModelProvider: async () => ({ text: raw }) });
|
|
314
|
+
try {
|
|
315
|
+
const result = await plugin.handle({ task: "查询", management: { ...management, listAgents: async () => { executed++; return []; } } });
|
|
316
|
+
strict_1.default.match(result.text, /doubao/);
|
|
317
|
+
strict_1.default.match(result.text, /结构化校验/);
|
|
318
|
+
}
|
|
319
|
+
finally {
|
|
320
|
+
await plugin.deactivate("stop");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
strict_1.default.equal(executed, 0);
|
|
324
|
+
});
|
|
325
|
+
(0, node_test_1.test)("来源异常不能借任意错误文字回显用户正文,保留底层错误码", async () => {
|
|
326
|
+
const logs = [];
|
|
327
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
328
|
+
await plugin.activate({ config: { modelProvider: "plugin", modelPlugin: "doubao", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn: (_message, fields) => logs.push(fields), error() { } }, callModelProvider: async () => {
|
|
329
|
+
throw new Error("今天的私密正文", { cause: Object.assign(new Error("连接失败"), { code: "ETIMEDOUT" }) });
|
|
330
|
+
} });
|
|
331
|
+
const result = await plugin.handle({ task: "今天的私密正文", management });
|
|
332
|
+
strict_1.default.match(result.text, /ETIMEDOUT/);
|
|
333
|
+
strict_1.default.doesNotMatch(JSON.stringify([result, logs]), /今天的私密正文/);
|
|
334
|
+
});
|
|
335
|
+
(0, node_test_1.test)("模型来源未就绪时保留端口或缺失接口原因并隐藏任意附加内容", async () => {
|
|
336
|
+
const cases = [
|
|
337
|
+
["模型来源插件“千问办公”未就绪:无法连接千问办公 CDP 端口 9228;token=private-value", /无法连接千问办公 CDP 端口 9228/],
|
|
338
|
+
["模型来源插件“千问办公”未就绪:插件未实现模型来源接口", /插件未实现模型来源接口/],
|
|
339
|
+
["模型来源插件 qwenwork 未安装", /qwenwork 未安装/],
|
|
340
|
+
];
|
|
341
|
+
for (const [reason, expected] of cases) {
|
|
342
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
343
|
+
await plugin.activate({ config: { modelProvider: "plugin", modelPlugin: "qwenwork", allowedPeers: [], allowedAgents: [] }, callModelProvider: async () => { throw new Error(reason); } });
|
|
344
|
+
try {
|
|
345
|
+
const result = await plugin.handle({ task: "测试", management });
|
|
346
|
+
strict_1.default.match(result.text, expected);
|
|
347
|
+
strict_1.default.doesNotMatch(result.text, /private-value/);
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
await plugin.deactivate("stop");
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
(0, node_test_1.test)("桌面模型提交失败显示固定按钮原因,不回显附加提示或路径", async () => {
|
|
355
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
356
|
+
await plugin.activate({ config: { modelProvider: "plugin", modelPlugin: "workbuddy", allowedPeers: [], allowedAgents: [] }, callModelProvider: async () => {
|
|
357
|
+
throw new agent_1.AgentOperationError("WORKBUDDY_TASK_FAILED", "向 WorkBuddy 提交任务", new Error("WorkBuddy 发送按钮未就绪;token=private-value;/home/private/prompt"));
|
|
358
|
+
} });
|
|
359
|
+
try {
|
|
360
|
+
const result = await plugin.handle({ task: "测试", management });
|
|
361
|
+
strict_1.default.match(result.text, /WORKBUDDY_TASK_FAILED/);
|
|
362
|
+
strict_1.default.match(result.text, /WorkBuddy 发送按钮未就绪/);
|
|
363
|
+
strict_1.default.doesNotMatch(result.text, /private-value|private\/prompt/);
|
|
364
|
+
}
|
|
365
|
+
finally {
|
|
366
|
+
await plugin.deactivate("stop");
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
(0, node_test_1.test)("旧核心缺少模型隔离保证时不调用 Claude CLI", async () => {
|
|
370
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
371
|
+
let calls = 0;
|
|
372
|
+
await plugin.activate({ config: { modelProvider: "claude-cli", allowedPeers: [], allowedAgents: [] }, callPlugin: async () => { calls++; return { text: "不应执行" }; } });
|
|
373
|
+
strict_1.default.equal((await plugin.health()).status, "degraded");
|
|
374
|
+
strict_1.default.match((await plugin.health()).message, /升级核心/);
|
|
375
|
+
strict_1.default.equal((await plugin.health()).details?.modelConfigured, false);
|
|
376
|
+
await plugin.handle({ task: "调用 WorkBuddy", management });
|
|
377
|
+
strict_1.default.equal(calls, 0);
|
|
378
|
+
});
|
|
379
|
+
(0, node_test_1.test)("AUN 上游取消会停止仍在运行的 Claude 模型请求", async () => {
|
|
380
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
381
|
+
let started;
|
|
382
|
+
const ready = new Promise(resolve => { started = resolve; });
|
|
383
|
+
let cliCancelled = false;
|
|
384
|
+
await plugin.activate({ modelInputGuard: true, config: { modelProvider: "claude-cli", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, callPlugin: async (_id, request) => new Promise((_resolve, reject) => {
|
|
385
|
+
request.signal.addEventListener("abort", () => { cliCancelled = true; reject(new Error("取消")); }, { once: true });
|
|
386
|
+
started();
|
|
387
|
+
}) });
|
|
388
|
+
const upstream = new AbortController();
|
|
389
|
+
const pending = plugin.handle({ task: "调用 Agent", management, signal: upstream.signal });
|
|
390
|
+
await ready;
|
|
391
|
+
upstream.abort();
|
|
392
|
+
strict_1.default.match((await pending).text, /取消/);
|
|
393
|
+
strict_1.default.equal(cliCancelled, true);
|
|
394
|
+
await plugin.deactivate("stop");
|
|
395
|
+
});
|
|
396
|
+
(0, node_test_1.test)("停用个人助理会取消 Claude 模型请求,未知模型来源不能就绪", async () => {
|
|
397
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
398
|
+
let started;
|
|
399
|
+
const ready = new Promise(resolve => { started = resolve; });
|
|
400
|
+
const context = { modelInputGuard: true, config: { modelProvider: "claude-cli", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, callPlugin: async (_id, request) => new Promise((_resolve, reject) => { request.signal.addEventListener("abort", () => reject(new Error("取消")), { once: true }); started(); }) };
|
|
401
|
+
await plugin.activate(context);
|
|
402
|
+
const pending = plugin.handle({ task: "查询状态", management });
|
|
403
|
+
await ready;
|
|
404
|
+
strict_1.default.equal((await plugin.health()).details?.modelConfigured, true);
|
|
405
|
+
await plugin.deactivate("stop");
|
|
406
|
+
strict_1.default.match((await pending).text, /取消/);
|
|
407
|
+
await plugin.activate({ ...context, config: { ...context.config, modelProvider: "unknown" } });
|
|
408
|
+
strict_1.default.equal((await plugin.health()).status, "degraded");
|
|
409
|
+
});
|
|
410
|
+
(0, node_test_1.test)("没有可信管理上下文时不调用模型", async () => {
|
|
411
|
+
let calls = 0;
|
|
412
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async () => { calls++; return new Response("{}", { status: 500 }); } });
|
|
413
|
+
const result = await plugin.handle({ task: "列出 Agent" });
|
|
414
|
+
strict_1.default.match(result.text, /授权的 AUN/);
|
|
415
|
+
strict_1.default.equal(calls, 0);
|
|
416
|
+
});
|
|
417
|
+
(0, node_test_1.test)("未配置模型时仍可启动并执行显式管理转发", async () => {
|
|
418
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
419
|
+
const context = { dataDir: "/tmp", config: { allowedPeers: ["devproj.agentid.cn"], allowedAgents: ["workbuddy"] }, logger: { info() { }, warn() { }, error() { } }, http: { register: () => undefined, unregister() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready" };
|
|
420
|
+
await plugin.activate(context);
|
|
421
|
+
strict_1.default.equal((await plugin.health()).status, "ready");
|
|
422
|
+
strict_1.default.deepEqual((await plugin.health()).details, { modelConfigured: false });
|
|
423
|
+
const result = await plugin.handle({ task: "/assistant agent call workbuddy 请仅回复 WORKBUDDY_LINK_OK", management: { ...management, callAgent: async () => ({ text: "WORKBUDDY_LINK_OK" }) } });
|
|
424
|
+
strict_1.default.equal(result.text, "WORKBUDDY_LINK_OK");
|
|
425
|
+
await plugin.deactivate("stop");
|
|
426
|
+
});
|
|
427
|
+
(0, node_test_1.test)("拒绝超过八次的模型工具调用且不执行其中的操作", async () => {
|
|
428
|
+
let executed = 0;
|
|
429
|
+
const tools = Array.from({ length: 9 }, (_, index) => ({ id: `call-${index}`, type: "function", function: { name: "agents_list", arguments: "{}" } }));
|
|
430
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async () => new Response(JSON.stringify({ choices: [{ message: { role: "assistant", tool_calls: tools } }] }), { status: 200 }) });
|
|
431
|
+
await plugin.activate({ dataDir: "/tmp", config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, http: { register: () => undefined, unregister() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready" });
|
|
432
|
+
const result = await plugin.handle({ task: "查询能力", management: { ...management, listAgents: async () => { executed++; return []; } } });
|
|
433
|
+
strict_1.default.match(result.text, /个人助理请求失败/);
|
|
434
|
+
strict_1.default.equal(executed, 0);
|
|
435
|
+
});
|
|
436
|
+
(0, node_test_1.test)("拒绝重复工具 ID 和无效工具参数", async () => {
|
|
437
|
+
let executed = 0;
|
|
438
|
+
let round = 0;
|
|
439
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async () => {
|
|
440
|
+
round++;
|
|
441
|
+
const calls = round === 1
|
|
442
|
+
? [{ id: "same", type: "function", function: { name: "agents_list", arguments: "{}" } }, { id: "same", type: "function", function: { name: "agents_list", arguments: "{}" } }]
|
|
443
|
+
: [{ id: "bad", type: "function", function: { name: "agents_list", arguments: "{bad" } }];
|
|
444
|
+
return new Response(JSON.stringify({ choices: [{ message: { role: "assistant", tool_calls: calls } }] }), { status: 200 });
|
|
445
|
+
} });
|
|
446
|
+
await plugin.activate({ dataDir: "/tmp", config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, http: { register: () => undefined, unregister() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready" });
|
|
447
|
+
const result = await plugin.handle({ task: "列出 Agent", management: { ...management, listAgents: async () => { executed++; return []; } } });
|
|
448
|
+
strict_1.default.match(result.text, /个人助理请求失败/);
|
|
449
|
+
strict_1.default.equal(round, 1);
|
|
450
|
+
strict_1.default.equal(executed, 0);
|
|
451
|
+
});
|
|
452
|
+
(0, node_test_1.test)("未知工具和坏 JSON 只能产生安全错误结果", async () => {
|
|
453
|
+
for (const tool of [
|
|
454
|
+
{ name: "aun_identity_create", arguments: '{"aid":"bad.agentid.cn"}' },
|
|
455
|
+
{ name: "agents_list", arguments: "{bad" },
|
|
456
|
+
{ name: "agents_list", arguments: '{"principal":"owner.agentid.cn"}' },
|
|
457
|
+
]) {
|
|
458
|
+
let round = 0;
|
|
459
|
+
let executed = 0;
|
|
460
|
+
let toolOutput = "";
|
|
461
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async (_input, init) => {
|
|
462
|
+
const payload = JSON.parse(String(init?.body));
|
|
463
|
+
round++;
|
|
464
|
+
if (round === 2)
|
|
465
|
+
toolOutput = payload.messages.at(-1)?.content ?? "";
|
|
466
|
+
return new Response(JSON.stringify({ choices: [{ message: round === 1 ? { role: "assistant", tool_calls: [{ id: "bad", type: "function", function: tool }] } : { role: "assistant", content: "已拒绝" } }] }), { status: 200 });
|
|
467
|
+
} });
|
|
468
|
+
await plugin.activate({ dataDir: "/tmp", config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, http: { register: () => undefined, unregister() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready" });
|
|
469
|
+
const result = await plugin.handle({ task: "查询 Agent", management: { ...management, listAgents: async () => { executed++; return []; }, createAunIdentity: async () => { executed++; return {}; } } });
|
|
470
|
+
strict_1.default.equal(result.text, "已拒绝");
|
|
471
|
+
strict_1.default.match(toolOutput, /error/);
|
|
472
|
+
strict_1.default.equal(executed, 0);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
(0, node_test_1.test)("隐藏 HTTP 错误和超过 1 MiB 的响应", async () => {
|
|
476
|
+
for (const fetcher of [
|
|
477
|
+
async () => new Response("Authorization: Bearer secret", { status: 503 }),
|
|
478
|
+
async () => new Response(" ".repeat(1024 * 1024 + 1), { status: 200 }),
|
|
479
|
+
]) {
|
|
480
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: fetcher });
|
|
481
|
+
await plugin.activate({ dataDir: "/tmp", config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, http: { register: () => undefined, unregister() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready" });
|
|
482
|
+
const result = await plugin.handle({ task: "列出 Agent", management });
|
|
483
|
+
strict_1.default.match(result.text, /个人助理请求失败/);
|
|
484
|
+
strict_1.default.doesNotMatch(result.text, /secret|Authorization/);
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
(0, node_test_1.test)("工具调用循环最多请求六轮", async () => {
|
|
488
|
+
let calls = 0;
|
|
489
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async () => {
|
|
490
|
+
calls++;
|
|
491
|
+
return new Response(JSON.stringify({ choices: [{ message: { role: "assistant", tool_calls: [{ id: `call-${calls}`, type: "function", function: { name: "agents_list", arguments: "{}" } }] } }] }), { status: 200 });
|
|
492
|
+
} });
|
|
493
|
+
await plugin.activate({ dataDir: "/tmp", config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, http: { register: () => undefined, unregister() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready" });
|
|
494
|
+
const result = await plugin.handle({ task: "列出 Agent", management });
|
|
495
|
+
strict_1.default.match(result.text, /个人助理请求失败/);
|
|
496
|
+
strict_1.default.equal(calls, 6);
|
|
497
|
+
});
|
|
498
|
+
(0, node_test_1.test)("模型请求超时后不再继续", async () => {
|
|
499
|
+
let calls = 0;
|
|
500
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async (_input, init) => new Promise((_resolve, reject) => {
|
|
501
|
+
calls++;
|
|
502
|
+
init?.signal?.addEventListener("abort", () => reject(new Error("abort")), { once: true });
|
|
503
|
+
}) });
|
|
504
|
+
await plugin.activate({ dataDir: "/tmp", config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, http: { register: () => undefined, unregister() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready" });
|
|
505
|
+
const result = await plugin.handle({ task: "列出 Agent", timeoutMs: 1000, management });
|
|
506
|
+
strict_1.default.match(result.text, /超时/);
|
|
507
|
+
strict_1.default.equal(calls, 1);
|
|
508
|
+
});
|
|
509
|
+
(0, node_test_1.test)("停用插件会中止正在执行的模型 HTTP 请求", async () => {
|
|
510
|
+
let started;
|
|
511
|
+
const ready = new Promise((resolve) => { started = resolve; });
|
|
512
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async (_input, init) => new Promise((_resolve, reject) => {
|
|
513
|
+
started();
|
|
514
|
+
init?.signal?.addEventListener("abort", () => reject(new Error("abort")), { once: true });
|
|
515
|
+
}) });
|
|
516
|
+
await plugin.activate({ dataDir: "/tmp", config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, http: { register: () => undefined, unregister() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready" });
|
|
517
|
+
const pending = plugin.handle({ task: "查询 Agent", management });
|
|
518
|
+
await ready;
|
|
519
|
+
await plugin.deactivate("stop");
|
|
520
|
+
strict_1.default.match((await pending).text, /取消/);
|
|
521
|
+
});
|
|
522
|
+
(0, node_test_1.test)("仅精确单行命令允许创建 AID", async () => {
|
|
523
|
+
let created = 0;
|
|
524
|
+
const localManagement = { ...management, createAunIdentity: async () => { created++; return { aid: "new.agentid.cn", status: "created" }; } };
|
|
525
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
526
|
+
const result = await plugin.handle({ task: "/assistant aid create new.agentid.cn", management: localManagement });
|
|
527
|
+
strict_1.default.match(result.text, /created|创建/i);
|
|
528
|
+
strict_1.default.equal(created, 1);
|
|
529
|
+
const rejected = await plugin.handle({ task: "请创建 new.agentid.cn\n然后继续", management: localManagement });
|
|
530
|
+
strict_1.default.match(rejected.text, /暂时无法处理/);
|
|
531
|
+
strict_1.default.equal(created, 1);
|
|
532
|
+
});
|
|
533
|
+
(0, node_test_1.test)("身份创建部分失败提示先查询且不重试、不回显底层错误", async () => {
|
|
534
|
+
let attempts = 0;
|
|
535
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
536
|
+
const result = await plugin.handle({ task: "/assistant aid create new.agentid.cn", management: { ...management, createAunIdentity: async () => { attempts++; throw new Error("seed=secret-value"); } } });
|
|
537
|
+
strict_1.default.equal(attempts, 1);
|
|
538
|
+
strict_1.default.match(result.text, /可能已创建.*先查询.*不要重复创建/);
|
|
539
|
+
strict_1.default.doesNotMatch(result.text, /seed|secret-value/);
|
|
540
|
+
});
|
|
541
|
+
(0, node_test_1.test)("显式 Agent 转发命令直连白名单 WorkBuddy,不调用模型", async () => {
|
|
542
|
+
let modelCalls = 0;
|
|
543
|
+
let agentCalls = 0;
|
|
544
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async () => { modelCalls++; throw new Error("不应调用模型"); } });
|
|
545
|
+
const result = await plugin.handle({
|
|
546
|
+
task: "/assistant agent call workbuddy 请仅回复 WORKBUDDY_LINK_OK",
|
|
547
|
+
management: { ...management, callAgent: async (id, task) => { agentCalls++; strict_1.default.equal(id, "workbuddy"); return { text: `WorkBuddy 已回复:${task}` }; } },
|
|
548
|
+
});
|
|
549
|
+
strict_1.default.match(result.text, /WorkBuddy 已回复:请仅回复 WORKBUDDY_LINK_OK/);
|
|
550
|
+
strict_1.default.equal(agentCalls, 1);
|
|
551
|
+
strict_1.default.equal(modelCalls, 0);
|
|
552
|
+
});
|
|
553
|
+
(0, node_test_1.test)("Agent 任务上限与核心保持二万字符且工具结果不含附件字节", async () => {
|
|
554
|
+
const task = "测".repeat(20_000);
|
|
555
|
+
strict_1.default.equal((0, tools_1.parseToolArguments)("agents_call", JSON.stringify({ id: "qwenwork", task })).task, task);
|
|
556
|
+
strict_1.default.throws(() => (0, tools_1.parseToolArguments)("agents_call", JSON.stringify({ id: "qwenwork", task: `${task}试` })), /参数不符合/);
|
|
557
|
+
const definition = (0, tools_1.toolDefinitions)().find((tool) => tool.function.name === "agents_call");
|
|
558
|
+
strict_1.default.equal(definition.function.parameters.properties.task.maxLength, 20_000);
|
|
559
|
+
const result = await (0, tools_1.executeTool)("agents_call", { id: "qwenwork", task: "处理任务" }, { ...management, callAgent: async () => ({ text: "完成", sessionId: "session-1", attachments: [{ data: new Uint8Array([1, 2, 3]), filename: "secret.txt", contentType: "text/plain", kind: "file" }], images: [{ data: new Uint8Array([4, 5]), filename: "image.png", contentType: "image/png" }] }) }, ["qwenwork"]);
|
|
560
|
+
strict_1.default.deepEqual(result, { text: "完成", sessionId: "session-1", attachments: [{ filename: "secret.txt", contentType: "text/plain" }], attachmentDelivery: "附件将随最终回复自动回传,由通道确认发送结果,无需模型另行读取或发送。" });
|
|
561
|
+
});
|
|
562
|
+
(0, node_test_1.test)("上游小于一秒的超时仍会中止请求", async () => {
|
|
563
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async (_input, init) => new Promise((_resolve, reject) => {
|
|
564
|
+
init?.signal?.addEventListener("abort", () => reject(new Error("abort")), { once: true });
|
|
565
|
+
}) });
|
|
566
|
+
await plugin.activate({ dataDir: "/tmp", config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn() { }, error() { } }, http: { register: () => undefined, unregister() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready" });
|
|
567
|
+
let timer;
|
|
568
|
+
try {
|
|
569
|
+
const result = await Promise.race([plugin.handle({ task: "查询 Agent", timeoutMs: 10, management }), new Promise((resolve) => { timer = setTimeout(() => resolve({ text: "未尊重上游超时" }), 200); })]);
|
|
570
|
+
strict_1.default.match(result.text, /请求超时/);
|
|
571
|
+
}
|
|
572
|
+
finally {
|
|
573
|
+
clearTimeout(timer);
|
|
574
|
+
await plugin.deactivate("stop");
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
(0, node_test_1.test)("模型异常日志不回显密钥或任意错误文字", async () => {
|
|
578
|
+
const logs = [];
|
|
579
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async () => { throw new Error("request failed apiKey=secret-value arbitrary-provider-error"); } });
|
|
580
|
+
await plugin.activate({ dataDir: "/tmp", config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret-value", allowedPeers: [], allowedAgents: [] }, logger: { info() { }, warn(message, fields) { logs.push({ message, fields }); }, error() { } }, http: { register: () => undefined, unregister() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready" });
|
|
581
|
+
await plugin.handle({ task: "查询 Agent", management });
|
|
582
|
+
strict_1.default.equal(logs.length, 1);
|
|
583
|
+
strict_1.default.doesNotMatch(JSON.stringify(logs), /secret-value|arbitrary-provider-error/);
|
|
584
|
+
});
|
|
585
|
+
(0, node_test_1.test)("个人助理显式调用保留目标图片且不替换助理会话", async () => {
|
|
586
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin();
|
|
587
|
+
const images = [{ data: new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]), filename: "画像.png", contentType: "image/png" }];
|
|
588
|
+
const result = await plugin.handle({ task: "/assistant agent call workbuddy 生成人像", sessionId: "助理会话", management: { ...management, callAgent: async () => ({ text: "完成", sessionId: "目标会话", images }) } });
|
|
589
|
+
strict_1.default.deepEqual(result.images, images);
|
|
590
|
+
strict_1.default.notEqual(result.sessionId, "目标会话");
|
|
591
|
+
});
|
|
592
|
+
(0, node_test_1.test)("个人助理自然语言调用回传真实附件,模型只接收附件信息且后续请求不复用附件", async () => {
|
|
593
|
+
const inputs = [];
|
|
594
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async (_url, init) => {
|
|
595
|
+
inputs.push(JSON.parse(String(init?.body)));
|
|
596
|
+
const message = inputs.length === 1
|
|
597
|
+
? { role: "assistant", tool_calls: [{ id: "图片调用", type: "function", function: { name: "agents_call", arguments: '{"id":"workbuddy","task":"生成画像"}' } }] }
|
|
598
|
+
: { role: "assistant", content: "完成" };
|
|
599
|
+
return new Response(JSON.stringify({ choices: [{ message }] }));
|
|
600
|
+
} });
|
|
601
|
+
await plugin.activate({ config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret", allowedPeers: ["*"], allowedAgents: ["workbuddy"] } });
|
|
602
|
+
strict_1.default.equal((await plugin.health()).status, "ready");
|
|
603
|
+
const attachments = [{ kind: "file", data: new TextEncoder().encode("仅回传给通道的文件正文"), filename: "结果.txt", contentType: "text/plain" }];
|
|
604
|
+
const mg = { ...management, callAgent: async () => ({ text: "已生成", attachments }) };
|
|
605
|
+
const result = await plugin.handle({ task: "调用 WorkBuddy", sessionId: "原会话", management: mg });
|
|
606
|
+
strict_1.default.deepEqual(result.attachments, attachments);
|
|
607
|
+
strict_1.default.match(inputs[1].messages.at(-1).content, /结果.txt/);
|
|
608
|
+
strict_1.default.match(inputs[1].messages.at(-1).content, /附件将随最终回复自动回传/);
|
|
609
|
+
strict_1.default.match(inputs[0].messages[0].content, /不能读取附件字节不代表不能回传/);
|
|
610
|
+
strict_1.default.doesNotMatch(JSON.stringify(inputs), /仅回传给通道的文件正文|\"data\"/);
|
|
611
|
+
const next = await plugin.handle({ task: "谢谢", sessionId: "原会话", management: mg });
|
|
612
|
+
strict_1.default.equal(next.attachments, undefined);
|
|
613
|
+
});
|
|
614
|
+
(0, node_test_1.test)("个人助理保存同会话上下文,隔离其他会话并在重载后保留", async (t) => {
|
|
615
|
+
const dataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "pa-context-reload-"));
|
|
616
|
+
const inputs = [];
|
|
617
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async (_url, init) => {
|
|
618
|
+
inputs.push(JSON.parse(String(init?.body)));
|
|
619
|
+
return new Response(JSON.stringify({ choices: [{ message: { role: "assistant", content: "已收到" } }] }));
|
|
620
|
+
} });
|
|
621
|
+
t.after(async () => { await plugin.deactivate("stop"); await (0, promises_1.rm)(dataDir, { recursive: true, force: true }); });
|
|
622
|
+
const ctx = { dataDir, config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret", allowedPeers: [], allowedAgents: [] } };
|
|
623
|
+
await plugin.activate(ctx);
|
|
624
|
+
await plugin.handle({ task: "画一位成年人", sessionId: "甲", management: management });
|
|
625
|
+
await plugin.handle({ task: "风格要古装西域", sessionId: "甲", management: management });
|
|
626
|
+
strict_1.default.deepEqual(inputs[1].messages.slice(1).map((m) => m.content), ["画一位成年人", "已收到", "风格要古装西域"]);
|
|
627
|
+
await plugin.handle({ task: "另一个用户", sessionId: "乙", management: management });
|
|
628
|
+
strict_1.default.equal(inputs[2].messages.length, 2);
|
|
629
|
+
await plugin.deactivate("reload");
|
|
630
|
+
await plugin.activate(ctx);
|
|
631
|
+
await plugin.handle({ task: "重载后", sessionId: "甲", management: management });
|
|
632
|
+
strict_1.default.deepEqual(inputs[3].messages.slice(1).map((m) => m.content), ["画一位成年人", "已收到", "风格要古装西域", "已收到", "重载后"]);
|
|
633
|
+
});
|
|
634
|
+
(0, node_test_1.test)("个人助理的通配来源配置通过 Schema,未知通配形式仍拒绝", async () => {
|
|
635
|
+
const { readFile } = await Promise.resolve().then(() => __importStar(require("node:fs/promises")));
|
|
636
|
+
const schema = JSON.parse(await readFile("plugins/personal-assistant/config.schema.json", "utf8"));
|
|
637
|
+
const pattern = new RegExp(schema.properties.allowedPeers.items.pattern);
|
|
638
|
+
strict_1.default.equal(pattern.test("*"), true);
|
|
639
|
+
strict_1.default.equal(pattern.test("owner.agentid.cn"), true);
|
|
640
|
+
strict_1.default.equal(pattern.test("*.agentid.cn"), false);
|
|
641
|
+
});
|
|
642
|
+
(0, node_test_1.test)("个人助理持久上下文按整轮裁剪并限制会话数量", async (t) => {
|
|
643
|
+
const dataDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "pa-context-limits-"));
|
|
644
|
+
const inputs = [];
|
|
645
|
+
const plugin = new personal_assistant_1.PersonalAssistantPlugin({ fetch: async (_url, init) => {
|
|
646
|
+
inputs.push(JSON.parse(String(init?.body)));
|
|
647
|
+
return new Response(JSON.stringify({ choices: [{ message: { role: "assistant", content: "收到" } }] }));
|
|
648
|
+
} });
|
|
649
|
+
t.after(async () => { await plugin.deactivate("stop"); await (0, promises_1.rm)(dataDir, { recursive: true, force: true }); });
|
|
650
|
+
await plugin.activate({ dataDir, config: { endpoint: "https://example.test/v1/chat/completions", model: "test", apiKey: "secret", allowedPeers: [], allowedAgents: [] } });
|
|
651
|
+
const send = (sessionId, task) => plugin.handle({ sessionId, task, management: management });
|
|
652
|
+
for (let i = 0; i < 22; i++)
|
|
653
|
+
await send("连续", String(i));
|
|
654
|
+
strict_1.default.equal(inputs.at(-1).messages.length, 42);
|
|
655
|
+
strict_1.default.equal(inputs.at(-1).messages[1].content, "1");
|
|
656
|
+
await send("连续", "长".repeat(120_000));
|
|
657
|
+
await send("连续", "边界");
|
|
658
|
+
strict_1.default.equal(inputs.at(-1).messages.length, 2);
|
|
659
|
+
for (let i = 0; i < 129; i++)
|
|
660
|
+
await send(String(i), "独立会话");
|
|
661
|
+
await send("0", "已淘汰");
|
|
662
|
+
strict_1.default.equal(inputs.at(-1).messages.length, 2);
|
|
663
|
+
});
|