agent-bridge-aun 1.0.30 → 1.0.32

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 CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  `agent-bridge-aun` 为 mu-agent-bridge 提供 AUN 消息通道,支持使用已有 AID 或通过管理 API 注册 AID,并将收到的文本交给指定 Agent 插件处理。
4
4
 
5
- 安装后在 ABWeb 的插件管理中启动 AUN 通道。配置 `aid` 和可选的 `agent_plugin`;默认 Agent 插件为 `qwenwork`。配置字段 `owner_aid` 是可选字符串,用作默认 Owner。AID 创建接口为 `POST /api/plugins/aun/aid`,请求内容为 `{"aid":"新AID.agentid.cn","owner_aid":"所有者AID.agentid.cn"}`。
5
+ 安装后在 ABWeb 的插件管理中启动 AUN 通道。收到的 AID 消息仅投递给用户在 AUN 身份页中明确关联的 Agent;未关联的 AID 消息会被忽略。旧配置中的 `agent_plugin` 字段不再作为默认路由。配置字段 `owner_aid` 是可选字符串,用作默认 Owner。AID 创建接口为 `POST /api/plugins/aun/aid`,请求内容为 `{"aid":"新AID.agentid.cn","owner_aid":"所有者AID.agentid.cn"}`。
6
6
 
7
7
  创建使用 SDK 的兼容注册流程;Owner AID 只保存为本地归属配置,不代表 AUN 协议 Owner 关系已签名或激活。Gateway 信任根使用随插件提供并校验指纹的 AUN 根证书,不从 Gateway 下载未经验证的 CA 链。
8
8
 
9
- 核心支持插件命令后,可运行 `mab aun status`、`mab aun list`、`mab aun agents`、`mab aun default-owner`、`mab aun default-owner set <owner-aid>`、`mab aun create <aid> [--owner <owner-aid>] [--agent <plugin-id>]`、`mab aun bind <aid> <plugin-id>` 和 `mab aun unbind <plugin-id>`。`default-owner` 不带参数显示当前配置,`default-owner set <owner-aid>` 校验格式并持久化新的默认值。`create` 明确传入 `--owner` 时优先使用该值;省略时回退到同一配置中的 `owner_aid`,两者都没有时返回错误,不会创建 AID。创建新身份时,实际使用的 Owner 会保存为下次默认值。ABWeb 从同一插件配置读取 `owner_aid` 并预填创建表单,因此 CLI 和 ABWeb 共用默认 Owner。Agent 从当前已就绪的插件动态发现。创建后连接或关联失败时,响应会标明 AID 已创建;先用 `mab aun list` 检查,不要重试创建。运行日志不记录原始参数或 Owner AID;默认 Owner 查询会在终端显示当前值。
9
+ 核心支持插件命令后,可运行 `mab aun status`、`mab aun list`、`mab aun agents`、`mab aun default-owner`、`mab aun default-owner set <owner-aid>`、`mab aun create <aid> [--owner <owner-aid>] [--agent <plugin-id>]`、`mab aun bind <aid> <plugin-id>` 和 `mab aun unbind <aid>`。关联或解绑只修改指定 AID,不会影响同一 Agent 下的其它 AID,也不会写入 Agent 全局 AID 配置。`default-owner` 不带参数显示当前配置,`default-owner set <owner-aid>` 校验格式并持久化新的默认值。`create` 明确传入 `--owner` 时优先使用该值;省略时回退到同一配置中的 `owner_aid`,两者都没有时返回错误,不会创建 AID。创建新身份时,实际使用的 Owner 会保存为下次默认值。ABWeb 从同一插件配置读取 `owner_aid` 并预填创建表单,因此 CLI 和 ABWeb 共用默认 Owner。Agent 从当前已就绪的插件动态发现。创建后连接或关联失败时,响应会标明 AID 已创建;先用 `mab aun list` 检查,不要重试创建。运行日志不记录原始参数或 Owner AID;默认 Owner 查询会在终端显示当前值。
10
10
 
11
11
  该插件依赖 `@agentunion/fastaun`,要求 Node.js 22.16 或更高版本。
12
12
 
@@ -7,7 +7,7 @@
7
7
  "binding_times": { "type": "object", "additionalProperties": { "type": "string" } },
8
8
  "aid": { "type": "string", "minLength": 1 },
9
9
  "owner_aid": { "type": "string", "minLength": 1 },
10
- "agent_plugin": { "type": "string", "minLength": 1, "default": "qwenwork" },
10
+ "agent_plugin": { "type": "string", "description": "兼容旧配置;消息只按 AID 的显式关联路由。" },
11
11
  "aun_path": { "type": "string", "minLength": 1 },
12
12
  "encryption_seed": { "type": "string" },
