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.
Files changed (59) hide show
  1. package/README.md +2 -0
  2. package/dist/src-ts/channel-sessions.js +90 -24
  3. package/dist/src-ts/control-server.js +48 -7
  4. package/dist/src-ts/plugin-manager.js +47 -0
  5. package/dist/src-ts/plugins/chatgpt/cdp-client.js +72 -0
  6. package/dist/src-ts/plugins/chatgpt/index.js +122 -4
  7. package/dist/src-ts/plugins/doubao/cdp-client.js +243 -18
  8. package/dist/src-ts/plugins/doubao/index.js +81 -5
  9. package/dist/src-ts/plugins/doubao/session-history.js +123 -0
  10. package/dist/src-ts/plugins/dsh/index.js +16 -2
  11. package/dist/src-ts/plugins/dsh/session-history.js +371 -0
  12. package/dist/src-ts/plugins/qwenwork/cdp-client.js +8 -0
  13. package/dist/src-ts/plugins/qwenwork/index.js +12 -1
  14. package/dist/src-ts/plugins/qwenwork/session-history.js +245 -0
  15. package/dist/src-ts/plugins/workbuddy/cdp-client.js +33 -0
  16. package/dist/src-ts/plugins/workbuddy/index.js +21 -1
  17. package/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
  18. package/dist/src-ts/session-archive.js +509 -0
  19. package/dist/src-ts/session-history.js +2 -0
  20. package/dist/src-ts/session-management.js +665 -0
  21. package/dist/src-ts/session-summary.js +171 -0
  22. package/dist/tests-ts/channel-sessions.test.js +233 -2
  23. package/dist/tests-ts/chatgpt-history.test.js +197 -0
  24. package/dist/tests-ts/control-server.test.js +39 -0
  25. package/dist/tests-ts/doubao-history.test.js +172 -0
  26. package/dist/tests-ts/doubao-plugin.test.js +124 -3
  27. package/dist/tests-ts/dsh-history.test.js +165 -0
  28. package/dist/tests-ts/native-session-management.test.js +195 -0
  29. package/dist/tests-ts/plugin-manager.test.js +254 -3
  30. package/dist/tests-ts/qwenwork-history.test.js +339 -0
  31. package/dist/tests-ts/session-archive.test.js +422 -0
  32. package/dist/tests-ts/session-management.test.js +323 -0
  33. package/dist/tests-ts/session-summary.test.js +199 -0
  34. package/dist/tests-ts/workbuddy-history.test.js +256 -0
  35. package/package.json +1 -1
  36. package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/cdp-client.js +72 -0
  37. package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +122 -4
  38. package/plugins/chatgpt/package.json +1 -1
  39. package/plugins/chatgpt/plugin.json +14 -1
  40. package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +243 -18
  41. package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +81 -5
  42. package/plugins/doubao/dist/src-ts/plugins/doubao/session-history.js +123 -0
  43. package/plugins/doubao/package.json +1 -1
  44. package/plugins/doubao/plugin.json +1 -1
  45. package/plugins/dsh/config.schema.json +2 -1
  46. package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +16 -2
  47. package/plugins/dsh/dist/src-ts/plugins/dsh/session-history.js +371 -0
  48. package/plugins/dsh/package.json +1 -1
  49. package/plugins/dsh/plugin.json +1 -1
  50. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +8 -0
  51. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +12 -1
  52. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/session-history.js +245 -0
  53. package/plugins/qwenwork/package.json +1 -1
  54. package/plugins/qwenwork/plugin.json +1 -1
  55. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +33 -0
  56. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +21 -1
  57. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
  58. package/plugins/workbuddy/package.json +1 -1
  59. 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
+ });