@songsid/agend 2.2.0-beta.13 → 2.2.0-beta.14

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.
@@ -1903,9 +1903,13 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
1903
1903
  private webImageCaches;
1904
1904
  private webImageCache;
1905
1905
  /**
1906
- * instance → the connection that is its own bot: configured now (a connection removed from fleet.yaml is nobody's,
1907
- * whatever an instance still names — #1635 review), not the primary, and answering for it alone.
1906
+ * The bots instances and ClassicBot rooms speak through, by connections configured now (a connection removed from
1907
+ * fleet.yaml is nobody's, whatever an instance still names — #1635 review). `own`: instance → its own bot (not the
1908
+ * primary, answering for it alone). `speaking` (#1867): instance → the bot it speaks through — its own, a shared one,
1909
+ * or, with none configured for it, the primary.
1908
1910
  */
1911
+ private webBots;
1912
+ /** instance → the connection that is its own bot (see webBots). */
1909
1913
  private ownBots;
1910
1914
  /**
1911
1915
  * The bot a connection is now: its running adapter and that bot's own user id. Everything cached for an own bot is
@@ -1921,9 +1925,15 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
1921
1925
  private webAccent;
1922
1926
  /** The cache key of an own bot's avatar, and its loader (which refuses an answer from a bot no longer there). */
1923
1927
  private botAvatarSource;
1924
- /** One instance's avatar and ring for the status frame. Synchronous: what is cached now; fetches run behind. */
1928
+ /**
1929
+ * One instance's avatar and ring for the status frame. Synchronous: what is cached now; fetches run behind. The bot
1930
+ * is its own, else the one it speaks through (#1867) — one cache entry per bot, so a shared bot is fetched once.
1931
+ */
1925
1932
  private webIdentityImage;
1926
- /** GET /ui/img/bot|avatar/<key>: what is cached, never a fetch. Only an own-bot connection, or a known instance's upload. */
1933
+ /**
1934
+ * GET /ui/img/bot|avatar/<key>: what is cached, never a fetch. Only a connection some instance or room speaks
1935
+ * through (#1867), or a known instance's upload.
1936
+ */
1927
1937
  webImage(kind: "bot" | "avatar", key: string): CachedImage | null;
1928
1938
  /**
1929
1939
  * #1682 GET /ui/mentions: names for the mention ids a page found in what it rendered — never a Discord request; in order:
@@ -10432,10 +10432,12 @@ export class FleetManager {
10432
10432
  return this.webImageCaches[kind];
10433
10433
  }
10434
10434
  /**
10435
- * instance → the connection that is its own bot: configured now (a connection removed from fleet.yaml is nobody's,
10436
- * whatever an instance still names — #1635 review), not the primary, and answering for it alone.
10435
+ * The bots instances and ClassicBot rooms speak through, by connections configured now (a connection removed from
10436
+ * fleet.yaml is nobody's, whatever an instance still names — #1635 review). `own`: instance → its own bot (not the
10437
+ * primary, answering for it alone). `speaking` (#1867): instance → the bot it speaks through — its own, a shared one,
10438
+ * or, with none configured for it, the primary.
10437
10439
  */
