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.
Files changed (195) hide show
  1. package/README.md +17 -10
  2. package/dist/src-ts/agent.js +35 -0
  3. package/dist/src-ts/channel-sessions.js +3 -2
  4. package/dist/src-ts/cli.js +67 -37
  5. package/dist/src-ts/control-server.js +12 -30
  6. package/dist/src-ts/core-updater.js +141 -33
  7. package/dist/src-ts/daemon-process.js +2 -1
  8. package/dist/src-ts/dsh-agent.js +31 -13
  9. package/dist/src-ts/http-agent.js +36 -0
  10. package/dist/src-ts/plugin-catalog.js +11 -2
  11. package/dist/src-ts/plugin-manager.js +351 -50
  12. package/dist/src-ts/plugins/abweb/index.js +1 -1
  13. package/dist/src-ts/plugins/abweb/public/app.js +73 -5
  14. package/dist/src-ts/plugins/abweb/public/index.html +15 -0
  15. package/dist/src-ts/plugins/abweb/public/style.css +4 -0
  16. package/dist/src-ts/plugins/aun/attachments.js +3 -21
  17. package/dist/src-ts/plugins/aun/index.js +155 -21
  18. package/dist/src-ts/plugins/aun/messages.js +4 -1
  19. package/dist/src-ts/plugins/aun/public/app.js +2 -1
  20. package/dist/src-ts/plugins/aun/public/index.html +1 -1
  21. package/dist/src-ts/plugins/cdp-draft.js +5 -2
  22. package/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
  23. package/dist/src-ts/plugins/chatgpt/index.js +80 -19
  24. package/dist/src-ts/plugins/claude-cli/index.js +19 -2
  25. package/dist/src-ts/plugins/codex-cli/index.js +19 -2
  26. package/dist/src-ts/plugins/desktop-launch.js +44 -3
  27. package/dist/src-ts/plugins/doubao/cdp-client.js +234 -45
  28. package/dist/src-ts/plugins/doubao/index.js +152 -8
  29. package/dist/src-ts/plugins/doubao/native-task.js +134 -0
  30. package/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
  31. package/dist/src-ts/plugins/dsh/index.js +172 -21
  32. package/dist/src-ts/plugins/dsh/session-history.js +31 -3
  33. package/dist/src-ts/plugins/feishu/index.js +1 -1
  34. package/dist/src-ts/plugins/local-cli/agent.js +74 -9
  35. package/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
  36. package/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
  37. package/dist/src-ts/plugins/personal-assistant/index.js +263 -0
  38. package/dist/src-ts/plugins/personal-assistant/model-client.js +181 -0
  39. package/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
  40. package/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
  41. package/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
  42. package/dist/src-ts/plugins/personal-assistant/tools.js +167 -0
  43. package/dist/src-ts/plugins/qwenwork/cdp-client.js +90 -18
  44. package/dist/src-ts/plugins/qwenwork/index.js +139 -24
  45. package/dist/src-ts/plugins/workbuddy/cdp-client.js +353 -82
  46. package/dist/src-ts/plugins/workbuddy/index.js +124 -41
  47. package/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
  48. package/dist/src-ts/plugins/zcode/index.js +97 -0
  49. package/dist/src-ts/session-management.js +132 -14
  50. package/dist/src-ts/version-manager.js +6 -3
  51. package/dist/tests-ts/abweb-cli.test.js +10 -2
  52. package/dist/tests-ts/abweb-model-config.test.js +73 -0
  53. package/dist/tests-ts/agent.test.js +27 -0
  54. package/dist/tests-ts/aun-attachments.test.js +19 -17
  55. package/dist/tests-ts/aun-default-owner-page.test.js +2 -1
  56. package/dist/tests-ts/aun-messages.test.js +20 -0
  57. package/dist/tests-ts/aun-plugin.test.js +292 -19
  58. package/dist/tests-ts/bundled-package.test.js +12 -2
  59. package/dist/tests-ts/cdp-draft-recovery.test.js +182 -3
  60. package/dist/tests-ts/channel-sessions.test.js +27 -0
  61. package/dist/tests-ts/chatgpt-plugin.test.js +118 -0
  62. package/dist/tests-ts/control-server.test.js +56 -0
  63. package/dist/tests-ts/core-updater.test.js +215 -46
  64. package/dist/tests-ts/daemon-process.test.js +126 -1
  65. package/dist/tests-ts/desktop-launch.test.js +73 -0
  66. package/dist/tests-ts/doubao-native-task.test.js +237 -0
  67. package/dist/tests-ts/doubao-plugin.test.js +773 -12
  68. package/dist/tests-ts/dsh-agent.test.js +157 -8
  69. package/dist/tests-ts/dsh-cdp.test.js +148 -0
  70. package/dist/tests-ts/dsh-history.test.js +37 -0
  71. package/dist/tests-ts/feishu-plugin.test.js +1 -1
  72. package/dist/tests-ts/http-agent.test.js +18 -0
  73. package/dist/tests-ts/installer-assets.test.js +1 -1
  74. package/dist/tests-ts/local-cli.test.js +146 -1
  75. package/dist/tests-ts/model-provider-cancellation.test.js +152 -0
  76. package/dist/tests-ts/native-session-management.test.js +8 -7
  77. package/dist/tests-ts/personal-assistant-coordination.test.js +92 -0
  78. package/dist/tests-ts/personal-assistant-extensions.test.js +35 -0
  79. package/dist/tests-ts/personal-assistant-memory.test.js +117 -0
  80. package/dist/tests-ts/personal-assistant-owner-context.test.js +107 -0
  81. package/dist/tests-ts/personal-assistant-remote-ability.test.js +123 -0
  82. package/dist/tests-ts/personal-assistant.test.js +663 -0
  83. package/dist/tests-ts/platform-integration.test.js +183 -1
  84. package/dist/tests-ts/plugin-catalog.test.js +12 -2
  85. package/dist/tests-ts/plugin-manager.test.js +356 -4
  86. package/dist/tests-ts/plugin-upgrade-deadline.test.js +354 -0
  87. package/dist/tests-ts/publish-packages.test.js +42 -0
  88. package/dist/tests-ts/qwenwork-plugin.test.js +135 -1
  89. package/dist/tests-ts/qwenwork-reconnect.test.js +195 -0
  90. package/dist/tests-ts/qwenwork-sse.test.js +33 -0
  91. package/dist/tests-ts/session-access-defaults.test.js +112 -0
  92. package/dist/tests-ts/session-management.test.js +159 -4
  93. package/dist/tests-ts/version-manager.test.js +40 -0
  94. package/dist/tests-ts/workbuddy-cdp-recovery.test.js +311 -0
  95. package/dist/tests-ts/workbuddy-plugin.test.js +499 -4
  96. package/dist/tests-ts/zcode-cdp.test.js +894 -0
  97. package/dist/tests-ts/zcode-plugin.test.js +396 -0
  98. package/package.json +1 -1
  99. package/plugins/abweb/dist/src-ts/agent.js +35 -0
  100. package/plugins/abweb/dist/src-ts/plugins/abweb/index.js +1 -1
  101. package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.js +73 -5
  102. package/plugins/abweb/dist/src-ts/plugins/abweb/public/app.ts +57 -5
  103. package/plugins/abweb/dist/src-ts/plugins/abweb/public/index.html +15 -0
  104. package/plugins/abweb/dist/src-ts/plugins/abweb/public/style.css +4 -0
  105. package/plugins/abweb/package.json +1 -1
  106. package/plugins/abweb/plugin.json +1 -1
  107. package/plugins/aun/config.schema.json +1 -1
  108. package/plugins/aun/dist/src-ts/agent.js +35 -0
  109. package/plugins/aun/dist/src-ts/plugins/aun/attachments.js +3 -21
  110. package/plugins/aun/dist/src-ts/plugins/aun/index.js +155 -21
  111. package/plugins/aun/dist/src-ts/plugins/aun/messages.js +4 -1
  112. package/plugins/aun/dist/src-ts/plugins/aun/public/app.js +2 -1
  113. package/plugins/aun/dist/src-ts/plugins/aun/public/index.html +1 -1
  114. package/plugins/aun/package.json +1 -1
  115. package/plugins/aun/plugin.json +1 -1
  116. package/plugins/chatgpt/dist/src-ts/agent.js +35 -0
  117. package/plugins/chatgpt/dist/src-ts/plugins/cdp-draft.js +5 -2
  118. package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/cdp-client.js +26 -12
  119. package/plugins/chatgpt/dist/src-ts/plugins/chatgpt/index.js +80 -19
  120. package/plugins/chatgpt/dist/src-ts/plugins/desktop-launch.js +44 -3
  121. package/plugins/chatgpt/package.json +1 -1
  122. package/plugins/chatgpt/plugin.json +1 -1
  123. package/plugins/claude-cli/dist/src-ts/agent.js +35 -0
  124. package/plugins/claude-cli/dist/src-ts/plugins/claude-cli/index.js +19 -2
  125. package/plugins/claude-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
  126. package/plugins/claude-cli/package.json +1 -1
  127. package/plugins/claude-cli/plugin.json +3 -2
  128. package/plugins/codex-cli/dist/src-ts/agent.js +35 -0
  129. package/plugins/codex-cli/dist/src-ts/plugins/codex-cli/index.js +19 -2
  130. package/plugins/codex-cli/dist/src-ts/plugins/local-cli/agent.js +74 -9
  131. package/plugins/codex-cli/package.json +1 -1
  132. package/plugins/codex-cli/plugin.json +3 -2
  133. package/plugins/doubao/config.schema.json +2 -1
  134. package/plugins/doubao/dist/src-ts/agent.js +35 -0
  135. package/plugins/doubao/dist/src-ts/plugins/desktop-launch.js +44 -3
  136. package/plugins/doubao/dist/src-ts/plugins/doubao/cdp-client.js +234 -45
  137. package/plugins/doubao/dist/src-ts/plugins/doubao/index.js +152 -8
  138. package/plugins/doubao/dist/src-ts/plugins/doubao/native-task.js +134 -0
  139. package/plugins/doubao/package.json +1 -1
  140. package/plugins/doubao/plugin.json +3 -2
  141. package/plugins/dsh/config.schema.json +5 -2
  142. package/plugins/dsh/dist/src-ts/agent.js +35 -0
  143. package/plugins/dsh/dist/src-ts/dsh-agent.js +31 -13
  144. package/plugins/dsh/dist/src-ts/plugins/desktop-launch.js +44 -3
  145. package/plugins/dsh/dist/src-ts/plugins/dsh/cdp-client.js +334 -0
  146. package/plugins/dsh/dist/src-ts/plugins/dsh/index.js +172 -21
  147. package/plugins/dsh/dist/src-ts/plugins/dsh/session-history.js +31 -3
  148. package/plugins/dsh/package.json +1 -1
  149. package/plugins/dsh/plugin.json +3 -2
  150. package/plugins/feishu/dist/src-ts/agent.js +35 -0
  151. package/plugins/feishu/dist/src-ts/plugins/feishu/index.js +1 -1
  152. package/plugins/feishu/package.json +1 -1
  153. package/plugins/feishu/plugin.json +1 -1
  154. package/plugins/personal-assistant/config.schema.json +23 -0
  155. package/plugins/personal-assistant/dist/src-ts/agent.js +69 -0
  156. package/plugins/personal-assistant/dist/src-ts/attachments.js +62 -0
  157. package/plugins/personal-assistant/dist/src-ts/plugin.js +13 -0
  158. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/claude-cli-model.js +30 -0
  159. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/coordination.js +57 -0
  160. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/index.js +263 -0
  161. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/model-client.js +181 -0
  162. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/owner-context.js +56 -0
  163. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/remote-ability.js +176 -0
  164. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/session-memory.js +124 -0
  165. package/plugins/personal-assistant/dist/src-ts/plugins/personal-assistant/tools.js +167 -0
  166. package/plugins/personal-assistant/package.json +39 -0
  167. package/plugins/personal-assistant/plugin.json +15 -0
  168. package/plugins/qwenwork/dist/src-ts/agent.js +35 -0
  169. package/plugins/qwenwork/dist/src-ts/plugins/cdp-draft.js +5 -2
  170. package/plugins/qwenwork/dist/src-ts/plugins/desktop-launch.js +44 -3
  171. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/cdp-client.js +90 -18
  172. package/plugins/qwenwork/dist/src-ts/plugins/qwenwork/index.js +139 -24
  173. package/plugins/qwenwork/package.json +1 -1
  174. package/plugins/qwenwork/plugin.json +3 -2
  175. package/plugins/workbuddy/config.schema.json +1 -1
  176. package/plugins/workbuddy/dist/src-ts/agent.js +35 -0
  177. package/plugins/workbuddy/dist/src-ts/plugins/desktop-launch.js +44 -3
  178. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/cdp-client.js +353 -82
  179. package/plugins/workbuddy/dist/src-ts/plugins/workbuddy/index.js +124 -41
  180. package/plugins/workbuddy/package.json +1 -1
  181. package/plugins/workbuddy/plugin.json +3 -2
  182. package/plugins/zcode/config.schema.json +13 -0
  183. package/plugins/zcode/dist/src-ts/agent.js +69 -0
  184. package/plugins/zcode/dist/src-ts/attachments.js +62 -0
  185. package/plugins/zcode/dist/src-ts/plugin.js +13 -0
  186. package/plugins/zcode/dist/src-ts/plugins/desktop-launch.js +238 -0
  187. package/plugins/zcode/dist/src-ts/plugins/workbuddy/cdp-client.js +896 -0
  188. package/plugins/zcode/dist/src-ts/plugins/workbuddy/cli-client.js +39 -0
  189. package/plugins/zcode/dist/src-ts/plugins/workbuddy/desktop-launch.js +10 -0
  190. package/plugins/zcode/dist/src-ts/plugins/workbuddy/index.js +567 -0
  191. package/plugins/zcode/dist/src-ts/plugins/workbuddy/session-history.js +121 -0
  192. package/plugins/zcode/dist/src-ts/plugins/zcode/cdp-client.js +500 -0
  193. package/plugins/zcode/dist/src-ts/plugins/zcode/index.js +97 -0
  194. package/plugins/zcode/package.json +16 -0
  195. package/plugins/zcode/plugin.json +11 -0
