a2a-xmtp 1.4.2 → 1.4.3
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
|
@@ -66,6 +66,14 @@ export class MessageOrchestrator {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* 群聊 self 消息计数(由 bridge.onSelfGroupMessage 触发)
|
|
71
|
+
* 保持 messageCount 与其他 agent 同步。
|
|
72
|
+
*/
|
|
73
|
+
handleSelfGroupMessage(conversationId: string): void {
|
|
74
|
+
this.groupScheduler.recordMessage(conversationId);
|
|
75
|
+
}
|
|
76
|
+
|
|
69
77
|
/**
|
|
70
78
|
* 处理收到的 XMTP 消息:群聊 round-robin 调度 → LLM 推理 → 安全校验 → 回复
|
|
71
79
|
*/
|
|
@@ -122,16 +130,21 @@ export class MessageOrchestrator {
|
|
|
122
130
|
const replyText = this.extractReplyText(messages);
|
|
123
131
|
if (!replyText) return;
|
|
124
132
|
|
|
133
|
+
// 发送前最终检查 turn budget(收窄 race window)
|
|
134
|
+
if (payload.conversation.isGroup && this.policyEngine.isTurnExhausted(payload.conversation.id)) {
|
|
135
|
+
this.logger.info(
|
|
136
|
+
`[a2a-xmtp] Turn budget exhausted before send in ${payload.conversation.id}, dropping reply`,
|
|
137
|
+
);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
125
141
|
// recordTurn 由 bridge.sendMessage 内部调用,此处不重复
|
|
126
142
|
await bridge.sendMessage(payload.from.xmtpAddress, replyText, {
|
|
127
143
|
conversationId: payload.conversation.id,
|
|
128
144
|
});
|
|
129
145
|
|
|
130
|
-
//
|
|
146
|
+
// 群聊:清除 claim(self 消息计数由 bridge.onSelfGroupMessage 处理)
|
|
131
147
|
if (payload.conversation.isGroup) {
|
|
132
|
-
// 自己发的消息 handleIncoming 不会收到(跳过 self),
|
|
133
|
-
// 手动递增以保持与其他 agent 的 messageCount 同步
|
|
134
|
-
this.groupScheduler.recordMessage(payload.conversation.id);
|
|
135
148
|
this.groupScheduler.clearClaim(payload.conversation.id);
|
|
136
149
|
}
|
|
137
150
|
|
|
@@ -29,9 +29,10 @@ export class PolicyEngine {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* 入站检查:consent + TTL
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* 入站检查:consent + TTL + turn budget(不检查 cool-down,
|
|
33
|
+
* 否则刚发完消息后收到的回复会被静默丢弃)。
|
|
34
|
+
* 注意:turn budget 拦截不影响 messageCount 同步,因为
|
|
35
|
+
* self 消息通过 bridge.onSelfGroupMessage 独立计数。
|
|
35
36
|
*/
|
|
36
37
|
checkIncoming(params: PolicyCheckParams): PolicyCheckResult {
|
|
37
38
|
const consent = this.getConsent(params.from);
|
|
@@ -50,6 +51,9 @@ export class PolicyEngine {
|
|
|
50
51
|
if (now - state.createdAt > ttlMs) {
|
|
51
52
|
return { allowed: false, reason: `Conversation TTL expired (${this.policy.ttlMinutes} min)` };
|
|
52
53
|
}
|
|
54
|
+
if (state.turn >= this.policy.maxTurns) {
|
|
55
|
+
return { allowed: false, reason: `Turn budget exhausted (${state.turn}/${this.policy.maxTurns})` };
|
|
56
|
+
}
|
|
53
57
|
return { allowed: true };
|
|
54
58
|
}
|
|
55
59
|
|
package/src/index.ts
CHANGED
|
@@ -186,6 +186,10 @@ export default definePluginEntry({
|
|
|
186
186
|
bridge.onClaim = (conversationId, senderAddress, messageId) =>
|
|
187
187
|
orchestrator.handleClaim(conversationId, senderAddress, messageId);
|
|
188
188
|
|
|
189
|
+
// 群聊 self 消息计数回调:保持 messageCount 同步
|
|
190
|
+
bridge.onSelfGroupMessage = (conversationId) =>
|
|
191
|
+
orchestrator.handleSelfGroupMessage(conversationId);
|
|
192
|
+
|
|
189
193
|
await bridge.start();
|
|
190
194
|
bridges.set(AGENT_ID, bridge);
|
|
191
195
|
|
|
@@ -28,6 +28,9 @@ export class XmtpBridge {
|
|
|
28
28
|
/** Claim 消息回调(由 orchestrator 设置,用于群聊调度) */
|
|
29
29
|
onClaim?: (conversationId: string, senderAddress: string, messageId: string) => void;
|
|
30
30
|
|
|
31
|
+
/** 群聊 self 消息计数回调(由 orchestrator 设置,用于 round-robin 同步) */
|
|
32
|
+
onSelfGroupMessage?: (conversationId: string) => void;
|
|
33
|
+
|
|
31
34
|
constructor(
|
|
32
35
|
readonly agentId: string,
|
|
33
36
|
private walletConfig: XmtpWalletConfig,
|
|
@@ -232,9 +235,7 @@ export class XmtpBridge {
|
|
|
232
235
|
|
|
233
236
|
private async handleIncoming(ctx: any, contentType: A2AContentType): Promise<void> {
|
|
234
237
|
const senderAddress: string = await ctx.getSenderAddress();
|
|
235
|
-
|
|
236
|
-
if (senderAddress.toLowerCase() === this.address.toLowerCase()) return;
|
|
237
|
-
|
|
238
|
+
const isSelf = senderAddress.toLowerCase() === this.address.toLowerCase();
|
|
238
239
|
const content = String(ctx.message.content);
|
|
239
240
|
|
|
240
241
|
// 识别 claim 消息:不计数、不缓存、通知调度器
|
|
@@ -246,6 +247,15 @@ export class XmtpBridge {
|
|
|
246
247
|
return;
|
|
247
248
|
}
|
|
248
249
|
|
|
250
|
+
// 群聊 self 消息:仅计数(保持 messageCount 同步),不触发响应
|
|
251
|
+
const isGroup = ctx.conversation.isGroup ?? false;
|
|
252
|
+
if (isSelf) {
|
|
253
|
+
if (isGroup && this.onSelfGroupMessage) {
|
|
254
|
+
this.onSelfGroupMessage(ctx.conversation.id);
|
|
255
|
+
}
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
249
259
|
const senderAgentId = await this.registry.resolveAgentId(senderAddress);
|
|
250
260
|
|
|
251
261
|
const policyResult = this.policyEngine.checkIncoming({
|
|
@@ -261,7 +271,6 @@ export class XmtpBridge {
|
|
|
261
271
|
// 群聊时获取参与者列表(含角色信息)
|
|
262
272
|
let participants: string[] = [];
|
|
263
273
|
let participantDetails: GroupParticipant[] | undefined;
|
|
264
|
-
const isGroup = ctx.conversation.isGroup ?? false;
|
|
265
274
|
if (isGroup) {
|
|
266
275
|
try {
|
|
267
276
|
const members = await ctx.conversation.members();
|