mu-agent-bridge 1.0.35 → 1.0.39
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 +2 -0
- package/dist/src-ts/channel-sessions.js +90 -24
- package/dist/src-ts/control-server.js +48 -7
- package/dist/src-ts/plugin-manager.js +47 -0
- package/dist/src-ts/plugins/chatgpt/cdp-client.js +72 -0
- package/dist/src-ts/plugins/chatgpt/index.js +122 -4
- package/dist/src-ts/plugins/doubao/cdp-client.js +243 -18
- package/dist/src-ts/plugins/doubao/index.js +81 -5
- package/dist/src-ts/plugins/doubao/session-history.js +123 -0
- package/dist/src-ts/plugins/dsh/index.js +16 -2
- package/dist/src-ts/plugins/dsh/session-history.js +371 -0
- package/dist/src-ts/plugins/qwenwork/cdp-client.js +8 -0
- package/dist/src-ts/plugins/qwenwork/index.js +12 -1
- package/dist/src-ts/plugins/qwenwork/session-history.js +245 -0
- package/dist/src-ts/plugins/workbuddy/cdp-client.js +33 -0
- package/dist/src-ts/plugins/workbuddy/index.js +21 -1
- package/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
- package/dist/src-ts/session-archive.js +509 -0
- package/dist/src-ts/session-history.js +2 -0
- package/dist/src-ts/session-management.js +665 -0
- package/dist/src-ts/session-summary.js +171 -0
- package/dist/tests-ts/channel-sessions.test.js +233 -2
- package/dist/tests-ts/chatgpt-history.test.js +197 -0
- package/dist/tests-ts/control-server.test.js +39 -0
- package/dist/tests-ts/doubao-history.test.js +172 -0
- package/dist/tests-ts/doubao-plugin.test.js +124 -3
- package/dist/tests-ts/dsh-history.test.js +165 -0
- package/dist/tests-ts/native-session-management.test.js +195 -0
- package/dist/tests-ts/plugin-manager.test.js +254 -3
- package/dist/tests-ts/qwenwork-history.test.js +339 -0
- package/dist/tests-ts/session-archive.test.js +422 -0
- package/dist/tests-ts/session-management.test.js +323 -0
- package/dist/tests-ts/session-summary.test.js +199 -0
- package/dist/tests-ts/workbuddy-history.test.js +256 -0
- package/package.json +1 -1
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/cdp-client.js +72 -0
- package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +122 -4
- package/plugins/chatgpt/package.json +1 -1
- package/plugins/chatgpt/plugin.json +14 -1
- package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +243 -18
- package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +81 -5
- package/plugins/doubao/dist/src-ts/plugins/doubao/session-history.js +123 -0
- package/plugins/doubao/package.json +1 -1
- package/plugins/doubao/plugin.json +1 -1
- package/plugins/dsh/config.schema.json +2 -1
- package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +16 -2
- package/plugins/dsh/dist/src-ts/plugins/dsh/session-history.js +371 -0
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/plugin.json +1 -1
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +8 -0
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +12 -1
- package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/session-history.js +245 -0
- package/plugins/qwenwork/package.json +1 -1
- package/plugins/qwenwork/plugin.json +1 -1
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +33 -0
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +21 -1
- package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
- package/plugins/workbuddy/package.json +1 -1
- package/plugins/workbuddy/plugin.json +1 -1
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
8
|
+
const session_history_1 = require("../src-ts/plugins/qwenwork/session-history");
|
|
9
|
+
const now = "2026-10-21T03:48:50.000Z";
|
|
10
|
+
function provider(responses) {
|
|
11
|
+
const calls = [];
|
|
12
|
+
const history = new session_history_1.QwenworkSessionHistoryProvider({
|
|
13
|
+
scope: "qwenwork:cdp:9228",
|
|
14
|
+
unavailableReason: () => "",
|
|
15
|
+
query: async (path, input) => {
|
|
16
|
+
calls.push({ path, input });
|
|
17
|
+
const key = path === "chats.listBySubChat" ? `${path}:${String(input.beforeSequence ?? "latest")}` : path;
|
|
18
|
+
const value = responses[key] ?? responses[path];
|
|
19
|
+
if (value instanceof Error)
|
|
20
|
+
throw value;
|
|
21
|
+
return value;
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
return { history, calls };
|
|
25
|
+
}
|
|
26
|
+
(0, node_test_1.default)("千问列出全部活跃、归档和删除会话并分页返回", async () => {
|
|
27
|
+
const { history, calls } = provider({
|
|
28
|
+
"chats.list": [{ id: "today", name: "今日任务", updatedAt: now }],
|
|
29
|
+
"chats.listArchived": [{ id: "archived", name: "归档任务", updatedAt: "2026-10-20T03:00:00.000Z", archivedAt: now }],
|
|
30
|
+
"chats.listDeleted": [{ id: "deleted", name: "已删除任务", updatedAt: "2026-10-19T03:00:00.000Z", deletedAt: now }],
|
|
31
|
+
});
|
|
32
|
+
const first = await history.listSessions({ limit: 2 });
|
|
33
|
+
strict_1.default.equal(first.supported, true);
|
|
34
|
+
if (!first.supported)
|
|
35
|
+
return;
|
|
36
|
+
strict_1.default.deepEqual(first.sessions.map(session => session.id), ["today", "archived"]);
|
|
37
|
+
strict_1.default.ok(first.nextCursor);
|
|
38
|
+
strict_1.default.equal(first.complete, true);
|
|
39
|
+
const second = await history.listSessions({ limit: 2, cursor: first.nextCursor });
|
|
40
|
+
strict_1.default.equal(second.supported, true);
|
|
41
|
+
if (!second.supported)
|
|
42
|
+
return;
|
|
43
|
+
strict_1.default.equal(second.sessions[0]?.id, "deleted");
|
|
44
|
+
strict_1.default.equal(second.nextCursor, undefined);
|
|
45
|
+
strict_1.default.equal(calls.filter(call => call.path === "chats.list").length, 2);
|
|
46
|
+
});
|
|
47
|
+
(0, node_test_1.default)("千问读取所有 sub-chat,保留真实消息时间并支持正文分页", async () => {
|
|
48
|
+
const { history, calls } = provider({
|
|
49
|
+
"chats.get": { id: "today", name: "今日任务", updatedAt: now, subChats: [
|
|
50
|
+
{ id: "sub-a", name: "主任务", createdAt: now, updatedAt: now },
|
|
51
|
+
{ id: "sub-b", name: "子任务", createdAt: now, updatedAt: now },
|
|
52
|
+
] },
|
|
53
|
+
"chats.listBySubChat": {
|
|
54
|
+
messages: [
|
|
55
|
+
{ id: "m3", role: "assistant", parts: [{ type: "tool-Thinking", text: "隐藏推理" }, { type: "text", text: "处理完成" }], createdAt: "2026-10-21T04:00:00.000Z", sequence: 3 },
|
|
56
|
+
{ id: "m2", role: "user", parts: [{ type: "text", text: "请处理这个问题" }], createdAt: "2026-10-21T03:50:00.000Z", sequence: 2 },
|
|
57
|
+
], hasMoreBefore: true, hasMoreAfter: false, oldestSequence: 2, newestSequence: 3,
|
|
58
|
+
},
|
|
59
|
+
"chats.listBySubChat:2": {
|
|
60
|
+
messages: [{ id: "m1", role: "assistant", parts: [{ type: "text", text: "旧子任务结果" }], createdAt: "2026-10-21T03:40:00.000Z", sequence: 1 }],
|
|
61
|
+
hasMoreBefore: false, hasMoreAfter: true, oldestSequence: 1, newestSequence: 1,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
const first = await history.readSession({ sessionId: "today", limit: 2 });
|
|
65
|
+
strict_1.default.equal(first.supported, true);
|
|
66
|
+
if (!first.supported)
|
|
67
|
+
return;
|
|
68
|
+
strict_1.default.deepEqual(first.turns.map(turn => [turn.id, turn.time, turn.text]), [
|
|
69
|
+
["m1", "2026-10-21T03:40:00.000Z", "旧子任务结果"],
|
|
70
|
+
["m2", "2026-10-21T03:50:00.000Z", "请处理这个问题"],
|
|
71
|
+
]);
|
|
72
|
+
strict_1.default.ok(first.nextCursor);
|
|
73
|
+
strict_1.default.equal(first.complete, true);
|
|
74
|
+
const second = await history.readSession({ sessionId: "today", limit: 2, cursor: first.nextCursor });
|
|
75
|
+
strict_1.default.equal(second.supported, true);
|
|
76
|
+
if (!second.supported)
|
|
77
|
+
return;
|
|
78
|
+
strict_1.default.deepEqual(second.turns.map(turn => turn.id), ["m3"]);
|
|
79
|
+
strict_1.default.equal(second.nextCursor, undefined);
|
|
80
|
+
strict_1.default.equal(calls.filter(call => call.path === "chats.listBySubChat").length, 8);
|
|
81
|
+
});
|
|
82
|
+
(0, node_test_1.default)("千问未知可见正文块会标记不完整,思考块仍被排除", async () => {
|
|
83
|
+
const { history } = provider({
|
|
84
|
+
"chats.get": { id: "mixed", name: "混合正文", subChats: [{ id: "sub" }] },
|
|
85
|
+
"chats.listBySubChat": { messages: [
|
|
86
|
+
{ id: "m1", role: "user", parts: [{ type: "text", text: "可见问题" }, { type: "file", uri: "private" }], createdAt: now },
|
|
87
|
+
{ id: "m2", role: "assistant", parts: [{ type: "tool-Thinking", text: "不返回" }, { type: "text", text: "可见回答" }], createdAt: now },
|
|
88
|
+
], hasMoreBefore: false },
|
|
89
|
+
});
|
|
90
|
+
const result = await history.readSession({ sessionId: "mixed", limit: 10 });
|
|
91
|
+
strict_1.default.equal(result.supported, true);
|
|
92
|
+
if (result.supported) {
|
|
93
|
+
strict_1.default.equal(result.complete, false);
|
|
94
|
+
strict_1.default.deepEqual(result.turns.map(turn => turn.text), ["可见问题", "可见回答"]);
|
|
95
|
+
strict_1.default.ok(result.missingReasons.some(reason => reason.includes("parts")));
|
|
96
|
+
strict_1.default.ok(!JSON.stringify(result).includes("不返回"));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
(0, node_test_1.default)("千问会话或消息时间缺失时明确标记不完整", async () => {
|
|
100
|
+
const { history } = provider({
|
|
101
|
+
"chats.list": [{ id: "no-time", name: "未知时间" }],
|
|
102
|
+
"chats.listArchived": [],
|
|
103
|
+
"chats.listDeleted": [],
|
|
104
|
+
"chats.get": { id: "no-time", name: "未知时间", updatedAt: now, subChats: [{ id: "sub" }] },
|
|
105
|
+
"chats.listBySubChat": { messages: [{ id: "m", role: "user", parts: [{ type: "text", text: "问题" }] }], hasMoreBefore: false },
|
|
106
|
+
});
|
|
107
|
+
const listing = await history.listSessions({ limit: 10 });
|
|
108
|
+
strict_1.default.equal(listing.supported, true);
|
|
109
|
+
if (!listing.supported)
|
|
110
|
+
return;
|
|
111
|
+
strict_1.default.equal(listing.complete, false);
|
|
112
|
+
strict_1.default.match(listing.missingReasons.join(" "), /更新时间/);
|
|
113
|
+
const read = await history.readSession({ sessionId: "no-time", limit: 10 });
|
|
114
|
+
strict_1.default.equal(read.supported, true);
|
|
115
|
+
if (!read.supported)
|
|
116
|
+
return;
|
|
117
|
+
strict_1.default.equal(read.complete, false);
|
|
118
|
+
strict_1.default.match(read.missingReasons.join(" "), /创建时间/);
|
|
119
|
+
strict_1.default.deepEqual(read.turns, [{ id: "m", role: "user", text: "问题" }]);
|
|
120
|
+
});
|
|
121
|
+
(0, node_test_1.default)("千问畸形会话目录结构不会被当作完整空目录", async () => {
|
|
122
|
+
const malformed = provider({
|
|
123
|
+
"chats.list": [{ id: "valid", name: "有效会话", updatedAt: now }, null, { name: "缺少 ID" }],
|
|
124
|
+
"chats.listArchived": [],
|
|
125
|
+
"chats.listDeleted": [],
|
|
126
|
+
});
|
|
127
|
+
const partial = await malformed.history.listSessions({ limit: 10 });
|
|
128
|
+
strict_1.default.equal(partial.supported, true);
|
|
129
|
+
if (!partial.supported)
|
|
130
|
+
return;
|
|
131
|
+
strict_1.default.deepEqual(partial.sessions.map(session => session.id), ["valid"]);
|
|
132
|
+
strict_1.default.equal(partial.complete, false);
|
|
133
|
+
strict_1.default.match(partial.missingReasons.join(" "), /无法识别的条目/);
|
|
134
|
+
const invalid = provider({ "chats.list": "unexpected", "chats.listArchived": [], "chats.listDeleted": [] });
|
|
135
|
+
const unsupported = await invalid.history.listSessions({ limit: 10 });
|
|
136
|
+
strict_1.default.equal(unsupported.supported, false);
|
|
137
|
+
if (!unsupported.supported)
|
|
138
|
+
strict_1.default.match(unsupported.reason, /无法识别的结构/);
|
|
139
|
+
});
|
|
140
|
+
(0, node_test_1.default)("千问畸形 sub-chat 条目标记正文不完整,仍保留有效 sub-chat", async () => {
|
|
141
|
+
const { history } = provider({
|
|
142
|
+
"chats.get": { id: "chat", name: "任务", updatedAt: now, subChats: [null, { name: "缺少 ID" }, { id: "valid-sub" }] },
|
|
143
|
+
"chats.listBySubChat": { messages: [{ id: "m1", role: "user", parts: [{ type: "text", text: "消息" }], createdAt: now }], hasMoreBefore: false },
|
|
144
|
+
});
|
|
145
|
+
const result = await history.readSession({ sessionId: "chat", limit: 10 });
|
|
146
|
+
strict_1.default.equal(result.supported, true);
|
|
147
|
+
if (!result.supported)
|
|
148
|
+
return;
|
|
149
|
+
strict_1.default.equal(result.complete, false);
|
|
150
|
+
strict_1.default.match(result.missingReasons.join(" "), /sub-chat 目录缺失或含有无法识别的条目/);
|
|
151
|
+
strict_1.default.deepEqual(result.turns.map(turn => turn.id), ["m1"]);
|
|
152
|
+
});
|
|
153
|
+
(0, node_test_1.default)("千问正文游标可覆盖超过1000条消息", async () => {
|
|
154
|
+
const messages = Array.from({ length: 1002 }, (_, index) => ({
|
|
155
|
+
id: `m${index}`, role: "user", parts: [{ type: "text", text: `消息 ${index}` }],
|
|
156
|
+
createdAt: new Date(Date.parse(now) + index * 1000).toISOString(), sequence: 1002 - index,
|
|
157
|
+
}));
|
|
158
|
+
const { history } = provider({
|
|
159
|
+
"chats.get": { id: "chat", name: "任务", updatedAt: now, subChats: [{ id: "sub" }] },
|
|
160
|
+
"chats.listBySubChat": { messages, hasMoreBefore: false },
|
|
161
|
+
});
|
|
162
|
+
let result = await history.readSession({ sessionId: "chat", limit: 200 });
|
|
163
|
+
for (let page = 0; page < 5; page++) {
|
|
164
|
+
strict_1.default.equal(result.supported, true);
|
|
165
|
+
if (!result.supported || !result.nextCursor)
|
|
166
|
+
return;
|
|
167
|
+
result = await history.readSession({ sessionId: "chat", limit: 200, cursor: result.nextCursor });
|
|
168
|
+
}
|
|
169
|
+
strict_1.default.equal(result.supported, true);
|
|
170
|
+
if (!result.supported)
|
|
171
|
+
return;
|
|
172
|
+
strict_1.default.deepEqual(result.turns.map(turn => turn.id), ["m1000", "m1001"]);
|
|
173
|
+
});
|
|
174
|
+
(0, node_test_1.default)("千问 user/assistant 畸形 parts 标记不完整,纯 thinking 消息仍忽略", async () => {
|
|
175
|
+
const { history } = provider({
|
|
176
|
+
"chats.get": { id: "chat", name: "任务", updatedAt: now, subChats: [{ id: "sub" }] },
|
|
177
|
+
"chats.listBySubChat": { messages: [
|
|
178
|
+
{ id: "bad-parts", role: "user", parts: "错误结构", createdAt: now },
|
|
179
|
+
{ id: "bad-entry", role: "assistant", parts: [{ type: "text", text: "可见回复" }, null], createdAt: now },
|
|
180
|
+
{ id: "thinking-only", role: "assistant", parts: [{ type: "tool-Thinking", text: "隐藏思考" }], createdAt: now },
|
|
181
|
+
], hasMoreBefore: false },
|
|
182
|
+
});
|
|
183
|
+
const result = await history.readSession({ sessionId: "chat", limit: 10 });
|
|
184
|
+
strict_1.default.equal(result.supported, true);
|
|
185
|
+
if (!result.supported)
|
|
186
|
+
return;
|
|
187
|
+
strict_1.default.equal(result.complete, false);
|
|
188
|
+
strict_1.default.match(result.missingReasons.join(" "), /parts 格式无效/);
|
|
189
|
+
strict_1.default.match(result.missingReasons.join(" "), /无法识别的 parts/);
|
|
190
|
+
strict_1.default.deepEqual(result.turns.map(turn => [turn.id, turn.text]), [["bad-entry", "可见回复"]]);
|
|
191
|
+
});
|
|
192
|
+
(0, node_test_1.default)("千问纯 thinking 消息不产生正文,也不造成错误的不完整标记", async () => {
|
|
193
|
+
const { history } = provider({
|
|
194
|
+
"chats.get": { id: "chat", name: "任务", updatedAt: now, subChats: [{ id: "sub" }] },
|
|
195
|
+
"chats.listBySubChat": { messages: [
|
|
196
|
+
{ id: "thinking-only", role: "assistant", parts: [{ type: "tool-Thinking", input: { text: "隐藏思考" } }], createdAt: now },
|
|
197
|
+
], hasMoreBefore: false },
|
|
198
|
+
});
|
|
199
|
+
const result = await history.readSession({ sessionId: "chat", limit: 10 });
|
|
200
|
+
strict_1.default.equal(result.supported, true);
|
|
201
|
+
if (!result.supported)
|
|
202
|
+
return;
|
|
203
|
+
strict_1.default.equal(result.complete, true);
|
|
204
|
+
strict_1.default.deepEqual(result.turns, []);
|
|
205
|
+
strict_1.default.deepEqual(result.missingReasons, []);
|
|
206
|
+
});
|
|
207
|
+
(0, node_test_1.default)("since 过滤正文前仍参与快照校验,旧消息畸形 parts 不影响今日完整性", async () => {
|
|
208
|
+
const { history } = provider({
|
|
209
|
+
"chats.get": { id: "chat", name: "任务", updatedAt: now, subChats: [{ id: "sub" }] },
|
|
210
|
+
"chats.listBySubChat": { messages: [
|
|
211
|
+
{ id: "old", role: "assistant", parts: [{ unknownOldShape: true }], createdAt: "2026-10-20T23:59:59.000Z", sequence: 1 },
|
|
212
|
+
{ id: "today", role: "assistant", parts: [{ type: "text", text: "今天完成" }], createdAt: "2026-10-21T00:00:00.000Z", sequence: 2 },
|
|
213
|
+
], hasMoreBefore: false, oldestSequence: 1, newestSequence: 2 },
|
|
214
|
+
});
|
|
215
|
+
const result = await history.readSession({ sessionId: "chat", limit: 10, since: "2026-10-21T00:00:00+00:00" });
|
|
216
|
+
strict_1.default.equal(result.supported, true);
|
|
217
|
+
if (!result.supported)
|
|
218
|
+
return;
|
|
219
|
+
strict_1.default.equal(result.complete, true);
|
|
220
|
+
strict_1.default.deepEqual(result.turns.map(turn => [turn.id, turn.time, turn.text]), [["today", "2026-10-21T00:00:00.000Z", "今天完成"]]);
|
|
221
|
+
});
|
|
222
|
+
(0, node_test_1.default)("since 无效时拒绝;分页游标绑定规范化 since", async () => {
|
|
223
|
+
const { history } = provider({
|
|
224
|
+
"chats.get": { id: "chat", name: "任务", updatedAt: now, subChats: [{ id: "sub" }] },
|
|
225
|
+
"chats.listBySubChat": { messages: [
|
|
226
|
+
{ id: "m1", role: "user", parts: [{ type: "text", text: "一" }], createdAt: "2026-10-21T01:00:00.000Z" },
|
|
227
|
+
{ id: "m2", role: "assistant", parts: [{ type: "text", text: "二" }], createdAt: "2026-10-21T02:00:00.000Z" },
|
|
228
|
+
], hasMoreBefore: false },
|
|
229
|
+
});
|
|
230
|
+
const invalid = await history.readSession({ sessionId: "chat", limit: 10, since: "今天" });
|
|
231
|
+
strict_1.default.equal(invalid.supported, false);
|
|
232
|
+
if (!invalid.supported)
|
|
233
|
+
strict_1.default.match(invalid.reason, /since 时间格式无效/);
|
|
234
|
+
const first = await history.readSession({ sessionId: "chat", limit: 1, since: "2026-10-21T00:00:00Z" });
|
|
235
|
+
strict_1.default.equal(first.supported, true);
|
|
236
|
+
if (!first.supported || !first.nextCursor)
|
|
237
|
+
return;
|
|
238
|
+
const same = await history.readSession({ sessionId: "chat", limit: 1, since: "2026-10-21T00:00:00+00:00", cursor: first.nextCursor });
|
|
239
|
+
strict_1.default.equal(same.supported, true);
|
|
240
|
+
const changed = await history.readSession({ sessionId: "chat", limit: 1, since: "2026-10-21T00:01:00Z", cursor: first.nextCursor });
|
|
241
|
+
strict_1.default.equal(changed.supported, false);
|
|
242
|
+
if (!changed.supported)
|
|
243
|
+
strict_1.default.match(changed.reason, /游标无效/);
|
|
244
|
+
});
|
|
245
|
+
(0, node_test_1.default)("缺少创建时间的消息即使带 since 也标记不完整", async () => {
|
|
246
|
+
const { history } = provider({
|
|
247
|
+
"chats.get": { id: "chat", name: "任务", updatedAt: now, subChats: [{ id: "sub" }] },
|
|
248
|
+
"chats.listBySubChat": { messages: [{ id: "missing-time", role: "user", parts: [{ type: "text", text: "未知时间内容" }] }], hasMoreBefore: false },
|
|
249
|
+
});
|
|
250
|
+
const result = await history.readSession({ sessionId: "chat", limit: 10, since: "2026-10-21T00:00:00.000Z" });
|
|
251
|
+
strict_1.default.equal(result.supported, true);
|
|
252
|
+
if (!result.supported)
|
|
253
|
+
return;
|
|
254
|
+
strict_1.default.equal(result.complete, false);
|
|
255
|
+
strict_1.default.match(result.missingReasons.join(" "), /缺少有效创建时间/);
|
|
256
|
+
});
|
|
257
|
+
(0, node_test_1.default)("千问附件和文件产物转换成可供总结使用的提示文本", async () => {
|
|
258
|
+
const { history } = provider({
|
|
259
|
+
"chats.get": { id: "chat", name: "任务", updatedAt: now, subChats: [{ id: "sub" }] },
|
|
260
|
+
"chats.listBySubChat": { messages: [
|
|
261
|
+
{ id: "input", role: "user", parts: [{ type: "data-file", filename: "输入.txt", mediaType: "text/plain" }], createdAt: now },
|
|
262
|
+
{ id: "output", role: "assistant", parts: [{ type: "artifact", fileName: "结果.txt", mediaType: "file" }], createdAt: now },
|
|
263
|
+
], hasMoreBefore: false },
|
|
264
|
+
});
|
|
265
|
+
const result = await history.readSession({ sessionId: "chat", limit: 10, since: "2026-10-21T00:00:00Z" });
|
|
266
|
+
strict_1.default.equal(result.supported, true);
|
|
267
|
+
if (!result.supported)
|
|
268
|
+
return;
|
|
269
|
+
strict_1.default.equal(result.complete, true);
|
|
270
|
+
strict_1.default.deepEqual(result.turns.map(turn => turn.text), ["[附件:输入.txt]", "[文件产物:结果.txt]"]);
|
|
271
|
+
});
|
|
272
|
+
(0, node_test_1.default)("千问列表分页发现目录变化时要求重新列举", async () => {
|
|
273
|
+
let changed = false;
|
|
274
|
+
const history = new session_history_1.QwenworkSessionHistoryProvider({
|
|
275
|
+
scope: "qwenwork:cdp:9228", unavailableReason: () => "",
|
|
276
|
+
query: async (path) => {
|
|
277
|
+
if (path === "chats.list")
|
|
278
|
+
return changed
|
|
279
|
+
? [{ id: "new", name: "新会话", updatedAt: now }, { id: "a", name: "A", updatedAt: now }, { id: "b", name: "B", updatedAt: now }]
|
|
280
|
+
: [{ id: "a", name: "A", updatedAt: now }, { id: "b", name: "B", updatedAt: now }, { id: "c", name: "C", updatedAt: now }];
|
|
281
|
+
if (path === "chats.listArchived" || path === "chats.listDeleted")
|
|
282
|
+
return [];
|
|
283
|
+
throw new Error("unexpected");
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
const first = await history.listSessions({ limit: 2 });
|
|
287
|
+
strict_1.default.equal(first.supported, true);
|
|
288
|
+
if (!first.supported || !first.nextCursor)
|
|
289
|
+
return;
|
|
290
|
+
changed = true;
|
|
291
|
+
const next = await history.listSessions({ limit: 2, cursor: first.nextCursor });
|
|
292
|
+
strict_1.default.equal(next.supported, false);
|
|
293
|
+
if (!next.supported)
|
|
294
|
+
strict_1.default.match(next.reason, /发生变化,请重新列举/);
|
|
295
|
+
});
|
|
296
|
+
(0, node_test_1.default)("千问正文分页发现消息变化时要求重新读取", async () => {
|
|
297
|
+
let changed = false;
|
|
298
|
+
const history = new session_history_1.QwenworkSessionHistoryProvider({
|
|
299
|
+
scope: "qwenwork:cdp:9228", unavailableReason: () => "",
|
|
300
|
+
query: async (path, input) => {
|
|
301
|
+
if (path === "chats.get")
|
|
302
|
+
return { id: "chat", name: "任务", updatedAt: now, subChats: [{ id: "sub" }] };
|
|
303
|
+
if (path === "chats.listBySubChat") {
|
|
304
|
+
const messages = changed
|
|
305
|
+
? [{ id: "new", role: "assistant", parts: [{ type: "text", text: "新" }], createdAt: now }]
|
|
306
|
+
: [
|
|
307
|
+
{ id: "m1", role: "user", parts: [{ type: "text", text: "一" }], createdAt: "2026-10-21T03:00:00Z" },
|
|
308
|
+
{ id: "m2", role: "assistant", parts: [{ type: "text", text: "二" }], createdAt: "2026-10-21T04:00:00Z" },
|
|
309
|
+
];
|
|
310
|
+
return { messages, hasMoreBefore: false, oldestSequence: input.beforeSequence ? 1 : 2, newestSequence: 2 };
|
|
311
|
+
}
|
|
312
|
+
throw new Error("unexpected");
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
const first = await history.readSession({ sessionId: "chat", limit: 1 });
|
|
316
|
+
strict_1.default.equal(first.supported, true);
|
|
317
|
+
if (!first.supported || !first.nextCursor)
|
|
318
|
+
return;
|
|
319
|
+
changed = true;
|
|
320
|
+
const next = await history.readSession({ sessionId: "chat", limit: 1, cursor: first.nextCursor });
|
|
321
|
+
strict_1.default.equal(next.supported, false);
|
|
322
|
+
if (!next.supported)
|
|
323
|
+
strict_1.default.match(next.reason, /发生变化,请重新读取/);
|
|
324
|
+
});
|
|
325
|
+
(0, node_test_1.default)("千问无效游标和不可用状态不会访问客户端", async () => {
|
|
326
|
+
let requests = 0;
|
|
327
|
+
const history = new session_history_1.QwenworkSessionHistoryProvider({
|
|
328
|
+
scope: "qwenwork:cdp:9228", unavailableReason: () => "千问当前忙",
|
|
329
|
+
query: async () => { requests++; return []; },
|
|
330
|
+
});
|
|
331
|
+
const result = await history.listSessions({ limit: 10, cursor: "tampered" });
|
|
332
|
+
strict_1.default.equal(result.supported, false);
|
|
333
|
+
strict_1.default.match(result.reason, /当前忙/);
|
|
334
|
+
strict_1.default.equal(requests, 0);
|
|
335
|
+
const available = provider({});
|
|
336
|
+
const invalid = await available.history.readSession({ sessionId: "x", limit: 10, cursor: "tampered" });
|
|
337
|
+
strict_1.default.equal(invalid.supported, false);
|
|
338
|
+
strict_1.default.equal(available.calls.length, 0);
|
|
339
|
+
});
|