@@ -10,7 +10,7 @@ const session_summary_1 = require("./session-summary");
10
10
  class ManagementError extends Error {
11
11
  }
12
12
  const dailyPlugins = ["qwenwork", "workbuddy", "doubao", "chatgpt", "dsh"];
13
- const usage = "用法:/sessions today [插件ID] [--summary];/sessions last <1–24>h [插件ID] [--summary];/sessions list [插件ID] [--page 页码 --snapshot 编号];/sessions show <编号>;/sessions native-list <插件ID>;/sessions native-show <编号>;/sessions summary --from <编号,...> [主题]";
13
+ const usage = "用法:/sessions today [插件ID] [--summary];/sessions last <1-24>h [插件ID] [--summary];/sessions list [插件ID] [--page 页码 --snapshot 编号];/sessions show <编号>;/sessions native-list <插件ID>;/sessions native-show <编号>;/sessions summary --from <编号,...> [主题]";
14
14
  function isDailySummaryRequest(text) {
15
15
  return /(?:今天|今日).{0,20}(?:聊了什么|聊了哪些|话题|小结|总结)|(?:小结|总结).{0,20}(?:今天|今日|对话)|最近\s*\d+\s*(?:小时|h).{0,20}(?:聊|话题|小结|总结|对话|消息)/i.test(text.trim());
16
16
  }
