@songsid/agend 2.0.11-beta.15 → 2.0.11-beta.17

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.
@@ -185,7 +185,7 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
185
185
  saveFleetConfig(): void;
186
186
  removeInstance(name: string): Promise<void>;
187
187
  startStatuslineWatcher(name: string): void;
188
- reactMessageStatus(chatId: string, messageId: string, emoji: string): void;
188
+ reactMessageStatus(instanceName: string, chatId: string, messageId: string, emoji: string): void;
189
189
  private static FAILOVER_TRIGGER_PCT;
190
190
  private static FAILOVER_RECOVER_PCT;
191
191
  checkModelFailover(name: string, fiveHourPct: number): void;
@@ -516,8 +516,9 @@ export class FleetManager {
516
516
  // Rotate classic channel chat logs daily (piggyback on daily summary timer)
517
517
  this.classicChannels?.rotateLogs();
518
518
  this.rotateInboxes();
519
- // Auto-create general instance(s)one per adapter that lacks a general
519
+ // Auto-create/adopt a general dispatcherONLY for the primary adapter.
520
520
  const channelConfigs = fleet.channels ?? (fleet.channel ? [fleet.channel] : []);
521
+ const primaryAdapterId = channelConfigs[0] ? (channelConfigs[0].id ?? channelConfigs[0].type) : undefined;
521
522
  const generalInstances = Object.entries(fleet.instances).filter(([, inst]) => inst.general_topic === true);
522
523
  let generalsCreated = false;
523
524
  // Collect unbound generals (no channel_id set) for auto-assignment
@@ -526,6 +527,14 @@ export class FleetManager {
526
527
  const needsGeneral = [];
527
528
  for (const ch of channelConfigs) {
528
529
  const adapterId = ch.id ?? ch.type;
530
+ // Only the primary adapter gets an auto-general. Secondary (persona) bots
531
+ // answer for their explicitly-bound instances only — they don't need or
532
+ // auto-claim a general dispatcher, and must never adopt the primary's
533
+ // unbound general. A general a user manually bound to a secondary
534
+ // (channel_id: <persona>) is left untouched — the auto logic just won't
535
+ // create or reassign bindings for non-primary adapters.
536
+ if (adapterId !== primaryAdapterId)
537
+ continue;
529
538
  // Check if any general is explicitly bound to this adapter
530
539
  if (generalInstances.some(([, inst]) => inst.channel_id === adapterId))
531
540
  continue;
@@ -2609,15 +2618,17 @@ export class FleetManager {
2609
2618
  startStatuslineWatcher(name) {
2610
2619
  this.statuslineWatcher.watch(name);
2611
2620
  }
2612
- reactMessageStatus(chatId, messageId, emoji) {
2613
- // Find the adapter that owns this chatId (check all adapters, not just primary)
2614
- for (const [, w] of this.worlds) {
2615
- if (w.type === "discord") {
2616
- w.react(chatId, messageId, emoji)
2617
- .catch(e => this.logger.debug({ err: e.message }, "Message status react failed"));
2618
- return;
2619
- }
2620
- }
2621
+ reactMessageStatus(instanceName, chatId, messageId, emoji) {
2622
+ // React via the adapter BOUND to this instance NOT the first discord world.
2623
+ // Otherwise, in a same-channel/same-guild multi-bot setup, the inbound 👀
2624
+ // (bound bot) and the delivery/confirm reactions (some other bot) come from
2625
+ // different bots, leaving a duplicate 👀 that never turns into ✅.
2626
+ const adapter = this.getAdapterForInstance(instanceName) ?? this.adapter;
2627
+ // Status reactions are Discord-only (TG/others use the inbound react path).
2628
+ if (!adapter || adapter.type !== "discord")
2629
+ return;
2630
+ adapter.react(chatId, messageId, emoji)
2631
+ .catch(e => this.logger.debug({ err: e.message }, "Message status react failed"));
2621
2632
  }
2622
2633
  // ── Model failover ──────────────────────────────────────────────────────
2623
2634
  static FAILOVER_TRIGGER_PCT = 90;