agent-bridge-aun 1.0.49 → 1.0.58

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.
@@ -0,0 +1,333 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AunMenu = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const promises_1 = require("node:fs/promises");
6
+ const node_path_1 = require("node:path");
7
+ const runtime_menu_1 = require("./runtime-menu");
8
+ const requestTypes = new Set(["menu.token.request", "menu.list", "menu.query", "menu.options", "menu.update", "menu.action"]);
9
+ const booleanResources = ["allowInternalContacts", "allowExternalContacts", "allowAllContacts"];
10
+ const resources = [...booleanResources, "allowedAids", "deniedAids", "requestLimit"];
11
+ class MenuError extends Error {
12
+ code;
13
+ constructor(code, message) {
14
+ super(message);
15
+ this.code = code;
16
+ }
17
+ }
18
+ function object(value) { return !!value && typeof value === "object" && !Array.isArray(value); }
19
+ function hash(value) { return (0, node_crypto_1.createHash)("sha256").update(value).digest("hex"); }
20
+ function allowedFrom(peer, allowFrom) {
21
+ const rules = (allowFrom ?? "").split(",").map(item => item.trim()).filter(Boolean);
22
+ return !rules.length || rules.includes("*") || rules.includes(peer);
23
+ }
24
+ function resourceValid(resource, value) {
25
+ if (resource === "allowedAids" || resource === "deniedAids")
26
+ return Array.isArray(value)
27
+ && value.every(item => typeof item === "string" && item.split(".").length >= 3
28
+ && item.split(".").every(label => /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(label))) && new Set(value).size === value.length;
29
+ if (resource === "requestLimit")
30
+ return object(value) && Object.keys(value).every(key => key === "maxTimes" || key === "coolDown")
31
+ && Number.isSafeInteger(value.maxTimes) && value.maxTimes >= 1 && value.maxTimes <= 100_000
32
+ && Number.isSafeInteger(value.coolDown) && value.coolDown >= 1 && value.coolDown <= 86_400;
33
+ return typeof value === "boolean";
34
+ }
35
+ function volatileReply(version) {
36
+ if (typeof version !== "string" || !/^\d+\.\d+\.\d+$/.test(version.trim()))
37
+ return false;
38
+ const parts = version.trim().split(".").map(Number);
39
+ return parts.every(Number.isSafeInteger) && (parts[0] > 0 || parts[1] > 5 || parts[1] === 5 && parts[2] >= 17);
40
+ }
41
+ function logField(value) {
42
+ return typeof value === "string" ? value.replace(/[\u0000-\u001f\u007f]/g, "").slice(0, 256) : "";
43
+ }
44
+ class AunMenu {
45
+ dataDir;
46
+ port;
47
+ records = {};
48
+ writes = Promise.resolve();
49
+ tokens = new Map();
50
+ responses = new Map();
51
+ idleSeconds = 30 * 24 * 60 * 60;
52
+ constructor(dataDir, port) {
53
+ this.dataDir = dataDir;
54
+ this.port = port;
55
+ }
56
+ async load() {
57
+ let text;
58
+ try {
59
+ text = await (0, promises_1.readFile)((0, node_path_1.join)(this.dataDir, "access-policy.json"), "utf8");
60
+ }
61
+ catch (error) {
62
+ if (error.code === "ENOENT")
63
+ return;
64
+ throw error;
65
+ }
66
+ const file = JSON.parse(text);
67
+ if (!object(file) || file.schemaVersion !== 1 || !object(file.records)
68
+ || Object.values(file.records).some(entry => !object(entry) || typeof entry.ownerAid !== "string"
69
+ || typeof entry.accessRevision !== "string" || !entry.accessRevision || !object(entry.policy)
70
+ || resources.some(key => !resourceValid(key, entry.policy[key]))))
71
+ throw new Error("AUN 访问策略文件无效,未启用通道");
72
+ this.records = file.records;
73
+ }
74
+ snapshot(aid, ownerAid) {
75
+ const exists = Object.hasOwn(this.records, aid);
76
+ const saved = exists ? this.records[aid] : undefined;
77
+ if (saved?.ownerAid === ownerAid)
78
+ return saved;
79
+ // 旧身份尚无策略时,查询如实反映既有全开放行为,显式写入后才启用名录准入。
80
+ const policy = { requestLimit: { maxTimes: 600, coolDown: 3600 }, allowInternalContacts: !exists,
81
+ allowExternalContacts: !exists, allowAllContacts: !exists, allowedAids: [], deniedAids: [] };
82
+ return { ownerAid, policy, accessRevision: `legacy-${hash(JSON.stringify([aid, ownerAid, saved?.accessRevision, policy]))}` };
83
+ }
84
+ async accept(aid, raw) {
85
+ const req = raw.payload;
86
+ if (!object(req) || typeof req.type !== "string" || !req.type.startsWith("menu."))
87
+ return false;
88
+ if (req.type === "menu.response" || req.type === "menu.event")
89
+ return true;
90
+ const peer = raw.from ?? raw.envelope?.from;
91
+ if (typeof peer !== "string" || !peer || peer === aid || !allowedFrom(peer, this.port.allowFrom()))
92
+ return true;
93
+ if (typeof req.id !== "string" || !req.id.trim() || req.id.length > 256)
94
+ return true;
95
+ const started = Date.now();
96
+ const fields = { aid, peer, type: logField(req.type), id: logField(req.id), name: logField(req.name ?? req.cmd) };
97
+ this.port.log("AUN 菜单请求已收到", fields);
98
+ const header = { type: "menu.response", id: req.id, ...(typeof req.name === "string" ? { name: req.name } : {}) };
99
+ let response;
100
+ try {
101
+ if (!requestTypes.has(req.type))
102
+ throw new MenuError("NOT_SUPPORTED", "当前 AUN 插件不支持此菜单请求");
103
+ if (raw.encrypted !== true || raw.to !== aid || raw.envelope?.from !== undefined && raw.envelope.from !== peer
104
+ || raw.envelope?.to !== undefined && raw.envelope.to !== aid || raw.group_id || raw.group_aid || req.agent !== undefined
105
+ || req.args !== undefined && !object(req.args))
106
+ throw new MenuError("NOT_ALLOWED", "菜单仅接受当前身份的直接加密私信");
107
+ const target = this.port.target(aid);
108
+ if (!target)
109
+ throw new MenuError("AGENT_UNAVAILABLE", "当前 Agent 关联不可用");
110
+ const ownerAid = await this.port.owner(aid);
111
+ if (ownerAid !== peer || this.port.target(aid) !== target)
112
+ throw new MenuError("NOT_ALLOWED", "需要当前身份的已确认 Owner");
113
+ if (req.args?.self !== undefined && req.args.self !== aid)
114
+ throw new MenuError("NOT_ALLOWED", "菜单目标身份不匹配");
115
+ for (const key of ["sender_device_id", "sender_slot_id"])
116
+ if (raw[key] !== undefined && (typeof raw[key] !== "string"
117
+ || !raw[key].trim() || raw[key].length > 256 || /[\u0000-\u001f\u007f]/.test(raw[key])))
118
+ throw new MenuError("NOT_ALLOWED", "菜单发送端身份无效");
119
+ const subject = JSON.stringify([aid, peer, target, raw.sender_device_id ?? "", raw.sender_slot_id ?? ""]);
120
+ const now = Date.now();
121
+ for (const [key, token] of this.tokens)
122
+ if (now - token.lastUsed >= this.idleSeconds * 1000)
123
+ this.tokens.delete(key);
124
+ if (req.type === "menu.token.request") {
125
+ if (this.tokens.size >= 1000 && !this.tokens.has(subject))
126
+ throw new MenuError("MENU_TOKEN_REJECTED", "菜单授权数量已达上限");
127
+ const token = `mt_${(0, node_crypto_1.randomBytes)(32).toString("base64url")}`;
128
+ this.tokens.set(subject, { hash: hash(token), lastUsed: now });
129
+ response = { ...header, data: { menu_token: token, idle_timeout: this.idleSeconds } };
130
+ }
131
+ else {
132
+ const token = this.tokens.get(subject);
133
+ if (!token || typeof req.menu_token !== "string" || req.menu_token.length > 256 || token.hash !== hash(req.menu_token))
134
+ throw new MenuError("MENU_TOKEN_REQUIRED", "请重新申请菜单授权");
135
+ token.lastUsed = now;
136
+ if (req.type === "menu.update") {
137
+ const key = JSON.stringify([subject, req.id]), requestHash = hash(JSON.stringify({ ...req, menu_token: undefined }));
138
+ for (const [key, item] of this.responses)
139
+ if (now - item.at > 10 * 60_000)
140
+ this.responses.delete(key);
141
+ const cached = this.responses.get(key);
142
+ if (cached && cached.hash !== requestHash)
143
+ throw new MenuError("INVALID_ARGUMENT", "同一请求 ID 不能用于不同更新");
144
+ if (cached)
145
+ response = await cached.response;
146
+ else {
147
+ const pending = this.execute(req, aid, ownerAid, target).then(data => ({ ...header, data }));
148
+ this.responses.set(key, { hash: requestHash, at: now, response: pending });
149
+ while (this.responses.size > 1000)
150
+ this.responses.delete(this.responses.keys().next().value);
151
+ try {
152
+ response = await pending;
153
+ }
154
+ catch (error) {
155
+ this.responses.delete(key);
156
+ throw error;
157
+ }
158
+ }
159
+ }
160
+ else if (this.port.agentmd && (req.name === "aid" || req.cmd === "/aid" || req.type === "menu.list" && req.args?.view === "catalog")) {
161
+ response = await this.port.agentmd(aid, raw);
162
+ }
163
+ else
164
+ response = { ...header, data: await this.execute(req, aid, ownerAid, target) };
165
+ }
166
+ }
167
+ catch (error) {
168
+ response = { ...header, error: error instanceof MenuError ? { code: error.code, message: error.message }
169
+ : { code: "INTERNAL_ERROR", message: "菜单处理失败,请稍后重试" } };
170
+ }
171
+ response.processing_ms ??= Date.now() - started;
172
+ const outcome = { ...fields, processing_ms: response.processing_ms, code: object(response.error) ? response.error.code : "OK" };
173
+ this.port.log("AUN 菜单请求已处理", outcome);
174
+ // 旧手机 SDK 不消费短时游标及设备定向回复;只根据已认证头判断版本。
175
+ const modern = volatileReply(raw.protected_headers?.sdk_version);
176
+ const device = typeof raw.sender_device_id === "string" && raw.sender_device_id.length <= 256
177
+ && !/[\u0000-\u001f\u007f]/.test(raw.sender_device_id) ? raw.sender_device_id.trim() : "";
178
+ if (modern && raw.sender_device_id !== undefined && !device) {
179
+ this.port.log("AUN 菜单响应发送失败", { ...outcome, code: "INVALID_MENU_REPLY_DEVICE" });
180
+ return true;
181
+ }
182
+ try {
183
+ await this.port.send(aid, { to: peer, payload: response, encrypt: true, message_class: "control",
184
+ ...(modern ? { ttl: 60, ...(device ? { to_device_id: device } : {}) } : {}) });
185
+ this.port.log("AUN 菜单响应已发送", outcome);
186
+ }
187
+ catch (error) {
188
+ this.port.log("AUN 菜单响应发送失败", { ...outcome, code: "SEND_FAILED" });
189
+ throw error;
190
+ }
191
+ return true;
192
+ }
193
+ async execute(req, aid, ownerAid, target) {
194
+ if (req.type === "menu.list") {
195
+ if (req.args?.view !== undefined)
196
+ throw new MenuError("NOT_SUPPORTED", "当前不支持此菜单目录视图");
197
+ return [{ group: "权限管理", commands: [
198
+ { cmd: "/agent", label: "身份归属", desc: "读取当前身份及已确认的 Owner" },
199
+ { cmd: "/baseagent", label: "关联 Agent", desc: "读取当前关联的本地 Agent" },
200
+ { cmd: "/connect", label: "通信准入", desc: "读取及更新联系人准入策略" },
201
+ ...(this.port.agentmd ? [{ cmd: "/aid", label: "能力介绍与名片", desc: "读取及发布当前身份的能力介绍" }] : []),
202
+ ...(this.port.runtime ? [
203
+ { cmd: "/model", label: "模型", desc: "读取当前关联 Agent 的模型信息" },
204
+ { cmd: "/effort", label: "推理强度", desc: "读取当前关联 Agent 的推理强度" },
205
+ { cmd: "/chatmode", label: "对话模式", desc: "读取当前私聊对话模式" },
206
+ ] : []),
207
+ ] }];
208
+ }
209
+ const name = req.name ?? (typeof req.cmd === "string" ? req.cmd.replace(/^\//, "") : undefined);
210
+ if (req.name !== undefined && req.cmd !== undefined && req.cmd !== `/${req.name}`)
211
+ throw new MenuError("INVALID_ARGUMENT", "菜单名称与命令不一致");
212
+ if (this.port.runtime && ["agent", "model", "effort", "chatmode", "role"].includes(name)) {
213
+ if (req.args?.aid !== undefined && req.args.aid !== aid)
214
+ throw new MenuError("NOT_ALLOWED", "只能查询当前身份");
215
+ if (req.type === "menu.options" && ["model", "effort", "chatmode"].includes(name))
216
+ return [];
217
+ if (req.type !== "menu.query")
218
+ throw new MenuError("NOT_SUPPORTED", "运行信息仅支持读取,请在关联客户端修改设置");
219
+ const baseagent = this.port.baseagent(aid);
220
+ if (!baseagent)
221
+ throw new MenuError("AGENT_UNAVAILABLE", "关联 Agent 不可用");
222
+ const snapshot = await this.port.runtime(aid);
223
+ if (this.port.target(aid) !== target || await this.port.owner(aid) !== ownerAid || this.port.target(aid) !== target)
224
+ throw new MenuError("NOT_ALLOWED", "读取期间身份归属或关联发生变化");
225
+ try {
226
+ return (0, runtime_menu_1.runtimeMenuQuery)(name, req.args ?? {}, aid, ownerAid, baseagent, snapshot);
227
+ }
228
+ catch (error) {
229
+ throw new MenuError(error.code ?? "INTERNAL_ERROR", "运行信息请求参数不受支持");
230
+ }
231
+ }
232
+ if (req.type === "menu.query" && (name === "baseagent" || name === "agent")) {
233
+ if (req.args?.aid !== undefined && req.args.aid !== aid)
234
+ throw new MenuError("NOT_ALLOWED", "只能查询当前身份");
235
+ if (name === "agent" && req.args?.aid !== aid)
236
+ throw new MenuError("INVALID_ARGUMENT", "agent 菜单需要当前 aid");
237
+ const baseagent = this.port.baseagent(aid);
238
+ if (!baseagent || this.port.target(aid) !== target)
239
+ throw new MenuError("AGENT_UNAVAILABLE", "当前 Agent 关联不可用");
240
+ if (name === "baseagent")
241
+ return { baseagent, scope: "agent", override: null, source: "agent" };
242
+ const config = { owner: ownerAid, owners: [ownerAid], active_baseagent: baseagent };
243
+ return { aid, config, safeConfig: config };
244
+ }
245
+ if ((req.name !== "connect" && req.cmd !== "/connect") || req.name !== undefined && req.name !== "connect"
246
+ || req.cmd !== undefined && req.cmd !== "/connect")
247
+ throw new MenuError("NOT_SUPPORTED", "当前仅支持 connect/access 菜单");
248
+ if (req.args?.self !== aid)
249
+ throw new MenuError("INVALID_ARGUMENT", "connect 菜单需要当前 self AID");
250
+ if (req.type === "menu.query" && req.args?.view === "access") {
251
+ const snapshot = this.snapshot(aid, ownerAid);
252
+ return { $schema_version: 1, self: aid, view: "access", policy: snapshot.policy, accessRevision: snapshot.accessRevision };
253
+ }
254
+ if (req.type !== "menu.update")
255
+ throw new MenuError("NOT_SUPPORTED", "当前不支持此菜单操作");
256
+ const resource = req.args?.resource;
257
+ if (!resources.includes(resource))
258
+ throw new MenuError("NOT_SUPPORTED", "不支持此访问策略资源");
259
+ if (typeof req.value !== "string" || typeof req.args?.expectedAccessRevision !== "string" || !req.args.expectedAccessRevision)
260
+ throw new MenuError("INVALID_ARGUMENT", "需要 JSON 字符串和访问策略版本");
261
+ let value;
262
+ try {
263
+ value = JSON.parse(req.value);
264
+ }
265
+ catch {
266
+ throw new MenuError("INVALID_ARGUMENT", "访问策略值必须是有效 JSON 字符串");
267
+ }
268
+ if (!resourceValid(resource, value))
269
+ throw new MenuError("INVALID_ARGUMENT", "访问策略值的类型或范围无效");
270
+ const mutation = this.writes.then(async () => {
271
+ const current = this.snapshot(aid, ownerAid);
272
+ if (current.accessRevision !== req.args.expectedAccessRevision)
273
+ throw new MenuError("CONFLICT", "访问策略已变更,请重新读取后操作");
274
+ const changed = resource === "requestLimit"
275
+ ? current.policy.requestLimit.maxTimes !== value.maxTimes
276
+ || current.policy.requestLimit.coolDown !== value.coolDown
277
+ : JSON.stringify(current.policy[resource]) !== JSON.stringify(value);
278
+ const saved = this.records[aid];
279
+ if (!changed && saved?.ownerAid === ownerAid)
280
+ return { $schema_version: 1, self: aid, resource, policy: current.policy, accessRevision: current.accessRevision, changed };
281
+ const next = { ownerAid, policy: { ...current.policy, [resource]: value }, accessRevision: (0, node_crypto_1.randomUUID)() };
282
+ const records = { ...this.records, [aid]: next };
283
+ const temporary = (0, node_path_1.join)(this.dataDir, `access-policy-${(0, node_crypto_1.randomUUID)()}.tmp`);
284
+ try {
285
+ await (0, promises_1.writeFile)(temporary, `${JSON.stringify({ schemaVersion: 1, records }, null, 2)}\n`, { mode: 0o600, flag: "wx" });
286
+ if (this.port.target(aid) !== target || await this.port.owner(aid) !== ownerAid || this.port.target(aid) !== target)
287
+ throw new MenuError("NOT_ALLOWED", "Owner 或 Agent 关联已变更,本次更新未保存");
288
+ await (0, promises_1.rename)(temporary, (0, node_path_1.join)(this.dataDir, "access-policy.json"));
289
+ this.records = records;
290
+ }
291
+ finally {
292
+ await (0, promises_1.rm)(temporary, { force: true });
293
+ }
294
+ return { $schema_version: 1, self: aid, resource, policy: next.policy, accessRevision: next.accessRevision, changed };
295
+ });
296
+ this.writes = mutation.catch(() => undefined);
297
+ return mutation;
298
+ }
299
+ async allowsChat(aid, peer) {
300
+ const saved = this.records[aid];
301
+ if (!saved)
302
+ return true;
303
+ const target = this.port.target(aid);
304
+ try {
305
+ const ownerAid = await this.port.owner(aid);
306
+ if (saved.ownerAid !== ownerAid || saved.policy.deniedAids.includes(peer))
307
+ return false;
308
+ let directory;
309
+ try {
310
+ directory = await this.port.directory(aid, ownerAid);
311
+ }
312
+ catch { /* 名录不可确认时,仅保留实时 Owner 的修复通道。 */ }
313
+ if (this.records[aid] !== saved || this.port.target(aid) !== target)
314
+ return false;
315
+ if (await this.port.owner(aid) !== ownerAid || this.records[aid] !== saved || this.port.target(aid) !== target)
316
+ return false;
317
+ if (!directory)
318
+ return peer === ownerAid;
319
+ if (directory.blocked_aids.some((entry) => entry.target_type === "aid" && entry.target_id === peer))
320
+ return false;
321
+ if (peer === ownerAid || peer === directory.personal_assistant?.aid)
322
+ return true;
323
+ if (saved.policy.allowAllContacts || saved.policy.allowedAids.includes(peer))
324
+ return true;
325
+ const contact = directory.entries.find((entry) => entry.aid === peer);
326
+ return Boolean(contact && (contact.owner_aid === ownerAid ? saved.policy.allowInternalContacts : saved.policy.allowExternalContacts));
327
+ }
328
+ catch {
329
+ return false;
330
+ }
331
+ }
332
+ }
333
+ exports.AunMenu = AunMenu;
@@ -36,7 +36,7 @@
36
36
  list.replaceChildren();
37
37
  const entries = [];
38
38
  const seen = new Set();
39
- for (const [group, label, values] of [[0, "本工程 · 已连接", identities.groups.muAgentBridge || []], [1, "EvolCore · 已连接", identities.groups.evolcore || []]]) {
39
+ for (const [group, label, values] of [[0, "本工程 · 已连接", identities.groups.muAgentBridge || []], [1, "EvolCore · 仅展示", identities.groups.evolcore || []]]) {
40
40
  for (const aid of values) {
41
41
  if (seen.has(aid)) continue;
42
42
  seen.add(aid);
@@ -44,10 +44,15 @@
44
44
  }
45
45
  }
46
46
  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));
47
- for (const { aid, label, bound } of entries) {
47
+ for (const { aid, label, bound, group } of entries) {
48
48
  const row = document.createElement("tr");
49
49
  for (const value of [aid, label, bound || "未关联"]) { const cell = document.createElement("td"); cell.textContent = value; row.append(cell); }
50
- const cell = document.createElement("td"); const select = document.createElement("select"); select.setAttribute("aria-label", `${aid} 关联 Agent`);
50
+ const cell = document.createElement("td");
51
+ if (group === 1) {
52
+ cell.textContent = "EvolCore 来源身份不能关联";
53
+ row.append(cell); list.append(row); continue;
54
+ }
55
+ const select = document.createElement("select"); select.setAttribute("aria-label", `${aid} 关联 Agent`);
51
56
  select.append(new Option("选择 Agent", ""), ...agents.map((agent) => new Option(agent.displayName || agent.id, agent.id)));
52
57
  select.value = bound; select.onchange = () => { if (select.value) void bind(aid, select.value); else if (bound) void unbind(aid); };
53
58
  cell.append(select); row.append(cell); list.append(row);
@@ -73,14 +78,16 @@
73
78
  show("正在创建 AID 并连接,请稍候……");
74
79
  void request("/api/plugins/aun/aid", "POST", { aid, ownerAid: owner }).then((result) => {
75
80
  const failed = result.connection === "degraded";
76
- const directoryFailed = result.directorySync?.status === "not_synced";
81
+ const pending = result.status === "等待 Owner 确认";
82
+ const directoryFailed = !pending && result.directorySync?.status === "not_synced";
77
83
  const message = [
78
84
  failed ? `${aid} 已创建,但连接失败:${result.connectionError || "未提供连接失败原因,请查看运行日志"}\n身份已经保存在本地,请勿重复注册。`
79
- : result.status === "等待 Owner 确认" ? `${aid} 等待 Owner 确认` : `${aid} 已创建并连接`,
80
- directoryFailed ? `Owner 名录未同步:${result.directorySync.error || "原因未知"}`
85
+ : `${aid} 已创建并连接`,
86
+ pending ? "关联请求已发送,等待 Owner 确认;确认前暂不加入通信名录,请勿重复创建 AID。"
87
+ : directoryFailed ? `Owner 名录未同步:${result.directorySync.error || "原因未知"}`
81
88
  : result.directorySync?.status === "synced" ? "已加入 Owner 名录,个人助理可按默认权限使用该联系人。" : "Owner 名录同步状态未知,请查看运行日志。",
82
89
  ].join("\n");
83
- notify(failed || directoryFailed ? "AID 创建完成,部分步骤失败" : "AID 创建结果", message, failed || directoryFailed);
90
+ notify(failed || directoryFailed ? "AID 创建完成,部分步骤失败" : pending ? "等待 Owner 确认" : "AID 创建结果", message, failed || directoryFailed);
84
91
  return load();
85
92
  }).catch((error) => notify("创建 AID 失败", errorMessage(error), true));
86
93
  };
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runtimeMenuQuery = runtimeMenuQuery;
4
+ function runtimeMenuQuery(name, args, aid, owner, baseagent, snapshot) {
5
+ const fail = (code, message) => { throw Object.assign(new Error(message), { code }); };
6
+ if (args.aid !== undefined && args.aid !== aid || args.self !== undefined && args.self !== aid
7
+ || args.baseagent !== undefined && args.baseagent !== baseagent)
8
+ fail("INVALID_ARGUMENT", "只能读取当前身份及其关联 Agent");
9
+ const scope = args.scope ?? "agent";
10
+ if (!["agent", "relation"].includes(scope))
11
+ fail("NOT_SUPPORTED", "仅支持 Agent 或当前私聊关系");
12
+ const peerKey = `aun#${owner}`;
13
+ if (args.peerKey !== undefined && ![owner, peerKey].includes(args.peerKey))
14
+ fail("NOT_ALLOWED", "仅支持当前 Owner 私聊关系");
15
+ const selected = { model: snapshot.model, effort: snapshot.effort };
16
+ if (name === "agent") {
17
+ const config = { owner, owners: [owner], active_baseagent: baseagent, baseagents: { [baseagent]: selected } };
18
+ return { aid, baseagent, status: snapshot.status, connection: { status: snapshot.connected ? "connected" : "disconnected" },
19
+ sessions: { active: snapshot.activeSessions, active_peers: snapshot.activePeers }, config, safeConfig: config };
20
+ }
21
+ if (name === "model" || name === "effort")
22
+ return { [name]: snapshot[name], baseagent, source: snapshot.source,
23
+ scope, self: aid, field: `baseagents.${baseagent}.${name}`, override: null, writable: false,
24
+ ...(scope === "relation" ? { peerKey } : {}) };
25
+ if (name === "chatmode") {
26
+ if (args.field !== undefined && !["private", "chatmode.private"].includes(args.field))
27
+ fail("NOT_SUPPORTED", "当前只支持私聊交互模式");
28
+ return { mode: "interactive", source: "agent", scope, self: aid, field: "chatmode.private", override: null, writable: false,
29
+ ...(scope === "relation" ? { peerKey } : {}) };
30
+ }
31
+ if (name === "role") {
32
+ const common = { $schema_version: 1, self: aid, policyRevision: "aun-runtime-readonly-v1" };
33
+ if (args.view === "target") {
34
+ if (args.target?.kind !== "private" || args.target.peerAid !== owner)
35
+ fail("NOT_ALLOWED", "仅提供当前 Owner 的私聊只读快照");
36
+ return { ...common, target: args.target, relationRevision: null, explicitRole: null,
37
+ effectiveRole: { roleId: "owner", source: "ownership", allowAccess: true },
38
+ relationConfig: { explicit: {}, fields: Object.entries({ "chatmode.private": "interactive", [`baseagents.${baseagent}.model`]: snapshot.model, [`baseagents.${baseagent}.effort`]: snapshot.effort }).map(([field, value]) => ({
39
+ field, hasExplicitValue: false, effectiveValue: value, effectiveContext: "private", source: "agent", readable: true, writable: false, writeReason: "由关联客户端管理"
40
+ })) } };
41
+ }
42
+ if (args.view === "role" && args.roleId === "owner")
43
+ return { ...common, roleId: "owner",
44
+ meta: { origin: "builtin", overridden: false, kind: "management", assignable: false, editable: false, deletable: false, resettable: false },
45
+ definition: { description: "当前协议 Owner 的运行信息只读视图", allowAccess: true, permissions: {}, commandPermissions: {} } };
46
+ if (args.view === "capabilities")
47
+ return { ...common, capabilities: Object.fromEntries([
48
+ "roleList", "roleRead", "roleWrite", "roleCreate", "roleDelete", "roleReset", "defaultRoleRead", "defaultRoleWrite", "permissionFields", "permissionValues", "operations", "privateTargetsList", "joinedGroupsList", "assignmentInventory", "relationSnapshot", "privateRoleAssign", "groupRoleAssign", "deleteWithReplacement", "jsonStringUpdateValue",
49
+ ].map(key => [key, key === "roleRead" || key === "relationSnapshot"])) };
50
+ }
51
+ return fail("NOT_SUPPORTED", "当前不支持此运行信息查询");
52
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-bridge-aun",
3
- "version": "1.0.49",
3
+ "version": "1.0.58",
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.49",
3
+ "version": "1.0.58",
4
4
  "type": "channel",
5
5
  "displayName": "AUN 通道",
6
6
  "entry": "dist/src-ts/plugins/aun/index.js",