@@ -20,7 +20,7 @@ class SessionManagement {
20
20
  bindings;
21
21
  nativeProviders;
22
22
  archive;
23
- settings = { enabled: false, grants: [] };
23
+ settings = { enabled: false, allowFrom: [], grants: [] };
24
24
  loaded;
25
25
  writes = Promise.resolve();
26
26
  snapshots = new Map();
@@ -41,12 +41,20 @@ class SessionManagement {
41
41
  this.settings = this.validate(JSON.parse(await (0, promises_1.readFile)((0, node_path_1.join)(this.directory, "session-management.json"), "utf8")));
42
42
  }
43
43
  catch (error) {
44
- if (error.code !== "ENOENT")
44
+ if (error.code !== "ENOENT") {
45
45
  this.audit("配置读取", { actor: "local", count: 0, status: "invalid" });
46
+ throw new ManagementError("会话设置读取失败,请检查本机配置");
47
+ }
46
48
  }
47
49
  this.archive.setEnabled(this.settings.enabled);
48
50
  })();
49
- await this.loaded;
51
+ try {
52
+ await this.loaded;
53
+ }
54
+ catch (error) {
55
+ this.loaded = undefined;
56
+ throw error;
57
+ }
50
58
  }
51
59
  async config() {
52
60
  await this.load();
@@ -111,13 +119,111 @@ class SessionManagement {
111
119
  if (revision !== this.revision)
112
120
  throw new ManagementError("权限或归档设置已变更,请重新执行 /sessions list");
113
121
  }
122
+ allowsAllHistory(context) {
123
+ return this.settings.allowFrom.length === 0 || this.settings.allowFrom.includes("*") || this.settings.allowFrom.includes(JSON.parse(context.source)[3]);
124
+ }
114
125
  sources(context) {
126
+ if (this.allowsAllHistory(context))
127
+ return undefined;
115
128
  const grant = this.settings.grants.find(item => item.managerSource === context.source && item.managerSessionId === context.sessionId);
116
129
  return [...new Set([context.source, ...(grant?.sources ?? [])])];
117
130
  }
118
131
  async catalog(sources, agentPlugin) {
119
132
  const result = await this.archive.snapshotSessions();
120
- return { sessions: result.sessions.filter(item => sources.includes(item.source) && (!agentPlugin || item.agentPlugin === agentPlugin)), complete: result.complete };
133
+ return { sessions: result.sessions.filter(item => (!sources || sources.includes(item.source)) && (!agentPlugin || item.agentPlugin === agentPlugin)), complete: result.complete };
134
+ }
135
+ async summarizeToday(context, now = new Date()) {
136
+ await this.load();
137
+ validateSource(context.source);
138
+ if (!context.sessionId)
139
+ throw new ManagementError("当前逻辑会话无效");
140
+ if (!(now instanceof Date) || !Number.isFinite(now.getTime()))
141
+ throw new ManagementError("总结时间无效");
142
+ const revision = this.revision;
143
+ const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
144
+ const end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
145
+ const cutoff = Math.min(now.getTime(), end.getTime());
146
+ const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
147
+ const range = `${formatLocalDate(start)} 至 ${formatLocalDate(new Date(cutoff))} (${zone})`;
148
+ const limitation = "原生插件历史未纳入今日总结:缺少可验证的历史时间,仅统计归档正文";
149
+ const selected = [];
150
+ let characters = 0;
151
+ try {
152
+ const catalog = await this.catalog(this.sources(context));
153
+ this.checkRevision(revision);
154
+ for (const session of catalog.sessions) {
155
+ const turns = [];
156
+ const reasons = new Set([
157
+ ...(session.missingReasons ?? []),
158
+ ...(catalog.complete ? [] : ["归档目录不完整"]),
159
+ limitation,
160
+ ]);
161
+ let complete = true;
162
+ for (let page = 1; page <= 20; page++) {
163
+ const result = await this.archive.readSession(session.sessionId, { page, limit: 100 });
164
+ this.checkRevision(revision);
165
+ if (!result.session) {
166
+ complete = false;
167
+ reasons.add("会话正文不存在");
168
+ break;
169
+ }
170
+ for (const reason of result.missingReasons)
171
+ reasons.add(reason);
172
+ complete &&= result.complete;
173
+ for (const turn of result.turns) {
174
+ const timestamp = Date.parse(turn.time);
175
+ if (!Number.isFinite(timestamp)) {
176
+ complete = false;
177
+ reasons.add("部分正文时间未知");
178
+ continue;
179
+ }
180
+ if (timestamp >= start.getTime() && timestamp <= cutoff) {
181
+ characters += turn.text.length;
182
+ if (characters > session_summary_1.MAX_SUMMARY_CHARACTERS)
183
+ throw new ManagementError("总结输入超过60000字符,请缩小范围");
184
+ turns.push({ role: turn.role, time: turn.time, text: turn.text });
185
+ }
186
+ }
187
+ if (!result.hasNext)
188
+ break;
189
+ if (page === 20) {
190
+ complete = false;
191
+ reasons.add("正文超过20页,未完整读取");
192
+ }
193
+ }
194
+ if (!turns.length)
195
+ continue;
196
+ if (this.bindings?.isBusy(session.source))
197
+ reasons.add("源会话正在运行,仅包含已完成轮次");
198
+ if (selected.length >= session_summary_1.MAX_SUMMARY_SESSIONS)
199
+ throw new ManagementError(`今日会话超过 ${session_summary_1.MAX_SUMMARY_SESSIONS} 个,请缩小来源范围`);
200
+ selected.push({
201
+ sessionId: session.sessionId, agentPlugin: session.agentPlugin, channel: session.channel,
202
+ ...(session.title ? { title: session.title } : {}), firstSeenAt: session.firstSeenAt, updatedAt: session.updatedAt,
203
+ complete: complete && reasons.size === 0, incompleteReasons: [...reasons], turns,
204
+ });
205
+ }
206
+ this.checkRevision(revision);
207
+ if (!selected.length) {
208
+ const result = { supported: false, text: `今日没有可总结的已归档会话(${range})。${limitation}`, complete: false, sources: [] };
209
+ this.audit("今日总结", { actor: context.source, count: 0, status: "empty" });
210
+ return result;
211
+ }
212
+ const summarizer = this.settings.summarizer?.model;
213
+ const result = await new session_summary_1.SessionSummarizer(this.settings.summarizer).summarizeSessions({ sessions: selected }).catch(error => {
214
+ if (error instanceof session_summary_1.SessionSummaryError && error.code === "limit")
215
+ throw new ManagementError(error.message);
216
+ throw new ManagementError("只读总结失败,请检查本机配置与模型服务");
217
+ });
218
+ this.checkRevision(revision);
219
+ const output = { ...result, text: `今日工作小结(${range}):\n${result.text}\n\n说明:${limitation}` };
220
+ this.audit("今日总结", { actor: context.source, count: selected.length, status: result.supported ? "ok" : "unsupported", refs: selected.map(item => item.sessionId), summarizer });
221
+ return output;
222
+ }
223
+ catch (error) {
224
+ this.audit("今日总结", { actor: context.source, count: selected.length, status: "failed", ...(selected.length ? { refs: selected.map(item => item.sessionId) } : {}) });
225
+ throw error;
226
+ }
121
227
  }
122
228
  async command(text, context) {
123
229
  const dailyRequest = isDailySummaryRequest(text);
@@ -137,15 +243,15 @@ class SessionManagement {
137
243
  const recent = input.match(/^\/sessions last (\d+)h(?: (qwenwork|workbuddy|doubao|chatgpt|dsh))?( --summary)?$/i);
138
244
  const naturalRecent = dailyRequest ? input.match(/最近\s*(\d+)\s*(?:小时|h)/i) : undefined;
139
245
  if (naturalRecent)
140
- return await this.summarizeToday(context, undefined, Boolean(this.settings.summarizer), Number(naturalRecent[1]));
246
+ return await this.summarizeNativeToday(context, undefined, Boolean(this.settings.summarizer), Number(naturalRecent[1]));
141
247
  if (recent)
142
- return await this.summarizeToday(context, recent[2], Boolean(recent[3]), Number(recent[1]));
248
+ return await this.summarizeNativeToday(context, recent[2], Boolean(recent[3]), Number(recent[1]));
143
249
  if (/^\/sessions last\b/i.test(input))
144
250
  throw new ManagementError("时间范围须为最近1至24小时,例如 /sessions last 4h");
145
251
  if (dailyRequest)
146
- return await this.summarizeToday(context, undefined, Boolean(this.settings.summarizer));
252
+ return await this.summarizeNativeToday(context, undefined, Boolean(this.settings.summarizer));
147
253
  if (today)
148
- return await this.summarizeToday(context, today[1], Boolean(today[2]));
254
+ return await this.summarizeNativeToday(context, today[1], Boolean(today[2]));
149
255
  const nativeList = input.match(/^\/sessions native-list ([a-z][a-z0-9-]*)(?: --page ([1-9]\d*))?$/);
150
256
  if (nativeList)
151
257
  return await this.listNative(context, nativeList[1], nativeList[2] ? Number(nativeList[2]) : undefined);
@@ -240,7 +346,7 @@ class SessionManagement {
240
346
  providers() {
241
347
  return this.nativeProviders().map(({ pluginId, provider, status }) => ({ pluginId, scope: provider.scope, mode: provider.mode, limitation: provider.limitation, status }));
242
348
  }
243
- async summarizeToday(context, onlyPlugin, summarize = false, hours) {
349
+ async summarizeNativeToday(context, onlyPlugin, summarize = false, hours) {
244
350
  const revision = this.revision;
245
351
  const cutoff = Date.now();
246
352
  const configured = this.settings.summarizer?.model;
@@ -370,6 +476,8 @@ class SessionManagement {
370
476
  return `${header}\n${result.text}`;
371
477
  }
372
478
  authorizedNative(context, pluginId, scope) {
479
+ if (this.allowsAllHistory(context))
480
+ return;
373
481
  const grant = this.settings.nativeGrants?.find(item => item.managerSource === context.source && item.managerSessionId === context.sessionId);
374
482
  if (!grant?.providers.some(item => item.pluginId === pluginId && (scope === undefined || item.scope === scope)))
375
483
  throw new ManagementError("无权读取该原生插件历史,请由本机管理员精确授权");
@@ -518,7 +626,7 @@ class SessionManagement {
518
626
  const catalog = await this.catalog(sources, plugin);
519
627
  const archived = catalog.sessions;
520
628
  const known = new Set(archived.map(item => item.sessionId));
521
- const bound = (await this.bindings?.list() ?? []).filter(item => sources.includes(item.source) && (!plugin || item.agentPlugin === plugin) && !known.has(item.logicalSessionId)).map(item => ({ sessionId: item.logicalSessionId, source: item.source, agentPlugin: item.agentPlugin, channel: JSON.parse(item.source)[0], updatedAt: item.updatedAt }));
629
+ const bound = (await this.bindings?.list() ?? []).filter(item => (!sources || sources.includes(item.source)) && (!plugin || item.agentPlugin === plugin) && !known.has(item.logicalSessionId)).map(item => ({ sessionId: item.logicalSessionId, source: item.source, agentPlugin: item.agentPlugin, channel: JSON.parse(item.source)[0], updatedAt: item.updatedAt }));
522
630
  const sessions = [...archived, ...bound].filter(item => item.sessionId !== context.sessionId).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt) || a.sessionId.localeCompare(b.sessionId));
523
631
  this.checkRevision(revision);
524
632
  const key = (0, node_crypto_1.randomUUID)().replace(/-/g, "").slice(0, 12);
@@ -543,7 +651,8 @@ class SessionManagement {
543
651
  if (!snapshot || snapshot.revision !== this.revision || snapshot.expires < Date.now() || snapshot.context.source !== context.source || snapshot.context.sessionId !== context.sessionId)
544
652
  throw new ManagementError("编号无效或已过期,请重新执行 /sessions list");
545
653
  const session = snapshot.sessions[Number(match[2]) - 1];
546
- if (!session || !this.sources(context).includes(session.source) || session.sessionId === context.sessionId)
654
+ const sources = this.sources(context);
655
+ if (!session || (sources && !sources.includes(session.source)) || session.sessionId === context.sessionId)
547
656
  throw new ManagementError("无权读取所选会话,请重新列举");
548
657
  return session;
549
658
  }
@@ -551,8 +660,11 @@ class SessionManagement {
551
660
  if (!value || typeof value !== "object" || Array.isArray(value))
552
661
  throw new ManagementError("归档设置必须是对象");
553
662
  const input = value;
554
- if (Object.keys(input).some(key => !["enabled", "grants", "nativeGrants", "summarizer"].includes(key)) || typeof input.enabled !== "boolean" || !Array.isArray(input.grants) || input.grants.length > 500)
663
+ if (Object.keys(input).some(key => !["enabled", "allowFrom", "grants", "nativeGrants", "summarizer"].includes(key)) || typeof input.enabled !== "boolean" || !Array.isArray(input.grants) || input.grants.length > 500)
555
664
  throw new ManagementError("归档设置字段无效");
665
+ const allowFrom = input.allowFrom === undefined ? [] : input.allowFrom;
666
+ if (!Array.isArray(allowFrom) || allowFrom.length > 500 || allowFrom.some(value => value !== "*" && !exactIdentifier(value, 512)) || new Set(allowFrom).size !== allowFrom.length)
667
+ throw new ManagementError("allowFrom 来源白名单无效");
556
668
  const managers = new Set();
557
669
  const grants = input.grants.map((raw) => {
558
670
  if (!raw || typeof raw !== "object" || Array.isArray(raw))
@@ -614,7 +726,7 @@ class SessionManagement {
614
726
  const apiKey = config.apiKey === undefined && config.endpoint === previousEndpoint ? previousKey : config.apiKey;
615
727
  summarizer = { endpoint: config.endpoint, model: config.model, ...(apiKey ? { apiKey } : {}) };
616
728
  }
617
- return { enabled: input.enabled, grants, ...(nativeGrants ? { nativeGrants } : {}), ...(summarizer ? { summarizer } : {}) };
729
+ return { enabled: input.enabled, allowFrom, grants, ...(nativeGrants ? { nativeGrants } : {}), ...(summarizer ? { summarizer } : {}) };
618
730
  }
619
731
  }
620
732
  exports.SessionManagement = SessionManagement;
@@ -631,6 +743,12 @@ function validateSource(value) {
631
743
  if (!Array.isArray(source) || source.length !== 5 || source.some(item => typeof item !== "string" || /[\u0000-\u001f]/.test(item)) || !["aun", "feishu"].includes(source[0]) || !source[3] || JSON.stringify(source) !== value)
632
744
  throw new ManagementError("来源格式无效");
633
745
  }
746
+ function formatLocalDate(value) {
747
+ const pad = (part) => String(part).padStart(2, "0");
748
+ const offset = -value.getTimezoneOffset();
749
+ const sign = offset >= 0 ? "+" : "-";
750
+ return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())} ${pad(value.getHours())}:${pad(value.getMinutes())}${sign}${pad(Math.floor(Math.abs(offset) / 60))}:${pad(Math.abs(offset) % 60)}`;
751
+ }
634
752
  function exactIdentifier(value, maximum) {
635
753
  return typeof value === "string" && value.length > 0 && value.length <= maximum && !/[\u0000-\u001f*]/.test(value);
636
754
  }
@@ -59,14 +59,17 @@ class VersionManager {
59
59
  if (!parseVersion(currentVersion))
60
60
  throw new Error("当前版本号无效");
61
61
  const raw = await (this.options.npm ?? this.queryNpm)(["view", packageName, "version", "dist-tags", "--json"]);
62
- const value = JSON.parse(raw);
63
- const latestVersion = typeof value === "string" ? value : String(value.version ?? value.latest ?? "");
62
+ const parsed = JSON.parse(raw);
63
+ const value = (Array.isArray(parsed) && parsed.length === 1 ? parsed[0] : parsed);
64
+ const latestVersion = typeof value === "string" ? value : String(value?.version ?? value?.latest ?? "");
64
65
  if (!parseVersion(latestVersion))
65
66
  throw new Error("npm 返回的版本号无效");
66
67
  status = { packageName, currentVersion, latestVersion, state: compareVersions(latestVersion, currentVersion) > 0 ? "update-available" : "latest", checkedAt: new Date().toISOString() };
67
68
  }
68
69
  catch (error) {
69
- status = { packageName, currentVersion, state: "error", error: error instanceof Error ? error.message : String(error), checkedAt: new Date().toISOString() };
70
+ const message = error instanceof Error ? error.message : String(error);
71
+ const unpublished = /npm\s+error\s+code\s+E404\b|404\s+Not Found/i.test(message);
72
+ status = { packageName, currentVersion, state: unpublished ? "unpublished" : "error", error: unpublished ? "npm 包未发布或当前无权访问" : message, checkedAt: new Date().toISOString() };
70
73
  }
71
74
  this.statuses.set(packageName, status);
72
75
  await this.save();
@@ -35,7 +35,12 @@ class Element {
35
35
  fetch: async (path, options) => {
36
36
  let data = {};
37
37
  if (path === "/api/plugins")
38
- data = { plugins: [{ id: "codex-cli", displayName: "Codex CLI", type: "agent", version: "1.0.0", status: "installed", path: "plugins/codex-cli", manifest: { executionMode: "cli" } }] };
38
+ data = { plugins: [
39
+ { id: "codex-cli", displayName: "Codex CLI", type: "agent", version: "1.0.0", status: "installed", path: "plugins/codex-cli", manifest: { executionMode: "cli" } },
40
+ { id: "qwenwork", displayName: "千问办公", type: "agent", version: "1.0.0", status: "ready", path: "plugins/qwenwork" },
41
+ { id: "doubao", displayName: "豆包", type: "agent", version: "1.0.0", status: "ready", path: "plugins/doubao", manifest: { executionMode: "cdp" } },
42
+ { id: "aun", displayName: "AUN", type: "channel", version: "1.0.0", status: "ready", path: "plugins/aun" },
43
+ ] };
39
44
  if (path.endsWith("/config")) {
40
45
  if (options.method === "PATCH")
41
46
  saved = JSON.parse(options.body);
@@ -46,7 +51,10 @@ class Element {
46
51
  });
47
52
  await new Promise(resolve => setImmediate(resolve));
48
53
  const row = node("#plugins").children[0];
49
- strict_1.default.match(row.children[1].textContent, /CLI/);
54
+ strict_1.default.equal(row.children[1].textContent, "Agent · CLI");
55
+ strict_1.default.equal(node("#plugins").children[1].children[1].textContent, "Agent · 桌面");
56
+ strict_1.default.equal(node("#plugins").children[2].children[1].textContent, "Agent · 桌面");
57
+ strict_1.default.equal(node("#plugins").children[3].children[1].textContent, "Channel");
50
58
  const configure = row.children.at(-1).children.find(child => child.textContent === "配置");
51
59
  strict_1.default.ok(configure, "CLI 插件必须提供配置按钮");
52
60
  await configure.onclick();
@@ -0,0 +1,73 @@
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 promises_1 = require("node:fs/promises");
9
+ const node_vm_1 = require("node:vm");
10
+ class Element {
11
+ hidden = false;
12
+ disabled = false;
13
+ value = "";
14
+ textContent = "";
15
+ placeholder = "";
16
+ open = false;
17
+ children = [];
18
+ options = [];
19
+ selectedIndex = 0;
20
+ onclick;
21
+ onchange;
22
+ onsubmit;
23
+ append(...children) { this.children.push(...children); this.options = this.children; }
24
+ replaceChildren(...children) { this.children = children; this.options = this.children; }
25
+ setAttribute() { }
26
+ showModal() { this.open = true; }
27
+ close() { this.open = false; }
28
+ }
29
+ for (const legacyWildcard of [false, true])
30
+ (0, node_test_1.default)(`ABWeb 个人助理模型设置保留配置,旧通配名单显示为空白(兼容模式:${legacyWildcard})`, async () => {
31
+ const script = await (0, promises_1.readFile)("src-ts/plugins/abweb/public/app.js", "utf8");
32
+ const nodes = new Map();
33
+ const node = (selector) => { if (!nodes.has(selector))
34
+ nodes.set(selector, new Element()); return nodes.get(selector); };
35
+ const calls = [];
36
+ const plugin = { id: "personal-assistant", type: "agent", displayName: "个人助理", status: "ready", version: "1.0.0", path: "/plugins/personal-assistant" };
37
+ const config = { modelProvider: "plugin", modelPlugin: "workbuddy", apiKey: "******", endpoint: "http://127.0.0.1:1234/v1/chat/completions", model: "local", allowedPeers: legacyWildcard ? ["*"] : ["peer.agentid.cn"], allowedAgents: legacyWildcard ? ["*"] : ["dsh"], ownerAid: "owner.agentid.cn", timeoutMs: 120000 };
38
+ (0, node_vm_1.runInNewContext)(script, {
39
+ document: { querySelector: node, createElement: () => new Element(), hidden: false },
40
+ localStorage: { getItem: () => null, setItem() { }, removeItem() { } },
41
+ window: { addEventListener() { } }, setInterval() { }, confirm: () => false, structuredClone,
42
+ fetch: async (path, init) => {
43
+ calls.push({ path, method: init.method, body: init.body ? JSON.parse(init.body) : undefined });
44
+ const data = path.endsWith("/config") ? config : path === "/api/model-providers" ? { providers: [
45
+ { id: "workbuddy", displayName: "WorkBuddy", status: "failed", available: false, reason: "桌面未连接" },
46
+ { id: "qwenwork", displayName: "千问办公", status: "ready", available: true, reason: "" },
47
+ ] } : { plugins: [plugin], messages: [], sessions: [], mode: "auto-install", config: { providers: {}, plugins: {} } };
48
+ return { ok: true, status: 200, json: async () => data };
49
+ },
50
+ });
51
+ await new Promise(resolve => setImmediate(resolve));
52
+ const buttons = (element) => [element, ...element.children.flatMap(buttons)];
53
+ const configure = buttons(node("#plugins")).find(item => item.textContent === "配置模型");
54
+ strict_1.default.ok(configure, "个人助理应显示配置入口");
55
+ configure.onclick();
56
+ await new Promise(resolve => setImmediate(resolve));
57
+ strict_1.default.equal(node("#assistant-config-dialog").open, true);
58
+ strict_1.default.equal(node("#assistant-model-provider").value, "plugin:workbuddy");
59
+ const choices = node("#assistant-model-provider").children;
60
+ strict_1.default.ok(choices.some(option => option.value === "plugin:qwenwork" && !option.disabled));
61
+ strict_1.default.ok(choices.some(option => option.value === "plugin:workbuddy" && option.disabled && /桌面未连接/.test(option.textContent)));
62
+ strict_1.default.ok(choices.every(option => option.value !== "claude-cli"), "未安装的 Claude CLI 不应被硬编码为可用来源");
63
+ strict_1.default.equal(node("#assistant-api-key").value, "");
64
+ strict_1.default.equal(node("#assistant-allowed-peers").value, legacyWildcard ? "" : "peer.agentid.cn");
65
+ strict_1.default.equal(node("#assistant-allowed-agents").value, legacyWildcard ? "" : "dsh");
66
+ node("#assistant-model-provider").value = "plugin:qwenwork";
67
+ node("#assistant-config-form").onsubmit({ preventDefault() { } });
68
+ await new Promise(resolve => setImmediate(resolve));
69
+ const saved = calls.find(call => call.method === "PATCH" && call.path.endsWith("personal-assistant/config"));
70
+ strict_1.default.ok(saved);
71
+ strict_1.default.deepEqual(saved.body, { modelPlugin: "qwenwork", ...(legacyWildcard ? { allowedPeers: [], allowedAgents: [] } : {}) });
72
+ strict_1.default.ok(calls.some(call => call.path === "/api/model-providers"));
73
+ });
@@ -0,0 +1,27 @@
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 agent_1 = require("../src-ts/agent");
9
+ (0, node_test_1.default)("取消只结束调用方等待,不会停止仍在运行的桌面操作", async () => {
10
+ const controller = new AbortController();
11
+ let finish;
12
+ let operationFinished = false;
13
+ const operation = new Promise(resolve => { finish = () => { operationFinished = true; resolve("迟到结果"); }; });
14
+ const waiting = (0, agent_1.abortable)(operation, controller.signal);
15
+ controller.abort(new Error("请求已取消"));
16
+ await strict_1.default.rejects(waiting, /请求已取消/);
17
+ strict_1.default.equal(operationFinished, false);
18
+ finish();
19
+ strict_1.default.equal(await operation, "迟到结果");
20
+ strict_1.default.equal(operationFinished, true);
21
+ });
22
+ (0, node_test_1.default)("预取消仍观察已经启动的操作拒绝", async () => {
23
+ const controller = new AbortController();
24
+ controller.abort(new Error("预取消"));
25
+ await strict_1.default.rejects((0, agent_1.abortable)(Promise.reject(new Error("底层迟到失败")), controller.signal), /预取消/);
26
+ await new Promise(resolve => setImmediate(resolve));
27
+ });
@@ -43,17 +43,20 @@ const sha256 = (0, node_crypto_1.createHash)("sha256").update(data).digest("hex"
43
43
  strict_1.default.match(calls[0], /peer\.agentid\.cn/);
44
44
  strict_1.default.match(calls[1], /limit[^0-9]*33554433/);
45
45
  });
46
- (0, node_test_1.default)("AUN 出站上传保持私有并按对端创建 24 小时分享", async () => {
46
+ (0, node_test_1.default)("AUN 出站上传使用公开标准对象引用,不创建私有分享", async () => {
47
47
  const calls = [];
48
48
  const storage = {
49
49
  async writeBytes(path, bytes, options) { calls.push(["write", { path, bytes, options }]); return { contentType: "text/plain", size_bytes: 3, sha256 }; },
50
50
  lowlevel: { async createShareLink(options) { calls.push(["share", options]); return { owner_aid: "self.agentid.cn", bucket: "default", object_key: options.objectKey, share_id: "Ab3xK9mZ2q", path_url: `https://self.agentid.cn/${options.objectKey}?t=Ab3xK9mZ2q`, allowed_aids: options.allowedAids }; } },
51
51
  };
52
52
  const result = await (0, attachments_1.uploadAunAttachments)({ storage }, "self.agentid.cn", "peer.agentid.cn", [{ kind: "file", data, filename: "报告.txt", contentType: "text/plain" }]);
53
- strict_1.default.equal(result[0].url, "https://self.agentid.cn/" + result[0].object_key + "?t=Ab3xK9mZ2q");
54
- strict_1.default.deepEqual((calls.find(([kind]) => kind === "share")?.[1]).allowedAids, ["peer.agentid.cn"]);
55
- strict_1.default.equal((calls.find(([kind]) => kind === "share")?.[1]).expireInSeconds, 86400);
56
- strict_1.default.equal((calls.find(([kind]) => kind === "write")?.[1]).options.public, false);
53
+ strict_1.default.deepEqual(result[0], {
54
+ owner_aid: "self.agentid.cn", object_key: (calls.find(([kind]) => kind === "write")?.[1]).path.slice(1),
55
+ filename: "报告.txt", content_type: "text/plain", size_bytes: 3, sha256,
56
+ });
57
+ strict_1.default.equal((calls.find(([kind]) => kind === "write")?.[1]).options.public, true);
58
+ strict_1.default.equal((calls.find(([kind]) => kind === "write")?.[1]).options.overwrite, true);
59
+ strict_1.default.equal(calls.filter(([kind]) => kind === "share").length, 0);
57
60
  });
58
61
  (0, node_test_1.default)("AUN 视频上传遵循 EvolCore 标准对象引用,不创建私有分享", async () => {
59
62
  const writes = [];
@@ -66,14 +69,14 @@ const sha256 = (0, node_crypto_1.createHash)("sha256").update(data).digest("hex"
66
69
  strict_1.default.deepEqual(writes[0].options, { contentType: "video/mp4", public: true, overwrite: true });
67
70
  strict_1.default.deepEqual(result, [{ owner_aid: "self.agentid.cn", object_key: writes[0].path.slice(1), filename: "视频.mp4", content_type: "video/mp4", size_bytes: 3, sha256 }]);
68
71
  });
69
- (0, node_test_1.default)("AUN 分享失败不会回退为公开上传", async () => {
70
- let writeOptions;
72
+ (0, node_test_1.default)("AUN 标准附件上传失败时不会创建分享", async () => {
73
+ let shareCalls = 0;
71
74
  const storage = {
72
- async writeBytes(_path, _bytes, options) { writeOptions = options; return {}; },
73
- lowlevel: { async createShareLink() { throw new Error("拒绝分享"); } },
75
+ async writeBytes() { throw new Error("存储不可用"); },
76
+ lowlevel: { async createShareLink() { shareCalls++; throw new Error("不应创建分享"); } },
74
77
  };
75
- await strict_1.default.rejects(() => (0, attachments_1.uploadAunAttachments)({ storage }, "self.agentid.cn", "peer.agentid.cn", [{ kind: "file", data, filename: "a.txt", contentType: "text/plain" }]), /分享/);
76
- strict_1.default.equal(writeOptions?.public, false);
78
+ await strict_1.default.rejects(() => (0, attachments_1.uploadAunAttachments)({ storage }, "self.agentid.cn", "peer.agentid.cn", [{ kind: "file", data, filename: "a.txt", contentType: "text/plain" }]), /上传 AUN FS 文件/);
79
+ strict_1.default.equal(shareCalls, 0);
77
80
  });
78
81
  (0, node_test_1.default)("AUN 元数据与正文的哈希不符时拒绝入站附件", async () => {
79
82
  const storage = { async stat() { return { size: 3, sha256: "0".repeat(64), contentType: "text/plain", type: "file" }; }, async readBytes() { return data; } };
@@ -86,12 +89,11 @@ const sha256 = (0, node_crypto_1.createHash)("sha256").update(data).digest("hex"
86
89
  await strict_1.default.rejects(() => (0, attachments_1.downloadAunAttachment)({ storage }, ref), /引用|路径/);
87
90
  strict_1.default.equal(reads, 0);
88
91
  });
89
- (0, node_test_1.default)("AUN 私有分享拒绝服务端返回的公开授权或不匹配元数据", async () => {
90
- const client = { storage: {
91
- async writeBytes() { return { size: 3, sha256, contentType: "text/plain" }; },
92
- lowlevel: { async createShareLink(options) { return { object_key: options.objectKey, share_id: "Ab3xK9mZ2q", allowed_aids: ["*"], path_url: `https://self.agentid.cn/${options.objectKey}?t=Ab3xK9mZ2q` }; } },
93
- } };
94
- await strict_1.default.rejects(() => (0, attachments_1.uploadAunAttachments)(client, "self.agentid.cn", "peer.agentid.cn", [{ kind: "file", data, filename: "a.txt", contentType: "text/plain" }]), /分享/);
92
+ (0, node_test_1.default)("AUN 标准附件拒绝上传结果中的错配对象或哈希", async () => {
93
+ for (const metadata of [{ owner_aid: "other.agentid.cn" }, { object_key: "shared/other.txt" }, { sha256: "0".repeat(64) }]) {
94
+ const client = { storage: { async writeBytes() { return { size: 3, sha256, contentType: "text/plain", ...metadata }; } } };
95
+ await strict_1.default.rejects(() => (0, attachments_1.uploadAunAttachments)(client, "self.agentid.cn", "peer.agentid.cn", [{ kind: "file", data, filename: "a.txt", contentType: "text/plain" }]), /引用|哈希/);
96
+ }
95
97
  });
96
98
  (0, node_test_1.default)("AUN 通用图片类型按普通文件传入", async () => {
97
99
  const storage = { async stat() { return { size: 3 }; }, async readBytes() { return data; } };
@@ -51,7 +51,7 @@ const flush = () => new Promise((resolve) => setImmediate(resolve));
51
51
  posts.push(JSON.parse(options.body));
52
52
  return { ok: true, json: async () => ({ status: "已创建", connection: "ready" }) };
53
53
  }
54
- const data = path === "/api/plugins/aun/config" ? { owner_aid: owner }
54
+ const data = path === "/api/plugins/aun/config" ? { owner_aid: owner, allow_from: "*" }
55
55
  : path === "/api/plugins/aun/identities" ? { groups: { muAgentBridge: [], evolcore: [] } }
56
56
  : { plugins: [] };
57
57
  return { ok: true, json: async () => data };
@@ -60,6 +60,7 @@ const flush = () => new Promise((resolve) => setImmediate(resolve));
60
60
  const create = node("#create-form");
61
61
  await flush();
62
62
  strict_1.default.equal(create.elements.ownerAid.value, "first.agentid.cn");
63
+ strict_1.default.equal(node("#config-form").elements.allow_from.value, "", "旧通配名单显示为空白,沿用接受所有来源的语义");
63
64
  owner = "second.agentid.cn";
64
65
  node("#refresh").onclick();
65
66
  await flush();
@@ -14,6 +14,19 @@ const messages_1 = require("../src-ts/plugins/aun/messages");
14
14
  peerId: "other.agentid.cn", messageId: "m1", text: "你好", threadId: "t1",
15
15
  });
16
16
  });
17
+ (0, node_test_1.default)("AUN 来源白名单使用 * 时接受所有 Peer", () => {
18
+ strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { text: "你好" } }, "self.agentid.cn", "*")?.peerId, "peer.agentid.cn");
19
+ });
20
+ (0, node_test_1.default)("AUN 来源白名单可用逗号分隔限制来源,包含 * 时仍接受所有 Peer", () => {
21
+ strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { text: "你好" } }, "self.agentid.cn", "other.agentid.cn, *")?.peerId, "peer.agentid.cn");
22
+ strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { text: "你好" } }, "self.agentid.cn", "other.agentid.cn"), undefined);
23
+ });
24
+ (0, node_test_1.default)("AUN 错误和通知事件不作为任务触发自动互答", () => {
25
+ for (const type of ["error", "notice"]) {
26
+ strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { type, text: "今日额度已用完", thread_id: "test-thread" } }, "self.agentid.cn"), undefined);
27
+ }
28
+ strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { type: "text", text: "请分析今日额度已用完的原因" } }, "self.agentid.cn")?.text, "请分析今日额度已用完的原因");
29
+ });
17
30
  (0, node_test_1.default)("AUN 纯附件消息保留可定位引用并拒绝任意 URL", () => {
18
31
  const message = (0, messages_1.normalizeAunMessage)({
19
32
  from: "peer.agentid.cn", message_id: "attachment-1", payload: {
@@ -61,3 +74,10 @@ const messages_1 = require("../src-ts/plugins/aun/messages");
61
74
  strict_1.default.equal(message?.attachments?.[0].ownerAid, "third.agentid.cn");
62
75
  strict_1.default.equal(message?.attachments?.[0].token, "Ab3xK9mZ2q");
63
76
  });
77
+ (0, node_test_1.default)("AUN 来源留空或未配置时接受所有 Peer,仍忽略自身与空来源", () => {
78
+ for (const rule of [undefined, "", " "]) {
79
+ strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "peer.agentid.cn", payload: { text: "你好" } }, "self.agentid.cn", rule)?.peerId, "peer.agentid.cn");
80
+ strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "self.agentid.cn", payload: { text: "你好" } }, "self.agentid.cn", rule), undefined);
81
+ strict_1.default.equal((0, messages_1.normalizeAunMessage)({ from: "", payload: { text: "你好" } }, "self.agentid.cn", rule), undefined);
82
+ }
83
+ });