10438
- ownBots() {
10440
+ webBots() {
10439
10441
  const instances = this.fleetConfig?.instances ?? {};
10440
10442
  const channels = this.fleetConfig?.channels ?? (this.fleetConfig?.channel ? [this.fleetConfig.channel] : []);
10441
10443
  const configured = new Set(channels.map(ch => ch.id ?? ch.type).filter((id) => typeof id === "string"));
@@ -10445,8 +10447,17 @@ export class FleetManager {
10445
10447
  if (!Object.hasOwn(instances, room.instanceName))
10446
10448
  assignments.push([room.instanceName, assigned(room.instanceName)]);
10447
10449
  }
10448
- return ownBotConnections(this.getPrimaryAdapterId(), assignments);
10450
+ const primary = this.getPrimaryAdapterId(), fallback = primary && configured.has(primary) ? primary : undefined;
10451
+ const speaking = new Map();
10452
+ for (const [instance, connection] of assignments) {
10453
+ const c = connection ?? fallback;
10454
+ if (c)
10455
+ speaking.set(instance, c);
10456
+ }
10457
+ return { own: ownBotConnections(primary, assignments), speaking };
10449
10458
  }
10459
+ /** instance → the connection that is its own bot (see webBots). */
10460
+ ownBots() { return this.webBots().own; }
10450
10461
  /**
10451
10462
  * The bot a connection is now: its running adapter and that bot's own user id. Everything cached for an own bot is
10452
10463
  * keyed by both, so a bot put in its place (a token replaced under the same connection id) never shows the old one's
@@ -10489,8 +10500,11 @@ export class FleetManager {
10489
10500
  },
10490
10501
  };
10491
10502
  }
10492
- /** One instance's avatar and ring for the status frame. Synchronous: what is cached now; fetches run behind. */
10493
- webIdentityImage(name, displayName, profileAvatar, own) {
10503
+ /**
10504
+ * One instance's avatar and ring for the status frame. Synchronous: what is cached now; fetches run behind. The bot
10505
+ * is its own, else the one it speaks through (#1867) — one cache entry per bot, so a shared bot is fetched once.
10506
+ */
10507
+ webIdentityImage(name, displayName, profileAvatar, bots) {
10494
10508
  let profileVersion = null;
10495
10509
  if (profileAvatar && imageMimeForFile(profileAvatar)) {
10496
10510
  try {
@@ -10498,22 +10512,26 @@ export class FleetManager {
10498
10512
  }
10499
10513
  catch { /* removed */ }
10500
10514
  }
10501
- const connection = own.get(name), owner = connection ? this.botOwner(connection) : null;
10515
+ const ownConnection = bots.own.get(name), connection = ownConnection ?? bots.speaking.get(name);
10516
+ const owner = connection ? this.botOwner(connection) : null;
10502
10517
  const source = connection && owner?.adapter.fetchBotAvatar ? this.botAvatarSource(connection, owner) : null;
10503
10518
  const bot = source ? this.webImageCache("avatars").peek(source.key, source.load) : null;
10504
10519
  const cfg = this.ownInstanceConfig(name);
10505
10520
  const avatar = avatarFor({ name, displayName, profileVersion, bot: bot && connection ? { connection, version: bot.version } : null,
10506
10521
  personaEmoji: cfg?.status_emojis?.delivered });
10507
- // The ring belongs to the bot's own face: only when that is what is shown.
10508
- const accent = !profileVersion && bot && connection && owner
10509
- ? this.webAccent(connection, owner, cfg ? (cfg.topic_id == null ? undefined : String(cfg.topic_id)) : this.classicChannels?.getChannelIdByInstance(name))
10522
+ // The ring belongs to an own bot's face: only when that is what is shown (a shared bot's would ring everyone alike).
10523
+ const accent = !profileVersion && bot && ownConnection && owner
10524
+ ? this.webAccent(ownConnection, owner, cfg ? (cfg.topic_id == null ? undefined : String(cfg.topic_id)) : this.classicChannels?.getChannelIdByInstance(name))
10510
10525
  : null;
10511
10526
  return accent ? { avatar, accent } : { avatar };
10512
10527
  }
10513
- /** GET /ui/img/bot|avatar/<key>: what is cached, never a fetch. Only an own-bot connection, or a known instance's upload. */
10528
+ /**
10529
+ * GET /ui/img/bot|avatar/<key>: what is cached, never a fetch. Only a connection some instance or room speaks
10530
+ * through (#1867), or a known instance's upload.
10531
+ */
10514
10532
  webImage(kind, key) {
10515
10533
  if (kind === "bot") {
10516
- if (![...this.ownBots().values()].includes(key))
10534
+ if (![...this.webBots().speaking.values()].includes(key))
10517
10535
  return null;
10518
10536
  const owner = this.botOwner(key);
10519
10537
  return owner ? this.webImageCache("avatars").read(this.botAvatarSource(key, owner).key) : null;
@@ -19873,12 +19891,12 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
19873
19891
  const classicRooms = new Map((this.classicChannels?.getAll() ?? []).map(ch => [ch.instanceName, ch]));
19874
19892
  const profiles = profileIdentities(this.dataDir);
19875
19893
  // #1630: decorative — whatever goes wrong deciding an avatar never costs the frame (every instance gets its first character).
19876
- let ownBots = new Map();
19894
+ let bots = { own: new Map(), speaking: new Map() };
19877
19895
  try {
19878
- ownBots = this.ownBots();
19896
+ bots = this.webBots();
19879
19897
  }
19880
19898
  catch (err) {
19881
- this.logger.debug({ err }, "own bots unreadable (getUiStatus)");
19899
+ this.logger.debug({ err }, "bots unreadable (getUiStatus)");
19882
19900
  }
19883
19901
  const instances = names.map(name => {
19884
19902
  const statusFile = join(this.getInstanceDir(name), "statusline.json");
@@ -19919,7 +19937,7 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
19919
19937
  const identity = resolveInstanceIdentity({ cfg: this.fleetConfig?.instances[name], classic: classicRooms.get(name), profile: profiles.get(name) });
19920
19938
  let image;
19921
19939
  try {
19922
- image = this.webIdentityImage(name, identity.display_name, profiles.get(name)?.avatar_path, ownBots);
19940
+ image = this.webIdentityImage(name, identity.display_name, profiles.get(name)?.avatar_path, bots);
19923
19941
  }
19924
19942
  catch (err) {
19925
19943
  this.logger.debug({ err, name }, "avatar unreadable (getUiStatus)");