mu-agent-bridge 1.0.45 → 1.0.47

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.
@@ -173,18 +173,28 @@ async function startDaemon() {
173
173
  child.unref();
174
174
  }
175
175
  async function startUpdatedDaemon() {
176
- const child = (0, node_child_process_1.spawn)(process.execPath, [(0, node_path_1.resolve)(__dirname, "cli.js"), "start"], { detached: true, stdio: "ignore", windowsHide: true });
176
+ let child;
177
+ const env = { ...process.env, AGENT_BRIDGE_CORE_UPDATE_CHILD: "1" };
178
+ if (process.platform === "win32" && !env.AGENT_BRIDGE_TOKEN) {
179
+ try {
180
+ env.AGENT_BRIDGE_TOKEN = (await (0, promises_1.readFile)((0, node_path_1.resolve)(process.env.USERPROFILE ?? (0, node_os_1.homedir)(), ".mab", "token.txt"), "utf8")).trim();
181
+ env.AGENT_BRIDGE_HOST ??= "0.0.0.0";
182
+ env.AGENT_BRIDGE_PORT ??= "26107";
183
+ }
184
+ catch { /* 当前 Windows 安装未使用 mab 启动器 */ }
185
+ }
186
+ child = (0, node_child_process_1.spawn)(process.execPath, [(0, node_path_1.resolve)(__dirname, "cli.js"), "start"], { detached: true, stdio: "ignore", windowsHide: true, env });
177
187
  let launchFailed = false;
178
- child.once("error", () => { launchFailed = true; });
179
- child.unref();
188
+ child?.once("error", () => { launchFailed = true; });
189
+ child?.unref();
180
190
  const healthy = async () => {
181
191
  for (let attempt = 0; attempt < 30; attempt++) {
182
- if (launchFailed || child.exitCode !== null || child.signalCode !== null)
192
+ if (launchFailed || child && (child.exitCode !== null || child.signalCode !== null))
183
193
  return false;
184
194
  const address = await daemonAddress();
185
195
  try {
186
196
  const response = await fetch(`http://127.0.0.1:${address.port}/api/health`, {
187
- headers: process.env.AGENT_BRIDGE_TOKEN ? { authorization: `Bearer ${process.env.AGENT_BRIDGE_TOKEN}` } : undefined,
197
+ headers: env.AGENT_BRIDGE_TOKEN ? { authorization: `Bearer ${env.AGENT_BRIDGE_TOKEN}` } : undefined,
188
198
  signal: AbortSignal.timeout(1000),
189
199
  });
190
200
  if (response.ok && (await response.json()).status === "ok")
@@ -195,7 +205,7 @@ async function startUpdatedDaemon() {
195
205
  }
196
206
  return false;
197
207
  };
198
- return { child, healthy };
208
+ return { healthy };
199
209
  }
200
210
  async function stop() {
201
211
  await stopDaemon();
@@ -371,6 +381,10 @@ async function main() {
371
381
  if (args[0] === "--self-test")
372
382
  return selfTest(args[1] ?? "hi");
373
383
  if (args[0] === "--serve" || args[0] === "start") {
384
+ if (process.env.AGENT_BRIDGE_CORE_UPDATE_CHILD === "1") {
385
+ delete process.env.AGENT_BRIDGE_CORE_UPDATE_CHILD;
386
+ return serve();
387
+ }
374
388
  const logs = new log_store_1.LogStore((0, node_path_1.resolve)(stateDirectory(), "logs"));
375
389
  let launched;
376
390
  const updated = await (0, core_updater_1.updateCoreAtStartup)({
@@ -386,6 +386,8 @@ class PluginManager {
386
386
  const trustedChannel = loaded.record.type === "channel" && (id === "aun" || id === "feishu");
387
387
  if (trustedChannel && (typeof message.peerId !== "string" || !message.peerId.trim() || [message.peerId, message.recipientId, message.chatId, message.threadId].some(value => value !== undefined && (typeof value !== "string" || value.length > 512 || /[\u0000-\u001f*]/.test(value)))))
388
388
  throw new agent_1.AgentError("通道来源标识无效");
389
+ if (id === "aun" && !message.agentPlugin)
390
+ throw new agent_1.AgentError("AUN 消息必须明确关联 Agent");
389
391
  const target = message.agentPlugin || "qwenwork";
390
392
  if (/^\/sessions(?:\s|$)/.test(message.text.trimStart()) || (0, session_management_1.isDailySummaryRequest)(message.text)) {
391
393
  if (!trustedChannel)
@@ -771,7 +771,8 @@ class AunPlugin {
771
771
  }
772
772
  };
773
773
  async onMessage(payload, aid) {
774
- if (!this.acceptingMessages || !this.bindings[aid])
774
+ const agentPlugin = this.bindings[aid];
775
+ if (!this.acceptingMessages || !agentPlugin)
775
776
  return;
776
777
  const message = (0, messages_1.normalizeAunMessage)(payload, aid, typeof this.config.allow_from === "string" ? this.config.allow_from : undefined);
777
778
  if (!message || !this.seen.add(message.messageId ? `${aid}:${message.messageId}` : ""))
@@ -787,7 +788,7 @@ class AunPlugin {
787
788
  }
788
789
  return;
789
790
  }
790
- const current = (pending ?? Promise.resolve()).catch(() => undefined).then(() => this.processMessage(message, aid));
791
+ const current = (pending ?? Promise.resolve()).catch(() => undefined).then(() => this.processMessage(message, aid, agentPlugin));
791
792
  this.pendingMessages.set(sourceKey, current);
792
793
  try {
793
794
  await current;
@@ -797,12 +798,22 @@ class AunPlugin {
797
798
  this.pendingMessages.delete(sourceKey);
798
799
  }
799
800
  }
800
- async processMessage(message, aid) {
801
+ async processMessage(message, aid, agentPlugin) {
801
802
  let replyAttempted = false;
802
803
  const client = this.clients.get(aid)?.client;
803
804
  let attachments = [];
804
805
  let uploadedItems;
805
806
  let uploadedRefs;
807
+ if (this.context?.pluginStatus(agentPlugin) !== "ready") {
808
+ this.context?.logger.warn("AUN 关联 Agent 尚未就绪,消息未转发", { aid, agentPlugin, status: this.context?.pluginStatus(agentPlugin) });
809
+ try {
810
+ await this.sendText(aid, message.peerId, `绑定的 Agent 插件 ${agentPlugin} 尚未就绪,消息未转发;AID 关联保持不变。`, message.threadId);
811
+ }
812
+ catch (error) {
813
+ this.context?.logger.warn("AUN Agent 未就绪提示发送失败", { aid, agentPlugin, error: safeError(error) });
814
+ }
815
+ return;
816
+ }
806
817
  try {
807
818
  if (message.attachmentError)
808
819
  throw new Error(message.attachmentError);
@@ -823,7 +834,7 @@ class AunPlugin {
823
834
  const channelMessage = {
824
835
  channel: "aun", recipientId: aid, peerId: message.peerId, messageId: message.messageId, text: message.text, threadId: message.threadId,
825
836
  ...(attachments.length ? { attachments } : {}),
826
- agentPlugin: this.bindings[aid],
837
+ agentPlugin,
827
838
  reply: async (text) => {
828
839
  if (!text.trim())
829
840
  return;
@@ -74,6 +74,37 @@ async function runAunCommand(routes, response, argv) {
74
74
  await (0, promises_1.rm)(root, { recursive: true, force: true });
75
75
  }
76
76
  });
77
+ (0, node_test_1.default)("AUN 绑定目标未就绪时不调用插件且不改动其它 AID", async () => {
78
+ const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "aun-unready-binding-"));
79
+ let receive;
80
+ const sent = [];
81
+ let published = false;
82
+ const sdk = {
83
+ AIDStore: class {
84
+ list() { return { ok: true, data: { identities: [{ aid: "selected.agentid.cn" }, { aid: "other.agentid.cn" }] } }; }
85
+ load(aid) { return { ok: true, data: { aid } }; }
86
+ close() { }
87
+ },
88
+ AUNClient: class {
89
+ async connect() { }
90
+ async close() { }
91
+ on(_event, handler) { receive = handler; return { unsubscribe() { } }; }
92
+ async call(_method, params) { sent.push(params); }
93
+ },
94
+ };
95
+ const plugin = new aun_1.AunPlugin({ sdk, ingress: { publish: async () => { published = true; } } });
96
+ try {
97
+ await plugin.activate({ dataDir: root, config: { bindings: { "selected.agentid.cn": "qwenwork", "other.agentid.cn": "workbuddy" } }, logger: { info() { }, warn() { }, error() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "discovered", http: { register() { }, unregister() { } } });
98
+ await receive({ from: "peer.agentid.cn", message_id: "unready-1", payload: { text: "不要转发" } });
99
+ strict_1.default.equal(published, false);
100
+ strict_1.default.deepEqual(sent, [{ to: "peer.agentid.cn", payload: { type: "text", text: "绑定的 Agent 插件 qwenwork 尚未就绪,消息未转发;AID 关联保持不变。" }, encrypt: true }]);
101
+ strict_1.default.deepEqual((await plugin.health()).details?.bindings, { "selected.agentid.cn": "qwenwork", "other.agentid.cn": "workbuddy" });
102
+ }
103
+ finally {
104
+ await plugin.deactivate("stop");
105
+ await (0, promises_1.rm)(root, { recursive: true, force: true });
106
+ }
107
+ });
77
108
  async function withAunAttachmentPlugin(run, publish, config = { allow_from: "peer.agentid.cn", bindings: { "local.agentid.cn": "qwenwork" } }) {
78
109
  const root = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "aun-attachments-plugin-"));
79
110
  let handler;
@@ -405,6 +436,8 @@ async function withAunAttachmentPlugin(run, publish, config = { allow_from: "pee
405
436
  const plugin = new aun_1.AunPlugin({ sdk, ingress: { publish: async () => undefined } });
406
437
  try {
407
438
  await plugin.activate({ dataDir: root, config: { bindings: { [aids[0]]: "qwenwork", [aids[1]]: "qwenwork" }, binding_times: { [aids[0]]: "old-1", [aids[1]]: "old-2" } }, logger: { info() { }, warn() { }, error() { } }, callPlugin: async () => ({ text: "" }), pluginStatus: () => "ready", http: { register() { }, unregister() { } } });
439
+ await plugin.identity.bind("qwenwork", aids[0], 26107);
440
+ strict_1.default.deepEqual((await plugin.health()).details?.bindings, { [aids[0]]: "qwenwork", [aids[1]]: "qwenwork" });
408
441
  await plugin.identity.bind("workbuddy", aids[0], 26107);
409
442
  strict_1.default.deepEqual((await plugin.health()).details?.bindings, { [aids[0]]: "workbuddy", [aids[1]]: "qwenwork" });
410
443
  await plugin.identity.unbind("workbuddy", aids[0], 26107);
@@ -605,16 +605,17 @@ const channel_sessions_1 = require("../src-ts/channel-sessions");
605
605
  await manager.start("aun");
606
606
  let reply = "";
607
607
  const ingress = globalThis.testIngress;
608
- await ingress.publish({ channel: "aun", peerId: "peer.agentid.pub", text: "你好", reply: async (text) => { reply = text; } });
608
+ await strict_1.default.rejects(ingress.publish({ channel: "aun", peerId: "peer.agentid.pub", text: "无目标", reply: async () => { } }), /必须明确关联 Agent/);
609
+ await ingress.publish({ channel: "aun", agentPlugin: "qwenwork", peerId: "peer.agentid.pub", text: "你好", reply: async (text) => { reply = text; } });
609
610
  strict_1.default.equal(reply, "回复:你好");
610
611
  const forwarded = [];
611
- await ingress.publish({ channel: "aun", peerId: "peer.agentid.pub", text: "中间消息", reply: async (text) => { forwarded.push(text); } });
612
+ await ingress.publish({ channel: "aun", agentPlugin: "qwenwork", peerId: "peer.agentid.pub", text: "中间消息", reply: async (text) => { forwarded.push(text); } });
612
613
  strict_1.default.deepEqual(forwarded, ["请补充视频时长", "已完成"]);
613
614
  const images = [];
614
- await ingress.publish({ channel: "aun", peerId: "peer.agentid.pub", text: "画猫", reply: async () => { strict_1.default.fail("图片不应作为纯文本丢弃"); }, replyAttachments: async (files, text) => { images.push(...files); strict_1.default.equal(text, "回复:画猫"); } });
615
+ await ingress.publish({ channel: "aun", agentPlugin: "qwenwork", peerId: "peer.agentid.pub", text: "画猫", reply: async () => { strict_1.default.fail("图片不应作为纯文本丢弃"); }, replyAttachments: async (files, text) => { images.push(...files); strict_1.default.equal(text, "回复:画猫"); } });
615
616
  strict_1.default.equal(images[0].filename, "cat.png");
616
617
  strict_1.default.deepEqual([...images[0].data], [137, 80, 78, 71, 13, 10, 26, 10]);
617
- await strict_1.default.rejects(ingress.publish({ channel: "aun", peerId: "peer", text: "画猫", reply: async (text) => { strict_1.default.match(text, /不支持附件回传/); } }), /不支持附件回传/);
618
+ await strict_1.default.rejects(ingress.publish({ channel: "aun", agentPlugin: "qwenwork", peerId: "peer", text: "画猫", reply: async (text) => { strict_1.default.match(text, /不支持附件回传/); } }), /不支持附件回传/);
618
619
  await manager.stop("aun");
619
620
  await manager.stop("qwenwork");
620
621
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mu-agent-bridge",
3
- "version": "1.0.45",
3
+ "version": "1.0.47",
4
4
  "description": "将 AUN Agent 连接到本地桌面 AI 客户端的插件平台。",
5
5
  "main": "dist/src-ts/cli.js",
6
6
  "bin": {
@@ -771,7 +771,8 @@ class AunPlugin {
771
771
  }
772
772
  };
773
773
  async onMessage(payload, aid) {
774
- if (!this.acceptingMessages || !this.bindings[aid])
774
+ const agentPlugin = this.bindings[aid];
775
+ if (!this.acceptingMessages || !agentPlugin)
775
776
  return;
776
777
  const message = (0, messages_1.normalizeAunMessage)(payload, aid, typeof this.config.allow_from === "string" ? this.config.allow_from : undefined);
777
778
  if (!message || !this.seen.add(message.messageId ? `${aid}:${message.messageId}` : ""))
@@ -787,7 +788,7 @@ class AunPlugin {
787
788
  }
788
789
  return;
789
790
  }
790
- const current = (pending ?? Promise.resolve()).catch(() => undefined).then(() => this.processMessage(message, aid));
791
+ const current = (pending ?? Promise.resolve()).catch(() => undefined).then(() => this.processMessage(message, aid, agentPlugin));
791
792
  this.pendingMessages.set(sourceKey, current);
792
793
  try {
793
794
  await current;
@@ -797,12 +798,22 @@ class AunPlugin {
797
798
  this.pendingMessages.delete(sourceKey);
798
799
  }
799
800
  }
800
- async processMessage(message, aid) {
801
+ async processMessage(message, aid, agentPlugin) {
801
802
  let replyAttempted = false;
802
803
  const client = this.clients.get(aid)?.client;
803
804
  let attachments = [];
804
805
  let uploadedItems;
805
806
  let uploadedRefs;
807
+ if (this.context?.pluginStatus(agentPlugin) !== "ready") {
808
+ this.context?.logger.warn("AUN 关联 Agent 尚未就绪,消息未转发", { aid, agentPlugin, status: this.context?.pluginStatus(agentPlugin) });
809
+ try {
810
+ await this.sendText(aid, message.peerId, `绑定的 Agent 插件 ${agentPlugin} 尚未就绪,消息未转发;AID 关联保持不变。`, message.threadId);
811
+ }
812
+ catch (error) {
813
+ this.context?.logger.warn("AUN Agent 未就绪提示发送失败", { aid, agentPlugin, error: safeError(error) });
814
+ }
815
+ return;
816
+ }
806
817
  try {
807
818
  if (message.attachmentError)
808
819
  throw new Error(message.attachmentError);
@@ -823,7 +834,7 @@ class AunPlugin {
823
834
  const channelMessage = {
824
835
  channel: "aun", recipientId: aid, peerId: message.peerId, messageId: message.messageId, text: message.text, threadId: message.threadId,
825
836
  ...(attachments.length ? { attachments } : {}),
826
- agentPlugin: this.bindings[aid],
837
+ agentPlugin,
827
838
  reply: async (text) => {
828
839
  if (!text.trim())
829
840
  return;