@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.
- package/dist/commands/send.d.ts +5 -0
- package/dist/commands/send.d.ts.map +1 -1
- package/dist/commands/webhooks.d.ts +37 -0
- package/dist/commands/webhooks.d.ts.map +1 -1
- package/dist/index.js +102 -16
- package/dist/sdk/client.d.ts +81 -12
- package/dist/sdk/client.d.ts.map +1 -1
- package/dist/sdk/index.d.ts +1 -1
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/types.generated.d.ts +258 -4
- package/dist/sdk/types.generated.d.ts.map +1 -1
- package/dist/server/index.js +527 -364
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -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.
|
|
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,
|
|
345763
|
-
|
|
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.
|
|
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,
|
|
346010
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
|
355844
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
356067
|
+
var log113;
|
|
356017
356068
|
var init_webhook_auth = __esm(() => {
|
|
356018
356069
|
init_src();
|
|
356019
356070
|
init_factory2();
|
|
356020
|
-
|
|
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(
|
|
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({
|
|
361777
|
-
|
|
361778
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
363903
|
+
log114.info("Agent resumed after session clear (was paused by handoff)", { instanceId, chatId });
|
|
363841
363904
|
}
|
|
363842
363905
|
}
|
|
363843
363906
|
} catch (disarmError) {
|
|
363844
|
-
|
|
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
|
-
|
|
363915
|
+
log114.info("Sent session cleared confirmation", { instanceId, chatId });
|
|
363853
363916
|
} catch (sendError) {
|
|
363854
|
-
|
|
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
|
-
|
|
363928
|
+
log114.debug("Session clearing skipped", { instanceId, reason: errorMessage });
|
|
363866
363929
|
return;
|
|
363867
363930
|
}
|
|
363868
|
-
|
|
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
|
-
|
|
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
|
-
|
|
363949
|
+
log114.info("Session cleaner initialized");
|
|
363887
363950
|
} catch (error3) {
|
|
363888
|
-
|
|
363951
|
+
log114.error("Failed to set up session cleaner", { error: String(error3) });
|
|
363889
363952
|
throw error3;
|
|
363890
363953
|
}
|
|
363891
363954
|
}
|
|
363892
|
-
var
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
366415
|
+
log115.info("QR Code generated for WhatsApp", { instanceId });
|
|
366342
366416
|
qrTerminal.generate(qrCode, { small: true });
|
|
366343
366417
|
} catch (error3) {
|
|
366344
|
-
|
|
366418
|
+
log115.info("QR Code generated", { instanceId, qrCode: `${qrCode.substring(0, 50)}...` });
|
|
366345
366419
|
if (error3 instanceof Error && process.env.DEBUG) {
|
|
366346
|
-
|
|
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
|
-
|
|
366432
|
+
log115.warn("Failed to set up QR code listener", { error: String(error3) });
|
|
366359
366433
|
}
|
|
366360
366434
|
}
|
|
366361
|
-
var
|
|
366435
|
+
var log115, qrCodes;
|
|
366362
366436
|
var init_qr_store = __esm(() => {
|
|
366363
366437
|
init_src();
|
|
366364
|
-
|
|
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
|
-
|
|
366462
|
+
log116.warn("Instance not found for replay", { instanceId });
|
|
366389
366463
|
return;
|
|
366390
366464
|
}
|
|
366391
366465
|
if (!instance4.agentId) {
|
|
366392
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
366835
|
+
log117.error("Failed to connect instance", { instanceId, error: errorMessage });
|
|
366762
366836
|
return;
|
|
366763
366837
|
}
|
|
366764
|
-
|
|
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
|
-
|
|
366876
|
+
log117.info("Removed instance from agent key", { keyName, instanceId });
|
|
366803
366877
|
}
|
|
366804
366878
|
} catch (err2) {
|
|
366805
|
-
|
|
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
|
-
|
|
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
|
-
|
|
366904
|
+
log117.info("Created scoped agent key", { keyName, instanceId, keyId: result.key.id });
|
|
366831
366905
|
}
|
|
366832
366906
|
} catch (err2) {
|
|
366833
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
370773
|
+
log119.debug("Resolved internal UUID to external ID", { messageId, externalId: message2.externalId });
|
|
370699
370774
|
return message2.externalId;
|
|
370700
370775
|
}
|
|
370701
|
-
var
|
|
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
|
-
|
|
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) =>
|
|
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) =>
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
375529
|
-
|
|
375530
|
-
|
|
375531
|
-
|
|
375532
|
-
|
|
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
|
-
|
|
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
|
-
|
|
376001
|
+
log123.debug("Persisted message.received", {
|
|
375928
376002
|
externalId: payload.externalId,
|
|
375929
376003
|
instanceId: metadata.instanceId
|
|
375930
376004
|
});
|
|
375931
376005
|
} catch (error3) {
|
|
375932
|
-
|
|
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
|
-
|
|
376050
|
+
log123.debug("Persisted message.sent", {
|
|
375977
376051
|
externalId: payload.externalId,
|
|
375978
376052
|
instanceId: metadata.instanceId
|
|
375979
376053
|
});
|
|
375980
376054
|
} catch (error3) {
|
|
375981
|
-
|
|
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
|
-
|
|
376091
|
+
log123.debug("Persisted message.delivered", {
|
|
376018
376092
|
externalId: payload.externalId
|
|
376019
376093
|
});
|
|
376020
376094
|
} catch (error3) {
|
|
376021
|
-
|
|
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
|
-
|
|
376130
|
+
log123.debug("Persisted message.read", {
|
|
376057
376131
|
externalId: payload.externalId
|
|
376058
376132
|
});
|
|
376059
376133
|
} catch (error3) {
|
|
376060
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
376198
|
+
log123.info("Event persistence initialized - listening for message events");
|
|
376125
376199
|
} catch (error3) {
|
|
376126
|
-
|
|
376200
|
+
log123.error("Failed to set up event persistence", { error: String(error3) });
|
|
376127
376201
|
throw error3;
|
|
376128
376202
|
}
|
|
376129
376203
|
}
|
|
376130
|
-
var
|
|
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
|
-
|
|
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
|
-
|
|
376295
|
+
log124.debug("Downloaded media from URL", { messageId, filePath: result.localPath });
|
|
376222
376296
|
return result.localPath;
|
|
376223
376297
|
} catch (error3) {
|
|
376224
|
-
|
|
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
|
-
|
|
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
|
-
|
|
376312
|
+
log124.debug("Downloaded inbound media via plugin", { messageId, channelType, filePath: result.localPath });
|
|
376239
376313
|
return result.localPath;
|
|
376240
376314
|
} catch (error3) {
|
|
376241
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
376652
|
+
log124.info("Media processor initialized");
|
|
376579
376653
|
}
|
|
376580
|
-
var
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
376710
|
+
var log125, INFLIGHT_REVOCATION_CEILING_SECONDS = 30, InflightRevocationError;
|
|
376637
376711
|
var init_inflight_revocation = __esm(() => {
|
|
376638
376712
|
init_src();
|
|
376639
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
376828
|
+
log126.info("Sync worker initialized - listening for sync.started events");
|
|
376755
376829
|
} catch (error3) {
|
|
376756
|
-
|
|
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
|
-
|
|
376868
|
+
log126.info("Built WhatsApp anchors", { instanceId, anchorCount: anchors.length });
|
|
376795
376869
|
return anchors;
|
|
376796
376870
|
}
|
|
376797
376871
|
function buildAnchorsForExplicitChatJids(jobId, chatJids, dbAnchors) {
|
|
376798
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
377071
|
+
log126.info("Queued media backfill batch after message sync", { jobId, batchJobId: batch.id, stored });
|
|
376998
377072
|
} catch (error3) {
|
|
376999
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
377625
|
+
log127.info("Follow-up hooks active (message.sent/received, chat.handoff_activated, chat.archived, chat.closed)");
|
|
377552
377626
|
} catch (error3) {
|
|
377553
|
-
|
|
377627
|
+
log127.error("Failed to set up follow-up hooks", { error: String(error3) });
|
|
377554
377628
|
}
|
|
377555
377629
|
}
|
|
377556
|
-
var
|
|
377630
|
+
var log127;
|
|
377557
377631
|
var init_follow_up_hooks = __esm(() => {
|
|
377558
377632
|
init_src();
|
|
377559
377633
|
init_worker_tenant_context();
|
|
377560
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
378478
|
+
log129.info("Scheduler stopped");
|
|
378404
378479
|
}
|
|
378405
|
-
var
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
378706
|
+
log131.info("Turn force-closed (timeout)", { turnId, duration, nudgeCount });
|
|
378632
378707
|
}
|
|
378633
378708
|
}
|
|
378634
|
-
var
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
378858
|
+
log132.info("terminated revoked stream subscriptions", {
|
|
378784
378859
|
terminated: stats.terminated,
|
|
378785
378860
|
tenantsChecked: stats.tenantsChecked
|
|
378786
378861
|
});
|
|
378787
378862
|
}
|
|
378788
|
-
}, (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
|
|
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
|
-
|
|
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
|
-
|
|
378951
|
+
log133.info("Server ready", { host, port, version: version3 });
|
|
378877
378952
|
}
|
|
378878
|
-
var
|
|
378953
|
+
var log133, COLORS2, BOX, ANSI_ESCAPE_PATTERN;
|
|
378879
378954
|
var init_startup_banner = __esm(() => {
|
|
378880
378955
|
init_src();
|
|
378881
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
379534
|
+
log135.warn("Skipping agent heartbeat consumer: no NATS connection");
|
|
379460
379535
|
}
|
|
379461
379536
|
} catch (error3) {
|
|
379462
|
-
|
|
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
|
-
|
|
379547
|
+
log135.info("Turn monitor started");
|
|
379473
379548
|
} catch (error3) {
|
|
379474
|
-
|
|
379549
|
+
log135.error("Failed to start turn monitor", { error: String(error3) });
|
|
379475
379550
|
}
|
|
379476
379551
|
}
|
|
379477
379552
|
async function waitForDatabaseReady(db3, maxAttempts = 30) {
|
|
379478
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
379601
|
+
log135.info("Starting Omni API v2");
|
|
379527
379602
|
enableDefaultMetrics();
|
|
379528
379603
|
if (process.env.PGSERVE_EMBEDDED === "true") {
|
|
379529
|
-
|
|
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) =>
|
|
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
|
-
|
|
379611
|
+
log135.info("Connecting to database", { enforcementMode });
|
|
379537
379612
|
} else {
|
|
379538
|
-
|
|
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
|
-
|
|
379618
|
+
log135.info("Shutdown during startup \u2014 cleaning up");
|
|
379544
379619
|
try {
|
|
379545
379620
|
await closeDb();
|
|
379546
379621
|
} catch (err2) {
|
|
379547
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
379648
|
+
log135.error(formatDriftReport(driftReport), { drift: driftReport.drift });
|
|
379574
379649
|
await closeDb();
|
|
379575
379650
|
process.exit(1);
|
|
379576
379651
|
}
|
|
379577
379652
|
} catch (error3) {
|
|
379578
|
-
|
|
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
|
-
|
|
379660
|
+
log135.info("Post-migration content snapshot", { DB_ROW_COUNT_INSTANCES: rowCount });
|
|
379586
379661
|
} catch (error3) {
|
|
379587
|
-
|
|
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
|
-
|
|
379683
|
+
log135.error("Failed to seed default settings", { error: String(error3) });
|
|
379609
379684
|
}
|
|
379610
|
-
|
|
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
|
-
|
|
379695
|
+
log135.info("Generated new primary API key");
|
|
379621
379696
|
} else if (keyResult.isFromEnv) {
|
|
379622
|
-
|
|
379697
|
+
log135.info("Using primary API key from environment");
|
|
379623
379698
|
} else {
|
|
379624
|
-
|
|
379699
|
+
log135.info("Using existing primary API key");
|
|
379625
379700
|
}
|
|
379626
379701
|
} catch (error3) {
|
|
379627
|
-
|
|
379702
|
+
log135.error("Failed to initialize primary API key", { error: String(error3) });
|
|
379628
379703
|
}
|
|
379629
379704
|
await setupEventBusServices(eventBus, services, db3);
|
|
379630
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
385405
|
-
|
|
385406
|
-
if (!channelId)
|
|
385487
|
+
const channelHint = event?.channel;
|
|
385488
|
+
if (typeof channelHint !== "string" || channelHint.length === 0)
|
|
385407
385489
|
return;
|
|
385408
|
-
const
|
|
385409
|
-
|
|
385410
|
-
|
|
385411
|
-
|
|
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
|
|
386816
|
-
|
|
386817
|
-
|
|
386818
|
-
|
|
386819
|
-
|
|
386820
|
-
|
|
386821
|
-
|
|
386822
|
-
|
|
386823
|
-
|
|
386824
|
-
|
|
386825
|
-
|
|
386826
|
-
|
|
386827
|
-
|
|
386828
|
-
|
|
386829
|
-
|
|
386830
|
-
|
|
386831
|
-
|
|
386832
|
-
|
|
386833
|
-
|
|
386834
|
-
|
|
386835
|
-
|
|
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,
|