@automagik/omni 2.260914.8 → 2.260914.10

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/index.js CHANGED
@@ -137252,7 +137252,7 @@ import { fileURLToPath } from "url";
137252
137252
  // package.json
137253
137253
  var package_default = {
137254
137254
  name: "@automagik/omni",
137255
- version: "2.260914.8",
137255
+ version: "2.260914.10",
137256
137256
  repository: {
137257
137257
  type: "git",
137258
137258
  url: "git+https://github.com/automagik-dev/omni.git",
@@ -44926,11 +44926,14 @@ class BaseChannelPlugin {
44926
44926
  ingestMode: isHistorySync ? "history-sync" : "realtime"
44927
44927
  };
44928
44928
  return this.publishClaimed("message.received", payload, instanceId, {
44929
- idempotencyKey: `${this.id}:${instanceId}:${payload.externalId}:${payload.content.type}`,
44929
+ idempotencyKey: `${this.id}:${instanceId}:${this.ingressKeyId(payload.chatId, payload.externalId)}:${payload.content.type}`,
44930
44930
  externalId: payload.externalId,
44931
44931
  options
44932
44932
  });
44933
44933
  }
44934
+ ingressKeyId(_chatId, id) {
44935
+ return id;
44936
+ }
44934
44937
  async publishClaimed(type, payload, instanceId, params) {
44935
44938
  const { idempotencyKey, externalId, options = {} } = params;
44936
44939
  if (!this.ingressClaim) {
@@ -44960,11 +44963,19 @@ class BaseChannelPlugin {
44960
44963
  reactionClaimParams(kind, instanceId, payload) {
44961
44964
  const raw = payload.rawPayload?.externalId;
44962
44965
  const externalId = typeof raw === "string" && raw.length > 0 ? raw : `${payload.messageId}:${payload.from}`;
44963
- return { idempotencyKey: `${this.id}:${instanceId}:${externalId}:${kind}:${payload.emoji}`, externalId };
44966
+ const keyId = this.ingressKeyId(payload.chatId, externalId);
44967
+ return { idempotencyKey: `${this.id}:${instanceId}:${keyId}:${kind}:${payload.emoji}`, externalId };
44964
44968
  }
44965
44969
  async emitMessageSent(params) {
44966
44970
  const { instanceId, ...payload } = params;
44967
- await this.publishEventInternal("message.sent", payload, instanceId);
44971
+ if (!payload.externalId) {
44972
+ await this.publishEventInternal("message.sent", payload, instanceId);
44973
+ return;
44974
+ }
44975
+ await this.publishClaimed("message.sent", payload, instanceId, {
44976
+ idempotencyKey: `${this.id}:${instanceId}:${this.ingressKeyId(payload.chatId, payload.externalId)}:message.sent`,
44977
+ externalId: payload.externalId
44978
+ });
44968
44979
  }
44969
44980
  async emitMessageFailed(params) {
44970
44981
  const { instanceId, ...payload } = params;
@@ -250935,7 +250946,7 @@ var init_sentry_scrub = __esm(() => {
250935
250946
  var require_package7 = __commonJS((exports, module) => {
250936
250947
  module.exports = {
250937
250948
  name: "@omni/api",
250938
- version: "2.260914.8",
250949
+ version: "2.260914.10",
250939
250950
  private: true,
250940
250951
  type: "module",
250941
250952
  exports: {
@@ -362462,7 +362473,7 @@ function createIngressClaim(db2) {
362462
362473
  channel: channelTypes.includes(channelType) ? channelType : "discord",
362463
362474
  instanceId,
362464
362475
  eventType,
362465
- direction: "inbound",
362476
+ direction: eventType === "message.sent" ? "outbound" : "inbound",
362466
362477
  status: "received",
362467
362478
  idempotencyKey,
362468
362479
  receivedAt: new Date,
@@ -397426,11 +397437,11 @@ async function setupMessageListener(eventBus) {
397426
397437
  try {
397427
397438
  await eventBus.subscribe("message.received", async (event) => {
397428
397439
  const { externalId, chatId, from, content, rawPayload } = event.payload;
397429
- const _instanceId = event.metadata.instanceId;
397430
- messageLog.info("Received", { from, chatId, externalId });
397431
- messageLog.debug("Payload", { content: JSON.stringify(content) });
397440
+ const { instanceId, channelType } = event.metadata;
397441
+ messageLog.info("Received", { instanceId, channel: channelType, from, chatId, externalId });
397442
+ messageLog.debug("Payload", { instanceId, content: JSON.stringify(content) });
397432
397443
  if (process.env.DEBUG_PAYLOADS === "true" && rawPayload) {
397433
- messageLog.debug("Raw payload", { rawPayload });
397444
+ messageLog.debug("Raw payload", { instanceId, rawPayload });
397434
397445
  }
397435
397446
  });
397436
397447
  messageLog.info("Listening for message.received events");
@@ -397671,8 +397682,7 @@ async function setupEventPersistence(eventBus, db2) {
397671
397682
  const sdb = scopedHandle(db2);
397672
397683
  const chatLink = await resolveChatLink(sdb, metadata.instanceId, payload.chatId);
397673
397684
  const replyToEventId = await resolveReplyToEventId(sdb, metadata.instanceId, payload.replyToId);
397674
- const newEvent = {
397675
- ...eventIdInsert(event.id),
397685
+ const columns2 = {
397676
397686
  externalId: payload.externalId,
397677
397687
  channel: mapChannelType(metadata.channelType),
397678
397688
  instanceId: metadata.instanceId,
@@ -397702,7 +397712,7 @@ async function setupEventPersistence(eventBus, db2) {
397702
397712
  conversationId: null,
397703
397713
  ...chatLink
397704
397714
  };
397705
- await sdb.insert(omniEvents).values(newEvent).onConflictDoNothing({ target: omniEvents.id });
397715
+ await sdb.insert(omniEvents).values({ ...eventIdInsert(event.id), ...columns2 }).onConflictDoUpdate({ target: omniEvents.id, set: columns2 });
397706
397716
  });
397707
397717
  log127.debug("Persisted message.sent", {
397708
397718
  externalId: payload.externalId,
@@ -410017,6 +410027,7 @@ var import_bolt = __toESM(require_dist16(), 1);
410017
410027
  var import_web_api = __toESM(require_dist13(), 1);
410018
410028
  var HTTP_MAX_BODY_BYTES = 1024 * 1024;
410019
410029
  var SOCKET_OPEN_TIMEOUT_MS = 1e4;
410030
+ var SOCKET_STALE_AFTER_MS = 5 * 60000;
410020
410031
  function buildActingClients(options, botClient) {
410021
410032
  if (options.authMode !== "user")
410022
410033
  return { actingClient: botClient };
@@ -410078,8 +410089,15 @@ function watchSocketLifecycle(connection, logger5) {
410078
410089
  connection.socketState = state;
410079
410090
  connection.onSocketStateChange?.(state);
410080
410091
  };
410092
+ const touch = () => {
410093
+ connection.lastSocketActivityAt = Date.now();
410094
+ };
410095
+ socketClient.on("ws_message", touch);
410081
410096
  socketClient.on("connected", () => {
410082
410097
  logger5.info("Slack Socket Mode WebSocket connected");
410098
+ touch();
410099
+ const raw = socketClient.websocket?.websocket;
410100
+ raw?.on?.("ping", touch);
410083
410101
  transition("connected");
410084
410102
  });
410085
410103
  socketClient.on("reconnecting", () => {
@@ -410219,6 +410237,7 @@ async function resolveIdentities(connection, logger5) {
410219
410237
  try {
410220
410238
  const authResult = await connection.app.client.auth.test();
410221
410239
  connection.botUserId = authResult.user_id ?? undefined;
410240
+ connection.botId = authResult.bot_id ?? undefined;
410222
410241
  connection.botName = authResult.user ?? undefined;
410223
410242
  connection.teamId = authResult.team_id ?? undefined;
410224
410243
  connection.teamName = authResult.team ?? undefined;
@@ -410275,9 +410294,15 @@ function isSocketOpen(connection) {
410275
410294
  return false;
410276
410295
  const websocket = socketClient.websocket;
410277
410296
  if (websocket)
410278
- return websocket.isActive();
410297
+ return websocket.isActive() && !isSocketStale(connection);
410279
410298
  return connection.socketState === "connected";
410280
410299
  }
410300
+ function isSocketStale(connection, now = Date.now()) {
410301
+ const last = connection.lastSocketActivityAt;
410302
+ if (last === undefined)
410303
+ return false;
410304
+ return now - last > (connection.socketStaleAfterMs ?? SOCKET_STALE_AFTER_MS);
410305
+ }
410281
410306
  async function destroyBoltConnection(connection, logger5) {
410282
410307
  connection.onSocketStateChange = undefined;
410283
410308
  try {
@@ -410627,12 +410652,14 @@ function extractMessageMeta(event) {
410627
410652
  channelType
410628
410653
  };
410629
410654
  }
410630
- function shouldSkipMessage(msg, selfUserIds) {
410631
- if (msg.subtype === "bot_message" || msg.bot_id)
410632
- return true;
410655
+ function shouldSkipMessage(msg, selfUserIds, ownBotId) {
410633
410656
  if (msg.subtype === "message_changed" || msg.subtype === "message_deleted")
410634
410657
  return true;
410635
410658
  const userId = msg.user;
410659
+ if (msg.subtype === "bot_message" && !userId)
410660
+ return true;
410661
+ if (msg.bot_id && (!userId || msg.bot_id === ownBotId))
410662
+ return true;
410636
410663
  if (!userId)
410637
410664
  return true;
410638
410665
  if (selfUserIds.some((id) => id && id === userId))
@@ -410754,20 +410781,30 @@ async function processMessage2(instanceId, msg, currentBotUserId, callbacks, log
410754
410781
  }
410755
410782
  await callbacks.onMessage(instanceId, meta.ts, meta.channelId, userId, content, replyToId, rawPayload, platformTimestamp, meta);
410756
410783
  }
410757
- function setupMessageHandlers2(app, instanceId, botUserId, callbacks, dmPolicyConfig, logger5, filterConfig, reliability, actingUserId) {
410784
+ function setupMessageHandlers2(app, instanceId, botUserId, callbacks, dmPolicyConfig, logger5, filterConfig, reliability, actingUserId, ownBotId) {
410758
410785
  const resolveBotUserId = () => typeof botUserId === "function" ? botUserId() : botUserId;
410759
410786
  const resolveActingUserId = () => typeof actingUserId === "function" ? actingUserId() : actingUserId;
410760
- app.message(async ({ message: message2 }) => {
410761
- const msg = message2;
410762
- if (shouldSkipMessage(msg, [resolveBotUserId(), resolveActingUserId()]))
410787
+ const handle = async (msg) => {
410788
+ if (shouldSkipMessage(msg, [resolveBotUserId(), resolveActingUserId()], ownBotId?.())) {
410789
+ logger5.debug("Message skipped: bot/self/edit", {
410790
+ instanceId,
410791
+ channelId: msg.channel,
410792
+ ts: msg.ts,
410793
+ subtype: msg.subtype,
410794
+ botId: msg.bot_id,
410795
+ userId: msg.user
410796
+ });
410763
410797
  return;
410798
+ }
410764
410799
  const userId = msg.user;
410765
410800
  const meta = extractMessageMeta(msg);
410766
410801
  if (await enforceDmPolicy(meta, userId, dmPolicyConfig, instanceId, callbacks, logger5))
410767
410802
  return;
410768
410803
  await processMessage2(instanceId, msg, resolveBotUserId(), callbacks, logger5, reliability, filterConfig);
410769
- });
410804
+ };
410805
+ app.message(async ({ message: message2 }) => handle(message2));
410770
410806
  logger5.info("Message handlers registered", { instanceId });
410807
+ return handle;
410771
410808
  }
410772
410809
 
410773
410810
  // ../channel-slack/src/handlers/pins.ts
@@ -411586,6 +411623,8 @@ class SlackPlugin extends BaseChannelPlugin {
411586
411623
  presenceStatusTimers = new Map;
411587
411624
  pendingAckReactions = new Map;
411588
411625
  activeNativeStreams = new Map;
411626
+ lastSeenTs = new Map;
411627
+ inboundHandlers = new Map;
411589
411628
  async onInitialize(_context) {}
411590
411629
  async onDestroy() {
411591
411630
  for (const [instanceId, connection] of this.connections) {
@@ -411677,6 +411716,7 @@ class SlackPlugin extends BaseChannelPlugin {
411677
411716
  ownerIdentifier: connection.botUserId
411678
411717
  });
411679
411718
  this.watchSocketState(instanceId, config2, connection);
411719
+ this.backfillMissedMessages(instanceId, connection);
411680
411720
  this.logger.info("Slack instance connected", {
411681
411721
  instanceId,
411682
411722
  botName: connection.botName,
@@ -411706,6 +411746,8 @@ class SlackPlugin extends BaseChannelPlugin {
411706
411746
  await destroyBoltConnection(connection, this.logger);
411707
411747
  this.connections.delete(instanceId);
411708
411748
  this.slackConfigs.delete(instanceId);
411749
+ this.lastSeenTs.delete(instanceId);
411750
+ this.inboundHandlers.delete(instanceId);
411709
411751
  for (const key of this.userNameCache.keys()) {
411710
411752
  if (key.startsWith(`${instanceId}:`))
411711
411753
  this.userNameCache.delete(key);
@@ -411765,6 +411807,7 @@ class SlackPlugin extends BaseChannelPlugin {
411765
411807
  return;
411766
411808
  if (state === "connected") {
411767
411809
  this.logger.info("Slack Socket Mode connection restored", { instanceId });
411810
+ this.backfillMissedMessages(instanceId, connection);
411768
411811
  setStatus({
411769
411812
  state: "connected",
411770
411813
  since: new Date,
@@ -412218,7 +412261,7 @@ class SlackPlugin extends BaseChannelPlugin {
412218
412261
  cursor
412219
412262
  });
412220
412263
  for (const msg of response.messages ?? []) {
412221
- const result = await this.buildHistorySyncMessage(msg, channelId, botUserId, botToken);
412264
+ const result = await this.buildHistorySyncMessage(msg, channelId, botUserId, botToken, connection.botId);
412222
412265
  if (result)
412223
412266
  messages.push(result);
412224
412267
  if (messages.length >= maxMessages)
@@ -412244,9 +412287,9 @@ class SlackPlugin extends BaseChannelPlugin {
412244
412287
  return { mimeType, localPath: undefined };
412245
412288
  }
412246
412289
  }
412247
- async buildHistorySyncMessage(msg, channelId, botUserId, botToken) {
412290
+ async buildHistorySyncMessage(msg, channelId, botUserId, botToken, ownBotId) {
412248
412291
  const userId = msg.user;
412249
- if (msg.bot_id || botUserId && userId === botUserId || !userId)
412292
+ if (!userId || shouldSkipMessage(msg, [botUserId], ownBotId))
412250
412293
  return null;
412251
412294
  const ts = msg.ts;
412252
412295
  const text = msg.text ?? "";
@@ -412362,6 +412405,83 @@ class SlackPlugin extends BaseChannelPlugin {
412362
412405
  instanceId
412363
412406
  });
412364
412407
  }
412408
+ recordSeen(instanceId, msg) {
412409
+ const channelId = msg.channel;
412410
+ const ts = msg.ts;
412411
+ if (!channelId || !ts)
412412
+ return;
412413
+ let seen = this.lastSeenTs.get(instanceId);
412414
+ if (!seen) {
412415
+ seen = new Map;
412416
+ this.lastSeenTs.set(instanceId, seen);
412417
+ }
412418
+ const threadTs = msg.thread_ts;
412419
+ const keys = threadTs && threadTs !== ts ? [channelId, `${channelId}:${threadTs}`] : [channelId];
412420
+ for (const key of keys) {
412421
+ const prev = seen.get(key);
412422
+ if (!prev || Number.parseFloat(ts) > Number.parseFloat(prev))
412423
+ seen.set(key, ts);
412424
+ }
412425
+ }
412426
+ async backfillMissedMessages(instanceId, connection) {
412427
+ const seen = this.lastSeenTs.get(instanceId);
412428
+ const handle = this.inboundHandlers.get(instanceId);
412429
+ if (!seen?.size || !handle)
412430
+ return 0;
412431
+ const missed = new Map;
412432
+ const collect = (channelId, oldest, msgs) => {
412433
+ for (const raw of msgs ?? []) {
412434
+ const ts = raw.ts;
412435
+ if (!ts || Number.parseFloat(ts) <= Number.parseFloat(oldest))
412436
+ continue;
412437
+ missed.set(`${channelId}:${ts}`, {
412438
+ ...raw,
412439
+ channel: channelId,
412440
+ channel_type: raw.channel_type ?? (channelId.startsWith("D") ? "im" : undefined)
412441
+ });
412442
+ }
412443
+ };
412444
+ for (const [key, oldest] of [...seen]) {
412445
+ const [channelId, threadTs] = key.split(":");
412446
+ try {
412447
+ if (threadTs) {
412448
+ const res2 = await connection.actingClient.conversations.replies({ channel: channelId, ts: threadTs, oldest });
412449
+ collect(channelId, oldest, res2.messages);
412450
+ continue;
412451
+ }
412452
+ const res = await connection.actingClient.conversations.history({ channel: channelId, oldest, limit: 200 });
412453
+ collect(channelId, oldest, res.messages);
412454
+ for (const parent of res.messages ?? []) {
412455
+ const latestReply = parent.latest_reply;
412456
+ if (!latestReply || Number.parseFloat(latestReply) <= Number.parseFloat(oldest))
412457
+ continue;
412458
+ const replies = await connection.actingClient.conversations.replies({
412459
+ channel: channelId,
412460
+ ts: parent.ts,
412461
+ oldest
412462
+ });
412463
+ collect(channelId, oldest, replies.messages);
412464
+ }
412465
+ } catch (err2) {
412466
+ this.logger.warn("Slack backfill fetch failed", { instanceId, channelId, threadTs, error: String(err2) });
412467
+ }
412468
+ }
412469
+ const ordered = [...missed.values()].sort((a, b2) => Number.parseFloat(a.ts) - Number.parseFloat(b2.ts));
412470
+ for (const msg of ordered) {
412471
+ this.recordSeen(instanceId, msg);
412472
+ try {
412473
+ await handle(msg);
412474
+ } catch (err2) {
412475
+ this.logger.warn("Slack backfill dispatch failed", { instanceId, ts: msg.ts, error: String(err2) });
412476
+ }
412477
+ }
412478
+ this.logger.info("Slack reconnect backfill complete", {
412479
+ instanceId,
412480
+ conversations: seen.size,
412481
+ recovered: ordered.length
412482
+ });
412483
+ return ordered.length;
412484
+ }
412365
412485
  trackActiveThread(instanceId, channelId, threadTs) {
412366
412486
  this.activeThreads.set(`${instanceId}:${channelId}`, threadTs);
412367
412487
  }
@@ -412440,7 +412560,10 @@ class SlackPlugin extends BaseChannelPlugin {
412440
412560
  await this.handleMessageReceived(instanceId, args.externalId, args.chatId, args.from, args.content, args.replyToId, enrichedPayload, args.platformTimestamp);
412441
412561
  }
412442
412562
  setupHandlers(instanceId, connection, config2, reliability) {
412443
- setupMessageHandlers2(connection.app, instanceId, () => connection.botUserId, {
412563
+ connection.app.message(async ({ message: message2 }) => {
412564
+ this.recordSeen(instanceId, message2);
412565
+ });
412566
+ const handleInbound = setupMessageHandlers2(connection.app, instanceId, () => connection.botUserId, {
412444
412567
  onMessage: async (_instId, externalId, chatId, from, content, replyToId, rawPayload, platformTimestamp, _meta) => {
412445
412568
  const threadTs = rawPayload.threadTs;
412446
412569
  this.trackActiveThread(instanceId, chatId, threadTs ?? externalId);
@@ -412473,7 +412596,8 @@ class SlackPlugin extends BaseChannelPlugin {
412473
412596
  channelAllowlist: config2.channelAllowlist,
412474
412597
  channelBlocklist: config2.channelBlocklist,
412475
412598
  channels: config2.channels
412476
- }, reliability, () => connection.actingUserId);
412599
+ }, reliability, () => connection.actingUserId, () => connection.botId);
412600
+ this.inboundHandlers.set(instanceId, handleInbound);
412477
412601
  setupReactionHandlers2(connection.app, instanceId, () => connection.botUserId, {
412478
412602
  onReaction: async (instId, messageId, chatId, userId, emoji, action) => {
412479
412603
  await this.handleReactionReceived(instId, messageId, chatId, userId, emoji, action);
@@ -414973,6 +415097,9 @@ class TelegramPlugin extends BaseChannelPlugin {
414973
415097
  configs = new Map;
414974
415098
  cleanups = new Map;
414975
415099
  dedupeCaches = new Map;
415100
+ ingressKeyId(chatId, id) {
415101
+ return `${chatId}:${id}`;
415102
+ }
414976
415103
  typingIntervals = new Map;
414977
415104
  async onInitialize(_context) {
414978
415105
  this.logger.info("Telegram plugin initialized");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/omni",
3
- "version": "2.260914.8",
3
+ "version": "2.260914.10",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/automagik-dev/omni.git",