@automagik/omni 2.260901.3 → 2.260902.2

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.
@@ -242901,7 +242901,7 @@ var init_sentry_scrub = __esm(() => {
242901
242901
  var require_package7 = __commonJS((exports, module) => {
242902
242902
  module.exports = {
242903
242903
  name: "@omni/api",
242904
- version: "2.260901.3",
242904
+ version: "2.260902.2",
242905
242905
  type: "module",
242906
242906
  exports: {
242907
242907
  ".": {
@@ -345619,6 +345619,10 @@ async function consumeStream(generator, sender, instanceId, chatId, traceId) {
345619
345619
  function extractThreadId(messages4) {
345620
345620
  return (messages4[0]?.payload.rawPayload ?? {}).threadId;
345621
345621
  }
345622
+ function extractCancelThreadId(messages4) {
345623
+ const parsed = CancelThreadPayloadSchema.safeParse(messages4[0]?.payload.rawPayload ?? {});
345624
+ return (parsed.success ? parsed.data.cancelThreadId : undefined) ?? extractThreadId(messages4);
345625
+ }
345622
345626
  function extractKhalSessionId(messages4) {
345623
345627
  for (const message2 of messages4) {
345624
345628
  const rawPayload = message2.payload.rawPayload ?? {};
@@ -345759,8 +345763,9 @@ async function dispatchViaStreamingProvider(services, instance4, messages4, trig
345759
345763
  const streamKey = `${instance4.id}:${chatId}`;
345760
345764
  activeStreams.set(streamKey, sender);
345761
345765
  const cancelController = new AbortController;
345762
- const runKey = runAbortKey(instance4.id, chatId, rawThreadId);
345763
- activeRunAborts.set(runKey, { controller: cancelController, startedAt: Date.now(), sender });
345766
+ const runKey = runAbortKey(instance4.id, chatId, extractCancelThreadId(messages4));
345767
+ const runEntry = { controller: cancelController, startedAt: Date.now(), sender };
345768
+ activeRunAborts.set(runKey, runEntry);
345764
345769
  trigger.abortSignal = cancelController.signal;
345765
345770
  const streamDispatchStart = Date.now();
345766
345771
  try {
@@ -345794,7 +345799,8 @@ async function dispatchViaStreamingProvider(services, instance4, messages4, trig
345794
345799
  return false;
345795
345800
  } finally {
345796
345801
  activeStreams.delete(streamKey);
345797
- activeRunAborts.delete(runKey);
345802
+ if (activeRunAborts.get(runKey) === runEntry)
345803
+ activeRunAborts.delete(runKey);
345798
345804
  }
345799
345805
  }
345800
345806
  function formatMediaContent(msg) {
@@ -346004,10 +346010,11 @@ function resolveReplySuppression(flags) {
346004
346010
  return "run_cancelled_by_user";
346005
346011
  return null;
346006
346012
  }
346007
- async function triggerProviderWithCancellation(provider, trigger, spanAttributes, instanceId, chatId, traceId) {
346013
+ async function triggerProviderWithCancellation(provider, trigger, spanAttributes, instanceId, chatId, traceId, cancelThreadId = trigger.source.threadId) {
346008
346014
  const cancelController = new AbortController;
346009
- const runKey = runAbortKey(instanceId, chatId, trigger.source.threadId);
346010
- activeRunAborts.set(runKey, { controller: cancelController, startedAt: Date.now() });
346015
+ const runKey = runAbortKey(instanceId, chatId, cancelThreadId);
346016
+ const runEntry = { controller: cancelController, startedAt: Date.now() };
346017
+ activeRunAborts.set(runKey, runEntry);
346011
346018
  try {
346012
346019
  const result = await withLifecycleSpan("omni.dispatch_to_agno", spanAttributes, () => provider.trigger({
346013
346020
  ...trigger,
@@ -346022,7 +346029,8 @@ async function triggerProviderWithCancellation(provider, trigger, spanAttributes
346022
346029
  }
346023
346030
  throw err2;
346024
346031
  } finally {
346025
- activeRunAborts.delete(runKey);
346032
+ if (activeRunAborts.get(runKey) === runEntry)
346033
+ activeRunAborts.delete(runKey);
346026
346034
  }
346027
346035
  }
346028
346036
  async function dispatchViaProvider(services, instance4, messages4, triggerType, channel5, chatId, senderId, personId, senderName, traceId, rawEvent, db2, extraContextMessages, senderAgentId) {
@@ -346098,7 +346106,7 @@ async function dispatchViaProvider(services, instance4, messages4, triggerType,
346098
346106
  ...lifecycleBase,
346099
346107
  stage: "dispatch_to_agno",
346100
346108
  extra: { trigger_type: triggerType, provider_id: provider.id, provider_schema: provider.schema }
346101
- }), instance4.id, chatId, traceId);
346109
+ }), instance4.id, chatId, traceId, extractCancelThreadId(messages4));
346102
346110
  if (!triggerOutcome)
346103
346111
  return true;
346104
346112
  const { result, cancelled: runCancelled } = triggerOutcome;
@@ -346488,6 +346496,7 @@ function takeQueuedAck(chatKey) {
346488
346496
  }
346489
346497
  async function ackQueuedMessage(info2) {
346490
346498
  const chatKey = `${info2.instanceId}:${info2.chatId}`;
346499
+ let entry;
346491
346500
  try {
346492
346501
  const existing = queuedMessageAcks.get(chatKey);
346493
346502
  if (existing && Date.now() - existing.at < QUEUED_ACK_TIMEOUT_MS)
@@ -346496,16 +346505,22 @@ async function ackQueuedMessage(info2) {
346496
346505
  const messageId = info2.message.payload.externalId;
346497
346506
  if (!instance4 || !messageId)
346498
346507
  return;
346499
- const entry = { at: Date.now() };
346508
+ entry = { at: Date.now() };
346500
346509
  queuedMessageAcks.set(chatKey, entry);
346501
346510
  const channel5 = info2.message.metadata.channelType ?? instance4.channel;
346502
346511
  const plugin10 = await getPlugin(channel5) ?? null;
346503
- entry.handle = startAck(plugin10, createPluginAckProvider(plugin10), instance4.id, info2.chatId, messageId, channel5, {
346512
+ const handle = startAck(plugin10, createPluginAckProvider(plugin10), instance4.id, info2.chatId, messageId, channel5, {
346504
346513
  ...buildAckConfig(instance4),
346505
346514
  ackTimeoutMs: QUEUED_ACK_TIMEOUT_MS
346506
346515
  });
346516
+ if (queuedMessageAcks.get(chatKey) !== entry) {
346517
+ handle.release();
346518
+ return;
346519
+ }
346520
+ entry.handle = handle;
346507
346521
  } catch (error3) {
346508
- queuedMessageAcks.delete(chatKey);
346522
+ if (entry && queuedMessageAcks.get(chatKey) === entry)
346523
+ queuedMessageAcks.delete(chatKey);
346509
346524
  log91.debug("Failed to ack queued message", { chatId: info2.chatId, error: String(error3) });
346510
346525
  }
346511
346526
  }
@@ -347876,7 +347891,7 @@ async function setupAgentDispatcher(eventBus, services, db2) {
347876
347891
  log91.info("Agent dispatcher shutdown complete");
347877
347892
  };
347878
347893
  }
347879
- var import_api32, log91, agentDispatchLimiter, _natsGenieProviderCtor, QUOTED_MESSAGE_MAX_CHARS = 4000, DM_HISTORY_LIMIT = 20, LIFECYCLE_PREVIEW_MAX_CHARS = 160, LIFECYCLE_SENSITIVE_KEY_PARTS, PRESENCE_DEFAULT_MIN_MS = 5000, PRESENCE_DEFAULT_MAX_WAIT_MS = 30000, activeMessageDebouncer = null, DEFAULT_DISPATCH_ERROR_MESSAGE = "Opa, tive um probleminha aqui \uD83D\uDE05 Pode mandar de novo?", DEFAULT_ERROR_HANDOFF_MESSAGE = "T\xF4 com um probleminha t\xE9cnico aqui agora. Logo algu\xE9m do time vai entrar em contato com voc\xEA.", TRANSIENT_DISPATCH_ERROR_PATTERNS, TRANSIENT_DISPATCH_RETRY_DELAYS_MS, CHANNEL_MESSAGE_LIMITS, DEFAULT_MESSAGE_LIMIT = 4000, MEDIA_BASE_PATH3, MEDIA_ICONS, MEDIA_WAIT_NULL, mediaCompletions, mediaResultCache, MEDIA_WAIT_TIMEOUT_MS = 30000, DEFAULT_SEND_MEDIA_PATH_TYPES, BOT_PREFIX = "\uD83E\uDD16 ", activeStreams, activeRunAborts, sessionActivityStore, PROC_REACT_START, PROC_REACT_DONE = "\u2705", queuedMessageAcks, QUEUED_ACK_TIMEOUT_MS = 120000, providerCache, openclawClientPool, _openClawClientCtor, nullFilterWarnedInstances, ACTIVE_OWNER_IDENTIFIER_CACHE_TTL_MS = 1e4, cachedActiveOwnerIdentifiers = null, cachedActiveOwnerIdentifiersAt = 0, tenantCachedActiveOwnerIdentifiers, DEFAULT_GATE_MODEL = "gemini-3-flash-preview", GATE_TIMEOUT_MS = 3000, setupAgentResponder;
347894
+ var import_api32, log91, agentDispatchLimiter, _natsGenieProviderCtor, QUOTED_MESSAGE_MAX_CHARS = 4000, DM_HISTORY_LIMIT = 20, LIFECYCLE_PREVIEW_MAX_CHARS = 160, LIFECYCLE_SENSITIVE_KEY_PARTS, PRESENCE_DEFAULT_MIN_MS = 5000, PRESENCE_DEFAULT_MAX_WAIT_MS = 30000, activeMessageDebouncer = null, DEFAULT_DISPATCH_ERROR_MESSAGE = "Opa, tive um probleminha aqui \uD83D\uDE05 Pode mandar de novo?", DEFAULT_ERROR_HANDOFF_MESSAGE = "T\xF4 com um probleminha t\xE9cnico aqui agora. Logo algu\xE9m do time vai entrar em contato com voc\xEA.", TRANSIENT_DISPATCH_ERROR_PATTERNS, TRANSIENT_DISPATCH_RETRY_DELAYS_MS, CHANNEL_MESSAGE_LIMITS, DEFAULT_MESSAGE_LIMIT = 4000, MEDIA_BASE_PATH3, MEDIA_ICONS, MEDIA_WAIT_NULL, mediaCompletions, mediaResultCache, MEDIA_WAIT_TIMEOUT_MS = 30000, DEFAULT_SEND_MEDIA_PATH_TYPES, BOT_PREFIX = "\uD83E\uDD16 ", activeStreams, activeRunAborts, CancelThreadPayloadSchema, sessionActivityStore, PROC_REACT_START, PROC_REACT_DONE = "\u2705", queuedMessageAcks, QUEUED_ACK_TIMEOUT_MS = 120000, providerCache, openclawClientPool, _openClawClientCtor, nullFilterWarnedInstances, ACTIVE_OWNER_IDENTIFIER_CACHE_TTL_MS = 1e4, cachedActiveOwnerIdentifiers = null, cachedActiveOwnerIdentifiersAt = 0, tenantCachedActiveOwnerIdentifiers, DEFAULT_GATE_MODEL = "gemini-3-flash-preview", GATE_TIMEOUT_MS = 3000, setupAgentResponder;
347880
347895
  var init_agent_dispatcher = __esm(() => {
347881
347896
  init_src2();
347882
347897
  init_src();
@@ -347884,6 +347899,7 @@ var init_agent_dispatcher = __esm(() => {
347884
347899
  init_src8();
347885
347900
  init_esm5();
347886
347901
  init_drizzle_orm();
347902
+ init_zod();
347887
347903
  init_idempotency();
347888
347904
  init_sentry_scrub();
347889
347905
  init_agent_runner();
@@ -347934,6 +347950,7 @@ var init_agent_dispatcher = __esm(() => {
347934
347950
  DEFAULT_SEND_MEDIA_PATH_TYPES = ["image", "video", "document"];
347935
347951
  activeStreams = new Map;
347936
347952
  activeRunAborts = new Map;
347953
+ CancelThreadPayloadSchema = exports_external.object({ cancelThreadId: exports_external.string().min(1).optional() });
347937
347954
  sessionActivityStore = new InMemorySessionActivityStore;
347938
347955
  PROC_REACT_START = { audio: "\uD83C\uDFA7", image: "\uD83D\uDC40", video: "\uD83D\uDC40", document: "\uD83D\uDC40" };
347939
347956
  queuedMessageAcks = new Map;
@@ -352912,9 +352929,23 @@ function openProviderSecrets(tenantId, row) {
352912
352929
 
352913
352930
  class ProviderService {
352914
352931
  db;
352932
+ clientCacheInvalidators = [];
352915
352933
  constructor(db2) {
352916
352934
  this.db = db2;
352917
352935
  }
352936
+ onProviderChanged(invalidate) {
352937
+ this.clientCacheInvalidators.push(invalidate);
352938
+ }
352939
+ invalidateCaches(providerId) {
352940
+ runAfterTenantCommit(() => {
352941
+ invalidateProviderCache(providerId);
352942
+ for (const invalidate of this.clientCacheInvalidators) {
352943
+ try {
352944
+ invalidate(providerId);
352945
+ } catch {}
352946
+ }
352947
+ });
352948
+ }
352918
352949
  get tenantId() {
352919
352950
  return currentTenantScope()?.tenantId ?? null;
352920
352951
  }
@@ -352955,7 +352986,7 @@ class ProviderService {
352955
352986
  if (!updated) {
352956
352987
  throw new NotFoundError("AgentProvider", id);
352957
352988
  }
352958
- invalidateProviderCache(id);
352989
+ this.invalidateCaches(id);
352959
352990
  return openProviderSecrets(tenantId, updated);
352960
352991
  }
352961
352992
  async delete(id) {
@@ -352963,7 +352994,7 @@ class ProviderService {
352963
352994
  if (!result.length) {
352964
352995
  throw new NotFoundError("AgentProvider", id);
352965
352996
  }
352966
- invalidateProviderCache(id);
352997
+ this.invalidateCaches(id);
352967
352998
  }
352968
352999
  async checkHealth(id) {
352969
353000
  const provider = await this.getById(id);
@@ -355406,6 +355437,9 @@ function createServices(db2, eventBus) {
355406
355437
  batchJobs2.setAuthPlane(authPlane.db);
355407
355438
  const mediaStorage = new MediaStorageService(db2);
355408
355439
  mediaStorage.setTenantAdmissibilityCheck((tenantId) => isTenantWorkAdmissible(authPlane.db, tenantId));
355440
+ const providers2 = new ProviderService(db2);
355441
+ const agentRunner = new AgentRunnerService(db2);
355442
+ providers2.onProviderChanged((providerId) => agentRunner.clearCache(providerId));
355409
355443
  return {
355410
355444
  agents: new AgentService(db2, eventBus),
355411
355445
  agentState: new AgentStateService(eventBus),
@@ -355418,7 +355452,7 @@ function createServices(db2, eventBus) {
355418
355452
  events: new EventService(db2),
355419
355453
  settings,
355420
355454
  access: new AccessService(db2, eventBus, accessCache),
355421
- providers: new ProviderService(db2),
355455
+ providers: providers2,
355422
355456
  routes: new RouteService(db2, routeResolver),
355423
355457
  routeResolver,
355424
355458
  deadLetters,
@@ -355430,7 +355464,7 @@ function createServices(db2, eventBus) {
355430
355464
  messages: new MessageService(db2, eventBus),
355431
355465
  syncJobs: new SyncJobService(db2, eventBus),
355432
355466
  batchJobs: batchJobs2,
355433
- agentRunner: new AgentRunnerService(db2),
355467
+ agentRunner,
355434
355468
  tts,
355435
355469
  turns: new TurnService(db2),
355436
355470
  consumerOffsets: new ConsumerOffsetService(db2),
@@ -355833,6 +355867,14 @@ var init_error3 = __esm(() => {
355833
355867
  });
355834
355868
 
355835
355869
  // ../api/src/middleware/rate-limit.ts
355870
+ function resolveClientIp(trustedProxyHeaderValue, remoteAddr) {
355871
+ const raw2 = trustedProxyHeaderValue ?? remoteAddr;
355872
+ if (!raw2)
355873
+ return;
355874
+ const hops = raw2.split(",").map((hop) => hop.trim()).filter(Boolean);
355875
+ const parsed = clientIpSchema.safeParse(hops.at(-1));
355876
+ return parsed.success ? parsed.data : undefined;
355877
+ }
355836
355878
  function createRateLimiter2(config4 = RATE_LIMITS.general, keyPrefix = "ratelimit") {
355837
355879
  return createMiddleware(async (c, next) => {
355838
355880
  const apiKey = c.get("apiKey");
@@ -355840,8 +355882,13 @@ function createRateLimiter2(config4 = RATE_LIMITS.general, keyPrefix = "ratelimi
355840
355882
  const trustedProxyIp = trustedProxyHeader ? c.req.header(trustedProxyHeader) : undefined;
355841
355883
  const server = c.env;
355842
355884
  const remoteAddr = server?.remoteAddr ?? server?.requestIP?.(c.req.raw)?.address;
355843
- const rawIp = trustedProxyIp ?? remoteAddr;
355844
- const normalizedIp = rawIp?.split(",")[0]?.trim() || undefined;
355885
+ const normalizedIp = resolveClientIp(trustedProxyIp, remoteAddr);
355886
+ if (trustedProxyIp !== undefined && normalizedIp === undefined && !warnedUnparseableProxyHop) {
355887
+ warnedUnparseableProxyHop = true;
355888
+ log112.warn("TRUSTED_PROXY_HEADER hop is not a bare IP literal; requests share the anonymous rate-limit bucket", {
355889
+ header: trustedProxyHeader
355890
+ });
355891
+ }
355845
355892
  const identifier = apiKey?.id ? `api:${apiKey.id}` : normalizedIp ? `ip:${normalizedIp}` : "anon";
355846
355893
  const key = `${keyPrefix}:${identifier}`;
355847
355894
  const now = Date.now();
@@ -355880,9 +355927,12 @@ function createRateLimiter2(config4 = RATE_LIMITS.general, keyPrefix = "ratelimi
355880
355927
  setRateLimitHeaders();
355881
355928
  });
355882
355929
  }
355883
- var rateLimitStore, cleanupTimer, RATE_LIMITS, rateLimitMiddleware, webhookIngressRateLimitMiddleware;
355930
+ var log112, warnedUnparseableProxyHop = false, rateLimitStore, cleanupTimer, RATE_LIMITS, clientIpSchema, rateLimitMiddleware, webhookIngressRateLimitMiddleware;
355884
355931
  var init_rate_limit = __esm(() => {
355932
+ init_src();
355885
355933
  init_factory2();
355934
+ init_zod();
355935
+ log112 = createLogger("api:rate-limit");
355886
355936
  rateLimitStore = new Map;
355887
355937
  cleanupTimer = setInterval(() => {
355888
355938
  const now = Date.now();
@@ -355899,6 +355949,7 @@ var init_rate_limit = __esm(() => {
355899
355949
  instances: { windowMs: 60 * 1000, maxRequests: 30 },
355900
355950
  general: { windowMs: 60 * 1000, maxRequests: 1000 }
355901
355951
  };
355952
+ clientIpSchema = exports_external.string().ip();
355902
355953
  rateLimitMiddleware = createRateLimiter2(RATE_LIMITS.general);
355903
355954
  webhookIngressRateLimitMiddleware = createRateLimiter2(RATE_LIMITS.events, "ratelimit:webhook-ingress");
355904
355955
  });
@@ -356002,22 +356053,22 @@ function createWebhookAuthMiddleware(config4) {
356002
356053
  const token = c.req.header("x-telegram-bot-api-secret-token");
356003
356054
  if (!token) {
356004
356055
  const ip = c.req.header("x-forwarded-for") || c.req.header("x-real-ip") || "unknown";
356005
- log112.warn("Webhook request missing secret token", { ip });
356056
+ log113.warn("Webhook request missing secret token", { ip });
356006
356057
  return c.json({ error: { code: "UNAUTHORIZED", message: "Missing webhook secret token" } }, 401);
356007
356058
  }
356008
356059
  if (token !== webhookSecret) {
356009
356060
  const ip = c.req.header("x-forwarded-for") || c.req.header("x-real-ip") || "unknown";
356010
- log112.warn("Webhook request with invalid secret token", { ip });
356061
+ log113.warn("Webhook request with invalid secret token", { ip });
356011
356062
  return c.json({ error: { code: "UNAUTHORIZED", message: "Invalid webhook secret token" } }, 401);
356012
356063
  }
356013
356064
  return next();
356014
356065
  });
356015
356066
  }
356016
- var log112;
356067
+ var log113;
356017
356068
  var init_webhook_auth = __esm(() => {
356018
356069
  init_src();
356019
356070
  init_factory2();
356020
- log112 = createLogger("api:webhook-auth");
356071
+ log113 = createLogger("api:webhook-auth");
356021
356072
  });
356022
356073
 
356023
356074
  // ../api/src/plugin-state.ts
@@ -360507,7 +360558,10 @@ var init_messages4 = __esm(() => {
360507
360558
  instanceId: exports_external.string().uuid().openapi({ description: "Instance UUID" }),
360508
360559
  to: exports_external.string().openapi({ description: "Recipient" }),
360509
360560
  audioSizeKb: exports_external.number().openapi({ description: "Audio size in KB" }),
360510
- durationMs: exports_external.number().openapi({ description: "Audio duration in ms" })
360561
+ durationMs: exports_external.number().openapi({ description: "Audio duration in ms" }),
360562
+ senderAgentId: exports_external.string().uuid().nullable().optional().openapi({
360563
+ description: "Present when the request set sentBy: 'agent' \u2014 the agent the send was attributed to, or null when the instance has no configured agent (attribution did not happen)"
360564
+ })
360511
360565
  });
360512
360566
  });
360513
360567
 
@@ -361627,6 +361681,7 @@ var init_voice = __esm(() => {
361627
361681
 
361628
361682
  // ../api/src/schemas/openapi/webhooks.ts
361629
361683
  function registerWebhookSchemas(registry4) {
361684
+ registry4.register("WebhookSignatureConfig", WebhookSignatureConfigSchema);
361630
361685
  registry4.register("WebhookSource", WebhookSourceSchema);
361631
361686
  registry4.register("CreateWebhookSourceRequest", CreateWebhookSourceSchema);
361632
361687
  registry4.register("TriggerEventRequest", TriggerEventSchema);
@@ -361716,7 +361771,7 @@ function registerWebhookSchemas(registry4) {
361716
361771
  operationId: "receiveWebhook",
361717
361772
  tags: ["Webhooks"],
361718
361773
  summary: "Receive webhook",
361719
- description: "Receive webhook from external system. Creates a custom event.",
361774
+ description: "Receive webhook from external system. Creates a custom event. An empty body is accepted as an empty " + "payload; a non-empty body that is not a JSON object (malformed, array, scalar) is rejected.",
361720
361775
  request: {
361721
361776
  params: exports_external.object({ source: exports_external.string().openapi({ description: "Source name" }) }),
361722
361777
  body: { content: { "application/json": { schema: exports_external.record(exports_external.string(), exports_external.unknown()) } } }
@@ -361725,7 +361780,8 @@ function registerWebhookSchemas(registry4) {
361725
361780
  200: {
361726
361781
  description: "Webhook received",
361727
361782
  content: { "application/json": { schema: WebhookReceiveResponseSchema } }
361728
- }
361783
+ },
361784
+ 400: { description: "Body is not a JSON object", content: { "application/json": { schema: ErrorSchema } } }
361729
361785
  }
361730
361786
  });
361731
361787
  registry4.registerPath({
@@ -361734,7 +361790,7 @@ function registerWebhookSchemas(registry4) {
361734
361790
  operationId: "receiveWebhookIngress",
361735
361791
  tags: ["Webhooks"],
361736
361792
  summary: "Public webhook ingress",
361737
- description: "Auth-exempt receiver for third-party webhook senders. Requires the source to have a signature " + "configuration; the request is verified against it (HMAC over the raw body, or token match) before any " + "event is published. Unknown, disabled, unconfigured, or badly signed requests all return the same 401.",
361793
+ description: "Auth-exempt receiver for third-party webhook senders. Requires the source to have a signature " + "configuration; the request is verified against it (HMAC over the raw body, or token match) before any " + "event is published. Unknown, disabled, unconfigured, or badly signed requests all return the same 401. " + "A non-empty body that is not a JSON object (malformed, array, scalar) is rejected with 400.",
361738
361794
  security: [],
361739
361795
  request: {
361740
361796
  params: exports_external.object({ source: exports_external.string().openapi({ description: "Source name" }) }),
@@ -361745,6 +361801,7 @@ function registerWebhookSchemas(registry4) {
361745
361801
  description: "Webhook received",
361746
361802
  content: { "application/json": { schema: WebhookReceiveResponseSchema } }
361747
361803
  },
361804
+ 400: { description: "Body is not a JSON object", content: { "application/json": { schema: ErrorSchema } } },
361748
361805
  401: { description: "Verification failed", content: { "application/json": { schema: ErrorSchema } } }
361749
361806
  }
361750
361807
  });
@@ -361767,16 +361824,22 @@ function registerWebhookSchemas(registry4) {
361767
361824
  }
361768
361825
  var WebhookSignatureConfigSchema, WebhookSourceSchema, CreateWebhookSourceSchema, TriggerEventSchema, WebhookReceiveResponseSchema;
361769
361826
  var init_webhooks2 = __esm(() => {
361827
+ init_src5();
361770
361828
  init_zod_openapi();
361771
361829
  init_common4();
361772
361830
  WebhookSignatureConfigSchema = exports_external.object({
361773
- algorithm: exports_external.enum(["hmac-sha256", "hmac-sha1", "token-match"]).openapi({
361831
+ algorithm: exports_external.enum(webhookSignatureAlgorithms).openapi({
361774
361832
  description: "How to verify: HMAC over the raw request body, or direct token match"
361775
361833
  }),
361776
- header: exports_external.string().openapi({ description: "Header carrying the signature/token (e.g. X-Hub-Signature-256)" }),
361777
- prefix: exports_external.string().optional().openapi({
361778
- description: 'Prefix before the hex digest (e.g. "sha256="). HMAC algorithms only \u2014 rejected with token-match'
361834
+ header: exports_external.string().min(1).max(200).openapi({
361835
+ description: "Header carrying the signature/token (e.g. X-Hub-Signature-256). 1-200 characters"
361836
+ }),
361837
+ prefix: exports_external.string().max(50).optional().openapi({
361838
+ description: 'Prefix before the hex digest (e.g. "sha256="), at most 50 characters. HMAC algorithms only \u2014 rejected with token-match'
361779
361839
  })
361840
+ }).refine((config4) => config4.algorithm !== "token-match" || config4.prefix === undefined, {
361841
+ message: "prefix is not applicable to algorithm 'token-match'",
361842
+ path: ["prefix"]
361780
361843
  });
361781
361844
  WebhookSourceSchema = exports_external.object({
361782
361845
  id: exports_external.string().uuid().openapi({ description: "Source UUID" }),
@@ -361790,14 +361853,14 @@ var init_webhooks2 = __esm(() => {
361790
361853
  updatedAt: exports_external.string().datetime().openapi({ description: "Last update timestamp" })
361791
361854
  });
361792
361855
  CreateWebhookSourceSchema = exports_external.object({
361793
- name: exports_external.string().min(1).max(100).openapi({ description: "Unique source name" }),
361856
+ name: exports_external.string().min(1).max(100).openapi({ description: "Unique source name (e.g., github, stripe, agno)" }),
361794
361857
  description: exports_external.string().optional().openapi({ description: "Description" }),
361795
361858
  expectedHeaders: exports_external.record(exports_external.string(), exports_external.boolean()).optional().openapi({ description: "Headers to validate" }),
361796
361859
  signatureConfig: WebhookSignatureConfigSchema.nullable().optional().openapi({
361797
- description: "Signature verification config; required for the source to be reachable on the public ingress"
361860
+ description: "Signature verification config; required for the source to be reachable on the public ingress. " + "Always paired with signatureSecret: a config without a stored secret is rejected (400), and clearing " + "the config (null) also clears the stored secret."
361798
361861
  }),
361799
361862
  signatureSecret: exports_external.string().min(8).max(512).nullable().optional().openapi({
361800
- description: "Shared secret used by signatureConfig (write-only, never returned)"
361863
+ description: "Shared secret used by signatureConfig (write-only, never returned; 8-512 characters). Cannot be set " + "without a signatureConfig (given in the same request, or already stored on update); null clears it."
361801
361864
  }),
361802
361865
  enabled: exports_external.boolean().default(true).openapi({ description: "Whether enabled" })
361803
361866
  });
@@ -363773,7 +363836,7 @@ async function clearAgentSession(services, db2, instanceId, from, chatId, option
363773
363836
  });
363774
363837
  const primaryDelete = await client.deleteSession?.(sessionId);
363775
363838
  const legacyDelete = legacySessionId !== sessionId ? await client.deleteSession?.(legacySessionId) : undefined;
363776
- log113.info("Agno session delete verified", {
363839
+ log114.info("Agno session delete verified", {
363777
363840
  instanceId,
363778
363841
  sessionId,
363779
363842
  legacySessionId,
@@ -363795,7 +363858,7 @@ async function handleTrashEmojiMessage(services, db2, event) {
363795
363858
  await runTrashEmojiCleanup(services, db2, event);
363796
363859
  });
363797
363860
  if (!result.executed) {
363798
- log113.debug("Trash emoji event already processed (NATS redelivery skipped)", {
363861
+ log114.debug("Trash emoji event already processed (NATS redelivery skipped)", {
363799
363862
  eventId: event.id,
363800
363863
  instanceId,
363801
363864
  chatId
@@ -363808,11 +363871,11 @@ async function runTrashEmojiCleanup(services, db2, event) {
363808
363871
  if (!instanceId)
363809
363872
  return;
363810
363873
  const tenantId = trustedTenantOf(event);
363811
- log113.info("Trash emoji detected, clearing session", { instanceId, chatId, from, personId });
363874
+ log114.info("Trash emoji detected, clearing session", { instanceId, chatId, from, personId });
363812
363875
  try {
363813
363876
  const identity = await clearAgentSession(services, db2, instanceId, from, chatId, { personId, rawPayload }, tenantId);
363814
363877
  const { sessionId, legacySessionId, sessionStrategy, source, canonicalSessionId, environment, channelSegment } = identity;
363815
- log113.info("Session cleared successfully", {
363878
+ log114.info("Session cleared successfully", {
363816
363879
  instanceId,
363817
363880
  sessionId,
363818
363881
  legacySessionId,
@@ -363837,11 +363900,11 @@ async function runTrashEmojiCleanup(services, db2, event) {
363837
363900
  await runTenantWorkDb(db2, tenantId, () => services.chats.update(dbChat.id, {
363838
363901
  settings: { agentPaused: false, agentResumedAt: new Date().toISOString() }
363839
363902
  }));
363840
- log113.info("Agent resumed after session clear (was paused by handoff)", { instanceId, chatId });
363903
+ log114.info("Agent resumed after session clear (was paused by handoff)", { instanceId, chatId });
363841
363904
  }
363842
363905
  }
363843
363906
  } catch (disarmError) {
363844
- log113.warn("Failed to disarm follow-up after session clear", {
363907
+ log114.warn("Failed to disarm follow-up after session clear", {
363845
363908
  instanceId,
363846
363909
  chatId,
363847
363910
  error: String(disarmError)
@@ -363849,9 +363912,9 @@ async function runTrashEmojiCleanup(services, db2, event) {
363849
363912
  }
363850
363913
  try {
363851
363914
  await sendMessage(services, db2, instanceId, chatId, "\u2705 Conversa limpa! Sua sess\xE3o com o assistente foi resetada.", tenantId);
363852
- log113.info("Sent session cleared confirmation", { instanceId, chatId });
363915
+ log114.info("Sent session cleared confirmation", { instanceId, chatId });
363853
363916
  } catch (sendError) {
363854
- log113.error("Failed to send confirmation message", { instanceId, chatId, error: String(sendError) });
363917
+ log114.error("Failed to send confirmation message", { instanceId, chatId, error: String(sendError) });
363855
363918
  }
363856
363919
  } catch (error3) {
363857
363920
  const errorMessage = error3 instanceof Error ? error3.message : String(error3);
@@ -363862,14 +363925,14 @@ async function runTrashEmojiCleanup(services, db2, event) {
363862
363925
  "Session clearing not supported for"
363863
363926
  ];
363864
363927
  if (skippableErrors.some((e) => errorMessage.includes(e))) {
363865
- log113.debug("Session clearing skipped", { instanceId, reason: errorMessage });
363928
+ log114.debug("Session clearing skipped", { instanceId, reason: errorMessage });
363866
363929
  return;
363867
363930
  }
363868
- log113.error("Failed to clear session", { instanceId, chatId, error: errorMessage });
363931
+ log114.error("Failed to clear session", { instanceId, chatId, error: errorMessage });
363869
363932
  try {
363870
363933
  await sendMessage(services, db2, instanceId, chatId, "\u274C Erro ao limpar sess\xE3o. Tente novamente.", tenantId);
363871
363934
  } catch (sendError) {
363872
- log113.error("Failed to send error message", { instanceId, chatId, error: String(sendError) });
363935
+ log114.error("Failed to send error message", { instanceId, chatId, error: String(sendError) });
363873
363936
  }
363874
363937
  }
363875
363938
  }
@@ -363883,13 +363946,13 @@ async function setupSessionCleaner(eventBus, services, db2) {
363883
363946
  startFrom: "new",
363884
363947
  concurrency: 5
363885
363948
  });
363886
- log113.info("Session cleaner initialized");
363949
+ log114.info("Session cleaner initialized");
363887
363950
  } catch (error3) {
363888
- log113.error("Failed to set up session cleaner", { error: String(error3) });
363951
+ log114.error("Failed to set up session cleaner", { error: String(error3) });
363889
363952
  throw error3;
363890
363953
  }
363891
363954
  }
363892
- var log113;
363955
+ var log114;
363893
363956
  var init_session_cleaner = __esm(() => {
363894
363957
  init_src();
363895
363958
  init_src5();
@@ -363899,7 +363962,7 @@ var init_session_cleaner = __esm(() => {
363899
363962
  init_worker_tenant_context();
363900
363963
  init_agent_dispatcher();
363901
363964
  init_loader2();
363902
- log113 = createLogger("session-cleaner");
363965
+ log114 = createLogger("session-cleaner");
363903
363966
  });
363904
363967
 
363905
363968
  // ../api/src/routes/v2/chats.ts
@@ -365274,6 +365337,12 @@ var init_handoffs = __esm(() => {
365274
365337
  });
365275
365338
 
365276
365339
  // ../api/src/lib/whatsapp-business-connection.ts
365340
+ function resolveMetaApiVersion(persisted) {
365341
+ const fromEnv = metaGraphApiVersionSchema.safeParse(process.env.META_GRAPH_API_VERSION);
365342
+ if (fromEnv.success)
365343
+ return fromEnv.data;
365344
+ return persisted ?? undefined;
365345
+ }
365277
365346
  function applyWhatsAppBusinessConnectionOptions(options, input) {
365278
365347
  if (input.metaAccessToken)
365279
365348
  options.metaAccessToken = input.metaAccessToken;
@@ -365285,7 +365354,7 @@ function applyWhatsAppBusinessConnectionOptions(options, input) {
365285
365354
  options.metaAppId = input.metaAppId;
365286
365355
  if (input.metaBusinessId)
365287
365356
  options.metaBusinessId = input.metaBusinessId;
365288
- const apiVersion = process.env.META_GRAPH_API_VERSION ?? input.metaApiVersion;
365357
+ const apiVersion = resolveMetaApiVersion(input.metaApiVersion);
365289
365358
  if (apiVersion)
365290
365359
  options.metaApiVersion = apiVersion;
365291
365360
  if (input.metaDisplayPhoneNumber)
@@ -365293,6 +365362,11 @@ function applyWhatsAppBusinessConnectionOptions(options, input) {
365293
365362
  if (input.metaConnectionMethod)
365294
365363
  options.metaConnectionMethod = input.metaConnectionMethod;
365295
365364
  }
365365
+ var metaGraphApiVersionSchema;
365366
+ var init_whatsapp_business_connection = __esm(() => {
365367
+ init_zod();
365368
+ metaGraphApiVersionSchema = exports_external.string().regex(/^v\d+\.\d+$/);
365369
+ });
365296
365370
 
365297
365371
  // ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
365298
365372
  var require_QRMode = __commonJS((exports, module) => {
@@ -366338,12 +366412,12 @@ async function printQrCodeToTerminal(qrCode, instanceId) {
366338
366412
  try {
366339
366413
  const qrTerminalModule = await Promise.resolve().then(() => __toESM(require_main(), 1));
366340
366414
  const qrTerminal = qrTerminalModule.default || qrTerminalModule;
366341
- log114.info("QR Code generated for WhatsApp", { instanceId });
366415
+ log115.info("QR Code generated for WhatsApp", { instanceId });
366342
366416
  qrTerminal.generate(qrCode, { small: true });
366343
366417
  } catch (error3) {
366344
- log114.info("QR Code generated", { instanceId, qrCode: `${qrCode.substring(0, 50)}...` });
366418
+ log115.info("QR Code generated", { instanceId, qrCode: `${qrCode.substring(0, 50)}...` });
366345
366419
  if (error3 instanceof Error && process.env.DEBUG) {
366346
- log114.debug("QR Terminal unavailable", { error: error3.message });
366420
+ log115.debug("QR Terminal unavailable", { error: error3.message });
366347
366421
  }
366348
366422
  }
366349
366423
  }
@@ -366355,13 +366429,13 @@ async function setupQrCodeListener(eventBus) {
366355
366429
  await printQrCodeToTerminal(qrCode, instanceId);
366356
366430
  });
366357
366431
  } catch (error3) {
366358
- log114.warn("Failed to set up QR code listener", { error: String(error3) });
366432
+ log115.warn("Failed to set up QR code listener", { error: String(error3) });
366359
366433
  }
366360
366434
  }
366361
- var log114, qrCodes;
366435
+ var log115, qrCodes;
366362
366436
  var init_qr_store = __esm(() => {
366363
366437
  init_src();
366364
- log114 = createLogger("api:qr-store");
366438
+ log115 = createLogger("api:qr-store");
366365
366439
  qrCodes = new Map;
366366
366440
  });
366367
366441
 
@@ -366385,29 +366459,29 @@ class AgentReplayService {
366385
366459
  agentId: instances.agentId
366386
366460
  }).from(instances).where(eq(instances.id, instanceId)).limit(1));
366387
366461
  if (!instance4) {
366388
- log115.warn("Instance not found for replay", { instanceId });
366462
+ log116.warn("Instance not found for replay", { instanceId });
366389
366463
  return;
366390
366464
  }
366391
366465
  if (!instance4.agentId) {
366392
- log115.debug("Instance has no agent, skipping replay", { instanceId });
366466
+ log116.debug("Instance has no agent, skipping replay", { instanceId });
366393
366467
  await this.updateLastSeenAt(instanceId, undefined, trustedTenantId);
366394
366468
  return;
366395
366469
  }
366396
366470
  if (!instance4.replayEnabled) {
366397
- log115.debug("Replay disabled for instance", { instanceId });
366471
+ log116.debug("Replay disabled for instance", { instanceId });
366398
366472
  await this.updateLastSeenAt(instanceId, undefined, trustedTenantId);
366399
366473
  return;
366400
366474
  }
366401
366475
  const since = instance4.lastSeenAt ?? null;
366402
366476
  if (!since) {
366403
- log115.debug("No lastSeenAt recorded, skipping replay (first connect)", { instanceId });
366477
+ log116.debug("No lastSeenAt recorded, skipping replay (first connect)", { instanceId });
366404
366478
  await this.updateLastSeenAt(instanceId, undefined, trustedTenantId);
366405
366479
  return;
366406
366480
  }
366407
- log115.info("Starting replay on reconnect", { instanceId, since: since.toISOString() });
366481
+ log116.info("Starting replay on reconnect", { instanceId, since: since.toISOString() });
366408
366482
  try {
366409
366483
  const result = await this.replayMissedMessages({ instanceId, since }, trustedTenantId);
366410
- log115.info("Replay complete", {
366484
+ log116.info("Replay complete", {
366411
366485
  instanceId,
366412
366486
  replayed: result.replayed,
366413
366487
  skipped: result.skipped,
@@ -366415,7 +366489,7 @@ class AgentReplayService {
366415
366489
  });
366416
366490
  await this.updateLastSeenAt(instanceId, result.until, trustedTenantId);
366417
366491
  } catch (error3) {
366418
- log115.error("Replay failed", { instanceId, error: String(error3) });
366492
+ log116.error("Replay failed", { instanceId, error: String(error3) });
366419
366493
  }
366420
366494
  }
366421
366495
  async replayMissedMessages(options, trustedTenantId) {
@@ -366423,7 +366497,7 @@ class AgentReplayService {
366423
366497
  const now = new Date;
366424
366498
  const cutoff = new Date(now.getTime() - MAX_REPLAY_WINDOW_MS);
366425
366499
  const since = options.since && options.since > cutoff ? options.since : cutoff;
366426
- log115.debug("Fetching missed messages", { instanceId, since: since.toISOString(), until: now.toISOString() });
366500
+ log116.debug("Fetching missed messages", { instanceId, since: since.toISOString(), until: now.toISOString() });
366427
366501
  let cursorTimestamp = since;
366428
366502
  let cursorId = null;
366429
366503
  let totalReplayed = 0;
@@ -366451,14 +366525,14 @@ class AgentReplayService {
366451
366525
  SELECT 1 FROM ${messages2} AS reply
366452
366526
  WHERE reply.chat_id = ${messages2.chatId}
366453
366527
  AND reply.is_from_me = true
366454
- AND COALESCE((reply.raw_payload ->> 'omniSystemNotice')::boolean, false) = false
366528
+ AND (reply.raw_payload -> 'omniSystemNotice') IS DISTINCT FROM 'true'::jsonb
366455
366529
  AND reply.platform_timestamp > ${messages2.platformTimestamp}
366456
366530
  AND reply.deleted_at IS NULL
366457
366531
  )`, cursorId ? or2(gt(messages2.platformTimestamp, cursorTimestamp), and2(eq(messages2.platformTimestamp, cursorTimestamp), gt(messages2.id, cursorId))) : gt(messages2.platformTimestamp, cursorTimestamp), lte(messages2.platformTimestamp, now))).orderBy(asc(messages2.platformTimestamp), asc(messages2.id)).limit(PAGE_SIZE));
366458
366532
  if (rows.length === 0)
366459
366533
  break;
366460
366534
  if (rows.length === PAGE_SIZE) {
366461
- log115.warn("Replay page cap hit, fetching next page", {
366535
+ log116.warn("Replay page cap hit, fetching next page", {
366462
366536
  instanceId,
366463
366537
  limit: PAGE_SIZE,
366464
366538
  cursor: cursorTimestamp.toISOString()
@@ -366486,7 +366560,7 @@ class AgentReplayService {
366486
366560
  await this.redispatchMessage(instanceId, row);
366487
366561
  replayed++;
366488
366562
  } catch (error3) {
366489
- log115.warn("Failed to redispatch message during replay", {
366563
+ log116.warn("Failed to redispatch message during replay", {
366490
366564
  instanceId,
366491
366565
  messageId: row.id,
366492
366566
  error: String(error3)
@@ -366543,14 +366617,14 @@ function mapMessageTypeToContentType(messageType) {
366543
366617
  return "text";
366544
366618
  }
366545
366619
  }
366546
- var log115, MAX_REPLAY_WINDOW_MS;
366620
+ var log116, MAX_REPLAY_WINDOW_MS;
366547
366621
  var init_agent_replay = __esm(() => {
366548
366622
  init_src();
366549
366623
  init_src5();
366550
366624
  init_drizzle_orm();
366551
366625
  init_tenant_scope();
366552
366626
  init_worker_tenant_context();
366553
- log115 = createLogger("agent-replay");
366627
+ log116 = createLogger("agent-replay");
366554
366628
  MAX_REPLAY_WINDOW_MS = 24 * 60 * 60 * 1000;
366555
366629
  });
366556
366630
 
@@ -366753,15 +366827,15 @@ async function connectInstanceWithPlugin(plugin10, instanceId, options) {
366753
366827
  async function triggerCreateConnection(channelRegistry2, channel5, instanceId, options) {
366754
366828
  const plugin10 = getPluginFromRegistry(channelRegistry2, channel5);
366755
366829
  if (!plugin10) {
366756
- log116.warn("No plugin found for channel", { channel: channel5 });
366830
+ log117.warn("No plugin found for channel", { channel: channel5 });
366757
366831
  return;
366758
366832
  }
366759
366833
  const errorMessage = await connectInstanceWithPlugin(plugin10, instanceId, options);
366760
366834
  if (errorMessage) {
366761
- log116.error("Failed to connect instance", { instanceId, error: errorMessage });
366835
+ log117.error("Failed to connect instance", { instanceId, error: errorMessage });
366762
366836
  return;
366763
366837
  }
366764
- log116.info("Triggered connection", { instanceId, channel: channel5 });
366838
+ log117.info("Triggered connection", { instanceId, channel: channel5 });
366765
366839
  }
366766
366840
  function buildTokenPersistUpdates(channel5, body) {
366767
366841
  const updates = {};
@@ -366799,10 +366873,10 @@ async function handleAgentKeyProvisioning(services, instanceId, newAgentId, oldA
366799
366873
  await services.apiKeys.update(existingKey.id, {
366800
366874
  instanceIds: updated.length > 0 ? updated : null
366801
366875
  });
366802
- log116.info("Removed instance from agent key", { keyName, instanceId });
366876
+ log117.info("Removed instance from agent key", { keyName, instanceId });
366803
366877
  }
366804
366878
  } catch (err2) {
366805
- log116.error("Failed to remove instance from old agent key", { oldAgentId, instanceId, error: String(err2) });
366879
+ log117.error("Failed to remove instance from old agent key", { oldAgentId, instanceId, error: String(err2) });
366806
366880
  }
366807
366881
  }
366808
366882
  if (newAgentId) {
@@ -366816,7 +366890,7 @@ async function handleAgentKeyProvisioning(services, instanceId, newAgentId, oldA
366816
366890
  await services.apiKeys.update(existingKey.id, {
366817
366891
  instanceIds: [...currentIds, instanceId]
366818
366892
  });
366819
- log116.info("Added instance to existing agent key", { keyName, instanceId });
366893
+ log117.info("Added instance to existing agent key", { keyName, instanceId });
366820
366894
  }
366821
366895
  } else {
366822
366896
  const agentScopes = Array.isArray(agent3.metadata?.omniScopes) ? agent3.metadata.omniScopes : [...DEFAULT_TURN_SCOPES];
@@ -366827,10 +366901,10 @@ async function handleAgentKeyProvisioning(services, instanceId, newAgentId, oldA
366827
366901
  instanceIds: [instanceId],
366828
366902
  createdBy: "system:auto-provision"
366829
366903
  });
366830
- log116.info("Created scoped agent key", { keyName, instanceId, keyId: result.key.id });
366904
+ log117.info("Created scoped agent key", { keyName, instanceId, keyId: result.key.id });
366831
366905
  }
366832
366906
  } catch (err2) {
366833
- log116.error("Failed to provision agent key", { newAgentId, instanceId, error: String(err2) });
366907
+ log117.error("Failed to provision agent key", { newAgentId, instanceId, error: String(err2) });
366834
366908
  }
366835
366909
  }
366836
366910
  }
@@ -367014,7 +367088,7 @@ function getCacheKey(instanceId, guildId) {
367014
367088
  function invalidateGuildCache(instanceId, guildId) {
367015
367089
  guildConfigCache.delete(getCacheKey(instanceId, guildId));
367016
367090
  }
367017
- var log116, instancesRoutes, instanceAccess2, listQuerySchema12, agentReplyFilterSchema, createInstanceSchema, updateInstanceSchema, DEFAULT_AGENT_REPLY_FILTER, SENSITIVE_INSTANCE_FIELDS, pairingCodeSchema, passkeyBase64UrlSchema, passkeyCredentialSchema, connectInstanceSchema, syncRequestSchema, listContactsQuerySchema, listGroupsQuerySchema, checkNumberSchema, updateBioSchema, blockContactSchema, groupParticipantActionSchema, groupSettingSchema, groupParticipantsSchema, groupParticipantsPatchSchema, updateGroupSubjectSchema, updateGroupDescriptionSchema, updateGroupSettingsSchema, patchGroupSchema, resyncSchema, replaySchema, pairingActionSchema, guildConfigOverrideSchema, guildConfigCache, GUILD_CONFIG_TTL, presenceSchema;
367091
+ var log117, instancesRoutes, instanceAccess2, listQuerySchema12, agentReplyFilterSchema, createInstanceSchema, updateInstanceSchema, DEFAULT_AGENT_REPLY_FILTER, SENSITIVE_INSTANCE_FIELDS, pairingCodeSchema, passkeyBase64UrlSchema, passkeyCredentialSchema, connectInstanceSchema, syncRequestSchema, listContactsQuerySchema, listGroupsQuerySchema, checkNumberSchema, updateBioSchema, blockContactSchema, groupParticipantActionSchema, groupSettingSchema, groupParticipantsSchema, groupParticipantsPatchSchema, updateGroupSubjectSchema, updateGroupDescriptionSchema, updateGroupSettingsSchema, patchGroupSchema, resyncSchema, replaySchema, pairingActionSchema, guildConfigOverrideSchema, guildConfigCache, GUILD_CONFIG_TTL, presenceSchema;
367018
367092
  var init_instances3 = __esm(() => {
367019
367093
  init_dist6();
367020
367094
  init_src();
@@ -367022,12 +367096,13 @@ var init_instances3 = __esm(() => {
367022
367096
  init_zod();
367023
367097
  init_cache_keys();
367024
367098
  init_scopes();
367099
+ init_whatsapp_business_connection();
367025
367100
  init_auth();
367026
367101
  init_agent_dispatcher();
367027
367102
  init_qr_store();
367028
367103
  init_access();
367029
367104
  init_agent_replay();
367030
- log116 = createLogger("api:instances");
367105
+ log117 = createLogger("api:instances");
367031
367106
  instancesRoutes = new Hono2;
367032
367107
  instanceAccess2 = requireInstanceAccess((c) => c.req.param("id") ?? "");
367033
367108
  listQuerySchema12 = exports_external.object({
@@ -367239,7 +367314,7 @@ var init_instances3 = __esm(() => {
367239
367314
  agentIdTouched: data.agentId !== undefined
367240
367315
  })) {
367241
367316
  data.agentReplyFilter = DEFAULT_AGENT_REPLY_FILTER;
367242
- log116.info("Agent assigned without reply filter \u2014 defaulting to mode:all onDm:true (omni#443)", {
367317
+ log117.info("Agent assigned without reply filter \u2014 defaulting to mode:all onDm:true (omni#443)", {
367243
367318
  agentId: data.agentId,
367244
367319
  defaultFilter: DEFAULT_AGENT_REPLY_FILTER
367245
367320
  });
@@ -367301,7 +367376,7 @@ var init_instances3 = __esm(() => {
367301
367376
  agentIdTouched: data.agentId !== undefined
367302
367377
  })) {
367303
367378
  data.agentReplyFilter = DEFAULT_AGENT_REPLY_FILTER;
367304
- log116.info("Agent assigned without reply filter \u2014 defaulting to mode:all onDm:true (omni#443)", {
367379
+ log117.info("Agent assigned without reply filter \u2014 defaulting to mode:all onDm:true (omni#443)", {
367305
367380
  instanceId: id,
367306
367381
  agentId: data.agentId,
367307
367382
  defaultFilter: DEFAULT_AGENT_REPLY_FILTER
@@ -367327,7 +367402,7 @@ var init_instances3 = __esm(() => {
367327
367402
  try {
367328
367403
  await plugin10.disconnect(id);
367329
367404
  } catch (error3) {
367330
- log116.error("Failed to disconnect instance before delete", { instanceId: id, error: String(error3) });
367405
+ log117.error("Failed to disconnect instance before delete", { instanceId: id, error: String(error3) });
367331
367406
  }
367332
367407
  }
367333
367408
  await services.instances.delete(id);
@@ -367335,7 +367410,7 @@ var init_instances3 = __esm(() => {
367335
367410
  try {
367336
367411
  await plugin10.logout(id);
367337
367412
  } catch (error3) {
367338
- log116.error("Failed to clear channel auth after delete", { instanceId: id, error: String(error3) });
367413
+ log117.error("Failed to clear channel auth after delete", { instanceId: id, error: String(error3) });
367339
367414
  }
367340
367415
  }
367341
367416
  return c.json({ success: true });
@@ -367566,7 +367641,7 @@ var init_instances3 = __esm(() => {
367566
367641
  try {
367567
367642
  await plugin10.disconnect(id);
367568
367643
  } catch (error3) {
367569
- log116.error("Failed to disconnect instance", { instanceId: id, error: String(error3) });
367644
+ log117.error("Failed to disconnect instance", { instanceId: id, error: String(error3) });
367570
367645
  }
367571
367646
  }
367572
367647
  }
@@ -367648,13 +367723,13 @@ var init_instances3 = __esm(() => {
367648
367723
  try {
367649
367724
  await plugin10.logout(id);
367650
367725
  } catch (error3) {
367651
- log116.error("Failed to logout instance", { instanceId: id, error: String(error3) });
367726
+ log117.error("Failed to logout instance", { instanceId: id, error: String(error3) });
367652
367727
  }
367653
367728
  } else if (plugin10) {
367654
367729
  try {
367655
367730
  await plugin10.disconnect(id);
367656
367731
  } catch (error3) {
367657
- log116.error("Failed to disconnect instance during logout", { instanceId: id, error: String(error3) });
367732
+ log117.error("Failed to disconnect instance during logout", { instanceId: id, error: String(error3) });
367658
367733
  }
367659
367734
  }
367660
367735
  }
@@ -368680,7 +368755,7 @@ var init_instances3 = __esm(() => {
368680
368755
  ...chatJids?.length ? { chatJids } : {}
368681
368756
  }
368682
368757
  });
368683
- log116.info("Resync triggered", {
368758
+ log117.info("Resync triggered", {
368684
368759
  instanceId: id,
368685
368760
  jobId: job.id,
368686
368761
  since: sinceDate.toISOString(),
@@ -368698,7 +368773,7 @@ var init_instances3 = __esm(() => {
368698
368773
  });
368699
368774
  } catch (error3) {
368700
368775
  const message2 = error3 instanceof Error ? error3.message : "Unknown error";
368701
- log116.error("Failed to trigger resync", { instanceId: id, error: message2 });
368776
+ log117.error("Failed to trigger resync", { instanceId: id, error: message2 });
368702
368777
  return c.json({ error: { code: "RESYNC_FAILED", message: message2 } }, 500);
368703
368778
  }
368704
368779
  });
@@ -368727,7 +368802,7 @@ var init_instances3 = __esm(() => {
368727
368802
  const sinceDate = since ? new Date(since) : undefined;
368728
368803
  try {
368729
368804
  const result = await replayService.replayMissedMessages({ instanceId: instance4.id, since: sinceDate });
368730
- log116.info("Manual replay triggered", {
368805
+ log117.info("Manual replay triggered", {
368731
368806
  instanceId: instance4.id,
368732
368807
  replayed: result.replayed,
368733
368808
  since: result.since.toISOString()
@@ -368743,7 +368818,7 @@ var init_instances3 = __esm(() => {
368743
368818
  });
368744
368819
  } catch (error3) {
368745
368820
  const message2 = error3 instanceof Error ? error3.message : "Unknown error";
368746
- log116.error("Manual replay failed", { instanceId: id, error: message2 });
368821
+ log117.error("Manual replay failed", { instanceId: id, error: message2 });
368747
368822
  return c.json({ error: { code: "REPLAY_FAILED", message: message2 } }, 500);
368748
368823
  }
368749
368824
  });
@@ -368876,7 +368951,7 @@ var init_instances3 = __esm(() => {
368876
368951
  plugin10.setGuildConfig(id, guildId, newConfig);
368877
368952
  }
368878
368953
  }
368879
- log116.info("Guild config updated", {
368954
+ log117.info("Guild config updated", {
368880
368955
  instanceId: id,
368881
368956
  guildId,
368882
368957
  apiKeyId: apiKey?.id,
@@ -368905,7 +368980,7 @@ var init_instances3 = __esm(() => {
368905
368980
  plugin10.removeGuildConfig(id, guildId);
368906
368981
  }
368907
368982
  }
368908
- log116.info("Guild config deleted", {
368983
+ log117.info("Guild config deleted", {
368909
368984
  instanceId: id,
368910
368985
  guildId,
368911
368986
  apiKeyId: apiKey?.id,
@@ -369820,7 +369895,7 @@ function isValidPathComponent(component) {
369820
369895
  function isValidUUID(str2) {
369821
369896
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(str2);
369822
369897
  }
369823
- var log117, mediaRoutes, mediaStorage = null, ttsAudioFormatSchema, ttsRequestSchema, sttRequestSchema, imagineAspectRatioSchema, imagineRequestSchema, visionRequestSchema, filmAspectRatioSchema, imageMimeTypeSchema, imageBase64Schema, filmRequestSchema, FILM_TIMEOUT_MS, FILM_POLL_INTERVAL_MS = 5000, musicRequestSchema;
369898
+ var log118, mediaRoutes, mediaStorage = null, ttsAudioFormatSchema, ttsRequestSchema, sttRequestSchema, imagineAspectRatioSchema, imagineRequestSchema, visionRequestSchema, filmAspectRatioSchema, imageMimeTypeSchema, imageBase64Schema, filmRequestSchema, FILM_TIMEOUT_MS, FILM_POLL_INTERVAL_MS = 5000, musicRequestSchema;
369824
369899
  var init_media2 = __esm(() => {
369825
369900
  init_dist6();
369826
369901
  init_src();
@@ -369828,7 +369903,7 @@ var init_media2 = __esm(() => {
369828
369903
  init_zod();
369829
369904
  init_registry3();
369830
369905
  init_media_storage();
369831
- log117 = createLogger("routes:media");
369906
+ log118 = createLogger("routes:media");
369832
369907
  mediaRoutes = new Hono2;
369833
369908
  ttsAudioFormatSchema = exports_external.enum(["mp3", "ogg", "opus", "wav", "pcm", "flac", "aac"]);
369834
369909
  ttsRequestSchema = exports_external.object({
@@ -369855,7 +369930,7 @@ var init_media2 = __esm(() => {
369855
369930
  provider = await providerRegistry.resolve("tts", body.provider);
369856
369931
  } catch (err2) {
369857
369932
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
369858
- log117.warn("TTS provider resolution failed", { provider: body.provider, error: message2 });
369933
+ log118.warn("TTS provider resolution failed", { provider: body.provider, error: message2 });
369859
369934
  return c.json({
369860
369935
  error: {
369861
369936
  code: "PROVIDER_NOT_AVAILABLE",
@@ -369891,7 +369966,7 @@ var init_media2 = __esm(() => {
369891
369966
  });
369892
369967
  } catch (err2) {
369893
369968
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
369894
- log117.error("TTS synthesis failed", { provider: provider.name, error: message2 });
369969
+ log118.error("TTS synthesis failed", { provider: provider.name, error: message2 });
369895
369970
  return c.json({
369896
369971
  error: {
369897
369972
  code: "TTS_SYNTHESIS_FAILED",
@@ -369919,7 +369994,7 @@ var init_media2 = __esm(() => {
369919
369994
  provider = await providerRegistry.resolve("stt", body.provider);
369920
369995
  } catch (err2) {
369921
369996
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
369922
- log117.warn("STT provider resolution failed", { provider: body.provider, error: message2 });
369997
+ log118.warn("STT provider resolution failed", { provider: body.provider, error: message2 });
369923
369998
  return c.json({
369924
369999
  error: {
369925
370000
  code: "PROVIDER_NOT_AVAILABLE",
@@ -369963,7 +370038,7 @@ var init_media2 = __esm(() => {
369963
370038
  });
369964
370039
  } catch (err2) {
369965
370040
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
369966
- log117.error("STT transcription failed", { provider: provider.name, error: message2 });
370041
+ log118.error("STT transcription failed", { provider: provider.name, error: message2 });
369967
370042
  return c.json({
369968
370043
  error: {
369969
370044
  code: "STT_TRANSCRIPTION_FAILED",
@@ -369995,7 +370070,7 @@ var init_media2 = __esm(() => {
369995
370070
  provider = await providerRegistry.resolve("imagegen", body.provider);
369996
370071
  } catch (err2) {
369997
370072
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
369998
- log117.warn("Imagegen provider resolution failed", { provider: body.provider, error: message2 });
370073
+ log118.warn("Imagegen provider resolution failed", { provider: body.provider, error: message2 });
369999
370074
  return c.json({
370000
370075
  error: {
370001
370076
  code: "PROVIDER_NOT_AVAILABLE",
@@ -370033,7 +370108,7 @@ var init_media2 = __esm(() => {
370033
370108
  });
370034
370109
  } catch (err2) {
370035
370110
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
370036
- log117.error("Image generation failed", { provider: provider.name, error: message2 });
370111
+ log118.error("Image generation failed", { provider: provider.name, error: message2 });
370037
370112
  return c.json({
370038
370113
  error: {
370039
370114
  code: "IMAGE_GEN_FAILED",
@@ -370058,7 +370133,7 @@ var init_media2 = __esm(() => {
370058
370133
  provider = await providerRegistry.resolve("vision", body.provider);
370059
370134
  } catch (err2) {
370060
370135
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
370061
- log117.warn("Vision provider resolution failed", { provider: body.provider, error: message2 });
370136
+ log118.warn("Vision provider resolution failed", { provider: body.provider, error: message2 });
370062
370137
  return c.json({
370063
370138
  error: {
370064
370139
  code: "PROVIDER_NOT_AVAILABLE",
@@ -370097,7 +370172,7 @@ var init_media2 = __esm(() => {
370097
370172
  });
370098
370173
  } catch (err2) {
370099
370174
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
370100
- log117.error("Vision description failed", { provider: provider.name, error: message2 });
370175
+ log118.error("Vision description failed", { provider: provider.name, error: message2 });
370101
370176
  return c.json({
370102
370177
  error: {
370103
370178
  code: "VISION_DESCRIBE_FAILED",
@@ -370135,7 +370210,7 @@ var init_media2 = __esm(() => {
370135
370210
  provider = await providerRegistry.resolve("videogen", body.provider);
370136
370211
  } catch (err2) {
370137
370212
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
370138
- log117.warn("Videogen provider resolution failed", { provider: body.provider, error: message2 });
370213
+ log118.warn("Videogen provider resolution failed", { provider: body.provider, error: message2 });
370139
370214
  return c.json({
370140
370215
  error: {
370141
370216
  code: "PROVIDER_NOT_AVAILABLE",
@@ -370178,7 +370253,7 @@ var init_media2 = __esm(() => {
370178
370253
  }
370179
370254
  if (operation.state === "failed" || !operation.video) {
370180
370255
  const message2 = operation.error ?? "Video generation failed without error";
370181
- log117.error("Videogen operation failed", { provider: provider.name, error: message2 });
370256
+ log118.error("Videogen operation failed", { provider: provider.name, error: message2 });
370182
370257
  return c.json({
370183
370258
  error: {
370184
370259
  code: "VIDEO_GEN_FAILED",
@@ -370189,7 +370264,7 @@ var init_media2 = __esm(() => {
370189
370264
  }, 500);
370190
370265
  }
370191
370266
  const video = operation.video;
370192
- log117.info("Videogen operation complete", {
370267
+ log118.info("Videogen operation complete", {
370193
370268
  provider: provider.name,
370194
370269
  operationId: operation.operationId,
370195
370270
  sizeBytes: video.data.length,
@@ -370208,7 +370283,7 @@ var init_media2 = __esm(() => {
370208
370283
  });
370209
370284
  } catch (err2) {
370210
370285
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
370211
- log117.error("Videogen request failed", { provider: provider.name, error: message2 });
370286
+ log118.error("Videogen request failed", { provider: provider.name, error: message2 });
370212
370287
  return c.json({
370213
370288
  error: {
370214
370289
  code: "VIDEO_GEN_FAILED",
@@ -370243,7 +370318,7 @@ var init_media2 = __esm(() => {
370243
370318
  provider = await providerRegistry.resolve("musicgen", body.provider);
370244
370319
  } catch (err2) {
370245
370320
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
370246
- log117.warn("Musicgen provider resolution failed", { provider: body.provider, error: message2 });
370321
+ log118.warn("Musicgen provider resolution failed", { provider: body.provider, error: message2 });
370247
370322
  return c.json({
370248
370323
  error: {
370249
370324
  code: "PROVIDER_NOT_AVAILABLE",
@@ -370282,7 +370357,7 @@ var init_media2 = __esm(() => {
370282
370357
  });
370283
370358
  } catch (err2) {
370284
370359
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
370285
- log117.error("Music generation failed", { provider: provider.name, error: message2 });
370360
+ log118.error("Music generation failed", { provider: provider.name, error: message2 });
370286
370361
  return c.json({
370287
370362
  error: {
370288
370363
  code: "MUSIC_GEN_FAILED",
@@ -370356,7 +370431,7 @@ var init_media2 = __esm(() => {
370356
370431
  }
370357
370432
  });
370358
370433
  } catch (err2) {
370359
- log117.error("Media backend read failed", { relativePath, error: String(err2) });
370434
+ log118.error("Media backend read failed", { relativePath, error: String(err2) });
370360
370435
  return c.json({ error: { code: "MEDIA_BACKEND_UNAVAILABLE", message: "Media storage backend unavailable, retry later" } }, 503);
370361
370436
  }
370362
370437
  });
@@ -370425,7 +370500,7 @@ async function resolveReactionTarget(services, instanceId, resolvedTo, messageId
370425
370500
  recoverable: false
370426
370501
  });
370427
370502
  }
370428
- log118.warn("Reaction target chat not found in DB; deferring fromMe to channel plugin fallback (#386)", {
370503
+ log119.warn("Reaction target chat not found in DB; deferring fromMe to channel plugin fallback (#386)", {
370429
370504
  instanceId,
370430
370505
  resolvedTo,
370431
370506
  messageId,
@@ -370435,7 +370510,7 @@ async function resolveReactionTarget(services, instanceId, resolvedTo, messageId
370435
370510
  }
370436
370511
  const target = isUUID(messageId) ? await getReactionTargetByOmniId(services, instanceId, chat2.id, messageId) : await services.messages.getByExternalId(chat2.id, messageId);
370437
370512
  if (!target) {
370438
- log118.warn("Reaction target message not found in DB; deferring fromMe to channel plugin fallback (#386)", {
370513
+ log119.warn("Reaction target message not found in DB; deferring fromMe to channel plugin fallback (#386)", {
370439
370514
  instanceId,
370440
370515
  chatId: chat2.id,
370441
370516
  messageId,
@@ -370586,7 +370661,7 @@ function resolveSentByAgentId(sentBy, instance4) {
370586
370661
  if (sentBy !== "agent")
370587
370662
  return;
370588
370663
  if (!instance4.agentId) {
370589
- log118.warn("sentBy=agent could not be attributed: instance has no configured agent", { instanceId: instance4.id });
370664
+ log119.warn("sentBy=agent could not be attributed: instance has no configured agent", { instanceId: instance4.id });
370590
370665
  return;
370591
370666
  }
370592
370667
  return instance4.agentId;
@@ -370695,10 +370770,10 @@ async function resolveChannelMessageId(services, messageId, instanceId) {
370695
370770
  return messageId;
370696
370771
  const message2 = await services.messages.getById(messageId);
370697
370772
  await verifyMessageInstanceOwnership(services, message2, instanceId);
370698
- log118.debug("Resolved internal UUID to external ID", { messageId, externalId: message2.externalId });
370773
+ log119.debug("Resolved internal UUID to external ID", { messageId, externalId: message2.externalId });
370699
370774
  return message2.externalId;
370700
370775
  }
370701
- var log118, mediaDownloadLog, messagesRoutes, MIME_BY_EXTENSION, DEFAULT_MIME_BY_MEDIA_TYPE, UUID_REGEX2, permalinkQuerySchema, MessageSourceSchema, MessageTypeSchema, MessageStatusSchema, DeliveryStatusSchema, listQuerySchema14, createMessageSchema, updateMessageSchema, recordEditSchema, addReactionSchema, removeReactionSchema, updateDeliveryStatusSchema, sentByField, MentionSchema, sendTextSchema, sendMediaSchema, sendReactionSchema, sendStickerSchema, sendContactSchema, sendLocationSchema, sendHandoffSchema, sendCloseContactSchema, messageRefSchema, _mediaStorageForDownload = null, sendTtsSchema, forwardMessageSchema, sendPresenceSchema, markMessageReadSchema, markBatchReadSchema, sendPollSchema, sendEmbedSchema, editMessageChannelSchema, deleteMessageChannelSchema, starMessageSchema;
370776
+ var log119, mediaDownloadLog, messagesRoutes, MIME_BY_EXTENSION, DEFAULT_MIME_BY_MEDIA_TYPE, UUID_REGEX2, permalinkQuerySchema, MessageSourceSchema, MessageTypeSchema, MessageStatusSchema, DeliveryStatusSchema, listQuerySchema14, createMessageSchema, updateMessageSchema, recordEditSchema, addReactionSchema, removeReactionSchema, updateDeliveryStatusSchema, sentByField, MentionSchema, sendTextSchema, sendMediaSchema, sendReactionSchema, sendStickerSchema, sendContactSchema, sendLocationSchema, sendHandoffSchema, sendCloseContactSchema, messageRefSchema, _mediaStorageForDownload = null, sendTtsSchema, forwardMessageSchema, sendPresenceSchema, markMessageReadSchema, markBatchReadSchema, sendPollSchema, sendEmbedSchema, editMessageChannelSchema, deleteMessageChannelSchema, starMessageSchema;
370702
370777
  var init_messages5 = __esm(() => {
370703
370778
  init_dist6();
370704
370779
  init_src2();
@@ -370714,7 +370789,7 @@ var init_messages5 = __esm(() => {
370714
370789
  init_media_storage();
370715
370790
  init_tenant_scope();
370716
370791
  init__close_contact_config();
370717
- log118 = createLogger("routes:messages");
370792
+ log119 = createLogger("routes:messages");
370718
370793
  mediaDownloadLog = createLogger("routes:messages:media-download");
370719
370794
  messagesRoutes = new Hono2;
370720
370795
  MIME_BY_EXTENSION = {
@@ -371669,7 +371744,7 @@ var init_messages5 = __esm(() => {
371669
371744
  channelHandoffSupported: hasNativeHandoff,
371670
371745
  ...data.motivoHandoff ? { motivoHandoff: data.motivoHandoff } : {}
371671
371746
  }
371672
- }).catch((err2) => log118.warn("Failed to persist handoff log", { error: String(err2) }));
371747
+ }).catch((err2) => log119.warn("Failed to persist handoff log", { error: String(err2) }));
371673
371748
  return c.json({
371674
371749
  data: {
371675
371750
  messageId: channelSendResult?.messageId ?? null,
@@ -371770,7 +371845,7 @@ var init_messages5 = __esm(() => {
371770
371845
  closedFields: data.closeFields ?? null,
371771
371846
  closedAt: closedAt.toISOString()
371772
371847
  };
371773
- services.eventBus.publish("chat.closed", payload, { instanceId: data.instanceId }).catch((err2) => log118.debug("Failed to publish chat.closed", { error: String(err2) }));
371848
+ services.eventBus.publish("chat.closed", payload, { instanceId: data.instanceId }).catch((err2) => log119.debug("Failed to publish chat.closed", { error: String(err2) }));
371774
371849
  }
371775
371850
  return c.json({
371776
371851
  data: {
@@ -373500,7 +373575,7 @@ function makeClient(cfg) {
373500
373575
  function variablesToBodyParameters(variables) {
373501
373576
  return Object.entries(variables).map(([k2, v2]) => [Number.parseInt(k2, 10), v2]).filter(([n2]) => Number.isFinite(n2) && n2 >= 1).sort((a, b3) => a[0] - b3[0]).map(([, v2]) => v2);
373502
373577
  }
373503
- var log119, templatesRoutes, instanceAccess3, idParamSchema6, templateIdParamSchema, templateNameParamSchema, listQuerySchema19, createBodySchema, sendTestBodySchema, sendByNameBodySchema;
373578
+ var log120, templatesRoutes, instanceAccess3, idParamSchema6, templateIdParamSchema, templateNameParamSchema, listQuerySchema19, createBodySchema, sendTestBodySchema, sendByNameBodySchema;
373504
373579
  var init_templates3 = __esm(() => {
373505
373580
  init_dist6();
373506
373581
  init_src6();
@@ -373511,7 +373586,7 @@ var init_templates3 = __esm(() => {
373511
373586
  init_dist2();
373512
373587
  init_zod();
373513
373588
  init_auth();
373514
- log119 = createLogger("routes:whatsapp-templates");
373589
+ log120 = createLogger("routes:whatsapp-templates");
373515
373590
  templatesRoutes = new Hono2;
373516
373591
  instanceAccess3 = requireInstanceAccess((c) => c.req.param("id") ?? "");
373517
373592
  idParamSchema6 = exports_external.object({
@@ -373638,7 +373713,7 @@ var init_templates3 = __esm(() => {
373638
373713
  });
373639
373714
  return c.json({ data: persisted.row }, persisted.created ? 201 : 200);
373640
373715
  } catch (dbErr) {
373641
- log119.error("[whatsapp-business] local template mirror failed after Meta create", {
373716
+ log120.error("[whatsapp-business] local template mirror failed after Meta create", {
373642
373717
  instanceId,
373643
373718
  metaTemplateId: result.id,
373644
373719
  name: body.name,
@@ -374151,35 +374226,41 @@ var init_voice2 = __esm(() => {
374151
374226
  });
374152
374227
  });
374153
374228
 
374229
+ // ../api/src/utils/json-body.ts
374230
+ function parseJsonObjectBody(rawBody) {
374231
+ if (!rawBody)
374232
+ return {};
374233
+ let parsed;
374234
+ try {
374235
+ parsed = JSON.parse(rawBody);
374236
+ } catch {
374237
+ throw new ValidationError2(NOT_AN_OBJECT);
374238
+ }
374239
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
374240
+ throw new ValidationError2(NOT_AN_OBJECT);
374241
+ }
374242
+ return parsed;
374243
+ }
374244
+ var NOT_AN_OBJECT = "Request body must be a JSON object";
374245
+ var init_json_body = __esm(() => {
374246
+ init_src();
374247
+ });
374248
+
374154
374249
  // ../api/src/routes/v2/webhooks.ts
374155
374250
  function sanitizeSource(source) {
374156
374251
  const { signatureSecret, ...rest } = source;
374157
374252
  return { ...rest, hasSignatureSecret: Boolean(signatureSecret) };
374158
374253
  }
374159
- var webhooksRoutes, signatureConfigSchema, createWebhookSourceSchema, updateWebhookSourceSchema, listQuerySchema21, triggerEventSchema;
374254
+ var webhooksRoutes, createWebhookSourceSchema, updateWebhookSourceSchema, listQuerySchema21, triggerEventSchema;
374160
374255
  var init_webhooks3 = __esm(() => {
374161
374256
  init_dist6();
374162
- init_src5();
374163
374257
  init_dist2();
374164
374258
  init_zod();
374259
+ init_webhooks2();
374165
374260
  init_api_keys();
374261
+ init_json_body();
374166
374262
  webhooksRoutes = new Hono2;
374167
- signatureConfigSchema = exports_external.object({
374168
- algorithm: exports_external.enum(webhookSignatureAlgorithms).describe("How to verify: HMAC over the raw body, or token match"),
374169
- header: exports_external.string().min(1).max(200).describe("Header carrying the signature/token (e.g. X-Hub-Signature-256)"),
374170
- prefix: exports_external.string().max(50).optional().describe("Prefix before the hex digest (HMAC algorithms only)")
374171
- }).refine((config4) => config4.algorithm !== "token-match" || config4.prefix === undefined, {
374172
- message: "prefix is not applicable to algorithm 'token-match'",
374173
- path: ["prefix"]
374174
- });
374175
- createWebhookSourceSchema = exports_external.object({
374176
- name: exports_external.string().min(1).max(100).describe("Unique source name (e.g., github, stripe, agno)"),
374177
- description: exports_external.string().optional().describe("Description of the webhook source"),
374178
- expectedHeaders: exports_external.record(exports_external.string(), exports_external.boolean()).optional().describe("Headers to validate"),
374179
- signatureConfig: signatureConfigSchema.nullable().optional().describe("Signature verification config"),
374180
- signatureSecret: exports_external.string().min(8).max(512).nullable().optional().describe("Shared secret (write-only)"),
374181
- enabled: exports_external.boolean().default(true).describe("Whether source is enabled")
374182
- });
374263
+ createWebhookSourceSchema = CreateWebhookSourceSchema;
374183
374264
  updateWebhookSourceSchema = createWebhookSourceSchema.partial();
374184
374265
  listQuerySchema21 = exports_external.object({
374185
374266
  enabled: exports_external.coerce.boolean().optional()
@@ -374219,12 +374300,7 @@ var init_webhooks3 = __esm(() => {
374219
374300
  const sourceName = c.req.param("source");
374220
374301
  const services = c.get("services");
374221
374302
  const rawBody = await c.req.text();
374222
- let payload;
374223
- try {
374224
- payload = rawBody ? JSON.parse(rawBody) : {};
374225
- } catch {
374226
- payload = {};
374227
- }
374303
+ const payload = parseJsonObjectBody(rawBody);
374228
374304
  const headers = {};
374229
374305
  for (const [key, value] of Object.entries(c.req.header())) {
374230
374306
  headers[key.toLowerCase()] = value ?? "";
@@ -374290,7 +374366,7 @@ function readMetaAppEnv() {
374290
374366
  return {
374291
374367
  appId: process.env.META_APP_ID,
374292
374368
  appSecret: process.env.META_APP_SECRET,
374293
- apiVersion: process.env.META_GRAPH_API_VERSION ?? "v25.0"
374369
+ apiVersion: resolveMetaApiVersion(undefined) ?? "v25.0"
374294
374370
  };
374295
374371
  }
374296
374372
  function ensureWhatsAppBusiness(instance4) {
@@ -374336,7 +374412,7 @@ async function readProfilePhotoUpload(c) {
374336
374412
  fileType: blob.type || "image/jpeg"
374337
374413
  };
374338
374414
  }
374339
- var log120, whatsappBusinessRoutes, instanceAccess4, analyticsQuerySchema2, updateProfileSchema;
374415
+ var log121, whatsappBusinessRoutes, instanceAccess4, analyticsQuerySchema2, updateProfileSchema;
374340
374416
  var init_whatsapp_business3 = __esm(() => {
374341
374417
  init_dist6();
374342
374418
  init_src6();
@@ -374345,8 +374421,9 @@ var init_whatsapp_business3 = __esm(() => {
374345
374421
  init_dist2();
374346
374422
  init_zod();
374347
374423
  init_oauth_token_cache();
374424
+ init_whatsapp_business_connection();
374348
374425
  init_auth();
374349
- log120 = createLogger("api:whatsapp-business");
374426
+ log121 = createLogger("api:whatsapp-business");
374350
374427
  whatsappBusinessRoutes = new Hono2;
374351
374428
  instanceAccess4 = requireInstanceAccess((c) => c.req.param("id") ?? "");
374352
374429
  whatsappBusinessRoutes.use("/:id/whatsapp-business/*", instanceAccess4);
@@ -374372,7 +374449,7 @@ var init_whatsapp_business3 = __esm(() => {
374372
374449
  });
374373
374450
  } catch (err2) {
374374
374451
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
374375
- log120.error("OAuth exchange failed", { error: message2 });
374452
+ log121.error("OAuth exchange failed", { error: message2 });
374376
374453
  return c.json({ error: { code: "OAUTH_EXCHANGE_FAILED", message: message2 } }, 400);
374377
374454
  }
374378
374455
  });
@@ -374432,7 +374509,7 @@ var init_whatsapp_business3 = __esm(() => {
374432
374509
  await services.instances.update(id, { metaDisplayPhoneNumber: displayPhoneNumber });
374433
374510
  }
374434
374511
  } catch (err2) {
374435
- log120.warn("Failed to fetch phone_number info during connect \u2014 continuing", {
374512
+ log121.warn("Failed to fetch phone_number info during connect \u2014 continuing", {
374436
374513
  instanceId: id,
374437
374514
  error: err2 instanceof Error ? err2.message : String(err2)
374438
374515
  });
@@ -374458,7 +374535,7 @@ var init_whatsapp_business3 = __esm(() => {
374458
374535
  });
374459
374536
  } catch (err2) {
374460
374537
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
374461
- log120.error("plugin.connect failed for whatsapp-business", { instanceId: id, error: message2 });
374538
+ log121.error("plugin.connect failed for whatsapp-business", { instanceId: id, error: message2 });
374462
374539
  return c.json({ error: { code: "CONNECT_FAILED", message: message2 } }, 500);
374463
374540
  }
374464
374541
  return c.json({
@@ -374486,7 +374563,7 @@ var init_whatsapp_business3 = __esm(() => {
374486
374563
  return c.json({ ok: true });
374487
374564
  } catch (err2) {
374488
374565
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
374489
- log120.error("register phone number failed", { instanceId: id, error: message2 });
374566
+ log121.error("register phone number failed", { instanceId: id, error: message2 });
374490
374567
  return c.json({ error: { code: "REGISTER_FAILED", message: message2 } }, 500);
374491
374568
  }
374492
374569
  });
@@ -374505,7 +374582,7 @@ var init_whatsapp_business3 = __esm(() => {
374505
374582
  return c.json({ subscribed: result.success });
374506
374583
  } catch (err2) {
374507
374584
  const message2 = err2 instanceof Error ? err2.message : "Unknown error";
374508
- log120.error("subscribe app failed", { instanceId: id, error: message2 });
374585
+ log121.error("subscribe app failed", { instanceId: id, error: message2 });
374509
374586
  return c.json({ error: { code: "SUBSCRIBE_FAILED", message: message2 } }, 500);
374510
374587
  }
374511
374588
  });
@@ -374543,7 +374620,7 @@ var init_whatsapp_business3 = __esm(() => {
374543
374620
  try {
374544
374621
  await plugin10.disconnect(id);
374545
374622
  } catch (err2) {
374546
- log120.warn("plugin.disconnect failed during connection delete", {
374623
+ log121.warn("plugin.disconnect failed during connection delete", {
374547
374624
  instanceId: id,
374548
374625
  error: err2 instanceof Error ? err2.message : String(err2)
374549
374626
  });
@@ -374756,7 +374833,7 @@ function flowJsonInvalid(issues) {
374756
374833
  issues
374757
374834
  };
374758
374835
  }
374759
- var log121, whatsappFlowsRoutes, instanceAccess5, idParamSchema8, flowIdParamSchema, FlowCategorySchema2, createBodySchema2, updateBodySchema, sendBodySchema;
374836
+ var log122, whatsappFlowsRoutes, instanceAccess5, idParamSchema8, flowIdParamSchema, FlowCategorySchema2, createBodySchema2, updateBodySchema, sendBodySchema;
374760
374837
  var init_whatsapp_flows3 = __esm(() => {
374761
374838
  init_dist6();
374762
374839
  init_src6();
@@ -374769,7 +374846,7 @@ var init_whatsapp_flows3 = __esm(() => {
374769
374846
  init_auth();
374770
374847
  init_sealed_credentials();
374771
374848
  init_whatsapp_business3();
374772
- log121 = createLogger("routes:whatsapp-flows");
374849
+ log122 = createLogger("routes:whatsapp-flows");
374773
374850
  whatsappFlowsRoutes = new Hono2;
374774
374851
  instanceAccess5 = requireInstanceAccess((c) => c.req.param("id") ?? "");
374775
374852
  idParamSchema8 = exports_external.object({
@@ -374880,7 +374957,7 @@ var init_whatsapp_flows3 = __esm(() => {
374880
374957
  }
374881
374958
  });
374882
374959
  const status = await resolved.client.getBusinessPublicKey().catch(() => null);
374883
- log121.info("flow encryption key registered", { instanceId });
374960
+ log122.info("flow encryption key registered", { instanceId });
374884
374961
  return c.json({
374885
374962
  data: {
374886
374963
  uploaded: true,
@@ -375012,7 +375089,7 @@ var init_whatsapp_flows3 = __esm(() => {
375012
375089
  metadata: { flow: { ...flow, flowToken } }
375013
375090
  });
375014
375091
  if (!result.success) {
375015
- log121.error("flow send failed", { instanceId, to, error: result.error, errorCode: result.errorCode });
375092
+ log122.error("flow send failed", { instanceId, to, error: result.error, errorCode: result.errorCode });
375016
375093
  return c.json(jsonError2(result.error ?? "Flow send failed", result.errorCode ?? "SEND_FAILED"), 500);
375017
375094
  }
375018
375095
  return c.json({ messageId: result.messageId, flowToken }, 201);
@@ -375524,18 +375601,14 @@ function createApp(db2, eventBus = null, channelRegistry2 = null) {
375524
375601
  const sourceName = c.req.param("source");
375525
375602
  const services2 = c.get("services");
375526
375603
  const rawBody = await c.req.text();
375527
- let payload = {};
375528
- if (rawBody) {
375529
- let parsed;
375530
- try {
375531
- parsed = JSON.parse(rawBody);
375532
- } catch {
375533
- parsed = undefined;
375534
- }
375535
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
375536
- return c.json({ error: { code: "VALIDATION", message: "Request body must be a JSON object" } }, 400);
375604
+ let payload;
375605
+ try {
375606
+ payload = parseJsonObjectBody(rawBody);
375607
+ } catch (error3) {
375608
+ if (error3 instanceof ValidationError2) {
375609
+ return c.json({ error: { code: "VALIDATION", message: error3.message } }, 400);
375537
375610
  }
375538
- payload = parsed;
375611
+ throw error3;
375539
375612
  }
375540
375613
  const headers = {};
375541
375614
  for (const [key, value] of Object.entries(c.req.header())) {
@@ -375638,6 +375711,7 @@ var init_app = __esm(() => {
375638
375711
  init_v2();
375639
375712
  init_platform_tenants2();
375640
375713
  init_a2a_discovery();
375714
+ init_json_body();
375641
375715
  httpLog = createLogger("http");
375642
375716
  });
375643
375717
 
@@ -375924,12 +375998,12 @@ async function setupEventPersistence(eventBus, db2) {
375924
375998
  const tracker = getJourneyTracker();
375925
375999
  tracker.recordCheckpoint(metadata.correlationId, "T4", JOURNEY_STAGES.T4);
375926
376000
  }
375927
- log122.debug("Persisted message.received", {
376001
+ log123.debug("Persisted message.received", {
375928
376002
  externalId: payload.externalId,
375929
376003
  instanceId: metadata.instanceId
375930
376004
  });
375931
376005
  } catch (error3) {
375932
- log122.error("Failed to persist message.received", {
376006
+ log123.error("Failed to persist message.received", {
375933
376007
  externalId: payload.externalId,
375934
376008
  error: String(error3)
375935
376009
  });
@@ -375973,12 +376047,12 @@ async function setupEventPersistence(eventBus, db2) {
375973
376047
  };
375974
376048
  await sdb.insert(omniEvents).values(newEvent).onConflictDoNothing({ target: omniEvents.id });
375975
376049
  });
375976
- log122.debug("Persisted message.sent", {
376050
+ log123.debug("Persisted message.sent", {
375977
376051
  externalId: payload.externalId,
375978
376052
  instanceId: metadata.instanceId
375979
376053
  });
375980
376054
  } catch (error3) {
375981
- log122.error("Failed to persist message.sent", {
376055
+ log123.error("Failed to persist message.sent", {
375982
376056
  externalId: payload.externalId,
375983
376057
  error: String(error3)
375984
376058
  });
@@ -376014,11 +376088,11 @@ async function setupEventPersistence(eventBus, db2) {
376014
376088
  await sdb.insert(omniEvents).values(newEvent).onConflictDoNothing({ target: omniEvents.id });
376015
376089
  }
376016
376090
  });
376017
- log122.debug("Persisted message.delivered", {
376091
+ log123.debug("Persisted message.delivered", {
376018
376092
  externalId: payload.externalId
376019
376093
  });
376020
376094
  } catch (error3) {
376021
- log122.error("Failed to persist message.delivered", {
376095
+ log123.error("Failed to persist message.delivered", {
376022
376096
  externalId: payload.externalId,
376023
376097
  error: String(error3)
376024
376098
  });
@@ -376053,11 +376127,11 @@ async function setupEventPersistence(eventBus, db2) {
376053
376127
  await sdb.insert(omniEvents).values(newEvent).onConflictDoNothing({ target: omniEvents.id });
376054
376128
  }
376055
376129
  });
376056
- log122.debug("Persisted message.read", {
376130
+ log123.debug("Persisted message.read", {
376057
376131
  externalId: payload.externalId
376058
376132
  });
376059
376133
  } catch (error3) {
376060
- log122.error("Failed to persist message.read", {
376134
+ log123.error("Failed to persist message.read", {
376061
376135
  externalId: payload.externalId,
376062
376136
  error: String(error3)
376063
376137
  });
@@ -376108,33 +376182,33 @@ async function setupEventPersistence(eventBus, db2) {
376108
376182
  }
376109
376183
  return updated.length;
376110
376184
  });
376111
- log122.debug("Persisted message.failed", {
376185
+ log123.debug("Persisted message.failed", {
376112
376186
  chatId: payload.chatId,
376113
376187
  externalId: payload.externalId,
376114
376188
  updatedRows,
376115
376189
  error: payload.error
376116
376190
  });
376117
376191
  } catch (error3) {
376118
- log122.error("Failed to persist message.failed", {
376192
+ log123.error("Failed to persist message.failed", {
376119
376193
  chatId: payload.chatId,
376120
376194
  error: String(error3)
376121
376195
  });
376122
376196
  }
376123
376197
  }, { ...CONSUMER_OPTIONS, durable: "event-persistence-failed" });
376124
- log122.info("Event persistence initialized - listening for message events");
376198
+ log123.info("Event persistence initialized - listening for message events");
376125
376199
  } catch (error3) {
376126
- log122.error("Failed to set up event persistence", { error: String(error3) });
376200
+ log123.error("Failed to set up event persistence", { error: String(error3) });
376127
376201
  throw error3;
376128
376202
  }
376129
376203
  }
376130
- var log122, CONSUMER_OPTIONS;
376204
+ var log123, CONSUMER_OPTIONS;
376131
376205
  var init_event_persistence = __esm(() => {
376132
376206
  init_src();
376133
376207
  init_src5();
376134
376208
  init_drizzle_orm();
376135
376209
  init_tenant_scope();
376136
376210
  init_worker_tenant_context();
376137
- log122 = createLogger("event-persistence");
376211
+ log123 = createLogger("event-persistence");
376138
376212
  CONSUMER_OPTIONS = {
376139
376213
  queue: "event-persistence",
376140
376214
  maxRetries: 3,
@@ -376218,27 +376292,27 @@ async function downloadMediaFromUrl(ctx, instanceId, messageId, mediaUrl, mimeTy
376218
376292
  try {
376219
376293
  const result = await ctx.mediaStorage.storeFromUrl(instanceId, messageId, mediaUrl, mimeType, platformTimestamp, fetchOptions, trustedTenantId);
376220
376294
  await runMediaDb(ctx, trustedTenantId, () => ctx.mediaStorage.updateMessageLocalPath(messageId, result.localPath));
376221
- log123.debug("Downloaded media from URL", { messageId, filePath: result.localPath });
376295
+ log124.debug("Downloaded media from URL", { messageId, filePath: result.localPath });
376222
376296
  return result.localPath;
376223
376297
  } catch (error3) {
376224
- log123.error("Failed to download media", { error: String(error3), mediaUrl });
376298
+ log124.error("Failed to download media", { error: String(error3), mediaUrl });
376225
376299
  return null;
376226
376300
  }
376227
376301
  }
376228
376302
  async function downloadMediaViaPlugin(ctx, instanceId, messageId, channelType, mediaRef, fallbackMimeType, platformTimestamp, trustedTenantId) {
376229
376303
  const plugin10 = await (ctx.resolveChannelPlugin ?? getPlugin)(channelType);
376230
376304
  if (!plugin10?.downloadInboundMedia) {
376231
- log123.warn("Channel plugin cannot materialize deferred media", { channelType, messageId });
376305
+ log124.warn("Channel plugin cannot materialize deferred media", { channelType, messageId });
376232
376306
  return null;
376233
376307
  }
376234
376308
  try {
376235
376309
  const { buffer: buffer3, mimeType } = pluginMediaSchema.parse(await plugin10.downloadInboundMedia(instanceId, mediaRef));
376236
376310
  const result = await ctx.mediaStorage.storeFromBuffer(instanceId, messageId, buffer3, mimeType || fallbackMimeType, platformTimestamp, trustedTenantId);
376237
376311
  await runMediaDb(ctx, trustedTenantId, () => ctx.mediaStorage.updateMessageLocalPath(messageId, result.localPath));
376238
- log123.debug("Downloaded inbound media via plugin", { messageId, channelType, filePath: result.localPath });
376312
+ log124.debug("Downloaded inbound media via plugin", { messageId, channelType, filePath: result.localPath });
376239
376313
  return result.localPath;
376240
376314
  } catch (error3) {
376241
- log123.error("Failed to download inbound media via plugin", {
376315
+ log124.error("Failed to download inbound media via plugin", {
376242
376316
  error: String(error3),
376243
376317
  channelType,
376244
376318
  messageId
@@ -376256,7 +376330,7 @@ async function resolveMediaPath2(ctx, instanceId, chatId, externalId, content, m
376256
376330
  chat2 = await runMediaDb(ctx, trustedTenantId, () => ctx.services.chats.findByExternalIdSmart(instanceId, chatId));
376257
376331
  }
376258
376332
  if (!chat2) {
376259
- log123.debug("Chat not found, cannot process media", { chatId, externalId });
376333
+ log124.debug("Chat not found, cannot process media", { chatId, externalId });
376260
376334
  return null;
376261
376335
  }
376262
376336
  const chatDbId = chat2.id;
@@ -376266,7 +376340,7 @@ async function resolveMediaPath2(ctx, instanceId, chatId, externalId, content, m
376266
376340
  message2 = await runMediaDb(ctx, trustedTenantId, () => ctx.services.messages.getByExternalId(chatDbId, externalId));
376267
376341
  }
376268
376342
  if (!message2) {
376269
- log123.debug("Message not found after waiting, cannot process media", { externalId });
376343
+ log124.debug("Message not found after waiting, cannot process media", { externalId });
376270
376344
  return null;
376271
376345
  }
376272
376346
  let filePath = message2.mediaLocalPath;
@@ -376281,7 +376355,7 @@ async function resolveMediaPath2(ctx, instanceId, chatId, externalId, content, m
376281
376355
  return null;
376282
376356
  }
376283
376357
  if (!filePath) {
376284
- log123.debug("No media file path available", { externalId });
376358
+ log124.debug("No media file path available", { externalId });
376285
376359
  return null;
376286
376360
  }
376287
376361
  return {
@@ -376304,13 +376378,13 @@ async function resolveSafeMediaContentEventId(ctx, envelope2, eventId) {
376304
376378
  return event?.id ?? null;
376305
376379
  });
376306
376380
  } catch (error3) {
376307
- log123.debug("Failed to validate media_content event FK", { eventId: id, error: String(error3) });
376381
+ log124.debug("Failed to validate media_content event FK", { eventId: id, error: String(error3) });
376308
376382
  return null;
376309
376383
  }
376310
376384
  if (found)
376311
376385
  return found;
376312
376386
  if (Date.now() >= deadline) {
376313
- log123.debug("Skipping media_content event FK; omni_event not found", { eventId: id });
376387
+ log124.debug("Skipping media_content event FK; omni_event not found", { eventId: id });
376314
376388
  return null;
376315
376389
  }
376316
376390
  await new Promise((resolve4) => setTimeout(resolve4, pollMs));
@@ -376346,7 +376420,7 @@ async function persistProcessingResult(ctx, messageId, safeEventId, result, cont
376346
376420
  await sdb.insert(mediaContent).values(row);
376347
376421
  }
376348
376422
  } catch (error3) {
376349
- log123.warn("Failed to insert media_content record (non-critical)", {
376423
+ log124.warn("Failed to insert media_content record (non-critical)", {
376350
376424
  messageId,
376351
376425
  error: String(error3)
376352
376426
  });
@@ -376369,14 +376443,14 @@ async function processMessageMedia(ctx, payload, metadata, envelope2) {
376369
376443
  const mimeType = getMimeType2(content);
376370
376444
  const trustedTenantId = envelopeTenantId(envelope2);
376371
376445
  if (!mimeType || !ctx.mediaService.canProcess(mimeType)) {
376372
- log123.debug("MIME type not processable or missing", { mimeType, externalId });
376446
+ log124.debug("MIME type not processable or missing", { mimeType, externalId });
376373
376447
  return;
376374
376448
  }
376375
376449
  const media = await resolveMediaPath2(ctx, instanceId, payload.chatId, externalId, content, mimeType, metadata.channelType, trustedTenantId);
376376
376450
  if (!media)
376377
376451
  return;
376378
376452
  const processable = await ctx.mediaStorage.materializeForProcessing(media.filePath);
376379
- log123.info("Processing media", { messageId: media.messageId, mimeType, filePath: processable.path });
376453
+ log124.info("Processing media", { messageId: media.messageId, mimeType, filePath: processable.path });
376380
376454
  let result;
376381
376455
  try {
376382
376456
  result = await ctx.mediaService.process(processable.path, mimeType, {
@@ -376389,7 +376463,7 @@ async function processMessageMedia(ctx, payload, metadata, envelope2) {
376389
376463
  }
376390
376464
  if (!result.success) {
376391
376465
  const reason2 = result.errorMessage ?? "unknown";
376392
- log123.warn("Media processing failed", { messageId: media.messageId, error: reason2 });
376466
+ log124.warn("Media processing failed", { messageId: media.messageId, error: reason2 });
376393
376467
  const processingType = result.processingType ?? inferProcessingType(content.type);
376394
376468
  const errorColumn = getContentFieldForType(processingType, content.type);
376395
376469
  if (errorColumn) {
@@ -376417,7 +376491,7 @@ async function processMessageMedia(ctx, payload, metadata, envelope2) {
376417
376491
  }
376418
376492
  const safeEventId = await resolveSafeMediaContentEventId(ctx, envelope2, eventId);
376419
376493
  await runConsumerInTenantContext(ctx.db, envelope2, () => persistProcessingResult(ctx, media.messageId, safeEventId, result, content.type));
376420
- log123.info("Media processing complete", {
376494
+ log124.info("Media processing complete", {
376421
376495
  messageId: media.messageId,
376422
376496
  processingType: result.processingType,
376423
376497
  provider: result.provider,
@@ -376457,7 +376531,7 @@ async function publishMediaCrashEvents(ctx, eventId, payload, metadata, error3,
376457
376531
  error: reason2
376458
376532
  }, meta);
376459
376533
  } catch (publishError) {
376460
- log123.error("Failed to publish media failure event", { error: String(publishError) });
376534
+ log124.error("Failed to publish media failure event", { error: String(publishError) });
376461
376535
  }
376462
376536
  }
376463
376537
  async function resolveMediaIdForCrash(ctx, instanceId, payload, trustedTenantId) {
@@ -376471,7 +376545,7 @@ async function resolveMediaIdForCrash(ctx, instanceId, payload, trustedTenantId)
376471
376545
  if (msg)
376472
376546
  return msg.id;
376473
376547
  } catch (lookupError) {
376474
- log123.debug("Failed to resolve DB message UUID for crash handler, falling back to externalId", {
376548
+ log124.debug("Failed to resolve DB message UUID for crash handler, falling back to externalId", {
376475
376549
  error: String(lookupError),
376476
376550
  instanceId,
376477
376551
  chatId: payload.chatId
@@ -376544,7 +376618,7 @@ async function setupMediaProcessor(eventBus, db2, services) {
376544
376618
  channelType: metadata.channelType
376545
376619
  }, event);
376546
376620
  } catch (error3) {
376547
- log123.error("Failed to process media", {
376621
+ log124.error("Failed to process media", {
376548
376622
  externalId: payload.externalId,
376549
376623
  error: String(error3)
376550
376624
  });
@@ -376559,14 +376633,14 @@ async function setupMediaProcessor(eventBus, db2, services) {
376559
376633
  concurrency: 5
376560
376634
  });
376561
376635
  setGlobalCircuitBreakerStateChangeCallback((name, from, to) => {
376562
- log123.warn("Circuit breaker state change", { provider: name, from, to });
376636
+ log124.warn("Circuit breaker state change", { provider: name, from, to });
376563
376637
  });
376564
376638
  await eventBus.subscribePattern("media.health.request", async (event) => {
376565
376639
  const tracker = getMediaHealthTracker();
376566
376640
  const report = tracker.getReport();
376567
376641
  const correlationId = event.metadata?.correlationId ?? event.id;
376568
376642
  await eventBus.publishGeneric("system.media.health.response", { correlationId, report }, { source: "media-processor" });
376569
- log123.debug("Published media health report", {
376643
+ log124.debug("Published media health report", {
376570
376644
  correlationId,
376571
376645
  totalRequests: report.overall.totalRequests,
376572
376646
  successRate: report.overall.successRate
@@ -376575,9 +376649,9 @@ async function setupMediaProcessor(eventBus, db2, services) {
376575
376649
  durable: "media-health-responder",
376576
376650
  queue: "media-health-responder"
376577
376651
  });
376578
- log123.info("Media processor initialized");
376652
+ log124.info("Media processor initialized");
376579
376653
  }
376580
- var log123, PROCESSABLE_MEDIA_TYPES, pluginMediaSchema;
376654
+ var log124, PROCESSABLE_MEDIA_TYPES, pluginMediaSchema;
376581
376655
  var init_media_processor = __esm(() => {
376582
376656
  init_src();
376583
376657
  init_src5();
@@ -376588,7 +376662,7 @@ var init_media_processor = __esm(() => {
376588
376662
  init_tenant_scope();
376589
376663
  init_worker_tenant_context();
376590
376664
  init_loader2();
376591
- log123 = createLogger("media-processor");
376665
+ log124 = createLogger("media-processor");
376592
376666
  PROCESSABLE_MEDIA_TYPES = new Set(["audio", "image", "document", "video"]);
376593
376667
  pluginMediaSchema = exports_external.object({
376594
376668
  buffer: exports_external.instanceof(Buffer),
@@ -376619,7 +376693,7 @@ function createInflightRevocationMonitor(options) {
376619
376693
  try {
376620
376694
  admissible = await check2(tenantId);
376621
376695
  } catch (error3) {
376622
- log124.warn("Admissibility check failed \u2014 refusing in-flight work", {
376696
+ log125.warn("Admissibility check failed \u2014 refusing in-flight work", {
376623
376697
  tenantId,
376624
376698
  error: String(error3)
376625
376699
  });
@@ -376633,10 +376707,10 @@ function createInflightRevocationMonitor(options) {
376633
376707
  }
376634
376708
  };
376635
376709
  }
376636
- var log124, INFLIGHT_REVOCATION_CEILING_SECONDS = 30, InflightRevocationError;
376710
+ var log125, INFLIGHT_REVOCATION_CEILING_SECONDS = 30, InflightRevocationError;
376637
376711
  var init_inflight_revocation = __esm(() => {
376638
376712
  init_src();
376639
- log124 = createLogger("inflight-revocation");
376713
+ log125 = createLogger("inflight-revocation");
376640
376714
  InflightRevocationError = class InflightRevocationError extends Error {
376641
376715
  code = "inflight_tenant_revoked";
376642
376716
  constructor(tenantId) {
@@ -376710,7 +376784,7 @@ async function setupSyncWorker(eventBus, services, channelRegistry2, database) {
376710
376784
  await eventBus.subscribe("sync.started", async (event) => {
376711
376785
  const payload = event.payload;
376712
376786
  const { jobId, instanceId, type, config: config4 } = payload;
376713
- log125.info("Processing sync job", { jobId, instanceId, type });
376787
+ log126.info("Processing sync job", { jobId, instanceId, type });
376714
376788
  const jobTenantId = trustedSyncTenant(event);
376715
376789
  try {
376716
376790
  await services.syncJobs.start(jobId, jobTenantId);
@@ -376738,12 +376812,12 @@ async function setupSyncWorker(eventBus, services, channelRegistry2, database) {
376738
376812
  case "history-push":
376739
376813
  break;
376740
376814
  default:
376741
- log125.warn("Unknown sync type", { jobId, type });
376815
+ log126.warn("Unknown sync type", { jobId, type });
376742
376816
  await services.syncJobs.fail(jobId, `Unknown sync type: ${type}`, jobTenantId);
376743
376817
  }
376744
376818
  } catch (error3) {
376745
376819
  const errorMessage = error3 instanceof Error ? error3.message : String(error3);
376746
- log125.error("Sync job failed", { jobId, error: errorMessage });
376820
+ log126.error("Sync job failed", { jobId, error: errorMessage });
376747
376821
  await services.syncJobs.fail(jobId, errorMessage, jobTenantId);
376748
376822
  }
376749
376823
  }, {
@@ -376751,9 +376825,9 @@ async function setupSyncWorker(eventBus, services, channelRegistry2, database) {
376751
376825
  queue: "sync-workers",
376752
376826
  startFrom: "new"
376753
376827
  });
376754
- log125.info("Sync worker initialized - listening for sync.started events");
376828
+ log126.info("Sync worker initialized - listening for sync.started events");
376755
376829
  } catch (error3) {
376756
- log125.error("Failed to set up sync worker", { error: String(error3) });
376830
+ log126.error("Failed to set up sync worker", { error: String(error3) });
376757
376831
  throw error3;
376758
376832
  }
376759
376833
  }
@@ -376791,20 +376865,20 @@ async function buildWhatsAppAnchors(database, instanceId) {
376791
376865
  });
376792
376866
  }
376793
376867
  }
376794
- log125.info("Built WhatsApp anchors", { instanceId, anchorCount: anchors.length });
376868
+ log126.info("Built WhatsApp anchors", { instanceId, anchorCount: anchors.length });
376795
376869
  return anchors;
376796
376870
  }
376797
376871
  function buildAnchorsForExplicitChatJids(jobId, chatJids, dbAnchors) {
376798
- log125.info("Using explicit chatJids for sync", { jobId, chatJids });
376872
+ log126.info("Using explicit chatJids for sync", { jobId, chatJids });
376799
376873
  const anchors = [];
376800
376874
  for (const jid of chatJids) {
376801
376875
  const dbAnchor = dbAnchors.find((a) => a.chatJid === jid);
376802
376876
  if (dbAnchor) {
376803
- log125.debug("Found DB anchor for chatJid", { jobId, chatJid: jid, anchorId: dbAnchor.messageKey.id });
376877
+ log126.debug("Found DB anchor for chatJid", { jobId, chatJid: jid, anchorId: dbAnchor.messageKey.id });
376804
376878
  anchors.push(dbAnchor);
376805
376879
  continue;
376806
376880
  }
376807
- log125.debug("No DB anchor for chatJid, will fetch recent", { jobId, chatJid: jid });
376881
+ log126.debug("No DB anchor for chatJid, will fetch recent", { jobId, chatJid: jid });
376808
376882
  anchors.push({
376809
376883
  chatJid: jid,
376810
376884
  messageKey: { remoteJid: jid, id: "", fromMe: false },
@@ -376830,7 +376904,7 @@ async function discoverAnchorsFromPlugin(jobId, instanceId, plugin10, dbAnchors,
376830
376904
  discovered.push({ chatJid: jid, messageKey: { remoteJid: jid, id: "", fromMe: false }, timestamp: Date.now() });
376831
376905
  }
376832
376906
  if (discovered.length > 0) {
376833
- log125.info("Discovered chats from DB + Baileys not in anchors", {
376907
+ log126.info("Discovered chats from DB + Baileys not in anchors", {
376834
376908
  jobId,
376835
376909
  discoveredCount: discovered.length,
376836
376910
  fromDb: dbExternalIds.length,
@@ -376856,7 +376930,7 @@ async function processMessageSync(jobId, instanceId, channelType, config4, servi
376856
376930
  await revocation.assertAdmissible();
376857
376931
  let inflightRevoked = null;
376858
376932
  if (!("fetchHistory" in plugin10) || typeof plugin10.fetchHistory !== "function") {
376859
- log125.warn("Plugin does not support fetchHistory", { channelType });
376933
+ log126.warn("Plugin does not support fetchHistory", { channelType });
376860
376934
  await services.syncJobs.complete(jobId, jobTenantId);
376861
376935
  return;
376862
376936
  }
@@ -376865,7 +376939,7 @@ async function processMessageSync(jobId, instanceId, channelType, config4, servi
376865
376939
  let fetched = 0;
376866
376940
  let stored = 0;
376867
376941
  let duplicates = 0;
376868
- log125.info("Starting message sync", {
376942
+ log126.info("Starting message sync", {
376869
376943
  jobId,
376870
376944
  instanceId,
376871
376945
  channelType,
@@ -376875,13 +376949,13 @@ async function processMessageSync(jobId, instanceId, channelType, config4, servi
376875
376949
  if (channelType === "whatsapp-baileys" && config4.chatJids?.length) {
376876
376950
  const dbAnchors = handle ? await inSyncWorkerScope(envelope2, () => buildWhatsAppAnchors(handle, instanceId)) : [];
376877
376951
  anchors = await resolveWhatsAppAnchors(jobId, instanceId, config4, plugin10, dbAnchors, services, envelope2);
376878
- log125.info("WhatsApp per-chat active sync", { jobId, anchorCount: anchors.length, chatJids: config4.chatJids });
376952
+ log126.info("WhatsApp per-chat active sync", { jobId, anchorCount: anchors.length, chatJids: config4.chatJids });
376879
376953
  } else if (channelType === "whatsapp-baileys") {
376880
376954
  const dbAnchors = handle ? await inSyncWorkerScope(envelope2, () => buildWhatsAppAnchors(handle, instanceId)) : [];
376881
376955
  const discoveredAnchors = await discoverAnchorsFromPlugin(jobId, instanceId, plugin10, dbAnchors, services, envelope2);
376882
376956
  anchors = [...dbAnchors, ...discoveredAnchors];
376883
376957
  if (anchors.length > 0) {
376884
- log125.info("WhatsApp default sync with DB+cache discovery", {
376958
+ log126.info("WhatsApp default sync with DB+cache discovery", {
376885
376959
  jobId,
376886
376960
  instanceId,
376887
376961
  dbAnchorCount: dbAnchors.length,
@@ -376889,7 +376963,7 @@ async function processMessageSync(jobId, instanceId, channelType, config4, servi
376889
376963
  totalAnchors: anchors.length
376890
376964
  });
376891
376965
  } else {
376892
- log125.info("WhatsApp passive sync (no prior data)", { jobId, instanceId });
376966
+ log126.info("WhatsApp passive sync (no prior data)", { jobId, instanceId });
376893
376967
  }
376894
376968
  }
376895
376969
  const fetchOptions = {
@@ -376952,7 +377026,7 @@ async function processMessageSync(jobId, instanceId, channelType, config4, servi
376952
377026
  else
376953
377027
  stored++;
376954
377028
  } catch (error3) {
376955
- log125.warn("Failed to store synced message", {
377029
+ log126.warn("Failed to store synced message", {
376956
377030
  externalId: msg.externalId,
376957
377031
  error: String(error3)
376958
377032
  });
@@ -376972,7 +377046,7 @@ async function processMessageSync(jobId, instanceId, channelType, config4, servi
376972
377046
  }, jobTenantId);
376973
377047
  await queueMediaBackfillAfterSync(services, config4, instanceId, jobId, stored, jobTenantId);
376974
377048
  await services.syncJobs.complete(jobId, jobTenantId);
376975
- log125.info("Message sync completed", {
377049
+ log126.info("Message sync completed", {
376976
377050
  jobId,
376977
377051
  fetched,
376978
377052
  stored,
@@ -376994,9 +377068,9 @@ async function queueMediaBackfillAfterSync(services, config4, instanceId, jobId,
376994
377068
  delayMaxMs: config4.delayMaxMs ?? 3000
376995
377069
  };
376996
377070
  const batch = await services.batchJobs.create(params, trustedTenantId);
376997
- log125.info("Queued media backfill batch after message sync", { jobId, batchJobId: batch.id, stored });
377071
+ log126.info("Queued media backfill batch after message sync", { jobId, batchJobId: batch.id, stored });
376998
377072
  } catch (error3) {
376999
- log125.warn("Failed to queue media backfill batch after message sync", { jobId, error: String(error3) });
377073
+ log126.warn("Failed to queue media backfill batch after message sync", { jobId, error: String(error3) });
377000
377074
  }
377001
377075
  }
377002
377076
  function mapContentType3(contentType) {
@@ -377045,14 +377119,14 @@ async function processContactsSync(jobId, instanceId, channelType, config4, serv
377045
377119
  await revocation.assertAdmissible();
377046
377120
  let inflightRevoked = null;
377047
377121
  if (!("fetchContacts" in plugin10) || typeof plugin10.fetchContacts !== "function") {
377048
- log125.warn("Plugin does not support fetchContacts", { channelType });
377122
+ log126.warn("Plugin does not support fetchContacts", { channelType });
377049
377123
  await services.syncJobs.complete(jobId, jobTenantId);
377050
377124
  return;
377051
377125
  }
377052
377126
  let fetched = 0;
377053
377127
  let stored = 0;
377054
377128
  let linked = 0;
377055
- log125.info("Starting contacts sync", {
377129
+ log126.info("Starting contacts sync", {
377056
377130
  jobId,
377057
377131
  instanceId,
377058
377132
  channelType
@@ -377102,7 +377176,7 @@ async function processContactsSync(jobId, instanceId, channelType, config4, serv
377102
377176
  await updateDmChatName(services, instanceId, c.platformUserId, c.name, envelope2);
377103
377177
  }
377104
377178
  } catch (error3) {
377105
- log125.warn("Failed to store synced contact", {
377179
+ log126.warn("Failed to store synced contact", {
377106
377180
  platformUserId: c.platformUserId,
377107
377181
  error: String(error3)
377108
377182
  });
@@ -377121,7 +377195,7 @@ async function processContactsSync(jobId, instanceId, channelType, config4, serv
377121
377195
  duplicates: 0
377122
377196
  }, jobTenantId);
377123
377197
  await services.syncJobs.complete(jobId, jobTenantId);
377124
- log125.info("Contacts sync completed", {
377198
+ log126.info("Contacts sync completed", {
377125
377199
  jobId,
377126
377200
  fetched,
377127
377201
  stored,
@@ -377173,7 +377247,7 @@ async function processGroupsSync(jobId, instanceId, channelType, _config, servic
377173
377247
  let inflightRevoked = null;
377174
377248
  const fetchMethod = channelType === "discord" ? "fetchGuilds" : "fetchGroups";
377175
377249
  if (!(fetchMethod in plugin10) || typeof plugin10[fetchMethod] !== "function") {
377176
- log125.warn(`Plugin does not support ${fetchMethod}`, { channelType });
377250
+ log126.warn(`Plugin does not support ${fetchMethod}`, { channelType });
377177
377251
  await services.syncJobs.complete(jobId, jobTenantId);
377178
377252
  return;
377179
377253
  }
@@ -377184,7 +377258,7 @@ async function processGroupsSync(jobId, instanceId, channelType, _config, servic
377184
377258
  let fetched = 0;
377185
377259
  let stored = 0;
377186
377260
  let updated = 0;
377187
- log125.info("Starting groups sync", {
377261
+ log126.info("Starting groups sync", {
377188
377262
  jobId,
377189
377263
  instanceId,
377190
377264
  channelType
@@ -377218,7 +377292,7 @@ async function processGroupsSync(jobId, instanceId, channelType, _config, servic
377218
377292
  else
377219
377293
  stored++;
377220
377294
  } catch (error3) {
377221
- log125.warn("Failed to store synced group", {
377295
+ log126.warn("Failed to store synced group", {
377222
377296
  externalId: g2.externalId,
377223
377297
  error: String(error3)
377224
377298
  });
@@ -377265,7 +377339,7 @@ async function processGroupsSync(jobId, instanceId, channelType, _config, servic
377265
377339
  else
377266
377340
  stored++;
377267
377341
  } catch (error3) {
377268
- log125.warn("Failed to store synced guild", {
377342
+ log126.warn("Failed to store synced guild", {
377269
377343
  externalId: g2.externalId,
377270
377344
  error: String(error3)
377271
377345
  });
@@ -377282,7 +377356,7 @@ async function processGroupsSync(jobId, instanceId, channelType, _config, servic
377282
377356
  duplicates: updated
377283
377357
  }, jobTenantId);
377284
377358
  await services.syncJobs.complete(jobId, jobTenantId);
377285
- log125.info("Groups sync completed", {
377359
+ log126.info("Groups sync completed", {
377286
377360
  jobId,
377287
377361
  fetched,
377288
377362
  stored,
@@ -377406,7 +377480,7 @@ async function setupHistoryPushTracker(eventBus, services) {
377406
377480
  throw error3;
377407
377481
  }
377408
377482
  }
377409
- var log125, RATE_LIMITS2, db2 = null, historyPushLog;
377483
+ var log126, RATE_LIMITS2, db2 = null, historyPushLog;
377410
377484
  var init_sync_worker = __esm(() => {
377411
377485
  init_src();
377412
377486
  init_src5();
@@ -377415,7 +377489,7 @@ var init_sync_worker = __esm(() => {
377415
377489
  init_periodic_tenant_work();
377416
377490
  init_tenant_scope();
377417
377491
  init_worker_tenant_context();
377418
- log125 = createLogger("sync-worker");
377492
+ log126 = createLogger("sync-worker");
377419
377493
  RATE_LIMITS2 = {
377420
377494
  "whatsapp-baileys": 30,
377421
377495
  discord: 50,
@@ -377437,7 +377511,7 @@ async function resolveChatId2(services, db3, tenantId, instanceId, externalId) {
377437
377511
  const chat2 = await runTenantWorkDb(db3, tenantId, () => services.chats.findByExternalIdSmart(instanceId, externalId));
377438
377512
  return chat2?.id ?? null;
377439
377513
  } catch (err2) {
377440
- log126.warn("follow-up-hooks: failed to resolve chatId", { instanceId, externalId, error: String(err2) });
377514
+ log127.warn("follow-up-hooks: failed to resolve chatId", { instanceId, externalId, error: String(err2) });
377441
377515
  return null;
377442
377516
  }
377443
377517
  }
@@ -377548,16 +377622,16 @@ async function setupFollowUpHooks(eventBus, services, db3) {
377548
377622
  retryDelayMs: 500,
377549
377623
  startFrom: "new"
377550
377624
  });
377551
- log126.info("Follow-up hooks active (message.sent/received, chat.handoff_activated, chat.archived, chat.closed)");
377625
+ log127.info("Follow-up hooks active (message.sent/received, chat.handoff_activated, chat.archived, chat.closed)");
377552
377626
  } catch (error3) {
377553
- log126.error("Failed to set up follow-up hooks", { error: String(error3) });
377627
+ log127.error("Failed to set up follow-up hooks", { error: String(error3) });
377554
377628
  }
377555
377629
  }
377556
- var log126;
377630
+ var log127;
377557
377631
  var init_follow_up_hooks = __esm(() => {
377558
377632
  init_src();
377559
377633
  init_worker_tenant_context();
377560
- log126 = createLogger("follow-up-hooks");
377634
+ log127 = createLogger("follow-up-hooks");
377561
377635
  });
377562
377636
 
377563
377637
  // ../api/src/plugins/instance-monitor.ts
@@ -377998,6 +378072,7 @@ var logger7, DEFAULT_CONFIG3, STALE_TRANSITION_MAX_AGE_MS;
377998
378072
  var init_instance_monitor = __esm(() => {
377999
378073
  init_src5();
378000
378074
  init_drizzle_orm();
378075
+ init_whatsapp_business_connection();
378001
378076
  init_instance_owner_registry();
378002
378077
  init_periodic_tenant_work();
378003
378078
  init_tenant_scope();
@@ -378042,7 +378117,7 @@ async function resolveCallAgentChatIds(services, db3, ctx, trustedTenantId) {
378042
378117
  senderId: ctx.senderId === ctx.chatId ? chat2.externalId : ctx.senderId
378043
378118
  };
378044
378119
  } catch {
378045
- log127.warn("call_agent: chat UUID not resolvable, using raw value (session may diverge)", {
378120
+ log128.warn("call_agent: chat UUID not resolvable, using raw value (session may diverge)", {
378046
378121
  chatId: ctx.chatId,
378047
378122
  instanceId: ctx.instanceId
378048
378123
  });
@@ -378129,7 +378204,7 @@ function buildAutomationEngineDeps(services, db3, options = {}) {
378129
378204
  releaseIdleTimeoutClaim: (claimToken) => releaseIdleTimeoutClaim(claimToken)
378130
378205
  };
378131
378206
  }
378132
- var log127, UUID_RE3;
378207
+ var log128, UUID_RE3;
378133
378208
  var init_automation_actions = __esm(() => {
378134
378209
  init_src();
378135
378210
  init_src5();
@@ -378138,7 +378213,7 @@ var init_automation_actions = __esm(() => {
378138
378213
  init_tenant_scope();
378139
378214
  init_worker_tenant_context();
378140
378215
  init_loader2();
378141
- log127 = createLogger("automation-actions");
378216
+ log128 = createLogger("automation-actions");
378142
378217
  UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
378143
378218
  });
378144
378219
 
@@ -378161,7 +378236,7 @@ async function createDailySyncJobs(services, type) {
378161
378236
  });
378162
378237
  jobsCreated++;
378163
378238
  } catch (err2) {
378164
- log128.warn(`Failed to create ${type} sync job for instance`, {
378239
+ log129.warn(`Failed to create ${type} sync job for instance`, {
378165
378240
  instanceId: instance4.id,
378166
378241
  error: String(err2)
378167
378242
  });
@@ -378220,7 +378295,7 @@ function setupScheduler(services, channelRegistry2) {
378220
378295
  recordScheduledJob("dead-letter-auto-retry", "success", durationSec);
378221
378296
  const pendingCount = await services.deadLetters.getPendingCount();
378222
378297
  deadLettersPending.set(pendingCount);
378223
- log128.info("Dead letter auto-retry completed", result);
378298
+ log129.info("Dead letter auto-retry completed", result);
378224
378299
  } catch (err2) {
378225
378300
  const durationSec = (Date.now() - startTime3) / 1000;
378226
378301
  recordScheduledJob("dead-letter-auto-retry", "failure", durationSec);
@@ -378242,7 +378317,7 @@ function setupScheduler(services, channelRegistry2) {
378242
378317
  recordScheduledJob("payload-cleanup", "success", durationSec);
378243
378318
  const stats = await services.payloadStore.getStats();
378244
378319
  payloadStorageSize.set(stats.totalSizeCompressed);
378245
- log128.info("Payload cleanup completed", { deleted });
378320
+ log129.info("Payload cleanup completed", { deleted });
378246
378321
  } catch (err2) {
378247
378322
  const durationSec = (Date.now() - startTime3) / 1000;
378248
378323
  recordScheduledJob("payload-cleanup", "failure", durationSec);
@@ -378262,7 +378337,7 @@ function setupScheduler(services, channelRegistry2) {
378262
378337
  const deleted = await services.deadLetters.cleanupExpired();
378263
378338
  const durationSec = (Date.now() - startTime3) / 1000;
378264
378339
  recordScheduledJob("dead-letter-cleanup", "success", durationSec);
378265
- log128.info("Dead letter cleanup completed", { deleted });
378340
+ log129.info("Dead letter cleanup completed", { deleted });
378266
378341
  } catch (err2) {
378267
378342
  const durationSec = (Date.now() - startTime3) / 1000;
378268
378343
  recordScheduledJob("dead-letter-cleanup", "failure", durationSec);
@@ -378282,7 +378357,7 @@ function setupScheduler(services, channelRegistry2) {
378282
378357
  const jobsCreated = await createDailySyncJobs(services, "contacts");
378283
378358
  const durationSec = (Date.now() - startTime3) / 1000;
378284
378359
  recordScheduledJob("contacts-sync-daily", "success", durationSec);
378285
- log128.info("Daily contacts sync jobs created", { jobsCreated });
378360
+ log129.info("Daily contacts sync jobs created", { jobsCreated });
378286
378361
  } catch (err2) {
378287
378362
  const durationSec = (Date.now() - startTime3) / 1000;
378288
378363
  recordScheduledJob("contacts-sync-daily", "failure", durationSec);
@@ -378302,7 +378377,7 @@ function setupScheduler(services, channelRegistry2) {
378302
378377
  const jobsCreated = await createDailySyncJobs(services, "groups");
378303
378378
  const durationSec = (Date.now() - startTime3) / 1000;
378304
378379
  recordScheduledJob("groups-sync-daily", "success", durationSec);
378305
- log128.info("Daily groups sync jobs created", { jobsCreated });
378380
+ log129.info("Daily groups sync jobs created", { jobsCreated });
378306
378381
  } catch (err2) {
378307
378382
  const durationSec = (Date.now() - startTime3) / 1000;
378308
378383
  recordScheduledJob("groups-sync-daily", "failure", durationSec);
@@ -378323,7 +378398,7 @@ function setupScheduler(services, channelRegistry2) {
378323
378398
  const durationSec = (Date.now() - startTime3) / 1000;
378324
378399
  recordScheduledJob("follow-up-sweeper", "success", durationSec);
378325
378400
  if (stats.scanned > 0) {
378326
- log128.debug("Follow-up sweep tick", stats);
378401
+ log129.debug("Follow-up sweep tick", stats);
378327
378402
  }
378328
378403
  } catch (err2) {
378329
378404
  const durationSec = (Date.now() - startTime3) / 1000;
@@ -378346,7 +378421,7 @@ function setupScheduler(services, channelRegistry2) {
378346
378421
  const stats = await scheduledMessages2.sweep();
378347
378422
  recordScheduledJob("scheduled-message-sweeper", "success", (Date.now() - startTime3) / 1000);
378348
378423
  if (stats.scanned > 0) {
378349
- log128.debug("Scheduled-message sweep tick", { ...stats });
378424
+ log129.debug("Scheduled-message sweep tick", { ...stats });
378350
378425
  }
378351
378426
  } catch (err2) {
378352
378427
  recordScheduledJob("scheduled-message-sweeper", "failure", (Date.now() - startTime3) / 1000);
@@ -378369,7 +378444,7 @@ function setupScheduler(services, channelRegistry2) {
378369
378444
  const instanceCount = await refreshUnreadCounts(services, channelRegistry2);
378370
378445
  const durationSec = (Date.now() - startTime3) / 1000;
378371
378446
  recordScheduledJob("unread-count-refresh", "success", durationSec);
378372
- log128.debug("Refreshed unread counts", { instanceCount });
378447
+ log129.debug("Refreshed unread counts", { instanceCount });
378373
378448
  } catch (err2) {
378374
378449
  const durationSec = (Date.now() - startTime3) / 1000;
378375
378450
  recordScheduledJob("unread-count-refresh", "failure", durationSec);
@@ -378380,7 +378455,7 @@ function setupScheduler(services, channelRegistry2) {
378380
378455
  });
378381
378456
  updateSchedulerMetrics();
378382
378457
  scheduler.start();
378383
- log128.info("Scheduler started", {
378458
+ log129.info("Scheduler started", {
378384
378459
  jobs: scheduler.listJobs().map((j2) => ({
378385
378460
  name: j2.name,
378386
378461
  cron: j2.cron,
@@ -378400,16 +378475,16 @@ function updateSchedulerMetrics() {
378400
378475
  function stopScheduler() {
378401
378476
  const scheduler = getScheduler();
378402
378477
  scheduler.stop();
378403
- log128.info("Scheduler stopped");
378478
+ log129.info("Scheduler stopped");
378404
378479
  }
378405
- var log128;
378480
+ var log129;
378406
378481
  var init_scheduler2 = __esm(() => {
378407
378482
  init_src();
378408
378483
  init_esm5();
378409
378484
  init_sentry_scrub();
378410
378485
  init_scheduled_messages();
378411
378486
  init_periodic_tenant_work();
378412
- log128 = createLogger("scheduler:setup");
378487
+ log129 = createLogger("scheduler:setup");
378413
378488
  });
378414
378489
 
378415
378490
  // ../api/src/services/agent-heartbeat.ts
@@ -378421,11 +378496,11 @@ class AgentHeartbeatConsumer {
378421
378496
  return;
378422
378497
  const { natsConnection, turnService, db: db3 } = options;
378423
378498
  if (natsConnection.isClosed()) {
378424
- log129.warn("Cannot start agent-heartbeat: NATS connection is closed");
378499
+ log130.warn("Cannot start agent-heartbeat: NATS connection is closed");
378425
378500
  return;
378426
378501
  }
378427
378502
  this.subscription = natsConnection.subscribe(HEARTBEAT_SUBJECT);
378428
- log129.info("Agent heartbeat consumer started", { subject: HEARTBEAT_SUBJECT });
378503
+ log130.info("Agent heartbeat consumer started", { subject: HEARTBEAT_SUBJECT });
378429
378504
  const sub = this.subscription;
378430
378505
  this.loop = (async () => {
378431
378506
  for await (const msg of sub) {
@@ -378433,25 +378508,25 @@ class AgentHeartbeatConsumer {
378433
378508
  const raw2 = sc3.decode(msg.data);
378434
378509
  const parsed = parseHeartbeat(raw2);
378435
378510
  if (!parsed) {
378436
- log129.warn("Discarded malformed agent heartbeat", {
378511
+ log130.warn("Discarded malformed agent heartbeat", {
378437
378512
  subject: msg.subject,
378438
378513
  raw: raw2.slice(0, 200)
378439
378514
  });
378440
378515
  continue;
378441
378516
  }
378442
378517
  runDetachedFromTenantScope(async () => recordHeartbeatActivity(turnService, db3, parsed)).catch((error3) => {
378443
- log129.warn("recordActivity failed for heartbeat (turn likely closed)", {
378518
+ log130.warn("recordActivity failed for heartbeat (turn likely closed)", {
378444
378519
  turnId: parsed.turnId,
378445
378520
  error: error3 instanceof Error ? error3.message : String(error3)
378446
378521
  });
378447
378522
  });
378448
- log129.debug("Agent heartbeat applied", {
378523
+ log130.debug("Agent heartbeat applied", {
378449
378524
  turnId: parsed.turnId,
378450
378525
  instanceId: parsed.instanceId,
378451
378526
  chatId: parsed.chatId
378452
378527
  });
378453
378528
  } catch (error3) {
378454
- log129.warn("Failed to process agent heartbeat", {
378529
+ log130.warn("Failed to process agent heartbeat", {
378455
378530
  subject: msg.subject,
378456
378531
  error: error3 instanceof Error ? error3.message : String(error3)
378457
378532
  });
@@ -378468,7 +378543,7 @@ class AgentHeartbeatConsumer {
378468
378543
  await this.loop;
378469
378544
  } catch {} finally {
378470
378545
  this.loop = null;
378471
- log129.info("Agent heartbeat consumer stopped");
378546
+ log130.info("Agent heartbeat consumer stopped");
378472
378547
  }
378473
378548
  }
378474
378549
  }
@@ -378510,14 +378585,14 @@ async function closeAgentHeartbeat() {
378510
378585
  await consumer.stop();
378511
378586
  consumer = null;
378512
378587
  }
378513
- var import_nats6, log129, sc3, HEARTBEAT_SUBJECT = "omni.agent.heartbeat.>", consumer = null;
378588
+ var import_nats6, log130, sc3, HEARTBEAT_SUBJECT = "omni.agent.heartbeat.>", consumer = null;
378514
378589
  var init_agent_heartbeat = __esm(() => {
378515
378590
  init_src();
378516
378591
  init_instance_owner_registry();
378517
378592
  init_tenant_scope();
378518
378593
  init_worker_tenant_context();
378519
378594
  import_nats6 = __toESM(require_mod4(), 1);
378520
- log129 = createLogger("agent-heartbeat");
378595
+ log130 = createLogger("agent-heartbeat");
378521
378596
  sc3 = import_nats6.StringCodec();
378522
378597
  });
378523
378598
 
@@ -378532,7 +378607,7 @@ class TurnMonitor {
378532
378607
  start() {
378533
378608
  if (this.intervalId)
378534
378609
  return;
378535
- log130.info("Turn monitor started", {
378610
+ log131.info("Turn monitor started", {
378536
378611
  pollIntervalMs: POLL_INTERVAL_MS2,
378537
378612
  nudgeMs: NUDGE_THRESHOLD_MS,
378538
378613
  defaultStalledMs: DEFAULT_STALLED_THRESHOLD_MS,
@@ -378544,7 +378619,7 @@ class TurnMonitor {
378544
378619
  if (this.intervalId) {
378545
378620
  clearInterval(this.intervalId);
378546
378621
  this.intervalId = null;
378547
- log130.info("Turn monitor stopped");
378622
+ log131.info("Turn monitor stopped");
378548
378623
  }
378549
378624
  }
378550
378625
  async tick() {
@@ -378568,7 +378643,7 @@ class TurnMonitor {
378568
378643
  env: this.deps.env
378569
378644
  }, (turn, tenantId) => this.processTurn(turn, tenantId));
378570
378645
  } catch (error3) {
378571
- log130.error("Turn monitor tick failed", { error: String(error3) });
378646
+ log131.error("Turn monitor tick failed", { error: String(error3) });
378572
378647
  } finally {
378573
378648
  this.running = false;
378574
378649
  }
@@ -378599,13 +378674,13 @@ class TurnMonitor {
378599
378674
  idleSec,
378600
378675
  message: `Turn idle for ${idleSec}s. Are you still working?`
378601
378676
  });
378602
- log130.info("Turn nudge emitted", { turnId, nudgeCount, idleSec });
378677
+ log131.info("Turn nudge emitted", { turnId, nudgeCount, idleSec });
378603
378678
  }
378604
378679
  async handleStalled(turnId, instanceId, chatId, stalledAtMs, threshold, tenantId) {
378605
378680
  await runTenantWorkDb(this.deps.db, tenantId, () => this.deps.turnService.incrementNudge(turnId));
378606
378681
  const payload = { turnId, instanceId, chatId, stalledAtMs, threshold };
378607
378682
  publishTurnStalled(instanceId, chatId, payload);
378608
- log130.warn("Turn stalled \u2014 internal event emitted (no channel message sent)", payload);
378683
+ log131.warn("Turn stalled \u2014 internal event emitted (no channel message sent)", payload);
378609
378684
  }
378610
378685
  async handleTimeout(turnId, instanceId, chatId, idleSec, nudgeCount, tenantId) {
378611
378686
  const closed = await runTenantWorkDb(this.deps.db, tenantId, () => this.deps.turnService.close(turnId, {
@@ -378628,16 +378703,16 @@ class TurnMonitor {
378628
378703
  nudgeCount,
378629
378704
  messagesSent: closed.messagesSent
378630
378705
  });
378631
- log130.info("Turn force-closed (timeout)", { turnId, duration, nudgeCount });
378706
+ log131.info("Turn force-closed (timeout)", { turnId, duration, nudgeCount });
378632
378707
  }
378633
378708
  }
378634
- var log130, NUDGE_THRESHOLD_MS = 120000, DEFAULT_STALLED_THRESHOLD_MS = 600000, TIMEOUT_THRESHOLD_MS = 1800000, POLL_INTERVAL_MS2 = 1e4;
378709
+ var log131, NUDGE_THRESHOLD_MS = 120000, DEFAULT_STALLED_THRESHOLD_MS = 600000, TIMEOUT_THRESHOLD_MS = 1800000, POLL_INTERVAL_MS2 = 1e4;
378635
378710
  var init_turn_monitor = __esm(() => {
378636
378711
  init_src();
378637
378712
  init_periodic_tenant_work();
378638
378713
  init_worker_tenant_context();
378639
378714
  init_turn_events();
378640
- log130 = createLogger("turn-monitor");
378715
+ log131 = createLogger("turn-monitor");
378641
378716
  });
378642
378717
 
378643
378718
  // ../api/src/tenancy/enforcement-posture.ts
@@ -378717,7 +378792,7 @@ class TenantStreamRegistry {
378717
378792
  try {
378718
378793
  binding.close(reason2);
378719
378794
  } catch (err2) {
378720
- log131.warn("stream termination failed", {
378795
+ log132.warn("stream termination failed", {
378721
378796
  tenantId,
378722
378797
  reason: reason2,
378723
378798
  error: err2 instanceof Error ? err2.message : String(err2)
@@ -378739,7 +378814,7 @@ async function sweepRevokedStreamSubscriptions(authPlaneDb, registry5, env2 = pr
378739
378814
  try {
378740
378815
  state = await readState(authPlaneDb, tenantId);
378741
378816
  } catch (err2) {
378742
- log131.warn("stream revocation read failed; terminating fail-closed", {
378817
+ log132.warn("stream revocation read failed; terminating fail-closed", {
378743
378818
  tenantId,
378744
378819
  error: err2 instanceof Error ? err2.message : String(err2)
378745
378820
  });
@@ -378764,7 +378839,7 @@ function terminateSupersededEpochs(registry5, tenantId, currentEpoch) {
378764
378839
  try {
378765
378840
  binding.close("tenant_revoked");
378766
378841
  } catch (err2) {
378767
- log131.warn("stream termination failed", {
378842
+ log132.warn("stream termination failed", {
378768
378843
  tenantId,
378769
378844
  error: err2 instanceof Error ? err2.message : String(err2)
378770
378845
  });
@@ -378780,17 +378855,17 @@ function startStreamRevocationSweeper(authPlaneDb, registry5, env2 = process.env
378780
378855
  const timer2 = setInterval(() => {
378781
378856
  sweepRevokedStreamSubscriptions(authPlaneDb, registry5, env2).then((stats) => {
378782
378857
  if (stats.terminated > 0) {
378783
- log131.info("terminated revoked stream subscriptions", {
378858
+ log132.info("terminated revoked stream subscriptions", {
378784
378859
  terminated: stats.terminated,
378785
378860
  tenantsChecked: stats.tenantsChecked
378786
378861
  });
378787
378862
  }
378788
- }, (err2) => log131.warn("stream revocation sweep failed", { error: err2 instanceof Error ? err2.message : String(err2) }));
378863
+ }, (err2) => log132.warn("stream revocation sweep failed", { error: err2 instanceof Error ? err2.message : String(err2) }));
378789
378864
  }, resolveStreamSweepIntervalMs());
378790
378865
  timer2.unref?.();
378791
378866
  return { stop: () => clearInterval(timer2) };
378792
378867
  }
378793
- var log131, UUID7, STREAM_TERMINATION_CEILING_SECONDS = 30, readTenantRevocationState = async (authPlaneDb, tenantId) => {
378868
+ var log132, UUID7, STREAM_TERMINATION_CEILING_SECONDS = 30, readTenantRevocationState = async (authPlaneDb, tenantId) => {
378794
378869
  const [row] = await authPlaneDb.select({ status: tenants.status, revocationEpoch: tenants.revocationEpoch }).from(tenants).where(eq(tenants.id, tenantId)).limit(1);
378795
378870
  return row ?? null;
378796
378871
  };
@@ -378798,7 +378873,7 @@ var init_tenant_stream_subscriptions = __esm(() => {
378798
378873
  init_src();
378799
378874
  init_src5();
378800
378875
  init_drizzle_orm();
378801
- log131 = createLogger("tenant-stream-subscriptions");
378876
+ log132 = createLogger("tenant-stream-subscriptions");
378802
378877
  UUID7 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
378803
378878
  });
378804
378879
 
@@ -378873,12 +378948,12 @@ function printStartupBanner(options) {
378873
378948
  process.stdout.write(`${line3}
378874
378949
  `);
378875
378950
  }
378876
- log132.info("Server ready", { host, port, version: version3 });
378951
+ log133.info("Server ready", { host, port, version: version3 });
378877
378952
  }
378878
- var log132, COLORS2, BOX, ANSI_ESCAPE_PATTERN;
378953
+ var log133, COLORS2, BOX, ANSI_ESCAPE_PATTERN;
378879
378954
  var init_startup_banner = __esm(() => {
378880
378955
  init_src();
378881
- log132 = createLogger("api:startup");
378956
+ log133 = createLogger("api:startup");
378882
378957
  COLORS2 = {
378883
378958
  reset: "\x1B[0m",
378884
378959
  dim: "\x1B[2m",
@@ -378981,7 +379056,7 @@ class VoiceStreamRegistry {
378981
379056
  }
378982
379057
  }
378983
379058
  });
378984
- log133.info("Voice WS client connected", {
379059
+ log134.info("Voice WS client connected", {
378985
379060
  sessionId: client.params.sessionId,
378986
379061
  format: client.params.format,
378987
379062
  filterUser: client.params.filterUserId ?? "all"
@@ -379070,12 +379145,12 @@ async function authorizeVoiceApiKey(validate, apiKey) {
379070
379145
  return false;
379071
379146
  }
379072
379147
  }
379073
- var log133, opusCodec = null;
379148
+ var log134, opusCodec = null;
379074
379149
  var init_voice3 = __esm(() => {
379075
379150
  init_src();
379076
379151
  init_src3();
379077
379152
  init_tenant_stream_subscriptions();
379078
- log133 = createLogger("ws:voice");
379153
+ log134 = createLogger("ws:voice");
379079
379154
  });
379080
379155
 
379081
379156
  // ../api/src/ws/voice-upgrade-authorization.ts
@@ -379401,65 +379476,65 @@ function setupShutdownHandlers(server, earlyShutdown) {
379401
379476
  }
379402
379477
  async function setupEventBusServices(eventBus, services, db3) {
379403
379478
  if (!eventBus) {
379404
- log134.warn("Skipping event bus services (no event bus)");
379479
+ log135.warn("Skipping event bus services (no event bus)");
379405
379480
  return;
379406
379481
  }
379407
379482
  try {
379408
379483
  await setupMessagePersistence(eventBus, services);
379409
379484
  } catch (error3) {
379410
- log134.error("Failed to set up message persistence", { error: String(error3) });
379485
+ log135.error("Failed to set up message persistence", { error: String(error3) });
379411
379486
  }
379412
379487
  try {
379413
379488
  await setupMediaProcessor(eventBus, db3, services);
379414
379489
  } catch (error3) {
379415
- log134.error("Failed to set up media processor", { error: String(error3) });
379490
+ log135.error("Failed to set up media processor", { error: String(error3) });
379416
379491
  }
379417
379492
  try {
379418
379493
  globalDispatcherCleanup = await setupAgentResponder(eventBus, services, db3);
379419
379494
  } catch (error3) {
379420
- log134.error("Failed to set up agent dispatcher", { error: String(error3) });
379495
+ log135.error("Failed to set up agent dispatcher", { error: String(error3) });
379421
379496
  }
379422
379497
  try {
379423
379498
  await services.automations.startEngine(buildAutomationEngineDeps(services, db3));
379424
379499
  } catch (error3) {
379425
- log134.error("Failed to start automation engine", { error: String(error3) });
379500
+ log135.error("Failed to start automation engine", { error: String(error3) });
379426
379501
  }
379427
379502
  try {
379428
379503
  await setupSessionCleaner(eventBus, services, db3);
379429
379504
  } catch (error3) {
379430
- log134.error("Failed to set up session cleaner", { error: String(error3) });
379505
+ log135.error("Failed to set up session cleaner", { error: String(error3) });
379431
379506
  }
379432
379507
  try {
379433
379508
  await setupFollowUpHooks(eventBus, services, db3);
379434
379509
  } catch (error3) {
379435
- log134.error("Failed to set up follow-up hooks", { error: String(error3) });
379510
+ log135.error("Failed to set up follow-up hooks", { error: String(error3) });
379436
379511
  }
379437
379512
  if (globalChannelRegistry) {
379438
379513
  try {
379439
379514
  await setupSyncWorker(eventBus, services, globalChannelRegistry, db3);
379440
379515
  } catch (error3) {
379441
- log134.error("Failed to set up sync worker", { error: String(error3) });
379516
+ log135.error("Failed to set up sync worker", { error: String(error3) });
379442
379517
  }
379443
379518
  }
379444
379519
  try {
379445
379520
  await setupHistoryPushTracker(eventBus, services);
379446
379521
  } catch (error3) {
379447
- log134.error("Failed to set up history-push tracker", { error: String(error3) });
379522
+ log135.error("Failed to set up history-push tracker", { error: String(error3) });
379448
379523
  }
379449
379524
  try {
379450
379525
  await initTurnEvents(NATS_URL);
379451
379526
  } catch (error3) {
379452
- log134.error("Failed to initialize turn events", { error: String(error3) });
379527
+ log135.error("Failed to initialize turn events", { error: String(error3) });
379453
379528
  }
379454
379529
  try {
379455
379530
  const turnEventsConn = getTurnEventsConnection();
379456
379531
  if (turnEventsConn) {
379457
379532
  initAgentHeartbeat({ natsConnection: turnEventsConn, turnService: services.turns, db: services.db });
379458
379533
  } else {
379459
- log134.warn("Skipping agent heartbeat consumer: no NATS connection");
379534
+ log135.warn("Skipping agent heartbeat consumer: no NATS connection");
379460
379535
  }
379461
379536
  } catch (error3) {
379462
- log134.error("Failed to initialize agent heartbeat consumer", { error: String(error3) });
379537
+ log135.error("Failed to initialize agent heartbeat consumer", { error: String(error3) });
379463
379538
  }
379464
379539
  try {
379465
379540
  globalTurnMonitor = new TurnMonitor({
@@ -379469,23 +379544,23 @@ async function setupEventBusServices(eventBus, services, db3) {
379469
379544
  authPlaneDb: services.authPlane.db
379470
379545
  });
379471
379546
  globalTurnMonitor.start();
379472
- log134.info("Turn monitor started");
379547
+ log135.info("Turn monitor started");
379473
379548
  } catch (error3) {
379474
- log134.error("Failed to start turn monitor", { error: String(error3) });
379549
+ log135.error("Failed to start turn monitor", { error: String(error3) });
379475
379550
  }
379476
379551
  }
379477
379552
  async function waitForDatabaseReady(db3, maxAttempts = 30) {
379478
- log134.info("Waiting for database readiness");
379553
+ log135.info("Waiting for database readiness");
379479
379554
  for (let attempt = 1;attempt <= maxAttempts; attempt++) {
379480
379555
  try {
379481
379556
  await db3.execute(sql`SELECT 1`);
379482
- log134.info("Database ready", { attempt });
379557
+ log135.info("Database ready", { attempt });
379483
379558
  return;
379484
379559
  } catch {
379485
379560
  if (attempt === maxAttempts) {
379486
379561
  throw new Error(`Database not ready after ${maxAttempts} attempts`);
379487
379562
  }
379488
- log134.warn("Database not ready, retrying...", { attempt });
379563
+ log135.warn("Database not ready, retrying...", { attempt });
379489
379564
  await new Promise((resolve4) => setTimeout(resolve4, 1000));
379490
379565
  }
379491
379566
  }
@@ -379503,7 +379578,7 @@ async function runStartupMigrations(db3, enforced) {
379503
379578
  return;
379504
379579
  });
379505
379580
  if (enforced && scrubDdlCredential()) {
379506
- log134.info("DDL credential scrubbed from process environment");
379581
+ log135.info("DDL credential scrubbed from process environment");
379507
379582
  }
379508
379583
  }
379509
379584
  }
@@ -379512,39 +379587,39 @@ async function verifyEnforcedRuntimeIdentity(db3, mode) {
379512
379587
  return;
379513
379588
  try {
379514
379589
  const identity = await assertEnforcedRuntimeIdentity(db3);
379515
- log134.info("Enforced runtime identity verified", {
379590
+ log135.info("Enforced runtime identity verified", {
379516
379591
  currentUser: identity.currentUser,
379517
379592
  forcedTables: identity.enforcement.forced.length
379518
379593
  });
379519
379594
  } catch (error3) {
379520
- log134.error("Enforcement-mode startup refused", { error: String(error3) });
379595
+ log135.error("Enforcement-mode startup refused", { error: String(error3) });
379521
379596
  await closeDb();
379522
379597
  throw error3;
379523
379598
  }
379524
379599
  }
379525
379600
  async function main() {
379526
- log134.info("Starting Omni API v2");
379601
+ log135.info("Starting Omni API v2");
379527
379602
  enableDefaultMetrics();
379528
379603
  if (process.env.PGSERVE_EMBEDDED === "true") {
379529
- log134.warn("PGSERVE_EMBEDDED=true is set but embedded mode was removed in pgserve-singleton-no-proxy. Run `omni doctor --fix` (or `pgserve install` directly) to migrate to consumer-only pgserve. omni-api will continue using DATABASE_URL.");
379604
+ log135.warn("PGSERVE_EMBEDDED=true is set but embedded mode was removed in pgserve-singleton-no-proxy. Run `omni doctor --fix` (or `pgserve install` directly) to migrate to consumer-only pgserve. omni-api will continue using DATABASE_URL.");
379530
379605
  }
379531
379606
  const enforcementMode = resolveEnforcementMode();
379532
- warnOnMixedTenancyState(enforcementMode, (message2) => log134.warn(message2));
379607
+ warnOnMixedTenancyState(enforcementMode, (message2) => log135.warn(message2));
379533
379608
  const enforcedIdentities = enforcementMode === "enforced" ? resolveEnforcedBootIdentities() : null;
379534
379609
  const databaseUrl = enforcedIdentities ? enforcedIdentities.runtimeUrl : getDefaultDatabaseUrl();
379535
379610
  if (enforcementMode === "enforced") {
379536
- log134.info("Connecting to database", { enforcementMode });
379611
+ log135.info("Connecting to database", { enforcementMode });
379537
379612
  } else {
379538
- log134.info("Connecting to database");
379613
+ log135.info("Connecting to database");
379539
379614
  }
379540
379615
  const db3 = createDb({ url: databaseUrl });
379541
379616
  globalDbRef = db3;
379542
379617
  const earlyShutdown = async () => {
379543
- log134.info("Shutdown during startup \u2014 cleaning up");
379618
+ log135.info("Shutdown during startup \u2014 cleaning up");
379544
379619
  try {
379545
379620
  await closeDb();
379546
379621
  } catch (err2) {
379547
- log134.error("Cleanup failed during early shutdown", { error: String(err2) });
379622
+ log135.error("Cleanup failed during early shutdown", { error: String(err2) });
379548
379623
  } finally {
379549
379624
  process.exit(1);
379550
379625
  }
@@ -379557,7 +379632,7 @@ async function main() {
379557
379632
  await closeDb();
379558
379633
  throw error3;
379559
379634
  }
379560
- log134.info("Running database migrations");
379635
+ log135.info("Running database migrations");
379561
379636
  const migrationStart = Date.now();
379562
379637
  try {
379563
379638
  await runStartupMigrations(db3, enforcedIdentities);
@@ -379565,26 +379640,26 @@ async function main() {
379565
379640
  await closeDb();
379566
379641
  throw error3;
379567
379642
  }
379568
- log134.info("Database migrations complete", { durationMs: Date.now() - migrationStart });
379643
+ log135.info("Database migrations complete", { durationMs: Date.now() - migrationStart });
379569
379644
  await verifyEnforcedRuntimeIdentity(db3, enforcementMode);
379570
379645
  try {
379571
379646
  const driftReport = await verifyCriticalColumns(db3, API_CRITICAL_COLUMNS);
379572
379647
  if (!driftReport.ok) {
379573
- log134.error(formatDriftReport(driftReport), { drift: driftReport.drift });
379648
+ log135.error(formatDriftReport(driftReport), { drift: driftReport.drift });
379574
379649
  await closeDb();
379575
379650
  process.exit(1);
379576
379651
  }
379577
379652
  } catch (error3) {
379578
- log134.error("Schema drift check failed", { error: String(error3) });
379653
+ log135.error("Schema drift check failed", { error: String(error3) });
379579
379654
  await closeDb();
379580
379655
  process.exit(1);
379581
379656
  }
379582
379657
  try {
379583
379658
  const [countRow] = await db3.select({ count: sql`count(*)::int` }).from(instances);
379584
379659
  const rowCount = countRow?.count ?? 0;
379585
- log134.info("Post-migration content snapshot", { DB_ROW_COUNT_INSTANCES: rowCount });
379660
+ log135.info("Post-migration content snapshot", { DB_ROW_COUNT_INSTANCES: rowCount });
379586
379661
  } catch (error3) {
379587
- log134.warn("Failed to read instances row count (non-fatal)", { error: String(error3) });
379662
+ log135.warn("Failed to read instances row count (non-fatal)", { error: String(error3) });
379588
379663
  }
379589
379664
  const eventBus = await connectToNats(db3);
379590
379665
  if (eventBus) {
@@ -379605,9 +379680,9 @@ async function main() {
379605
379680
  try {
379606
379681
  await services.settings.seedDefaults();
379607
379682
  } catch (error3) {
379608
- log134.error("Failed to seed default settings", { error: String(error3) });
379683
+ log135.error("Failed to seed default settings", { error: String(error3) });
379609
379684
  }
379610
- log134.info("Initializing API key");
379685
+ log135.info("Initializing API key");
379611
379686
  let apiKeyInfo;
379612
379687
  try {
379613
379688
  const keyResult = await services.apiKeys.initializePrimaryKey();
@@ -379617,17 +379692,17 @@ async function main() {
379617
379692
  isFromEnv: keyResult.isFromEnv
379618
379693
  };
379619
379694
  if (keyResult.isNew) {
379620
- log134.info("Generated new primary API key");
379695
+ log135.info("Generated new primary API key");
379621
379696
  } else if (keyResult.isFromEnv) {
379622
- log134.info("Using primary API key from environment");
379697
+ log135.info("Using primary API key from environment");
379623
379698
  } else {
379624
- log134.info("Using existing primary API key");
379699
+ log135.info("Using existing primary API key");
379625
379700
  }
379626
379701
  } catch (error3) {
379627
- log134.error("Failed to initialize primary API key", { error: String(error3) });
379702
+ log135.error("Failed to initialize primary API key", { error: String(error3) });
379628
379703
  }
379629
379704
  await setupEventBusServices(eventBus, services, db3);
379630
- log134.info("Starting scheduler");
379705
+ log135.info("Starting scheduler");
379631
379706
  setupScheduler(services, globalChannelRegistry);
379632
379707
  const server = startBunServer(app);
379633
379708
  printStartupBanner({
@@ -379641,7 +379716,7 @@ async function main() {
379641
379716
  });
379642
379717
  setupShutdownHandlers(server, earlyShutdown);
379643
379718
  }
379644
- var import__package3, log134, natsLog, pluginLog, shutdownLog, PORT, HOST, NATS_URL, voiceStreamRegistry, globalEventBus = null, globalChannelRegistry = null, globalInstanceMonitor = null, globalDispatcherCleanup = null, globalTurnMonitor = null, globalDbRef = null, globalServicesRef = null, globalStreamSweeper = null;
379719
+ var import__package3, log135, natsLog, pluginLog, shutdownLog, PORT, HOST, NATS_URL, voiceStreamRegistry, globalEventBus = null, globalChannelRegistry = null, globalInstanceMonitor = null, globalDispatcherCleanup = null, globalTurnMonitor = null, globalDbRef = null, globalServicesRef = null, globalStreamSweeper = null;
379645
379720
  var init_src9 = __esm(() => {
379646
379721
  init_instrument2();
379647
379722
  init_tracing5();
@@ -379674,7 +379749,7 @@ var init_src9 = __esm(() => {
379674
379749
  level: process.env.LOG_LEVEL ?? "info",
379675
379750
  format: process.env.LOG_FORMAT ?? "auto"
379676
379751
  });
379677
- log134 = createLogger("api:startup");
379752
+ log135 = createLogger("api:startup");
379678
379753
  natsLog = createLogger("api:nats");
379679
379754
  pluginLog = createLogger("api:plugins");
379680
379755
  shutdownLog = createLogger("api:shutdown");
@@ -379683,7 +379758,7 @@ var init_src9 = __esm(() => {
379683
379758
  NATS_URL = process.env.NATS_URL ?? "nats://localhost:4222";
379684
379759
  voiceStreamRegistry = new VoiceStreamRegistry;
379685
379760
  main().catch((error3) => {
379686
- log134.error("Failed to start API server", { error: String(error3) });
379761
+ log135.error("Failed to start API server", { error: String(error3) });
379687
379762
  process.exit(1);
379688
379763
  });
379689
379764
  });
@@ -385399,16 +385474,33 @@ async function checkBoltHealth(connection) {
385399
385474
  }
385400
385475
 
385401
385476
  // ../channel-slack/src/handlers/agent-sessions.ts
385477
+ init_zod();
385478
+ var AgentSessionStoppedEventSchema = exports_external.object({
385479
+ channel: exports_external.string().min(1),
385480
+ thread_ts: exports_external.string().optional(),
385481
+ user: exports_external.string().optional(),
385482
+ event_ts: exports_external.string().optional(),
385483
+ streaming_message_ts: exports_external.array(exports_external.string()).optional()
385484
+ }).passthrough();
385402
385485
  function setupAgentSessionHandlers(app, instanceId, callbacks, logger5) {
385403
385486
  app.event("agent_session_stopped", async ({ event }) => {
385404
- const evt = event;
385405
- const channelId = evt.channel;
385406
- if (!channelId)
385487
+ const channelHint = event?.channel;
385488
+ if (typeof channelHint !== "string" || channelHint.length === 0)
385407
385489
  return;
385408
- const threadTs = evt.thread_ts;
385409
- const userId = evt.user;
385410
- const eventTs = evt.event_ts;
385411
- const streamingMessageTs = Array.isArray(evt.streaming_message_ts) ? evt.streaming_message_ts : [];
385490
+ const parsed = AgentSessionStoppedEventSchema.safeParse(event);
385491
+ if (!parsed.success) {
385492
+ logger5.warn("Ignoring malformed agent_session_stopped event", {
385493
+ instanceId,
385494
+ channelId: channelHint,
385495
+ issues: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
385496
+ });
385497
+ return;
385498
+ }
385499
+ const channelId = parsed.data.channel;
385500
+ const threadTs = parsed.data.thread_ts;
385501
+ const userId = parsed.data.user;
385502
+ const eventTs = parsed.data.event_ts;
385503
+ const streamingMessageTs = parsed.data.streaming_message_ts ?? [];
385412
385504
  logger5.info("Agent session stopped by user", { instanceId, channelId, threadTs, userId, streamingMessageTs });
385413
385505
  await callbacks.onSessionStopped(instanceId, { channelId, threadTs, userId, streamingMessageTs, eventTs });
385414
385506
  });
@@ -385739,6 +385831,7 @@ function buildRawPayload(meta, msg, extra) {
385739
385831
  ts: meta.ts,
385740
385832
  threadTs: meta.threadTs,
385741
385833
  threadId: !meta.isDm && meta.threadTs ? meta.threadTs : undefined,
385834
+ cancelThreadId: meta.threadTs,
385742
385835
  channelType: meta.channelType,
385743
385836
  teamId: meta.teamId,
385744
385837
  isDm: meta.isDm,
@@ -386354,6 +386447,7 @@ function createNativeStreamSender(options) {
386354
386447
  } = options;
386355
386448
  let streamer;
386356
386449
  let lastSentContent = "";
386450
+ let streamTs;
386357
386451
  let stopped = false;
386358
386452
  let fallback;
386359
386453
  function buildFallback() {
@@ -386384,7 +386478,7 @@ function createNativeStreamSender(options) {
386384
386478
  channel: channelId,
386385
386479
  ...threadTs ? { thread_ts: threadTs } : {}
386386
386480
  });
386387
- await streamer.append({ markdown_text: firstChunk });
386481
+ noteStreamTs(await streamer.append({ markdown_text: firstChunk }));
386388
386482
  lastSentContent = firstChunk;
386389
386483
  } catch (err2) {
386390
386484
  logger5.warn("native-stream: chat.startStream failed, falling back to replace mode", {
@@ -386394,12 +386488,27 @@ function createNativeStreamSender(options) {
386394
386488
  fallback = buildFallback();
386395
386489
  }
386396
386490
  }
386491
+ function noteStreamTs(response) {
386492
+ if (streamTs || typeof response !== "object" || response === null)
386493
+ return;
386494
+ const ts = response.ts;
386495
+ if (typeof ts === "string" && ts.length > 0)
386496
+ streamTs = ts;
386497
+ }
386498
+ function resolveStreamTs() {
386499
+ if (streamTs)
386500
+ return streamTs;
386501
+ const fromStreamer = streamer?.streamTs;
386502
+ if (typeof fromStreamer === "string" && fromStreamer.length > 0)
386503
+ streamTs = fromStreamer;
386504
+ return streamTs;
386505
+ }
386397
386506
  async function appendDelta(newContent) {
386398
386507
  const delta = newContent.slice(lastSentContent.length);
386399
386508
  if (!delta)
386400
386509
  return;
386401
386510
  try {
386402
- await streamer?.append({ markdown_text: delta });
386511
+ noteStreamTs(await streamer?.append({ markdown_text: delta }));
386403
386512
  lastSentContent = newContent;
386404
386513
  } catch (err2) {
386405
386514
  logger5.warn("native-stream: append failed", { error: String(err2) });
@@ -386459,6 +386568,16 @@ function createNativeStreamSender(options) {
386459
386568
  if (fallback)
386460
386569
  return fallback.cancel ? fallback.cancel() : fallback.abort();
386461
386570
  await stopStream();
386571
+ },
386572
+ markStoppedByPlatform(stoppedTs) {
386573
+ if (fallback || stopped || !streamer)
386574
+ return false;
386575
+ const ts = resolveStreamTs();
386576
+ if (!ts || !stoppedTs.includes(ts))
386577
+ return false;
386578
+ stopped = true;
386579
+ logger5.debug("native-stream: stream already halted by Slack, skipping chat.stopStream", { channelId, ts });
386580
+ return true;
386462
386581
  }
386463
386582
  };
386464
386583
  }
@@ -386620,6 +386739,7 @@ class SlackPlugin extends BaseChannelPlugin {
386620
386739
  activeThreads = new Map;
386621
386740
  presenceStatusTimers = new Map;
386622
386741
  pendingAckReactions = new Map;
386742
+ activeNativeStreams = new Map;
386623
386743
  async onInitialize(_context) {}
386624
386744
  async onDestroy() {
386625
386745
  for (const [instanceId, connection] of this.connections) {
@@ -386647,6 +386767,7 @@ class SlackPlugin extends BaseChannelPlugin {
386647
386767
  this.presenceStatusTimers.clear();
386648
386768
  this.activeThreads.clear();
386649
386769
  this.pendingAckReactions.clear();
386770
+ this.activeNativeStreams.clear();
386650
386771
  }
386651
386772
  async connect(instanceId, config2) {
386652
386773
  const existing = this.connections.get(instanceId);
@@ -386751,6 +386872,10 @@ class SlackPlugin extends BaseChannelPlugin {
386751
386872
  clearTimeout(timer);
386752
386873
  this.presenceStatusTimers.delete(key);
386753
386874
  }
386875
+ for (const key of this.activeNativeStreams.keys()) {
386876
+ if (key.startsWith(`${instanceId}:`))
386877
+ this.activeNativeStreams.delete(key);
386878
+ }
386754
386879
  for (const key of this.pendingAckReactions.keys()) {
386755
386880
  if (key.startsWith(`${instanceId}:`))
386756
386881
  this.pendingAckReactions.delete(key);
@@ -386812,29 +386937,40 @@ class SlackPlugin extends BaseChannelPlugin {
386812
386937
  const threadTs = this.resolveThreadTs(replyToMode, replyToMessageId, undefined);
386813
386938
  const streamMode = resolveStreamMode(slackConfig.streamMode);
386814
386939
  const throttleMs = resolveStreamThrottle(slackConfig.streamThrottleMs);
386815
- const base2 = streamMode === "native" ? createNativeStreamSender({
386816
- client: connection.actingClient,
386817
- channelId: chatId,
386818
- threadTs,
386819
- throttleMs,
386820
- username: slackConfig.defaultUsername,
386821
- iconUrl: slackConfig.defaultIconUrl,
386822
- iconEmoji: slackConfig.defaultIconEmoji,
386823
- formatMode: options?.formatMode ?? "convert",
386824
- logger: this.logger
386825
- }) : createSlackStreamSender({
386826
- client: connection.actingClient,
386827
- channelId: chatId,
386828
- threadTs,
386829
- streamMode,
386830
- throttleMs,
386831
- username: slackConfig.defaultUsername,
386832
- iconUrl: slackConfig.defaultIconUrl,
386833
- iconEmoji: slackConfig.defaultIconEmoji,
386834
- formatMode: options?.formatMode ?? "convert",
386835
- logger: this.logger
386836
- });
386940
+ const streamKey = `${instanceId}:${chatId}`;
386941
+ let native;
386942
+ let base2;
386943
+ if (streamMode === "native") {
386944
+ native = createNativeStreamSender({
386945
+ client: connection.actingClient,
386946
+ channelId: chatId,
386947
+ threadTs,
386948
+ throttleMs,
386949
+ username: slackConfig.defaultUsername,
386950
+ iconUrl: slackConfig.defaultIconUrl,
386951
+ iconEmoji: slackConfig.defaultIconEmoji,
386952
+ formatMode: options?.formatMode ?? "convert",
386953
+ logger: this.logger
386954
+ });
386955
+ base2 = native;
386956
+ this.trackNativeStream(streamKey, native);
386957
+ } else {
386958
+ base2 = createSlackStreamSender({
386959
+ client: connection.actingClient,
386960
+ channelId: chatId,
386961
+ threadTs,
386962
+ streamMode,
386963
+ throttleMs,
386964
+ username: slackConfig.defaultUsername,
386965
+ iconUrl: slackConfig.defaultIconUrl,
386966
+ iconEmoji: slackConfig.defaultIconEmoji,
386967
+ formatMode: options?.formatMode ?? "convert",
386968
+ logger: this.logger
386969
+ });
386970
+ }
386837
386971
  const cleanup = () => {
386972
+ if (native)
386973
+ this.untrackNativeStream(streamKey, native);
386838
386974
  this.removeAckReaction(instanceId, chatId, connection, replyToMessageId, threadTs);
386839
386975
  this.clearActiveTyping(instanceId, chatId, connection, threadTs).catch(() => {});
386840
386976
  };
@@ -386859,6 +386995,32 @@ class SlackPlugin extends BaseChannelPlugin {
386859
386995
  }
386860
386996
  };
386861
386997
  }
386998
+ trackNativeStream(streamKey, sender) {
386999
+ let set = this.activeNativeStreams.get(streamKey);
387000
+ if (!set) {
387001
+ set = new Set;
387002
+ this.activeNativeStreams.set(streamKey, set);
387003
+ }
387004
+ set.add(sender);
387005
+ }
387006
+ untrackNativeStream(streamKey, sender) {
387007
+ const set = this.activeNativeStreams.get(streamKey);
387008
+ if (!set)
387009
+ return;
387010
+ set.delete(sender);
387011
+ if (set.size === 0)
387012
+ this.activeNativeStreams.delete(streamKey);
387013
+ }
387014
+ markStreamsStoppedByPlatform(instanceId, channelId, stoppedTs) {
387015
+ if (stoppedTs.length === 0)
387016
+ return;
387017
+ const senders2 = this.activeNativeStreams.get(`${instanceId}:${channelId}`);
387018
+ if (!senders2)
387019
+ return;
387020
+ for (const sender of senders2) {
387021
+ sender.markStoppedByPlatform(stoppedTs);
387022
+ }
387023
+ }
386862
387024
  async sendTyping(instanceId, chatId, duration) {
386863
387025
  await this.sendPresenceStatus(instanceId, chatId, duration === 0 ? "paused" : "typing", duration);
386864
387026
  }
@@ -387260,6 +387422,7 @@ class SlackPlugin extends BaseChannelPlugin {
387260
387422
  async handleAgentSessionStopped(instanceId, connection, args) {
387261
387423
  const parsedEventTs = args.eventTs ? Number.parseFloat(args.eventTs) : Number.NaN;
387262
387424
  const requestedAt = Number.isFinite(parsedEventTs) ? Math.round(parsedEventTs * 1000) : Date.now();
387425
+ this.markStreamsStoppedByPlatform(instanceId, args.channelId, args.streamingMessageTs ?? []);
387263
387426
  try {
387264
387427
  await this.eventBus.publish("agent.run.cancel_requested", {
387265
387428
  instanceId,