@songsid/agend 2.1.4-beta.58 → 2.1.4-beta.59
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/dist/fleet-manager.d.ts +5 -0
- package/dist/fleet-manager.js +59 -36
- package/dist/fleet-manager.js.map +1 -1
- package/package.json +1 -1
package/dist/fleet-manager.d.ts
CHANGED
|
@@ -574,6 +574,11 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
|
|
|
574
574
|
* message actually went out — a failed delivery keeps them queued.
|
|
575
575
|
*/
|
|
576
576
|
private pendingReactionsMeta;
|
|
577
|
+
/**
|
|
578
|
+
* Why this adapter must ignore a bot/webhook message, or null to accept it.
|
|
579
|
+
* Pure: callers rely on being able to ask before claiming the dedup key.
|
|
580
|
+
*/
|
|
581
|
+
private botMessageDropReason;
|
|
577
582
|
private handleInboundMessage;
|
|
578
583
|
/** Handle outbound tool calls from a daemon instance */
|
|
579
584
|
/** Warn (but don't block) when rate limits are high. 30-min debounce per instance. */
|
package/dist/fleet-manager.js
CHANGED
|
@@ -3780,9 +3780,68 @@ export class FleetManager {
|
|
|
3780
3780
|
consume: () => this.eventLog?.markReactionsConsumed(instanceName, pending.maxId),
|
|
3781
3781
|
};
|
|
3782
3782
|
}
|
|
3783
|
+
/**
|
|
3784
|
+
* Why this adapter must ignore a bot/webhook message, or null to accept it.
|
|
3785
|
+
* Pure: callers rely on being able to ask before claiming the dedup key.
|
|
3786
|
+
*/
|
|
3787
|
+
botMessageDropReason(msg, threadId) {
|
|
3788
|
+
if (!threadId) {
|
|
3789
|
+
// TG classic: allow if the bot @mentions our bot or access mode is open
|
|
3790
|
+
const world = this.worlds.get(msg.adapterId ?? "");
|
|
3791
|
+
const botUser = world?.botUsername;
|
|
3792
|
+
const isOpen = this.getChannelConfig(msg.adapterId)?.access?.mode === "open";
|
|
3793
|
+
const mentionsUs = !!(botUser && msg.text?.toLowerCase().includes(`@${botUser.toLowerCase()}`));
|
|
3794
|
+
return (!isOpen && !mentionsUs) ? "tg-classic: not open and does not mention us" : null;
|
|
3795
|
+
}
|
|
3796
|
+
if (this.classicChannels?.hasChannel(threadId)) {
|
|
3797
|
+
// Classic channel (per-bot): bot messages only when THIS bot owns an
|
|
3798
|
+
// agent here and collab is on for it.
|
|
3799
|
+
if (!this.classicChannels.getInstanceByChannel(threadId, msg.adapterId)) {
|
|
3800
|
+
return "classic: this bot owns no agent in the channel";
|
|
3801
|
+
}
|
|
3802
|
+
if (!this.classicChannels.isCollab(threadId, msg.adapterId))
|
|
3803
|
+
return "classic: collab off";
|
|
3804
|
+
return null;
|
|
3805
|
+
}
|
|
3806
|
+
const target = this.routing.resolve(threadId);
|
|
3807
|
+
if (!target)
|
|
3808
|
+
return "fleet topic: no instance routed for this thread";
|
|
3809
|
+
// A fleet topic is served by exactly one instance, so exactly one bot should
|
|
3810
|
+
// answer a webhook there. Several bots sharing the guild each receive their
|
|
3811
|
+
// own copy; pick that instance's own adapter deterministically rather than
|
|
3812
|
+
// letting whichever inbound fired first win. The primary is per-instance —
|
|
3813
|
+
// the adapter the instance is bound to, falling back to channels[0] when it
|
|
3814
|
+
// is not bound — so an instance answered by a non-default bot still receives
|
|
3815
|
+
// its webhooks. (Classic channels are exempt above: there each bot owns its
|
|
3816
|
+
// own agent and must handle its own copy.)
|
|
3817
|
+
const ownerAdapterId = this.getInstanceAdapterId(target.name);
|
|
3818
|
+
if (msg.adapterId && ownerAdapterId && msg.adapterId !== ownerAdapterId) {
|
|
3819
|
+
return `fleet topic: not the adapter bound to ${target.name} (that is ${ownerAdapterId})`;
|
|
3820
|
+
}
|
|
3821
|
+
// Fleet topic: allow if collab enabled OR access mode is open
|
|
3822
|
+
const isOpen = this.getChannelConfig(msg.adapterId)?.access?.mode === "open";
|
|
3823
|
+
if (!isOpen && !this.collabInstances.has(target.name)) {
|
|
3824
|
+
return `fleet topic: adapter not open and collab off for ${target.name}`;
|
|
3825
|
+
}
|
|
3826
|
+
return null;
|
|
3827
|
+
}
|
|
3783
3828
|
async handleInboundMessage(msg) {
|
|
3784
3829
|
const threadId = msg.threadId || undefined;
|
|
3785
3830
|
this.logger.debug({ source: msg.source, chatId: msg.chatId, threadId, userId: msg.userId, isBotMessage: msg.isBotMessage, textLen: (msg.text ?? "").length, text: (msg.text ?? "").slice(0, 80) }, "handleInboundMessage entry");
|
|
3831
|
+
// Bot messages are filtered per-adapter, and the filter reads THIS adapter's
|
|
3832
|
+
// access config. That has to happen before the dedup claim below: when two
|
|
3833
|
+
// bots share a guild they both receive the same webhook, and a copy this
|
|
3834
|
+
// adapter is going to drop must not consume the shared dedup key — otherwise
|
|
3835
|
+
// the sibling adapter that would have accepted it sees a duplicate and the
|
|
3836
|
+
// message is lost entirely. Claiming the key only on the copies that survive
|
|
3837
|
+
// also keeps delivery single when several adapters would accept it.
|
|
3838
|
+
if (msg.isBotMessage) {
|
|
3839
|
+
const drop = this.botMessageDropReason(msg, threadId);
|
|
3840
|
+
if (drop) {
|
|
3841
|
+
this.logger.debug({ adapterId: msg.adapterId, threadId: threadId ?? null, messageId: msg.messageId, reason: drop }, "Bot message dropped by adapter filter — dedup key not consumed");
|
|
3842
|
+
return;
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3786
3845
|
// Multi-adapter dedup: when several bots share a guild, each adapter fires
|
|
3787
3846
|
// its own "message" event for the same underlying message. Process it once.
|
|
3788
3847
|
// Routing (by topic/channel) and reply-adapter selection (by channel_id
|
|
@@ -3811,42 +3870,6 @@ export class FleetManager {
|
|
|
3811
3870
|
this.recentMessageIds.delete(oldest);
|
|
3812
3871
|
}
|
|
3813
3872
|
}
|
|
3814
|
-
// Bot messages: only allow in collab channels or TG classic with @mention
|
|
3815
|
-
if (msg.isBotMessage) {
|
|
3816
|
-
if (!threadId) {
|
|
3817
|
-
// TG classic: allow if bot @mentions our bot or access mode is open
|
|
3818
|
-
const world = this.worlds.get(msg.adapterId ?? "");
|
|
3819
|
-
const botUser = world?.botUsername;
|
|
3820
|
-
const channelCfg = this.getChannelConfig(msg.adapterId);
|
|
3821
|
-
const isOpen = channelCfg?.access?.mode === "open";
|
|
3822
|
-
const mentionsUs = !!(botUser && msg.text?.toLowerCase().includes(`@${botUser.toLowerCase()}`));
|
|
3823
|
-
this.logger.debug({ botUser, mentionsUs, isOpen, isBotMessage: true, threadId: null }, "Bot message filter (no threadId path)");
|
|
3824
|
-
if (!isOpen && !mentionsUs)
|
|
3825
|
-
return;
|
|
3826
|
-
// Fall through to TG classic handling below
|
|
3827
|
-
}
|
|
3828
|
-
else if (this.classicChannels?.hasChannel(threadId)) {
|
|
3829
|
-
// Classic channel (per-bot): bot messages only when THIS bot owns an
|
|
3830
|
-
// agent here and collab is on for it.
|
|
3831
|
-
const classicName = this.classicChannels.getInstanceByChannel(threadId, msg.adapterId);
|
|
3832
|
-
if (!classicName)
|
|
3833
|
-
return;
|
|
3834
|
-
if (!this.classicChannels.isCollab(threadId, msg.adapterId))
|
|
3835
|
-
return;
|
|
3836
|
-
// Fall through to channel handling
|
|
3837
|
-
}
|
|
3838
|
-
else {
|
|
3839
|
-
const target = this.routing.resolve(threadId);
|
|
3840
|
-
if (!target)
|
|
3841
|
-
return;
|
|
3842
|
-
// Fleet topic: allow if collab enabled OR access mode is open
|
|
3843
|
-
const channelCfg = this.getChannelConfig(msg.adapterId);
|
|
3844
|
-
const isOpen = channelCfg?.access?.mode === "open";
|
|
3845
|
-
if (!isOpen && !this.collabInstances.has(target.name))
|
|
3846
|
-
return;
|
|
3847
|
-
// Fall through to channel handling
|
|
3848
|
-
}
|
|
3849
|
-
}
|
|
3850
3873
|
// Access control — classic channels are open to all, others require allowed user
|
|
3851
3874
|
const am = (msg.adapterId ? this.worlds.get(msg.adapterId)?.accessManager : undefined) ?? this.accessManager;
|
|
3852
3875
|
if (am && !am.isAllowed(msg.userId)) {
|