13
13
  "verify_ssl": { "type": "boolean", "default": true },
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AgentError = void 0;
3
+ exports.AgentOperationError = exports.AgentError = void 0;
4
+ exports.errorDetails = errorDetails;
4
5
  class AgentError extends Error {
5
6
  constructor(message, options) {
6
7
  super(message, options);
@@ -8,3 +9,26 @@ class AgentError extends Error {
8
9
  }
9
10
  }
10
11
  exports.AgentError = AgentError;
12
+ /** 保留底层错误码,避免只留下 fetch failed 或超时这类表层提示。 */
13
+ function errorDetails(error) {
14
+ const parts = [];
15
+ const seen = new Set();
16
+ for (let current = error; current && !seen.has(current) && seen.size < 5;) {
17
+ seen.add(current);
18
+ const value = current;
19
+ const message = typeof value.message === "string" ? value.message : String(current);
20
+ parts.push(`${message}${value.code ? `(${String(value.code)})` : ""}`);
21
+ current = value.cause;
22
+ }
23
+ return parts.join(";原因:") || String(error);
24
+ }
25
+ class AgentOperationError extends AgentError {
26
+ code;
27
+ stage;
28
+ constructor(code, stage, cause) {
29
+ super(`${code}:${stage}失败:${errorDetails(cause)}`, { cause });
30
+ this.code = code;
31
+ this.stage = stage;
32
+ }
33
+ }
34
+ exports.AgentOperationError = AgentOperationError;
@@ -8,6 +8,7 @@ exports.uploadAunAttachments = uploadAunAttachments;
8
8
  const node_crypto_1 = require("node:crypto");
9
9
  const node_util_1 = require("node:util");
10
10
  const attachments_1 = require("../../attachments");
11
+ const agent_1 = require("../../agent");
11
12
  const aidPattern = /^[a-z0-9][a-z0-9_-]{2,63}\.[a-z0-9-]+(?:\.[a-z0-9-]+)+$/;
12
13
  const mimePattern = /^[A-Za-z0-9!#$&^_.+-]+\/[A-Za-z0-9!#$&^_.+-]+$/;
13
14
  const imageTypes = new Set(["image/png", "image/jpeg", "image/webp"]);
@@ -252,13 +253,15 @@ async function uploadAunAttachments(client, ownerAid, peerAid, items) {
252
253
  // 视频播放器不携带 AID 凭据,沿用 EvolCore 的标准可读对象上传方式。
253
254
  const video = contentType.startsWith("video/");
254
255
  const ref = { ownerAid, bucket: "default", objectKey, filename, contentType, sizeBytes: item.data.byteLength, sha256 };
255
- const uploaded = await client.storage.writeBytes(`/${objectKey}`, item.data, { contentType, public: video, overwrite: video });
256
+ const uploaded = await client.storage.writeBytes(`/${objectKey}`, item.data, { contentType, public: video, overwrite: video })
257
+ .catch(error => { throw new agent_1.AgentOperationError("AUN_FS_UPLOAD_FAILED", `上传 AUN FS 文件“${filename}”`, error); });
256
258
  checkedMetadata({ ...uploaded, size: uploaded.size ?? uploaded.size_bytes ?? item.data.byteLength }, ref);
257
259
  if (video) {
258
260
  result.push({ owner_aid: ownerAid, object_key: objectKey, filename, content_type: contentType, size_bytes: item.data.byteLength, sha256 });
259
261
  continue;
260
262
  }
261
- const share = await client.storage.lowlevel.createShareLink({ owner: ownerAid, bucket: "default", objectKey, allowedAids: [peerAid], expireInSeconds: 86400 });
263
+ const share = await client.storage.lowlevel.createShareLink({ owner: ownerAid, bucket: "default", objectKey, allowedAids: [peerAid], expireInSeconds: 86400 })
264
+ .catch(error => { throw new agent_1.AgentOperationError("AUN_FS_SHARE_FAILED", `创建 AUN FS 文件“${filename}”的接收方授权`, error); });
262
265
  if (share.owner_aid && share.owner_aid !== ownerAid || share.bucket && share.bucket !== "default" || share.object_key && share.object_key !== objectKey)
263
266
  throw new Error("AUN 附件分享对象不一致");
264
267
  if (share.allowed_aids !== undefined && (!Array.isArray(share.allowed_aids) || share.allowed_aids.length !== 1 || share.allowed_aids[0] !== peerAid))
@@ -8,10 +8,11 @@ const node_module_1 = require("node:module");
8
8
  const node_path_1 = require("node:path");
9
9
  const node_os_1 = require("node:os");
10
10
  const node_url_1 = require("node:url");
11
+ const agent_1 = require("../../agent");
11
12
  const messages_1 = require("./messages");
12
13
  const attachments_1 = require("./attachments");
13
14
  const manifest = {
14
- id: "aun", version: "1.0.30", type: "channel", displayName: "AUN 通道",
15
+ id: "aun", version: "1.0.32", type: "channel", displayName: "AUN 通道",
15
16
  entry: "dist/src-ts/plugins/aun/index.js", apiVersion: "1", process: "in-process",
16
17
  capabilities: ["channel", "channel.identity.create", "channel.receive", "channel.send", "health", "cli"],
17
18
  configSchema: "config.schema.json",
@@ -41,7 +42,7 @@ const cliCommands = [
41
42
  { name: "default-owner", summary: "查询或设置默认 Owner AID", usage: "mab aun default-owner [set <owner-aid>]", examples: ["mab aun default-owner", "mab aun default-owner set owner.agentid.cn"] },
42
43
  { name: "create", summary: "创建并连接 AID,可选关联 Agent", usage: "mab aun create <aid> [--owner <owner-aid>] [--agent <plugin-id>]", examples: ["mab aun create new.agentid.cn --owner owner.agentid.cn --agent qwenwork"] },
43
44
  { name: "bind", summary: "将 AID 关联到 Agent", usage: "mab aun bind <aid> <plugin-id>", examples: ["mab aun bind new.agentid.cn qwenwork"] },
44
- { name: "unbind", summary: "解除 Agent 与 AID 的关联", usage: "mab aun unbind <plugin-id>", examples: ["mab aun unbind qwenwork"] },
45
+ { name: "unbind", summary: "解除指定 AID 与 Agent 的关联", usage: "mab aun unbind <aid>", examples: ["mab aun unbind old.agentid.cn"] },
45
46
  ];
46
47
  class AunCommandError extends Error {
47
48
  statusCode;
@@ -140,11 +141,6 @@ class AunPlugin {
140
141
  await this.connectAid(aid);
141
142
  if (!this.clients.has(aid))
142
143
  throw new Error("AID 当前未连接");
143
- for (const [identity, target] of Object.entries(this.bindings))
144
- if (target === pluginId) {
145
- delete this.bindings[identity];
146
- delete this.bindingTimes[identity];
147
- }
148
144
  this.bindings[aid] = pluginId;
149
145
  }
150
146
  else {
@@ -157,21 +153,11 @@ class AunPlugin {
157
153
  await this.connectAid(aid);
158
154
  if (!this.clients.has(aid))
159
155
  throw new Error("AID 当前未连接");
160
- for (const [identity, target] of Object.entries(this.bindings))
161
- if (target === pluginId) {
162
- delete this.bindings[identity];
163
- delete this.bindingTimes[identity];
164
- }
165
156
  this.bindings[aid] = pluginId;
166
157
  }
167
158
  }
168
- else {
169
- for (const [identity, target] of Object.entries(this.bindings))
170
- if (target === pluginId) {
171
- delete this.bindings[identity];
172
- delete this.bindingTimes[identity];
173
- }
174
- }
159
+ else
160
+ throw new Error("必须指定要关联的 AID");
175
161
  if (aid)
176
162
  this.bindingTimes[aid] = new Date().toISOString();
177
163
  await this.persist();
@@ -186,6 +172,29 @@ class AunPlugin {
186
172
  }
187
173
  this.context?.logger.info("AUN 身份关联已更新", { aid, pluginId });
188
174
  },
175
+ unbind: async (pluginId, aid) => {
176
+ if (!aidPattern.test(aid))
177
+ throw new Error("AID 格式无效");
178
+ if (this.bindings[aid] !== pluginId)
179
+ throw new Error("指定 AID 当前未关联到该 Agent");
180
+ const previous = this.bindings;
181
+ const previousTimes = this.bindingTimes;
182
+ this.bindings = { ...previous };
183
+ this.bindingTimes = { ...previousTimes };
184
+ delete this.bindings[aid];
185
+ delete this.bindingTimes[aid];
186
+ try {
187
+ await this.persist();
188
+ }
189
+ catch (error) {
190
+ this.bindings = previous;
191
+ this.bindingTimes = previousTimes;
192
+ this.config.bindings = previous;
193
+ this.config.binding_times = previousTimes;
194
+ throw error;
195
+ }
196
+ this.context?.logger.info("AUN 单个身份关联已解除", { aid, pluginId });
197
+ },
189
198
  };
190
199
  async persist() {
191
200
  this.config.identities = [...this.identities].sort();
@@ -214,7 +223,7 @@ class AunPlugin {
214
223
  if (typeof aid === "string" && aidPattern.test(aid))
215
224
  this.createdAids.add(aid);
216
225
  }
217
- for (const key of ["aid", "owner_aid", "aun_path", "encryption_seed", "root_ca_path", "allow_from", "agent_plugin"]) {
226
+ for (const key of ["aid", "owner_aid", "aun_path", "encryption_seed", "root_ca_path", "allow_from"]) {
218
227
  if (this.config[key] !== undefined && typeof this.config[key] !== "string")
219
228
  throw new Error(`${key} 必须是字符串`);
220
229
  }
@@ -236,6 +245,7 @@ class AunPlugin {
236
245
  ["POST", "/api/plugins/aun/aid", this.createAid],
237
246
  ["GET", "/api/plugins/aun/identities", this.listIdentities],
238
247
  ["POST", "/api/plugins/aun/bind", this.bindAgent],
248
+ ["POST", "/api/plugins/aun/unbind", this.unbindAgent],
239
249
  ["POST", "/api/plugins/aun/settings", this.saveSettings],
240
250
  ["GET", "/api/plugins/aun/commands", this.listCommands],
241
251
  ["POST", "/api/plugins/aun/commands", this.runCommand],
@@ -419,12 +429,12 @@ class AunPlugin {
419
429
  throw new Error("请求内容过大");
420
430
  }
421
431
  const body = JSON.parse(raw || "{}");
422
- for (const key of ["agent_plugin", "allow_from", "aun_path"])
432
+ for (const key of ["allow_from", "aun_path"])
423
433
  if (typeof body[key] !== "string")
424
434
  return this.json(response, 400, { error: `${key} 必须是字符串` });
425
435
  if (body.encryption_seed !== undefined && typeof body.encryption_seed !== "string")
426
436
  return this.json(response, 400, { error: "encryption_seed 必须是字符串" });
427
- this.config = { ...this.config, agent_plugin: body.agent_plugin, allow_from: body.allow_from, aun_path: body.aun_path, ...(body.encryption_seed ? { encryption_seed: body.encryption_seed } : {}) };
437
+ this.config = { ...this.config, allow_from: body.allow_from, aun_path: body.aun_path, ...(body.encryption_seed ? { encryption_seed: body.encryption_seed } : {}) };
428
438
  await this.persist();
429
439
  this.context?.logger.info("AUN 通道配置已保存");
430
440
  this.json(response, 200, { ok: true });
@@ -455,6 +465,28 @@ class AunPlugin {
455
465
  this.json(response, 400, { error: safeError(error) });
456
466
  }
457
467
  };
468
+ unbindAgent = async (request, response) => {
469
+ try {
470
+ let raw = "";
471
+ for await (const chunk of request) {
472
+ raw += chunk;
473
+ if (raw.length > 65536)
474
+ throw new Error("请求内容过大");
475
+ }
476
+ const body = JSON.parse(raw || "{}");
477
+ const aid = typeof body.aid === "string" ? body.aid.trim() : "";
478
+ if (!aidPattern.test(aid))
479
+ return this.json(response, 400, { error: "AID 格式无效" });
480
+ const pluginId = this.bindings[aid];
481
+ if (!pluginId)
482
+ return this.json(response, 404, { error: "指定 AID 当前未关联 Agent" });
483
+ await this.identity.unbind(pluginId, aid, Number(this.context?.runtime?.ports?.aun ?? 0));
484
+ this.json(response, 200, { ok: true, aid, pluginId });
485
+ }
486
+ catch (error) {
487
+ this.json(response, 400, { error: safeError(error) });
488
+ }
489
+ };
458
490
  listCommands = async (_request, response) => {
459
491
  response.setHeader("cache-control", "no-store");
460
492
  this.json(response, 200, { protocolVersion: 1, plugin: "aun", version: manifest.version, commands: cliCommands });
@@ -515,7 +547,6 @@ class AunPlugin {
515
547
  throw new AunCommandError("AID 格式无效");
516
548
  this.validatePluginId(pluginId);
517
549
  await this.requireAgent(pluginId);
518
- const replacedAids = (await this.agentBindings(pluginId)).filter((previousAid) => previousAid !== aid);
519
550
  try {
520
551
  await this.patchAgentAid(pluginId, aid);
521
552
  }
@@ -524,23 +555,25 @@ class AunPlugin {
524
555
  throw new AunCommandError(`AID 关联请求失败,已回读当前状态:${safeError(error)}`, error instanceof AunCommandError ? error.statusCode : 502, { aid, agent: pluginId, ...(bindingState ? { bindingState } : { bindingState: "unknown" }) });
525
556
  }
526
557
  return {
527
- message: replacedAids.length ? `AID ${replacedAids.join("、")} 已替换为 ${aid} 并关联到 ${pluginId}` : `AID 已关联到 ${pluginId}`,
528
- data: { aid, agent: pluginId, replacedAids },
558
+ message: `AID ${aid} 已关联到 ${pluginId}`,
559
+ data: { aid, agent: pluginId },
529
560
  };
530
561
  }
531
562
  if (command === "unbind" && args.length === 1) {
532
- const [pluginId] = args;
533
- this.validatePluginId(pluginId);
534
- await this.requireAgent(pluginId, false);
535
- const unboundAids = await this.agentBindings(pluginId);
563
+ const [aid] = args;
564
+ if (!aidPattern.test(aid))
565
+ throw new AunCommandError("AID 格式无效");
566
+ const pluginId = this.bindings[aid];
567
+ if (!pluginId)
568
+ throw new AunCommandError("指定 AID 当前未关联 Agent");
536
569
  try {
537
- await this.patchAgentAid(pluginId, "");
570
+ await this.identity.unbind(pluginId, aid, 0);
538
571
  }
539
572
  catch (error) {
540
573
  const bindingState = await this.bindingState(pluginId).catch(() => undefined);
541
- throw new AunCommandError(`解除关联请求失败,已回读当前状态:${safeError(error)}`, error instanceof AunCommandError ? error.statusCode : 502, { agent: pluginId, ...(bindingState ? { bindingState } : { bindingState: "unknown" }) });
574
+ throw new AunCommandError(`解除关联请求失败,已回读当前状态:${safeError(error)}`, error instanceof AunCommandError ? error.statusCode : 502, { aid, agent: pluginId, ...(bindingState ? { bindingState } : { bindingState: "unknown" }) });
542
575
  }
543
- return { message: `已解除 ${pluginId} 的 AID 关联`, data: { agent: pluginId, unboundAids } };
576
+ return { message: `已解除 AID ${aid} 与 ${pluginId} 的关联`, data: { aid, agent: pluginId } };
544
577
  }
545
578
  if (!["status", "list", "agents", "default-owner", "create", "bind", "unbind"].includes(command ?? ""))
546
579
  throw new AunCommandError(`未知 AUN 命令:${command ?? ""}`);
@@ -604,10 +637,8 @@ class AunPlugin {
604
637
  const data = { created: true, aid, connection: created.connection ?? "ready" };
605
638
  if (created.connection !== "ready")
606
639
  throw new AunCommandError("AID 已创建,但连接失败;请先执行 mab aun list 确认,不要重试创建", 502, { ...data, connectionError: created.connectionError });
607
- let replacedAids = [];
608
640
  if (agent) {
609
641
  try {
610
- replacedAids = (await this.agentBindings(agent)).filter((previousAid) => previousAid !== aid);
611
642
  await this.patchAgentAid(agent, aid);
612
643
  }
613
644
  catch (error) {
@@ -616,8 +647,8 @@ class AunPlugin {
616
647
  }
617
648
  }
618
649
  return {
619
- message: agent ? (replacedAids.length ? `AID ${replacedAids.join("、")} 已替换为 ${aid} 并关联到 ${agent}` : `AID 已创建并关联到 ${agent}`) : "AID 已创建并连接",
620
- data: { ...data, ...(agent ? { agent, replacedAids } : {}) },
650
+ message: agent ? `AID 已创建并关联到 ${agent}` : "AID 已创建并连接",
651
+ data: { ...data, ...(agent ? { agent } : {}) },
621
652
  };
622
653
  }
623
654
  async agents(readyOnly = true) {
@@ -671,7 +702,7 @@ class AunPlugin {
671
702
  method,
672
703
  headers: { accept: "application/json", ...(body === undefined ? {} : { "content-type": "application/json" }) },
673
704
  ...(body === undefined ? {} : { body: JSON.stringify(body) }),
674
- signal: AbortSignal.timeout(5000),
705
+ signal: AbortSignal.timeout(method === "PATCH" ? 120_000 : 5000),
675
706
  redirect: "error",
676
707
  });
677
708
  }
@@ -741,7 +772,8 @@ class AunPlugin {
741
772
  }
742
773
  };
743
774
  async onMessage(payload, aid) {
744
- if (!this.acceptingMessages)
775
+ const agentPlugin = this.bindings[aid];
776
+ if (!this.acceptingMessages || !agentPlugin)
745
777
  return;
746
778
  const message = (0, messages_1.normalizeAunMessage)(payload, aid, typeof this.config.allow_from === "string" ? this.config.allow_from : undefined);
747
779
  if (!message || !this.seen.add(message.messageId ? `${aid}:${message.messageId}` : ""))
@@ -757,7 +789,7 @@ class AunPlugin {
757
789
  }
758
790
  return;
759
791
  }
760
- const current = (pending ?? Promise.resolve()).catch(() => undefined).then(() => this.processMessage(message, aid));
792
+ const current = (pending ?? Promise.resolve()).catch(() => undefined).then(() => this.processMessage(message, aid, agentPlugin));
761
793
  this.pendingMessages.set(sourceKey, current);
762
794
  try {
763
795
  await current;
@@ -767,12 +799,24 @@ class AunPlugin {
767
799
  this.pendingMessages.delete(sourceKey);
768
800
  }
769
801
  }
770
- async processMessage(message, aid) {
771
- let replyAttempted = false;
802
+ async processMessage(message, aid, agentPlugin) {
803
+ let deliveryFailed = false;
804
+ const trace = { aid, peer: message.peerId, threadId: message.threadId ?? "", messageId: message.messageId ?? "", agentPlugin };
805
+ this.context?.logger.info("AUN 收到任务", { ...trace, stage: "接收消息", attachments: message.attachments?.length ?? 0 });
772
806
  const client = this.clients.get(aid)?.client;
773
807
  let attachments = [];
774
808
  let uploadedItems;
775
809
  let uploadedRefs;
810
+ if (this.context?.pluginStatus(agentPlugin) !== "ready") {
811
+ this.context?.logger.warn("AUN 关联 Agent 尚未就绪,消息未转发", { aid, agentPlugin, status: this.context?.pluginStatus(agentPlugin) });
812
+ try {
813
+ await this.sendText(aid, message.peerId, `绑定的 Agent 插件 ${agentPlugin} 尚未就绪,消息未转发;AID 关联保持不变。`, message.threadId);
814
+ }
815
+ catch (error) {
816
+ this.context?.logger.warn("AUN Agent 未就绪提示发送失败", { aid, agentPlugin, error: safeError(error) });
817
+ }
818
+ return;
819
+ }
776
820
  try {
777
821
  if (message.attachmentError)
778
822
  throw new Error(message.attachmentError);
@@ -783,9 +827,9 @@ class AunPlugin {
783
827
  }
784
828
  }
785
829
  catch (error) {
786
- this.context?.logger.warn("AUN 附件下载失败", { error: safeError(error), ...(error instanceof attachments_1.AunDownloadUrlError ? { downloadUrl: error.diagnostic } : {}) });
830
+ this.context?.logger.warn("AUN 附件下载失败", { ...trace, stage: "下载入站附件", error: safeError((0, agent_1.errorDetails)(error)), ...(error instanceof attachments_1.AunDownloadUrlError ? { downloadUrl: error.diagnostic } : {}) });
787
831
  try {
788
- await this.sendText(aid, message.peerId, `附件接收失败:${safeError(error)}`, message.threadId);
832
+ await this.sendText(aid, message.peerId, `附件接收失败:下载入站附件失败:${safeError((0, agent_1.errorDetails)(error))}`, message.threadId);
789
833
  }
790
834
  catch { /* 尽力反馈 */ }
791
835
  return;
@@ -793,17 +837,22 @@ class AunPlugin {
793
837
  const channelMessage = {
794
838
  channel: "aun", recipientId: aid, peerId: message.peerId, messageId: message.messageId, text: message.text, threadId: message.threadId,
795
839
  ...(attachments.length ? { attachments } : {}),
796
- agentPlugin: this.bindings[aid] || (typeof this.config.agent_plugin === "string" && this.config.agent_plugin.trim() ? this.config.agent_plugin.trim() : "qwenwork"),
840
+ agentPlugin,
797
841
  reply: async (text) => {
798
842
  if (!text.trim())
799
843
  return;
800
- replyAttempted = true;
801
- await this.sendText(aid, message.peerId, text, message.threadId);
844
+ try {
845
+ await this.sendText(aid, message.peerId, text, message.threadId);
846
+ this.context?.logger.info("AUN 文本已发送", { ...trace, stage: "发送文本消息" });
847
+ }
848
+ catch (error) {
849
+ deliveryFailed = true;
850
+ throw new agent_1.AgentOperationError("AUN_TEXT_SEND_FAILED", "发送 AUN 文本消息(结果未确认,不自动重发)", error);
851
+ }
802
852
  },
803
853
  replyAttachments: async (items, text) => {
804
854
  if (!items.length)
805
855
  return;
806
- replyAttempted = true;
807
856
  let sent = 0;
808
857
  let groupsCount = 0;
809
858
  let sending = false;
@@ -811,6 +860,7 @@ class AunPlugin {
811
860
  if (!client?.storage)
812
861
  throw new Error("AUN SDK 不支持附件存储接口");
813
862
  const refs = uploadedRefs && uploadedItems === items ? uploadedRefs : await (0, attachments_1.uploadAunAttachments)({ storage: client.storage }, aid, message.peerId, items);
863
+ this.context?.logger.info("AUN FS 文件上传与授权完成", { ...trace, stage: "上传并授权附件", files: items.map(item => ({ filename: item.filename, sizeBytes: item.data.byteLength })) });
814
864
  if (!uploadedRefs || uploadedItems !== items) {
815
865
  uploadedItems = items;
816
866
  uploadedRefs = refs;
@@ -832,32 +882,37 @@ class AunPlugin {
832
882
  sending = true;
833
883
  // message.send 失败时可能已送达,不自动重试或降级明文,避免重复发送和泄露引用。
834
884
  await client.call("message.send", { to: message.peerId, payload, encrypt: true });
885
+ this.context?.logger.info("AUN 附件消息已发送", { ...trace, stage: "发送附件消息", kind, count: group.length });
835
886
  sending = false;
836
887
  sent++;
837
888
  }
838
889
  }
839
890
  catch (error) {
891
+ deliveryFailed = true;
892
+ const failure = sending ? new agent_1.AgentOperationError("AUN_ATTACHMENT_SEND_FAILED", "发送 AUN 附件消息(FS 上传已完成)", error) : error;
840
893
  const progress = sending ? `(已确认发送 ${sent}/${groupsCount} 组,当前组结果未确认,请勿重复发送)` : "";
841
894
  try {
842
- await this.sendText(aid, message.peerId, `附件回传失败${progress}:${safeError(error)}`, message.threadId);
895
+ await this.sendText(aid, message.peerId, `附件回传失败${progress}:${safeError((0, agent_1.errorDetails)(failure))}`, message.threadId);
843
896
  }
844
897
  catch { /* 尽力反馈 */ }
845
- throw error;
898
+ throw failure;
846
899
  }
847
900
  },
848
901
  };
849
902
  try {
903
+ this.context?.logger.info("AUN 开始调用 Agent", { ...trace, stage: "调用 Agent" });
850
904
  await this.ingress.publish(channelMessage);
905
+ this.context?.logger.info("AUN 任务处理完成", { ...trace, stage: "回复已发送" });
851
906
  }
852
907
  catch (error) {
853
- this.context?.logger.warn("AUN 消息处理失败", { error: safeError(error) });
854
- if (!replyAttempted) {
908
+ this.context?.logger.warn("AUN 消息处理失败", { ...trace, stage: deliveryFailed ? "回传回复" : "执行 Agent 任务", error: safeError((0, agent_1.errorDetails)(error)) });
909
+ if (!deliveryFailed) {
855
910
  const action = attachments.length > 0 ? "附件处理失败" : newSessionCommand.test(message.text.trim()) ? "新建会话失败" : "消息处理失败";
856
911
  try {
857
- await channelMessage.reply(`${action}:${safeError(error)}`);
912
+ await channelMessage.reply(`${action}:${safeError((0, agent_1.errorDetails)(error))}`);
858
913
  }
859
914
  catch (replyError) {
860
- this.context?.logger.warn("AUN 错误回复失败", { error: safeError(replyError) });
915
+ this.context?.logger.warn("AUN 错误回复失败", { ...trace, stage: "发送最终错误消息", error: safeError((0, agent_1.errorDetails)(replyError)) });
861
916
  }
862
917
  }
863
918
  }
@@ -28,11 +28,7 @@
28
28
  const [identities, plugins, config] = await Promise.all([request("/api/plugins/aun/identities"), request("/api/plugins"), request("/api/plugins/aun/config")]);
29
29
  agents = (plugins.plugins || []).filter((plugin) => plugin.type === "agent");
30
30
  if (refreshForm) createForm.elements.ownerAid.value = config.owner_aid || "";
31
- await Promise.all(agents.map(async (agent) => { agent.configAid = (await request(`/api/plugins/${encodeURIComponent(agent.id)}/config`)).aid || ""; }));
32
- const target = configForm.elements.agent_plugin;
33
31
  if (refreshForm) {
34
- target.replaceChildren(new Option("默认 Agent", ""), ...agents.map((agent) => new Option(agent.displayName || agent.id, agent.id)));
35
- target.value = config.agent_plugin || "";
36
32
  for (const key of ["allow_from", "aun_path"]) configForm.elements[key].value = config[key] || "";
37
33
  configForm.elements.encryption_seed.value = "";
38
34
  }
@@ -43,7 +39,7 @@
43
39
  for (const aid of values) {
44
40
  if (seen.has(aid)) continue;
45
41
  seen.add(aid);
46
- entries.push({ aid, label, group, bound: identities.bindings?.[aid] || agents.find((agent) => agent.id && agent.configAid === aid)?.id || "", time: Date.parse(identities.bindingTimes?.[aid] || "") || 0 });
42
+ entries.push({ aid, label, group, bound: identities.bindings?.[aid] || "", time: Date.parse(identities.bindingTimes?.[aid] || "") || 0 });
47
43
  }
48
44
  }
49
45
  entries.sort((a, b) => Number(Boolean(b.bound)) - Number(Boolean(a.bound)) || a.group - b.group || b.time - a.time || a.aid.localeCompare(b.aid));
@@ -52,7 +48,7 @@
52
48
  for (const value of [aid, label, bound || "未关联"]) { const cell = document.createElement("td"); cell.textContent = value; row.append(cell); }
53
49
  const cell = document.createElement("td"); const select = document.createElement("select"); select.setAttribute("aria-label", `${aid} 关联 Agent`);
54
50
  select.append(new Option("选择 Agent", ""), ...agents.map((agent) => new Option(agent.displayName || agent.id, agent.id)));
55
- select.value = bound; select.onchange = () => { if (select.value) void bind(aid, select.value); };
51
+ select.value = bound; select.onchange = () => { if (select.value) void bind(aid, select.value); else if (bound) void unbind(aid); };
56
52
  cell.append(select); row.append(cell); list.append(row);
57
53
  }
58
54
  if (!list.children.length) { const row = document.createElement("tr"); const cell = document.createElement("td"); cell.colSpan = 4; cell.textContent = "暂无已连接 AID"; row.append(cell); list.append(row); }
@@ -66,6 +62,12 @@
66
62
  await load(); notify("关联成功", `${aid} 已关联 ${pluginId}`);
67
63
  } catch (error) { notify("关联失败", errorMessage(error), true); }
68
64
  }
65
+ async function unbind(aid) {
66
+ try {
67
+ await request("/api/plugins/aun/unbind", "POST", { aid });
68
+ await load(); notify("解除关联成功", `${aid} 已解除关联`);
69
+ } catch (error) { notify("解除关联失败", errorMessage(error), true); }
70
+ }
69
71
  createForm.onsubmit = (event) => { event.preventDefault(); const aid = createForm.elements.aid.value.trim(); const owner = createForm.elements.ownerAid.value.trim();
70
72
  show("正在创建 AID 并连接,请稍候……");
71
73
  void request("/api/plugins/aun/aid", "POST", { aid, ownerAid: owner }).then((result) => {
@@ -76,7 +78,7 @@
76
78
  return load();
77
79
  }).catch((error) => notify("创建 AID 失败", errorMessage(error), true));
78
80
  };
79
- configForm.onsubmit = (event) => { event.preventDefault(); const changes = { agent_plugin: configForm.elements.agent_plugin.value, allow_from: configForm.elements.allow_from.value, aun_path: configForm.elements.aun_path.value };
81
+ configForm.onsubmit = (event) => { event.preventDefault(); const changes = { allow_from: configForm.elements.allow_from.value, aun_path: configForm.elements.aun_path.value };
80
82
  if (configForm.elements.encryption_seed.value) changes.encryption_seed = configForm.elements.encryption_seed.value;
81
83
  void request("/api/plugins/aun/settings", "POST", changes).then(() => request("/api/plugins/aun/reload", "POST")).then(() => load()).then(() => notify("保存成功", "配置已保存")).catch((error) => notify("保存配置失败", errorMessage(error), true));
82
84
  };
@@ -7,7 +7,7 @@
7
7
  <p id="status" role="status"></p>
8
8
  <div class="table-scroll"><table><thead><tr><th>AID</th><th>来源</th><th>已关联 Agent</th><th>操作</th></tr></thead><tbody id="identities"></tbody></table></div>
9
9
  <form id="create-form"><h2>创建 AID</h2><div class="fields"><label>新 AID<input name="aid" required placeholder="例如:example.agentid.cn"></label><label>Owner AID<input name="ownerAid" placeholder="例如:owner.agentid.cn"><small class="hint">留空时使用已配置的默认 Owner;未配置时需填写。Owner 保存为本地归属配置;不代表 AUN 协议中的 Owner 关系已激活。</small></label></div><button type="submit">创建</button></form>
10
- <form id="config-form"><h2>通道配置</h2><div class="fields"><label>默认 Agent<select name="agent_plugin"></select></label><label>允许的来源<input name="allow_from"></label><label>AUN 存储路径<input name="aun_path"></label><label>加密种子<input name="encryption_seed" type="password" autocomplete="new-password" placeholder="留空则不修改"></label></div><button type="submit">保存配置</button></form>
10
+ <form id="config-form"><h2>通道配置</h2><div class="fields"><label>允许的来源<input name="allow_from"></label><label>AUN 存储路径<input name="aun_path"></label><label>加密种子<input name="encryption_seed" type="password" autocomplete="new-password" placeholder="留空则不修改"></label></div><button type="submit">保存配置</button></form>
11
11
  </main>
12
12
  <script src="/abweb/channels/aun/app.js"></script>
13
13
  </body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bridge-aun",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
4
4
  "description": "mu-agent-bridge 的 AUN 通道插件。",
5
5
  "keywords": [
6
6
  "mu-agent-bridge-plugin",
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "aun",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
4
4
  "type": "channel",
5
5
  "displayName": "AUN 通道",
6
6
  "entry": "dist/src-ts/plugins/aun/index.js",