a2a-xmtp 1.4.1 → 1.4.2
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/package.json
CHANGED
|
@@ -39,16 +39,11 @@ export class GroupScheduler {
|
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* 初始化或获取群组状态。成员变化时自动重算。
|
|
42
|
-
* @param
|
|
43
|
-
* 非 agent(如群主/人类)不参与 speaking order。
|
|
42
|
+
* @param members 参与轮询的成员地址(调用方已按角色过滤,仅含 agent)
|
|
44
43
|
*/
|
|
45
|
-
getOrInitGroup(groupId: string, members: string[]
|
|
44
|
+
getOrInitGroup(groupId: string, members: string[]): GroupConversationState {
|
|
46
45
|
const existing = this.groups.get(groupId);
|
|
47
|
-
|
|
48
|
-
const agentMembers = agentAddresses
|
|
49
|
-
? members.filter((m) => agentAddresses.has(m.toLowerCase()))
|
|
50
|
-
: members;
|
|
51
|
-
const order = this.computeSpeakingOrder(groupId, agentMembers);
|
|
46
|
+
const order = this.computeSpeakingOrder(groupId, members);
|
|
52
47
|
|
|
53
48
|
// 成员变化 → 重算并重置计数
|
|
54
49
|
if (existing && !arraysEqual(existing.speakingOrder, order)) {
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
// ============================================================
|
|
5
5
|
|
|
6
6
|
import type { XmtpBridge } from "../transport/xmtp-bridge.js";
|
|
7
|
-
import type { IdentityRegistry } from "../transport/identity-registry.js";
|
|
8
7
|
import { formatA2AMessage, type A2AInjectPayload } from "../types.js";
|
|
9
8
|
import { GroupScheduler } from "./group-scheduler.js";
|
|
10
9
|
import type { PolicyEngine } from "./policy-engine.js";
|
|
@@ -50,7 +49,6 @@ export class MessageOrchestrator {
|
|
|
50
49
|
private logger: Logger,
|
|
51
50
|
private policyEngine: PolicyEngine,
|
|
52
51
|
groupScheduler: GroupScheduler,
|
|
53
|
-
private registry: IdentityRegistry,
|
|
54
52
|
) {
|
|
55
53
|
this.groupScheduler = groupScheduler;
|
|
56
54
|
}
|
|
@@ -155,20 +153,17 @@ export class MessageOrchestrator {
|
|
|
155
153
|
payload: A2AInjectPayload,
|
|
156
154
|
): Promise<boolean> {
|
|
157
155
|
const convId = payload.conversation.id;
|
|
158
|
-
const members = payload.conversation.participants;
|
|
159
156
|
const myAddress = bridge.address;
|
|
160
157
|
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
}
|
|
158
|
+
// 用 XMTP 原生角色信息筛选:只有普通 Member (permissionLevel=0) 参与轮询
|
|
159
|
+
// Admin/SuperAdmin 通常是人类,不参与 round-robin
|
|
160
|
+
const details = payload.conversation.participantDetails;
|
|
161
|
+
const agentMembers = details
|
|
162
|
+
? details.filter((p) => p.permissionLevel === 0).map((p) => p.address)
|
|
163
|
+
: payload.conversation.participants; // fallback:无角色信息时用全部成员
|
|
169
164
|
|
|
170
|
-
//
|
|
171
|
-
this.groupScheduler.getOrInitGroup(convId,
|
|
165
|
+
// 初始化群组状态
|
|
166
|
+
this.groupScheduler.getOrInitGroup(convId, agentMembers);
|
|
172
167
|
|
|
173
168
|
// 检查 turn budget 是否耗尽
|
|
174
169
|
if (this.policyEngine.isTurnExhausted(convId)) {
|
|
@@ -29,8 +29,9 @@ export class PolicyEngine {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* 入站检查:consent + TTL
|
|
33
|
-
*
|
|
32
|
+
* 入站检查:consent + TTL(不检查 turn budget 和 cool-down)
|
|
33
|
+
* turn budget 仅在出站/调度层检查,确保 turn 耗尽后仍能接收消息、
|
|
34
|
+
* 缓存到 inbox、维持 messageCount 同步。
|
|
34
35
|
*/
|
|
35
36
|
checkIncoming(params: PolicyCheckParams): PolicyCheckResult {
|
|
36
37
|
const consent = this.getConsent(params.from);
|
|
@@ -43,16 +44,12 @@ export class PolicyEngine {
|
|
|
43
44
|
reason: `Sender ${params.from} not explicitly allowed (consent: ${consent})`,
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
|
-
// 仅检查 TTL + turn budget,不检查 cool-down
|
|
47
47
|
const state = this.getOrCreateState(params.conversationId);
|
|
48
48
|
const now = Date.now();
|
|
49
49
|
const ttlMs = this.policy.ttlMinutes * 60 * 1000;
|
|
50
50
|
if (now - state.createdAt > ttlMs) {
|
|
51
51
|
return { allowed: false, reason: `Conversation TTL expired (${this.policy.ttlMinutes} min)` };
|
|
52
52
|
}
|
|
53
|
-
if (state.turn >= this.policy.maxTurns) {
|
|
54
|
-
return { allowed: false, reason: `Turn budget exhausted (${state.turn}/${this.policy.maxTurns})` };
|
|
55
|
-
}
|
|
56
53
|
return { allowed: true };
|
|
57
54
|
}
|
|
58
55
|
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { Agent, createUser, createSigner } from "@xmtp/agent-sdk";
|
|
|
7
7
|
import type { IdentityRegistry } from "./identity-registry.js";
|
|
8
8
|
import { PolicyEngine } from "../coordination/policy-engine.js";
|
|
9
9
|
import type { XmtpWalletConfig, InboxMessage, A2AContentType, A2AInjectPayload, GroupInfo } from "../types.js";
|
|
10
|
-
import { createA2APayload, CLAIM_PREFIX } from "../types.js";
|
|
10
|
+
import { createA2APayload, CLAIM_PREFIX, type GroupParticipant } from "../types.js";
|
|
11
11
|
import { GroupScheduler } from "../coordination/group-scheduler.js";
|
|
12
12
|
|
|
13
13
|
/** IdentifierKind.Ethereum = 0 (const enum, 不能在 isolatedModules 下直接访问) */
|
|
@@ -258,8 +258,9 @@ export class XmtpBridge {
|
|
|
258
258
|
const convState = this.policyEngine.getConversationState(ctx.conversation.id);
|
|
259
259
|
const turn = convState?.turn ?? 0;
|
|
260
260
|
|
|
261
|
-
//
|
|
261
|
+
// 群聊时获取参与者列表(含角色信息)
|
|
262
262
|
let participants: string[] = [];
|
|
263
|
+
let participantDetails: GroupParticipant[] | undefined;
|
|
263
264
|
const isGroup = ctx.conversation.isGroup ?? false;
|
|
264
265
|
if (isGroup) {
|
|
265
266
|
try {
|
|
@@ -267,6 +268,10 @@ export class XmtpBridge {
|
|
|
267
268
|
participants = members.map((m: any) =>
|
|
268
269
|
m.accountAddresses?.[0]?.toLowerCase() ?? m.inboxId,
|
|
269
270
|
);
|
|
271
|
+
participantDetails = members.map((m: any) => ({
|
|
272
|
+
address: (m.accountAddresses?.[0]?.toLowerCase() ?? m.inboxId) as string,
|
|
273
|
+
permissionLevel: (m.permissionLevel ?? 0) as number,
|
|
274
|
+
}));
|
|
270
275
|
} catch { /* 获取成员失败时保持空数组 */ }
|
|
271
276
|
}
|
|
272
277
|
|
|
@@ -276,6 +281,7 @@ export class XmtpBridge {
|
|
|
276
281
|
conversationId: ctx.conversation.id,
|
|
277
282
|
isGroup,
|
|
278
283
|
participants,
|
|
284
|
+
participantDetails,
|
|
279
285
|
messageId: ctx.message.id ?? crypto.randomUUID(),
|
|
280
286
|
content,
|
|
281
287
|
contentType,
|
package/src/types.ts
CHANGED
|
@@ -12,6 +12,13 @@ export interface XmtpWalletConfig {
|
|
|
12
12
|
|
|
13
13
|
export type XmtpEnv = "dev" | "production";
|
|
14
14
|
|
|
15
|
+
/** 群成员信息(含角色) */
|
|
16
|
+
export interface GroupParticipant {
|
|
17
|
+
address: string;
|
|
18
|
+
/** XMTP PermissionLevel: 0=Member, 1=Admin, 2=SuperAdmin */
|
|
19
|
+
permissionLevel: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
/** 注入 Agent session 的消息格式 */
|
|
16
23
|
export interface A2AInjectPayload {
|
|
17
24
|
type: "a2a-xmtp";
|
|
@@ -24,6 +31,8 @@ export interface A2AInjectPayload {
|
|
|
24
31
|
id: string;
|
|
25
32
|
isGroup: boolean;
|
|
26
33
|
participants: string[];
|
|
34
|
+
/** 群成员详细信息(含角色),仅群聊时有值 */
|
|
35
|
+
participantDetails?: GroupParticipant[];
|
|
27
36
|
};
|
|
28
37
|
message: {
|
|
29
38
|
id: string;
|
|
@@ -86,8 +95,8 @@ export interface GroupConversationState {
|
|
|
86
95
|
lastClaim: { sender: string; timestamp: number; messageId: string } | null;
|
|
87
96
|
}
|
|
88
97
|
|
|
89
|
-
/** Claim
|
|
90
|
-
export const CLAIM_PREFIX = "
|
|
98
|
+
/** Claim 消息前缀(可见标记,避免 XMTP 传输时被 strip) */
|
|
99
|
+
export const CLAIM_PREFIX = "__CLAIM__:";
|
|
91
100
|
|
|
92
101
|
/** 默认群聊调度配置 */
|
|
93
102
|
export const DEFAULT_GROUP_SCHEDULING: GroupSchedulingConfig = {
|
|
@@ -150,6 +159,7 @@ export function createA2APayload(params: {
|
|
|
150
159
|
conversationId: string;
|
|
151
160
|
isGroup: boolean;
|
|
152
161
|
participants: string[];
|
|
162
|
+
participantDetails?: GroupParticipant[];
|
|
153
163
|
messageId: string;
|
|
154
164
|
content: string;
|
|
155
165
|
contentType: A2AContentType;
|
|
@@ -168,6 +178,7 @@ export function createA2APayload(params: {
|
|
|
168
178
|
id: params.conversationId,
|
|
169
179
|
isGroup: params.isGroup,
|
|
170
180
|
participants: params.participants,
|
|
181
|
+
participantDetails: params.participantDetails,
|
|
171
182
|
},
|
|
172
183
|
message: {
|
|
173
184
|
id: params.messageId,
|