diff --git a/apps/api/package.json b/apps/api/package.json index b57c35defda..e884c60d309 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -44,6 +44,10 @@ }, "dependencies": { "@aws-sdk/client-secrets-manager": "^3.971.0", + "@chat-adapter/slack": "^4.25.0", + "@chat-adapter/state-redis": "^4.25.0", + "@chat-adapter/teams": "^4.25.0", + "@chat-adapter/whatsapp": "^4.25.0", "@godaddy/terminus": "^4.12.1", "@google-cloud/storage": "^6.2.3", "@nestjs/axios": "3.0.3", @@ -82,6 +86,7 @@ "bcrypt": "^5.0.0", "body-parser": "^2.2.1", "bull": "^4.2.1", + "chat": "^4.25.0", "class-transformer": "0.5.1", "class-validator": "0.14.1", "clickhouse-migrations": "^1.2.0", diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts index 49166047871..e6d7d9ed989 100644 --- a/apps/api/src/app.module.ts +++ b/apps/api/src/app.module.ts @@ -83,6 +83,10 @@ const enterpriseImports = (): Array; +} + +export class AgentReplyPayloadDto { + @ApiProperty() + @IsString() + @IsNotEmpty() + conversationId: string; + + @ApiProperty() + @IsString() + @IsNotEmpty() + integrationIdentifier: string; + + @ApiPropertyOptional({ type: TextContentDto }) + @IsOptional() + @IsObject() + @ValidateNested() + @Type(() => TextContentDto) + reply?: TextContentDto; + + @ApiPropertyOptional({ type: TextContentDto }) + @IsOptional() + @IsObject() + @ValidateNested() + @Type(() => TextContentDto) + update?: TextContentDto; + + @ApiPropertyOptional({ type: ResolveDto }) + @IsOptional() + @IsObject() + @ValidateNested() + @Type(() => ResolveDto) + resolve?: ResolveDto; + + @ApiPropertyOptional({ type: [SignalDto] }) + @IsOptional() + @IsArray() + @ValidateNested({ each: true }) + @Type(() => SignalDto) + signals?: SignalDto[]; +} diff --git a/apps/api/src/app/agents/e2e/agents.e2e.ts b/apps/api/src/app/agents/e2e/agents.e2e.ts index d91dcfd0877..80cedae52d2 100644 --- a/apps/api/src/app/agents/e2e/agents.e2e.ts +++ b/apps/api/src/app/agents/e2e/agents.e2e.ts @@ -8,6 +8,10 @@ describe('Agents API - /agents #novu-v2', () => { const agentRepository = new AgentRepository(); const agentIntegrationRepository = new AgentIntegrationRepository(); + before(() => { + process.env.IS_CONVERSATIONAL_AGENTS_ENABLED = 'true'; + }); + beforeEach(async () => { session = new UserSession(); await session.initialize(); diff --git a/apps/api/src/app/agents/guards/agent-conversation-enabled.guard.ts b/apps/api/src/app/agents/guards/agent-conversation-enabled.guard.ts new file mode 100644 index 00000000000..3ae19d56e95 --- /dev/null +++ b/apps/api/src/app/agents/guards/agent-conversation-enabled.guard.ts @@ -0,0 +1,30 @@ +import { CanActivate, ExecutionContext, Injectable, NotFoundException } from '@nestjs/common'; +import { FeatureFlagsService } from '@novu/application-generic'; +import { FeatureFlagsKeysEnum, UserSessionData } from '@novu/shared'; + +@Injectable() +export class AgentConversationEnabledGuard implements CanActivate { + constructor(private readonly featureFlagsService: FeatureFlagsService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const user: UserSessionData | undefined = request.user; + + if (!user?.organizationId || !user?.environmentId) { + return true; + } + + const isEnabled = await this.featureFlagsService.getFlag({ + key: FeatureFlagsKeysEnum.IS_CONVERSATIONAL_AGENTS_ENABLED, + defaultValue: false, + organization: { _id: user.organizationId }, + environment: { _id: user.environmentId }, + }); + + if (!isEnabled) { + throw new NotFoundException(); + } + + return true; + } +} diff --git a/apps/api/src/app/agents/services/agent-conversation.service.ts b/apps/api/src/app/agents/services/agent-conversation.service.ts new file mode 100644 index 00000000000..47ff8ae004b --- /dev/null +++ b/apps/api/src/app/agents/services/agent-conversation.service.ts @@ -0,0 +1,166 @@ +import { Injectable } from '@nestjs/common'; +import { PinoLogger, shortId } from '@novu/application-generic'; +import { + ConversationActivityEntity, + ConversationActivityRepository, + ConversationActivitySenderTypeEnum, + ConversationEntity, + ConversationParticipantTypeEnum, + ConversationRepository, + ConversationStatusEnum, +} from '@novu/dal'; + +export interface CreateOrGetConversationParams { + environmentId: string; + organizationId: string; + agentId: string; + platform: string; + integrationId: string; + platformThreadId: string; + participantId: string; + participantType: ConversationParticipantTypeEnum; + platformUserId: string; + firstMessageText: string; +} + +export interface PersistInboundMessageParams { + conversationId: string; + platform: string; + integrationId: string; + platformThreadId: string; + senderType: ConversationActivitySenderTypeEnum; + senderId: string; + senderName?: string; + content: string; + platformMessageId?: string; + environmentId: string; + organizationId: string; +} + +@Injectable() +export class AgentConversationService { + constructor( + private readonly conversationRepository: ConversationRepository, + private readonly activityRepository: ConversationActivityRepository, + private readonly logger: PinoLogger + ) {} + + async createOrGetConversation(params: CreateOrGetConversationParams): Promise { + const { environmentId, organizationId, platformThreadId } = params; + + const existing = await this.conversationRepository.findByPlatformThread( + environmentId, + organizationId, + platformThreadId + ); + + if (existing) { + if (existing.status === ConversationStatusEnum.RESOLVED) { + await this.conversationRepository.updateStatus(environmentId, organizationId, existing._id, ConversationStatusEnum.ACTIVE); + existing.status = ConversationStatusEnum.ACTIVE; + + this.logger.debug(`Reopened resolved conversation ${existing._id} for thread ${platformThreadId}`); + } + + await this.ensureParticipant(existing, params); + + return existing; + } + + const conversation = await this.conversationRepository.create({ + identifier: `conv-${shortId(8)}`, + _agentId: params.agentId, + participants: [ + { type: params.participantType, id: params.participantId }, + { type: ConversationParticipantTypeEnum.AGENT, id: params.agentId }, + ], + channels: [ + { + platform: params.platform, + _integrationId: params.integrationId, + platformThreadId, + }, + ], + status: ConversationStatusEnum.ACTIVE, + title: params.firstMessageText.slice(0, 200), + metadata: {}, + _environmentId: environmentId, + _organizationId: organizationId, + lastActivityAt: new Date().toISOString(), + }); + + this.logger.debug(`Created conversation ${conversation._id} for thread ${platformThreadId}`); + + return conversation; + } + + private async ensureParticipant(conversation: ConversationEntity, params: CreateOrGetConversationParams) { + const alreadyPresent = conversation.participants.some( + (p) => p.id === params.participantId && p.type === params.participantType + ); + if (alreadyPresent) return; + + const platformIdentity = `${params.platform}:${params.platformUserId}`; + + if (params.participantType === ConversationParticipantTypeEnum.SUBSCRIBER) { + const platformUserIdx = conversation.participants.findIndex( + (p) => p.type === ConversationParticipantTypeEnum.PLATFORM_USER && p.id === platformIdentity + ); + + if (platformUserIdx !== -1) { + conversation.participants[platformUserIdx] = { type: params.participantType, id: params.participantId }; + + this.logger.debug( + `Upgraded participant ${platformIdentity} → subscriber ${params.participantId} in conversation ${conversation._id}` + ); + } else { + conversation.participants.push({ type: params.participantType, id: params.participantId }); + } + } else { + conversation.participants.push({ type: params.participantType, id: params.participantId }); + } + + await this.conversationRepository.updateParticipants( + params.environmentId, + params.organizationId, + conversation._id, + conversation.participants + ); + } + + async persistInboundMessage(params: PersistInboundMessageParams): Promise { + const [activity] = await Promise.all([ + this.activityRepository.createUserActivity({ + identifier: `act-${shortId(8)}`, + conversationId: params.conversationId, + platform: params.platform, + integrationId: params.integrationId, + platformThreadId: params.platformThreadId, + senderType: params.senderType, + senderId: params.senderId, + senderName: params.senderName, + content: params.content, + platformMessageId: params.platformMessageId, + environmentId: params.environmentId, + organizationId: params.organizationId, + }), + this.conversationRepository.touchActivity(params.environmentId, params.organizationId, params.conversationId, params.content), + ]); + + return activity; + } + + async getHistory(environmentId: string, conversationId: string, limit = 20): Promise { + return this.activityRepository.findByConversation(environmentId, conversationId, limit); + } + + async updateChannelThread( + environmentId: string, + organizationId: string, + conversationId: string, + platformThreadId: string, + serializedThread: Record + ): Promise { + await this.conversationRepository.updateChannelThread(environmentId, organizationId, conversationId, platformThreadId, serializedThread); + } +} diff --git a/apps/api/src/app/agents/services/agent-credential.service.ts b/apps/api/src/app/agents/services/agent-credential.service.ts new file mode 100644 index 00000000000..feb2c3ad7f3 --- /dev/null +++ b/apps/api/src/app/agents/services/agent-credential.service.ts @@ -0,0 +1,105 @@ +import { Injectable, NotFoundException, UnprocessableEntityException } from '@nestjs/common'; +import { decryptCredentials, FeatureFlagsService } from '@novu/application-generic'; +import { + AgentIntegrationRepository, + AgentRepository, + ChannelConnectionRepository, + ICredentialsEntity, + IntegrationRepository, +} from '@novu/dal'; +import { FeatureFlagsKeysEnum } from '@novu/shared'; +import { AgentPlatformEnum } from '../dtos/agent-platform.enum'; +import { resolveAgentPlatform } from '../utils/provider-to-platform'; + +export interface ResolvedPlatformConfig { + platform: AgentPlatformEnum; + credentials: ICredentialsEntity; + connectionAccessToken?: string; + environmentId: string; + organizationId: string; + agentIdentifier: string; + integrationIdentifier: string; + integrationId: string; +} + +@Injectable() +export class AgentCredentialService { + constructor( + private readonly featureFlagsService: FeatureFlagsService, + private readonly agentRepository: AgentRepository, + private readonly agentIntegrationRepository: AgentIntegrationRepository, + private readonly integrationRepository: IntegrationRepository, + private readonly channelConnectionRepository: ChannelConnectionRepository + ) {} + + async resolve(agentId: string, integrationIdentifier: string): Promise { + const agent = await this.agentRepository.findByIdForWebhook(agentId); + if (!agent) { + throw new NotFoundException(`Agent ${agentId} not found`); + } + + const { _environmentId: environmentId, _organizationId: organizationId } = agent; + + const isEnabled = await this.featureFlagsService.getFlag({ + key: FeatureFlagsKeysEnum.IS_CONVERSATIONAL_AGENTS_ENABLED, + defaultValue: false, + environment: { _id: environmentId }, + organization: { _id: organizationId }, + }); + if (!isEnabled) { + throw new NotFoundException(); + } + + const integration = await this.integrationRepository.findOne({ + _environmentId: environmentId, + _organizationId: organizationId, + identifier: integrationIdentifier, + }); + if (!integration) { + throw new NotFoundException(`Integration ${integrationIdentifier} not found for agent ${agentId}`); + } + + const agentIntegration = await this.agentIntegrationRepository.findOne( + { + _environmentId: environmentId, + _organizationId: organizationId, + _agentId: agentId, + _integrationId: integration._id, + }, + '*' + ); + if (!agentIntegration) { + throw new UnprocessableEntityException(`Agent ${agentId} is not linked to integration ${integrationIdentifier}`); + } + + const platform = resolveAgentPlatform(integration.providerId); + if (!platform) { + throw new UnprocessableEntityException( + `Provider ${integration.providerId} is not supported as an agent platform` + ); + } + + const credentials = decryptCredentials(integration.credentials); + + let connectionAccessToken: string | undefined; + const connection = await this.channelConnectionRepository.findOne({ + _environmentId: environmentId, + _organizationId: organizationId, + integrationIdentifier, + }); + if (connection) { + connectionAccessToken = connection.auth.accessToken; + } + + return { + platform, + credentials, + connectionAccessToken, + environmentId, + organizationId, + agentIdentifier: agent.identifier, + integrationIdentifier, + integrationId: integration._id, + }; + } +} diff --git a/apps/api/src/app/agents/services/agent-inbound-handler.service.ts b/apps/api/src/app/agents/services/agent-inbound-handler.service.ts new file mode 100644 index 00000000000..5bbbb5a1a9f --- /dev/null +++ b/apps/api/src/app/agents/services/agent-inbound-handler.service.ts @@ -0,0 +1,110 @@ +import { Injectable } from '@nestjs/common'; +import { PinoLogger } from '@novu/application-generic'; +import { ConversationActivitySenderTypeEnum, ConversationParticipantTypeEnum, SubscriberRepository } from '@novu/dal'; +import type { Message, Thread } from 'chat'; +import { AgentEventEnum } from '../dtos/agent-event.enum'; +import { ResolvedPlatformConfig } from './agent-credential.service'; +import { AgentConversationService } from './agent-conversation.service'; +import { AgentSubscriberResolver } from './agent-subscriber-resolver.service'; +import { BridgeExecutorService } from './bridge-executor.service'; + +@Injectable() +export class AgentInboundHandler { + constructor( + private readonly logger: PinoLogger, + private readonly subscriberResolver: AgentSubscriberResolver, + private readonly conversationService: AgentConversationService, + private readonly bridgeExecutor: BridgeExecutorService, + private readonly subscriberRepository: SubscriberRepository + ) {} + + async handle( + agentId: string, + config: ResolvedPlatformConfig, + thread: Thread, + message: Message, + event: AgentEventEnum + ): Promise { + const subscriberId = await this.subscriberResolver + .resolve({ + environmentId: config.environmentId, + organizationId: config.organizationId, + platform: config.platform, + platformUserId: message.author.userId, + integrationIdentifier: config.integrationIdentifier, + }) + .catch((err) => { + this.logger.warn(err, `[agent:${agentId}] Subscriber resolution failed, continuing without subscriber`); + + return null; + }); + + const participantId = subscriberId ?? `${config.platform}:${message.author.userId}`; + const participantType = subscriberId + ? ConversationParticipantTypeEnum.SUBSCRIBER + : ConversationParticipantTypeEnum.PLATFORM_USER; + + const conversation = await this.conversationService.createOrGetConversation({ + environmentId: config.environmentId, + organizationId: config.organizationId, + agentId, + platform: config.platform, + integrationId: config.integrationId, + platformThreadId: thread.id, + participantId, + participantType, + platformUserId: message.author.userId, + firstMessageText: message.text, + }); + + const senderType = subscriberId + ? ConversationActivitySenderTypeEnum.SUBSCRIBER + : ConversationActivitySenderTypeEnum.PLATFORM_USER; + + await this.conversationService.persistInboundMessage({ + conversationId: conversation._id, + platform: config.platform, + integrationId: config.integrationId, + platformThreadId: thread.id, + senderType, + senderId: participantId, + senderName: message.author.fullName, + content: message.text, + platformMessageId: message.id, + environmentId: config.environmentId, + organizationId: config.organizationId, + }); + + await thread.startTyping(); + + const serializedThread = thread.toJSON() as unknown as Record; + await this.conversationService.updateChannelThread( + config.environmentId, + config.organizationId, + conversation._id, + thread.id, + serializedThread + ); + + const [subscriber, history] = await Promise.all([ + subscriberId + ? this.subscriberRepository.findBySubscriberId(config.environmentId, subscriberId) + : Promise.resolve(null), + this.conversationService.getHistory(config.environmentId, conversation._id), + ]); + + await this.bridgeExecutor.execute({ + event, + config, + conversation, + subscriber, + history, + message, + platformContext: { + threadId: thread.id, + channelId: thread.channelId, + isDM: thread.isDM, + }, + }); + } +} diff --git a/apps/api/src/app/agents/services/agent-subscriber-resolver.service.ts b/apps/api/src/app/agents/services/agent-subscriber-resolver.service.ts new file mode 100644 index 00000000000..dcd973efc30 --- /dev/null +++ b/apps/api/src/app/agents/services/agent-subscriber-resolver.service.ts @@ -0,0 +1,57 @@ +import { Injectable } from '@nestjs/common'; +import { PinoLogger } from '@novu/application-generic'; +import { ChannelEndpointRepository } from '@novu/dal'; +import { AgentPlatformEnum } from '../dtos/agent-platform.enum'; +import { PLATFORM_ENDPOINT_CONFIG } from '../utils/platform-endpoint-config'; + +export interface ResolveSubscriberParams { + environmentId: string; + organizationId: string; + platform: AgentPlatformEnum; + platformUserId: string; + integrationIdentifier: string; +} + +@Injectable() +export class AgentSubscriberResolver { + constructor( + private readonly channelEndpointRepository: ChannelEndpointRepository, + private readonly logger: PinoLogger + ) {} + + async resolve(params: ResolveSubscriberParams): Promise { + const { environmentId, organizationId, platform, platformUserId, integrationIdentifier } = params; + const endpointConfig = PLATFORM_ENDPOINT_CONFIG[platform]; + + if (!endpointConfig) { + this.logger.debug( + `No endpoint config for platform ${platform} — subscriber resolution skipped (integration: ${integrationIdentifier})` + ); + + return null; + } + + const endpoint = await this.channelEndpointRepository.findByPlatformIdentity({ + _environmentId: environmentId, + _organizationId: organizationId, + integrationIdentifier, + type: endpointConfig.endpointType, + endpointField: endpointConfig.identityField, + endpointValue: platformUserId, + }); + + if (endpoint) { + this.logger.debug( + `Resolved platform user ${platform}:${platformUserId} → subscriber ${endpoint.subscriberId}` + ); + + return endpoint.subscriberId; + } + + this.logger.debug( + `No subscriber linked for platform user ${platform}:${platformUserId} (integration: ${integrationIdentifier})` + ); + + return null; + } +} diff --git a/apps/api/src/app/agents/services/bridge-executor.service.ts b/apps/api/src/app/agents/services/bridge-executor.service.ts new file mode 100644 index 00000000000..33fbb863d0f --- /dev/null +++ b/apps/api/src/app/agents/services/bridge-executor.service.ts @@ -0,0 +1,285 @@ +import { Injectable } from '@nestjs/common'; +import { + buildNovuSignatureHeader, + GetDecryptedSecretKey, + GetDecryptedSecretKeyCommand, + PinoLogger, +} from '@novu/application-generic'; +import { + ConversationActivityEntity, + ConversationActivitySenderTypeEnum, + ConversationActivityTypeEnum, + ConversationEntity, + EnvironmentRepository, + SubscriberEntity, +} from '@novu/dal'; +import type { Message } from 'chat'; +import { AgentEventEnum } from '../dtos/agent-event.enum'; +import { ResolvedPlatformConfig } from './agent-credential.service'; + +const MAX_RETRIES = 2; +const RETRY_BASE_DELAY_MS = 500; + +export interface BridgePlatformContext { + threadId: string; + channelId: string; + isDM: boolean; +} + +export interface BridgeExecutorParams { + event: AgentEventEnum; + config: ResolvedPlatformConfig; + conversation: ConversationEntity; + subscriber: SubscriberEntity | null; + history: ConversationActivityEntity[]; + message: Message | null; + platformContext: BridgePlatformContext; +} + +interface BridgeMessageAuthor { + userId: string; + fullName: string; + userName: string; + isBot: boolean | 'unknown'; +} + +interface BridgeMessage { + text: string; + platformMessageId: string; + author: BridgeMessageAuthor; + timestamp: string; +} + +interface BridgeConversation { + identifier: string; + status: string; + metadata: Record; + messageCount: number; + createdAt: string; + lastActivityAt: string; +} + +interface BridgeSubscriber { + subscriberId: string; + firstName?: string; + lastName?: string; + email?: string; + phone?: string; + avatar?: string; + locale?: string; + data?: Record; +} + +interface BridgeHistoryEntry { + role: ConversationActivitySenderTypeEnum; + type: ConversationActivityTypeEnum; + content: string; + senderName?: string; + signalData?: { type: string; payload?: Record }; + createdAt: string; +} + +export interface AgentBridgeRequest { + version: 1; + timestamp: string; + deliveryId: string; + event: AgentEventEnum; + agentId: string; + replyUrl: string; + conversationId: string; + integrationIdentifier: string; + message: BridgeMessage | null; + conversation: BridgeConversation; + subscriber: BridgeSubscriber | null; + history: BridgeHistoryEntry[]; + platform: string; + platformContext: BridgePlatformContext; +} + +@Injectable() +export class BridgeExecutorService { + constructor( + private readonly environmentRepository: EnvironmentRepository, + private readonly getDecryptedSecretKey: GetDecryptedSecretKey, + private readonly logger: PinoLogger + ) {} + + async execute(params: BridgeExecutorParams): Promise { + const agentIdentifier = params.config.agentIdentifier; + + try { + const { config, event } = params; + + const bridgeUrl = await this.resolveBridgeUrl(config.environmentId, config.organizationId, agentIdentifier, event); + if (!bridgeUrl) { + return; + } + + const secretKey = await this.getDecryptedSecretKey.execute( + GetDecryptedSecretKeyCommand.create({ environmentId: config.environmentId, organizationId: config.organizationId }) + ); + + const payload = this.buildPayload(params); + const signatureHeader = buildNovuSignatureHeader(secretKey, payload); + + this.fireWithRetries(bridgeUrl, payload, signatureHeader, agentIdentifier).catch((err) => { + this.logger.error(err, `[agent:${agentIdentifier}] Bridge delivery failed after ${MAX_RETRIES + 1} attempts`); + }); + } catch (err) { + this.logger.error(err, `[agent:${agentIdentifier}] Bridge setup failed — skipping bridge call`); + } + } + + private async fireWithRetries( + url: string, + payload: AgentBridgeRequest, + signatureHeader: string, + agentIdentifier: string + ): Promise { + const body = JSON.stringify(payload); + let lastError: Error | undefined; + + for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) { + try { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-novu-signature': signatureHeader, + }, + body, + }); + + if (response.ok) { + return; + } + + lastError = new Error(`Bridge returned ${response.status}: ${response.statusText}`); + this.logger.warn(`[agent:${agentIdentifier}] Bridge call attempt ${attempt + 1} failed: ${response.status}`); + } catch (err) { + lastError = err instanceof Error ? err : new Error(String(err)); + this.logger.warn(`[agent:${agentIdentifier}] Bridge call attempt ${attempt + 1} network error: ${lastError.message}`); + } + + if (attempt < MAX_RETRIES) { + await this.delay(RETRY_BASE_DELAY_MS * Math.pow(2, attempt)); + } + } + + throw lastError ?? new Error('Bridge call failed'); + } + + private delay(ms: number): Promise { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); + } + + private async resolveBridgeUrl( + environmentId: string, + organizationId: string, + agentIdentifier: string, + event: AgentEventEnum + ): Promise { + const environment = await this.environmentRepository.findOne( + { _id: environmentId, _organizationId: organizationId }, + ['bridge'] + ); + const baseUrl = environment?.bridge?.url; + + if (!baseUrl) { + this.logger.warn(`[agent:${agentIdentifier}] No bridge URL configured on environment, skipping bridge call`); + + return null; + } + + const url = new URL(baseUrl); + url.searchParams.set('action', 'agent-event'); + url.searchParams.set('agentId', agentIdentifier); + url.searchParams.set('event', event); + + return url.toString(); + } + + private buildPayload(params: BridgeExecutorParams): AgentBridgeRequest { + const { event, config, conversation, subscriber, history, message, platformContext } = params; + const agentIdentifier = config.agentIdentifier; + + const apiRootUrl = process.env.API_ROOT_URL || 'http://localhost:3000'; + const replyUrl = `${apiRootUrl}/v1/agents/${agentIdentifier}/reply`; + + const deliveryId = message?.id + ? `${conversation._id}:${message.id}` + : `${conversation._id}:${event}`; + + return { + version: 1, + timestamp: new Date().toISOString(), + deliveryId, + event, + agentId: agentIdentifier, + replyUrl, + conversationId: conversation._id, + integrationIdentifier: config.integrationIdentifier, + message: message ? this.mapMessage(message) : null, + conversation: this.mapConversation(conversation), + subscriber: this.mapSubscriber(subscriber), + history: this.mapHistory(history), + platform: config.platform, + platformContext, + }; + } + + private mapMessage(message: Message): BridgeMessage { + return { + text: message.text, + platformMessageId: message.id, + author: { + userId: message.author.userId, + fullName: message.author.fullName, + userName: message.author.userName, + isBot: message.author.isBot, + }, + timestamp: message.metadata?.dateSent?.toISOString() ?? new Date().toISOString(), + }; + } + + private mapConversation(conversation: ConversationEntity): BridgeConversation { + return { + identifier: conversation.identifier, + status: conversation.status, + metadata: conversation.metadata ?? {}, + messageCount: conversation.messageCount ?? 0, + createdAt: conversation.createdAt, + lastActivityAt: conversation.lastActivityAt, + }; + } + + private mapSubscriber(subscriber: SubscriberEntity | null): BridgeSubscriber | null { + if (!subscriber) { + return null; + } + + return { + subscriberId: subscriber.subscriberId, + firstName: subscriber.firstName || undefined, + lastName: subscriber.lastName || undefined, + email: subscriber.email || undefined, + phone: subscriber.phone || undefined, + avatar: subscriber.avatar || undefined, + locale: subscriber.locale || undefined, + data: subscriber.data || undefined, + }; + } + + private mapHistory(activities: ConversationActivityEntity[]): BridgeHistoryEntry[] { + return [...activities].reverse().map((activity) => ({ + role: activity.senderType, + type: activity.type, + content: activity.content, + senderName: activity.senderName || undefined, + signalData: activity.signalData || undefined, + createdAt: activity.createdAt, + })); + } +} diff --git a/apps/api/src/app/agents/services/chat-sdk.service.ts b/apps/api/src/app/agents/services/chat-sdk.service.ts new file mode 100644 index 00000000000..fd11e96b5c8 --- /dev/null +++ b/apps/api/src/app/agents/services/chat-sdk.service.ts @@ -0,0 +1,241 @@ +import { BadRequestException, Injectable, OnModuleDestroy } from '@nestjs/common'; +import { PinoLogger } from '@novu/application-generic'; +import type { Chat, Message, Thread } from 'chat'; +import { Request as ExpressRequest, Response as ExpressResponse } from 'express'; +import { LRUCache } from 'lru-cache'; +import { AgentEventEnum } from '../dtos/agent-event.enum'; +import { AgentPlatformEnum } from '../dtos/agent-platform.enum'; +import { sendWebResponse, toWebRequest } from '../utils/express-to-web-request'; +import { AgentCredentialService, ResolvedPlatformConfig } from './agent-credential.service'; +import { AgentInboundHandler } from './agent-inbound-handler.service'; + +/** + * ICredentials field mapping per platform adapter: + * + * Slack: credentials.apiKey → signingSecret + * connection.auth.accessToken → botToken + * + * Teams: credentials.clientId → appId + * credentials.secretKey → appPassword + * credentials.tenantId → appTenantId + * + * WhatsApp: credentials.token → accessToken + * credentials.secretKey → appSecret + * credentials.apiToken → verifyToken + * credentials.phoneNumberIdentification → phoneNumberId + */ + +// Chat SDK packages are ESM-only; SWC rewrites import() → require() for CJS output. +// Wrapping in new Function prevents SWC from seeing the import() keyword. +// eslint-disable-next-line @typescript-eslint/no-implied-eval +const esmImport = new Function('specifier', 'return import(specifier)') as (s: string) => Promise; + +const MAX_CACHED_INSTANCES = 200; +const INSTANCE_TTL_MS = 1000 * 60 * 30; + +@Injectable() +export class ChatSdkService implements OnModuleDestroy { + private readonly instances: LRUCache; + private readonly pendingCreations = new Map>(); + + constructor( + private readonly logger: PinoLogger, + private readonly agentCredentialService: AgentCredentialService, + private readonly inboundHandler: AgentInboundHandler + ) { + this.instances = new LRUCache({ + max: MAX_CACHED_INSTANCES, + ttl: INSTANCE_TTL_MS, + dispose: (chat, key) => { + chat.shutdown().catch((err) => { + this.logger.error(err, `Failed to shut down evicted Chat instance ${key}`); + }); + }, + }); + } + + async handleWebhook(agentId: string, integrationIdentifier: string, req: ExpressRequest, res: ExpressResponse) { + const config = await this.agentCredentialService.resolve(agentId, integrationIdentifier); + const { platform } = config; + const instanceKey = `${agentId}:${integrationIdentifier}`; + + const chat = await this.getOrCreate(instanceKey, agentId, platform, config); + const handler = chat.webhooks[platform]; + if (!handler) { + throw new BadRequestException(`Platform ${platform} not configured for agent ${agentId}`); + } + + const webRequest = toWebRequest(req); + const webResponse = await handler(webRequest); + await sendWebResponse(webResponse, res); + } + + evict(agentId: string, integrationIdentifier?: string) { + if (integrationIdentifier) { + this.instances.delete(`${agentId}:${integrationIdentifier}`); + } else { + for (const key of this.instances.keys()) { + if (key.startsWith(`${agentId}:`)) { + this.instances.delete(key); + } + } + } + } + + async onModuleDestroy() { + const shutdowns = [...this.instances.entries()].map(async ([key, chat]) => { + try { + await chat.shutdown(); + } catch (err) { + this.logger.error(err, `Failed to shut down Chat instance ${key}`); + } + }); + + await Promise.allSettled(shutdowns); + this.instances.clear(); + } + + async postToConversation( + agentId: string, + integrationIdentifier: string, + platform: string, + serializedThread: Record, + message: string + ): Promise { + const config = await this.agentCredentialService.resolve(agentId, integrationIdentifier); + const instanceKey = `${agentId}:${integrationIdentifier}`; + const chat = await this.getOrCreate(instanceKey, agentId, config.platform, config); + + const { ThreadImpl } = await esmImport('chat'); + const adapter = chat.getAdapter(platform); + const thread = ThreadImpl.fromJSON(serializedThread, adapter); + await thread.post(message); + } + + private async getOrCreate( + instanceKey: string, + agentId: string, + platform: AgentPlatformEnum, + config: ResolvedPlatformConfig + ): Promise { + const existing = this.instances.get(instanceKey); + if (existing) return existing; + + const pending = this.pendingCreations.get(instanceKey); + if (pending) return pending; + + const creation = this.createAndCache(instanceKey, agentId, platform, config); + this.pendingCreations.set(instanceKey, creation); + + try { + return await creation; + } finally { + this.pendingCreations.delete(instanceKey); + } + } + + private async createAndCache( + instanceKey: string, + agentId: string, + platform: AgentPlatformEnum, + config: ResolvedPlatformConfig + ): Promise { + const chat = await this.createChatInstance(instanceKey, platform, config); + this.registerEventHandlers(agentId, chat, config); + this.instances.set(instanceKey, chat); + + return chat; + } + + private async createChatInstance( + instanceKey: string, + platform: AgentPlatformEnum, + config: ResolvedPlatformConfig + ): Promise { + const [{ Chat }, { createRedisState }] = await Promise.all([ + esmImport('chat'), + esmImport('@chat-adapter/state-redis'), + ]); + + const adapters = await this.buildAdapters(platform, config); + const redisHost = process.env.REDIS_HOST || 'localhost'; + const redisPort = process.env.REDIS_PORT || '6379'; + const redisScheme = process.env.REDIS_TLS_ENABLED === 'true' ? 'rediss' : 'redis'; + const redisPassword = process.env.REDIS_PASSWORD; + const redisAuth = redisPassword ? `:${encodeURIComponent(redisPassword)}@` : ''; + + return new Chat({ + userName: `novu-agent-${instanceKey}`, + adapters, + state: createRedisState({ + url: `${redisScheme}://${redisAuth}${redisHost}:${redisPort}`, + keyPrefix: `novu:agent:${instanceKey}`, + }), + logger: 'silent', + }); + } + + private async buildAdapters( + platform: AgentPlatformEnum, + config: ResolvedPlatformConfig + ): Promise> { + const { credentials, connectionAccessToken } = config; + + switch (platform) { + case AgentPlatformEnum.SLACK: { + const { createSlackAdapter } = await esmImport('@chat-adapter/slack'); + + return { + slack: createSlackAdapter({ + botToken: connectionAccessToken!, + signingSecret: credentials.apiKey!, + }), + }; + } + case AgentPlatformEnum.TEAMS: { + const { createTeamsAdapter } = await esmImport('@chat-adapter/teams'); + + return { + teams: createTeamsAdapter({ + appId: credentials.clientId!, + appPassword: credentials.secretKey!, + appTenantId: credentials.tenantId!, + }), + }; + } + case AgentPlatformEnum.WHATSAPP: { + const { createWhatsAppAdapter } = await esmImport('@chat-adapter/whatsapp'); + + return { + whatsapp: createWhatsAppAdapter({ + accessToken: credentials.token!, + appSecret: credentials.secretKey!, + verifyToken: credentials.apiToken!, + phoneNumberId: credentials.phoneNumberIdentification!, + }), + }; + } + default: + throw new BadRequestException(`Unsupported platform: ${platform}`); + } + } + + private registerEventHandlers(agentId: string, chat: Chat, config: ResolvedPlatformConfig) { + chat.onNewMention(async (thread: Thread, message: Message) => { + try { + await thread.subscribe(); + await this.inboundHandler.handle(agentId, config, thread, message, AgentEventEnum.ON_START); + } catch (err) { + this.logger.error(err, `[agent:${agentId}] Error handling new mention`); + } + }); + + chat.onSubscribedMessage(async (thread: Thread, message: Message) => { + try { + await this.inboundHandler.handle(agentId, config, thread, message, AgentEventEnum.ON_MESSAGE); + } catch (err) { + this.logger.error(err, `[agent:${agentId}] Error handling subscribed message`); + } + }); + } +} diff --git a/apps/api/src/app/agents/usecases/handle-agent-reply/handle-agent-reply.command.ts b/apps/api/src/app/agents/usecases/handle-agent-reply/handle-agent-reply.command.ts new file mode 100644 index 00000000000..170018decf6 --- /dev/null +++ b/apps/api/src/app/agents/usecases/handle-agent-reply/handle-agent-reply.command.ts @@ -0,0 +1,36 @@ +import { IsArray, IsNotEmpty, IsObject, IsOptional, IsString } from 'class-validator'; +import { EnvironmentWithUserCommand } from '../../../shared/commands/project.command'; + +export class HandleAgentReplyCommand extends EnvironmentWithUserCommand { + @IsString() + @IsNotEmpty() + conversationId: string; + + @IsString() + @IsNotEmpty() + agentIdentifier: string; + + @IsString() + @IsNotEmpty() + integrationIdentifier: string; + + @IsOptional() + @IsObject() + reply?: { text: string }; + + @IsOptional() + @IsObject() + update?: { text: string }; + + @IsOptional() + @IsObject() + resolve?: { summary?: string }; + + @IsOptional() + @IsArray() + signals?: Signal[]; +} + +export type Signal = + | { type: 'metadata'; key: string; value: unknown } + | { type: 'trigger'; workflowId: string; to?: string; payload?: Record }; diff --git a/apps/api/src/app/agents/usecases/handle-agent-reply/handle-agent-reply.usecase.ts b/apps/api/src/app/agents/usecases/handle-agent-reply/handle-agent-reply.usecase.ts new file mode 100644 index 00000000000..0f5864cd8dd --- /dev/null +++ b/apps/api/src/app/agents/usecases/handle-agent-reply/handle-agent-reply.usecase.ts @@ -0,0 +1,240 @@ +import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; +import { PinoLogger, shortId } from '@novu/application-generic'; +import { + ConversationActivityRepository, + ConversationActivityTypeEnum, + ConversationChannel, + ConversationEntity, + ConversationRepository, + ConversationStatusEnum, + SubscriberRepository, +} from '@novu/dal'; +import { AgentEventEnum } from '../../dtos/agent-event.enum'; +import { AgentCredentialService } from '../../services/agent-credential.service'; +import { AgentConversationService } from '../../services/agent-conversation.service'; +import { BridgeExecutorService } from '../../services/bridge-executor.service'; +import { ChatSdkService } from '../../services/chat-sdk.service'; +import { HandleAgentReplyCommand } from './handle-agent-reply.command'; + +@Injectable() +export class HandleAgentReply { + constructor( + private readonly conversationRepository: ConversationRepository, + private readonly activityRepository: ConversationActivityRepository, + private readonly subscriberRepository: SubscriberRepository, + private readonly chatSdkService: ChatSdkService, + private readonly bridgeExecutor: BridgeExecutorService, + private readonly agentCredentialService: AgentCredentialService, + private readonly conversationService: AgentConversationService, + private readonly logger: PinoLogger + ) {} + + async execute(command: HandleAgentReplyCommand): Promise<{ status: string }> { + if (command.reply && command.update) { + throw new BadRequestException('Only one of reply or update can be provided'); + } + if (!command.reply && !command.update && !command.resolve && !command.signals?.length) { + throw new BadRequestException('At least one of reply, update, resolve, or signals must be provided'); + } + + const conversation = await this.conversationRepository.findOne( + { + _id: command.conversationId, + _environmentId: command.environmentId, + _organizationId: command.organizationId, + }, + '*' + ); + if (!conversation) { + throw new NotFoundException('Conversation not found'); + } + + const channel = this.getPrimaryChannel(conversation); + + if (command.update) { + await this.deliverMessage(command, conversation, channel, command.update.text, ConversationActivityTypeEnum.UPDATE); + + return { status: 'update_sent' }; + } + + if (command.reply) { + await this.deliverMessage(command, conversation, channel, command.reply.text, ConversationActivityTypeEnum.MESSAGE); + } + + if (command.signals?.length) { + await this.executeSignals(command, conversation, channel, command.signals); + } + + if (command.resolve) { + await this.executeResolveSignal(command, conversation, channel, command.resolve); + } + + return { status: 'ok' }; + } + + private getPrimaryChannel(conversation: ConversationEntity): ConversationChannel { + const channel = conversation.channels[0]; + if (!channel?.serializedThread) { + throw new BadRequestException('Conversation has no serialized thread — unable to deliver reply'); + } + + return channel; + } + + private async deliverMessage( + command: HandleAgentReplyCommand, + conversation: ConversationEntity, + channel: ConversationChannel, + text: string, + type: ConversationActivityTypeEnum + ): Promise { + await Promise.all([ + this.chatSdkService.postToConversation( + conversation._agentId, + command.integrationIdentifier, + channel.platform, + channel.serializedThread!, + text + ), + this.activityRepository.createAgentActivity({ + identifier: `act-${shortId(8)}`, + conversationId: conversation._id, + platform: channel.platform, + integrationId: channel._integrationId, + platformThreadId: channel.platformThreadId, + agentId: command.agentIdentifier, + content: text, + type, + environmentId: command.environmentId, + organizationId: command.organizationId, + }), + this.conversationRepository.touchActivity( + command.environmentId, + command.organizationId, + conversation._id, + text + ), + ]); + } + + private async executeSignals( + command: HandleAgentReplyCommand, + conversation: ConversationEntity, + channel: ConversationChannel, + signals: HandleAgentReplyCommand['signals'] + ): Promise { + const metadataSignals = (signals ?? []).filter( + (s): s is Extract[number], { type: 'metadata' }> => s.type === 'metadata' + ); + + if (metadataSignals.length) { + await this.executeMetadataSignals(command, conversation, channel, metadataSignals); + } + + const triggerSignals = (signals ?? []).filter((s) => s.type === 'trigger'); + if (triggerSignals.length) { + // TODO: execute trigger signals — requires wiring TriggerEvent or ParseEventRequest from EventsModule + } + } + + private async executeMetadataSignals( + command: HandleAgentReplyCommand, + conversation: ConversationEntity, + channel: ConversationChannel, + signals: Array<{ type: 'metadata'; key: string; value: unknown }> + ): Promise { + const merged = { ...(conversation.metadata ?? {}) }; + for (const signal of signals) { + merged[signal.key] = signal.value; + } + + const serialized = JSON.stringify(merged); + if (Buffer.byteLength(serialized) > 65_536) { + throw new BadRequestException('Conversation metadata exceeds 64KB limit'); + } + + await Promise.all([ + this.conversationRepository.updateMetadata( + command.environmentId, + command.organizationId, + conversation._id, + merged + ), + this.activityRepository.createSignalActivity({ + identifier: `act-${shortId(8)}`, + conversationId: conversation._id, + platform: channel.platform, + integrationId: channel._integrationId, + platformThreadId: channel.platformThreadId, + agentId: command.agentIdentifier, + content: `Metadata updated: ${signals.map((s) => s.key).join(', ')}`, + signalData: { type: 'metadata', payload: merged }, + environmentId: command.environmentId, + organizationId: command.organizationId, + }), + ]); + } + + private async executeResolveSignal( + command: HandleAgentReplyCommand, + conversation: ConversationEntity, + channel: ConversationChannel, + signal: { summary?: string } + ): Promise { + await Promise.all([ + this.conversationRepository.updateStatus( + command.environmentId, + command.organizationId, + conversation._id, + ConversationStatusEnum.RESOLVED + ), + this.activityRepository.createSignalActivity({ + identifier: `act-${shortId(8)}`, + conversationId: conversation._id, + platform: channel.platform, + integrationId: channel._integrationId, + platformThreadId: channel.platformThreadId, + agentId: command.agentIdentifier, + content: signal.summary ?? 'Conversation resolved', + signalData: { type: 'resolve', payload: signal.summary ? { summary: signal.summary } : undefined }, + environmentId: command.environmentId, + organizationId: command.organizationId, + }), + ]); + + this.fireOnResolveBridgeCall(command, conversation).catch((err) => { + this.logger.error(err, `[agent:${command.agentIdentifier}] Failed to fire onResolve bridge call`); + }); + } + + private async fireOnResolveBridgeCall( + command: HandleAgentReplyCommand, + conversation: ConversationEntity + ): Promise { + const config = await this.agentCredentialService.resolve(conversation._agentId, command.integrationIdentifier); + + const subscriberParticipant = conversation.participants.find((p) => p.type === 'subscriber'); + const [subscriber, history] = await Promise.all([ + subscriberParticipant + ? this.subscriberRepository.findBySubscriberId(command.environmentId, subscriberParticipant.id) + : Promise.resolve(null), + this.conversationService.getHistory(command.environmentId, conversation._id), + ]); + + const channel = conversation.channels[0]; + + await this.bridgeExecutor.execute({ + event: AgentEventEnum.ON_RESOLVE, + config, + conversation, + subscriber, + history, + message: null, + platformContext: { + threadId: channel?.platformThreadId ?? '', + channelId: '', + isDM: false, + }, + }); + } +} diff --git a/apps/api/src/app/agents/usecases/index.ts b/apps/api/src/app/agents/usecases/index.ts index ce4cef7cde6..8807ddbce8c 100644 --- a/apps/api/src/app/agents/usecases/index.ts +++ b/apps/api/src/app/agents/usecases/index.ts @@ -2,6 +2,7 @@ import { AddAgentIntegration } from './add-agent-integration/add-agent-integrati import { CreateAgent } from './create-agent/create-agent.usecase'; import { DeleteAgent } from './delete-agent/delete-agent.usecase'; import { GetAgent } from './get-agent/get-agent.usecase'; +import { HandleAgentReply } from './handle-agent-reply/handle-agent-reply.usecase'; import { ListAgentIntegrations } from './list-agent-integrations/list-agent-integrations.usecase'; import { ListAgents } from './list-agents/list-agents.usecase'; import { RemoveAgentIntegration } from './remove-agent-integration/remove-agent-integration.usecase'; @@ -18,4 +19,5 @@ export const USE_CASES = [ ListAgentIntegrations, UpdateAgentIntegration, RemoveAgentIntegration, + HandleAgentReply, ]; diff --git a/apps/api/src/app/agents/utils/express-to-web-request.ts b/apps/api/src/app/agents/utils/express-to-web-request.ts new file mode 100644 index 00000000000..e5d96479a1b --- /dev/null +++ b/apps/api/src/app/agents/utils/express-to-web-request.ts @@ -0,0 +1,65 @@ +import { Request as ExpressRequest, Response as ExpressResponse } from 'express'; + +/** + * Converts an Express Request to a Web API Request. + * Uses req.rawBody (Buffer) when available for signature verification integrity. + * Falls back to re-serializing req.body if rawBody is not present. + */ +export function toWebRequest(req: ExpressRequest): Request { + const protocol = req.protocol; + const host = req.get('host') || 'localhost'; + const url = `${protocol}://${host}${req.originalUrl}`; + + const headers = new Headers(); + for (const [key, value] of Object.entries(req.headers)) { + if (value === undefined) continue; + if (Array.isArray(value)) { + for (const v of value) { + headers.append(key, v); + } + } else { + headers.set(key, value); + } + } + + let body: BodyInit | undefined; + if (req.method !== 'GET' && req.method !== 'HEAD') { + const rawBody = (req as ExpressRequest & { rawBody?: Buffer }).rawBody; + if (rawBody) { + body = rawBody; + } else if (Buffer.isBuffer(req.body) || typeof req.body === 'string') { + body = req.body; + } else if (req.body !== undefined) { + body = JSON.stringify(req.body); + if (!headers.has('content-type')) { + headers.set('content-type', 'application/json'); + } + } + } + + return new Request(url, { + method: req.method, + headers, + body, + }); +} + +/** + * Writes a Web API Response back onto an Express Response. + */ +export async function sendWebResponse(webResponse: Response, res: ExpressResponse): Promise { + res.status(webResponse.status); + + webResponse.headers.forEach((value, key) => { + res.setHeader(key, value); + }); + + const contentType = webResponse.headers.get('content-type') || ''; + if (contentType.includes('application/json')) { + const json = await webResponse.json(); + res.json(json); + } else { + const text = await webResponse.text(); + res.send(text); + } +} diff --git a/apps/api/src/app/agents/utils/platform-endpoint-config.ts b/apps/api/src/app/agents/utils/platform-endpoint-config.ts new file mode 100644 index 00000000000..c0b0338dd07 --- /dev/null +++ b/apps/api/src/app/agents/utils/platform-endpoint-config.ts @@ -0,0 +1,22 @@ +import { ENDPOINT_TYPES, ChannelEndpointType } from '@novu/shared'; +import { AgentPlatformEnum } from '../dtos/agent-platform.enum'; + +interface PlatformEndpointMapping { + endpointType: ChannelEndpointType; + identityField: string; +} + +export const PLATFORM_ENDPOINT_CONFIG: Partial> = { + [AgentPlatformEnum.SLACK]: { + endpointType: ENDPOINT_TYPES.SLACK_USER, + identityField: 'userId', + }, + [AgentPlatformEnum.TEAMS]: { + endpointType: ENDPOINT_TYPES.MS_TEAMS_USER, + identityField: 'userId', + }, + [AgentPlatformEnum.WHATSAPP]: { + endpointType: ENDPOINT_TYPES.PHONE, + identityField: 'phoneNumber', + }, +}; diff --git a/apps/api/src/app/agents/utils/provider-to-platform.ts b/apps/api/src/app/agents/utils/provider-to-platform.ts new file mode 100644 index 00000000000..213412bd3d8 --- /dev/null +++ b/apps/api/src/app/agents/utils/provider-to-platform.ts @@ -0,0 +1,12 @@ +import { ChatProviderIdEnum } from '@novu/shared'; +import { AgentPlatformEnum } from '../dtos/agent-platform.enum'; + +const PROVIDER_TO_PLATFORM: Partial> = { + [ChatProviderIdEnum.Slack]: AgentPlatformEnum.SLACK, + [ChatProviderIdEnum.MsTeams]: AgentPlatformEnum.TEAMS, + [ChatProviderIdEnum.WhatsAppBusiness]: AgentPlatformEnum.WHATSAPP, +}; + +export function resolveAgentPlatform(providerId: string): AgentPlatformEnum | null { + return PROVIDER_TO_PLATFORM[providerId] ?? null; +} diff --git a/apps/api/src/bootstrap.ts b/apps/api/src/bootstrap.ts index 1096408132b..47cffa13c5a 100644 --- a/apps/api/src/bootstrap.ts +++ b/apps/api/src/bootstrap.ts @@ -47,16 +47,18 @@ export async function bootstrap( ): Promise<{ app: INestApplication; document: any }> { BullMqService.haveProInstalled(); + const agentRawBodyBuffer = (_req, _res, buffer, _encoding): void => { + if (buffer?.length) { + // eslint-disable-next-line no-param-reassign + (_req as any).rawBody = Buffer.from(buffer); + } + }; + let rawBodyBuffer: undefined | ((...args) => void); let nestOptions: Record = {}; if (process.env.NOVU_ENTERPRISE === 'true' || process.env.CI_EE_TEST === 'true') { - rawBodyBuffer = (_req, _res, buffer, _encoding): void => { - if (buffer?.length) { - // eslint-disable-next-line no-param-reassign - (_req as any).rawBody = Buffer.from(buffer); - } - }; + rawBodyBuffer = agentRawBodyBuffer; nestOptions = { bodyParser: false, rawBody: true, @@ -106,6 +108,8 @@ export async function bootstrap( app.use(extendedBodySizeRoutes, bodyParser.json({ limit: '26mb' })); app.use(extendedBodySizeRoutes, bodyParser.urlencoded({ limit: '26mb', extended: true })); + app.use('/v1/agents', bodyParser.json({ verify: agentRawBodyBuffer })); + // Add text/plain parser specifically for inbound webhooks (SNS confirmations) app.use( '/v2/inbound-webhooks/delivery-providers/:environmentId/:integrationId', diff --git a/enterprise/packages/api/package.json b/enterprise/packages/api/package.json index 7c3f23cdc9e..7a13b149b2b 100644 --- a/enterprise/packages/api/package.json +++ b/enterprise/packages/api/package.json @@ -18,6 +18,7 @@ "@novu/shared": "workspace:*", "@novu/stateless": "workspace:*", "@types/express": "4.17.17", + "class-transformer": "0.5.1", "class-validator": "0.14.1", "@novu/testing": "workspace:*", "express": "^5.0.1" diff --git a/libs/dal/src/index.ts b/libs/dal/src/index.ts index c7426e9515b..23c479248e2 100644 --- a/libs/dal/src/index.ts +++ b/libs/dal/src/index.ts @@ -5,6 +5,8 @@ export * from './repositories/ai-chat'; export * from './repositories/base-repository'; export * from './repositories/base-repository-v2'; export * from './repositories/change'; +export * from './repositories/conversation'; +export * from './repositories/conversation-activity'; export * from './repositories/channel-connection'; export * from './repositories/channel-endpoint'; export * from './repositories/context'; diff --git a/libs/dal/src/repositories/agent/agent.repository.ts b/libs/dal/src/repositories/agent/agent.repository.ts index 59bbf2c7839..bbc9b202d94 100644 --- a/libs/dal/src/repositories/agent/agent.repository.ts +++ b/libs/dal/src/repositories/agent/agent.repository.ts @@ -12,6 +12,17 @@ export class AgentRepository extends BaseRepositoryV2 { + const doc = await this.MongooseModel.findById(agentId).lean(); + if (!doc) return null; + + return this.mapProjectedEntity(doc) as AgentEntity; + } + async listAgents({ organizationId, environmentId, diff --git a/libs/dal/src/repositories/channel-endpoint/channel-endpoint.repository.ts b/libs/dal/src/repositories/channel-endpoint/channel-endpoint.repository.ts index 12277c6d9d3..2ea706144ee 100644 --- a/libs/dal/src/repositories/channel-endpoint/channel-endpoint.repository.ts +++ b/libs/dal/src/repositories/channel-endpoint/channel-endpoint.repository.ts @@ -1,3 +1,4 @@ +import type { ChannelEndpointType } from '@novu/shared'; import type { EnforceEnvOrOrgIds } from '../../types'; import { BaseRepository } from '../base-repository'; import { ChannelEndpointDBModel, ChannelEndpointEntity } from './channel-endpoint.entity'; @@ -11,4 +12,21 @@ export class ChannelEndpointRepository extends BaseRepository< constructor() { super(ChannelEndpoint, ChannelEndpointEntity); } + + async findByPlatformIdentity(params: { + _environmentId: string; + _organizationId: string; + integrationIdentifier: string; + type: ChannelEndpointType; + endpointField: string; + endpointValue: string; + }): Promise { + return this.findOne({ + _environmentId: params._environmentId, + _organizationId: params._organizationId, + integrationIdentifier: params.integrationIdentifier, + type: params.type, + [`endpoint.${params.endpointField}`]: params.endpointValue, + }); + } } diff --git a/libs/dal/src/repositories/conversation-activity/conversation-activity.entity.ts b/libs/dal/src/repositories/conversation-activity/conversation-activity.entity.ts new file mode 100644 index 00000000000..c42a1d29480 --- /dev/null +++ b/libs/dal/src/repositories/conversation-activity/conversation-activity.entity.ts @@ -0,0 +1,72 @@ +import { ChangePropsValueType } from '../../types/helpers'; +import { EnvironmentId } from '../environment'; +import { OrganizationId } from '../organization'; + +export enum ConversationActivityTypeEnum { + MESSAGE = 'message', + /** Interim status update sent via ctx.update() */ + UPDATE = 'update', + /** System-generated timeline event (e.g. workflow triggered, conversation resolved) */ + SIGNAL = 'signal', +} + +export enum ConversationActivitySenderTypeEnum { + SUBSCRIBER = 'subscriber', + PLATFORM_USER = 'platform_user', + AGENT = 'agent', + SYSTEM = 'system', +} + +export interface ConversationActivitySignalData { + /** The signal type that was executed (trigger, resolve, escalate) */ + type: string; + /** Relevant IDs or metadata about the signal execution */ + payload?: Record; +} + +export class ConversationActivityEntity { + _id: string; + + /** User-facing ID for API responses and webhook payloads */ + identifier: string; + + _conversationId: string; + + type: ConversationActivityTypeEnum; + + content: string; + + /** Platform slug this activity occurred on */ + platform: string; + + /** The Novu integration that handled this activity */ + _integrationId: string; + + /** Thread ID on the platform — ties the activity to a specific ConversationChannel */ + platformThreadId: string; + + senderType: ConversationActivitySenderTypeEnum; + + /** The ID of the sender — subscriberId, agentId, or "system" */ + senderId: string; + + /** Denormalized display name; avoids a join for simple rendering */ + senderName?: string; + + /** Platform-native message ID (e.g. Slack ts) — used for deduplication */ + platformMessageId?: string; + + /** Populated only when type === SIGNAL */ + signalData?: ConversationActivitySignalData; + + _environmentId: EnvironmentId; + + _organizationId: OrganizationId; + + createdAt: string; +} + +export type ConversationActivityDBModel = ChangePropsValueType< + ConversationActivityEntity, + '_conversationId' | '_environmentId' | '_organizationId' | '_integrationId' +>; diff --git a/libs/dal/src/repositories/conversation-activity/conversation-activity.repository.ts b/libs/dal/src/repositories/conversation-activity/conversation-activity.repository.ts new file mode 100644 index 00000000000..4f9a9ae4f78 --- /dev/null +++ b/libs/dal/src/repositories/conversation-activity/conversation-activity.repository.ts @@ -0,0 +1,121 @@ +import { Injectable } from '@nestjs/common'; +import { EnforceEnvOrOrgIds } from '../../types'; +import { BaseRepositoryV2 } from '../base-repository-v2'; +import { + ConversationActivityDBModel, + ConversationActivityEntity, + ConversationActivitySenderTypeEnum, + ConversationActivitySignalData, + ConversationActivityTypeEnum, +} from './conversation-activity.entity'; +import { ConversationActivity } from './conversation-activity.schema'; + +@Injectable() +export class ConversationActivityRepository extends BaseRepositoryV2< + ConversationActivityDBModel, + ConversationActivityEntity, + EnforceEnvOrOrgIds +> { + constructor() { + super(ConversationActivity, ConversationActivityEntity); + } + + async findByConversation( + environmentId: string, + conversationId: string, + limit = 20 + ): Promise { + return this.find({ _environmentId: environmentId, _conversationId: conversationId }, '*', { + sort: { createdAt: -1 }, + limit, + }); + } + + async createUserActivity(params: { + identifier: string; + conversationId: string; + platform: string; + integrationId: string; + platformThreadId: string; + senderType: ConversationActivitySenderTypeEnum; + senderId: string; + content: string; + platformMessageId?: string; + senderName?: string; + environmentId: string; + organizationId: string; + }): Promise { + return this.create({ + identifier: params.identifier, + _conversationId: params.conversationId, + type: ConversationActivityTypeEnum.MESSAGE, + platform: params.platform, + _integrationId: params.integrationId, + platformThreadId: params.platformThreadId, + senderType: params.senderType, + senderId: params.senderId, + content: params.content, + platformMessageId: params.platformMessageId, + senderName: params.senderName, + _environmentId: params.environmentId, + _organizationId: params.organizationId, + }); + } + + async createAgentActivity(params: { + identifier: string; + conversationId: string; + platform: string; + integrationId: string; + platformThreadId: string; + agentId: string; + content: string; + type?: ConversationActivityTypeEnum; + senderName?: string; + environmentId: string; + organizationId: string; + }): Promise { + return this.create({ + identifier: params.identifier, + _conversationId: params.conversationId, + type: params.type ?? ConversationActivityTypeEnum.MESSAGE, + platform: params.platform, + _integrationId: params.integrationId, + platformThreadId: params.platformThreadId, + senderType: ConversationActivitySenderTypeEnum.AGENT, + senderId: params.agentId, + content: params.content, + senderName: params.senderName, + _environmentId: params.environmentId, + _organizationId: params.organizationId, + }); + } + + async createSignalActivity(params: { + identifier: string; + conversationId: string; + platform: string; + integrationId: string; + platformThreadId: string; + agentId: string; + content: string; + signalData: ConversationActivitySignalData; + environmentId: string; + organizationId: string; + }): Promise { + return this.create({ + identifier: params.identifier, + _conversationId: params.conversationId, + type: ConversationActivityTypeEnum.SIGNAL, + platform: params.platform, + _integrationId: params.integrationId, + platformThreadId: params.platformThreadId, + senderType: ConversationActivitySenderTypeEnum.SYSTEM, + senderId: params.agentId, + content: params.content, + signalData: params.signalData, + _environmentId: params.environmentId, + _organizationId: params.organizationId, + }); + } +} diff --git a/libs/dal/src/repositories/conversation-activity/conversation-activity.schema.ts b/libs/dal/src/repositories/conversation-activity/conversation-activity.schema.ts new file mode 100644 index 00000000000..9b4e69263ef --- /dev/null +++ b/libs/dal/src/repositories/conversation-activity/conversation-activity.schema.ts @@ -0,0 +1,80 @@ +import mongoose, { Schema } from 'mongoose'; +import { schemaOptions } from '../schema-default.options'; +import { + ConversationActivityDBModel, + ConversationActivitySenderTypeEnum, + ConversationActivityTypeEnum, +} from './conversation-activity.entity'; + +const conversationActivitySchema = new Schema( + { + identifier: { + type: Schema.Types.String, + required: true, + }, + _conversationId: { + type: Schema.Types.ObjectId, + ref: 'Conversation', + required: true, + }, + type: { + type: Schema.Types.String, + enum: Object.values(ConversationActivityTypeEnum), + default: ConversationActivityTypeEnum.MESSAGE, + }, + content: { + type: Schema.Types.String, + required: true, + }, + platform: { + type: Schema.Types.String, + required: true, + }, + _integrationId: { + type: Schema.Types.ObjectId, + ref: 'Integration', + required: true, + }, + platformThreadId: { + type: Schema.Types.String, + required: true, + }, + senderType: { + type: Schema.Types.String, + enum: Object.values(ConversationActivitySenderTypeEnum), + required: true, + }, + senderId: { + type: Schema.Types.String, + required: true, + }, + platformMessageId: { + type: Schema.Types.String, + }, + senderName: { + type: Schema.Types.String, + }, + signalData: { + type: Schema.Types.Mixed, + }, + _environmentId: { + type: Schema.Types.ObjectId, + ref: 'Environment', + required: true, + }, + _organizationId: { + type: Schema.Types.ObjectId, + ref: 'Organization', + required: true, + }, + }, + schemaOptions +); + +conversationActivitySchema.index({ _conversationId: 1, createdAt: 1 }); +conversationActivitySchema.index({ _conversationId: 1, platformMessageId: 1 }, { sparse: true }); +conversationActivitySchema.index({ _environmentId: 1, identifier: 1 }, { unique: true }); + +export const ConversationActivity = + (mongoose.models.ConversationActivity as mongoose.Model) || + mongoose.model('ConversationActivity', conversationActivitySchema); diff --git a/libs/dal/src/repositories/conversation-activity/index.ts b/libs/dal/src/repositories/conversation-activity/index.ts new file mode 100644 index 00000000000..bee1f57602b --- /dev/null +++ b/libs/dal/src/repositories/conversation-activity/index.ts @@ -0,0 +1,3 @@ +export * from './conversation-activity.entity'; +export * from './conversation-activity.repository'; +export * from './conversation-activity.schema'; diff --git a/libs/dal/src/repositories/conversation/conversation.entity.ts b/libs/dal/src/repositories/conversation/conversation.entity.ts new file mode 100644 index 00000000000..21651dd6520 --- /dev/null +++ b/libs/dal/src/repositories/conversation/conversation.entity.ts @@ -0,0 +1,70 @@ +import { ChangePropsValueType } from '../../types/helpers'; +import { EnvironmentId } from '../environment'; +import { OrganizationId } from '../organization'; + +export enum ConversationStatusEnum { + ACTIVE = 'active', + RESOLVED = 'resolved', +} + +export enum ConversationParticipantTypeEnum { + SUBSCRIBER = 'subscriber', + AGENT = 'agent', + PLATFORM_USER = 'platform_user', +} + +export interface ConversationParticipant { + type: ConversationParticipantTypeEnum; + /** The referenced entity ID — subscriberId when type === SUBSCRIBER */ + id: string; +} + +export interface ConversationChannel { + /** Platform slug: slack | whatsapp | teams | gchat | github */ + platform: string; + /** The Novu integration used on this channel */ + _integrationId: string; + /** Unique thread identifier on the platform (e.g. Slack channel+ts, GitHub PR number) */ + platformThreadId: string; + /** Chat SDK SerializedThread — stored for reply delivery via ThreadImpl.fromJSON() */ + serializedThread?: Record; +} + +export class ConversationEntity { + _id: string; + + /** User-facing ID for API responses, URLs, and webhook payloads */ + identifier: string; + + /** References AgentEntity._id — populated once agent CRUD is implemented */ + _agentId: string; + + /** All parties in the conversation; extensible to agents/bots in future */ + participants: ConversationParticipant[]; + + /** Platform bindings — one entry per platform/thread the conversation spans */ + channels: ConversationChannel[]; + + status: ConversationStatusEnum; + + /** Human-readable label derived from the first user message or set explicitly by the agent */ + title: string; + + /** Customer-controlled key/value bag accumulated across turns, sent back in every bridge payload */ + metadata: Record; + + messageCount: number; + + /** Truncated preview of the most recent message (max 200 chars) */ + lastMessagePreview?: string; + + _environmentId: EnvironmentId; + + _organizationId: OrganizationId; + + createdAt: string; + + lastActivityAt: string; +} + +export type ConversationDBModel = ChangePropsValueType; diff --git a/libs/dal/src/repositories/conversation/conversation.repository.ts b/libs/dal/src/repositories/conversation/conversation.repository.ts new file mode 100644 index 00000000000..4ae15f70de1 --- /dev/null +++ b/libs/dal/src/repositories/conversation/conversation.repository.ts @@ -0,0 +1,111 @@ +import { Injectable } from '@nestjs/common'; +import { EnforceEnvOrOrgIds } from '../../types'; +import { BaseRepositoryV2 } from '../base-repository-v2'; +import { + ConversationDBModel, + ConversationEntity, + ConversationParticipant, + ConversationParticipantTypeEnum, + ConversationStatusEnum, +} from './conversation.entity'; +import { Conversation } from './conversation.schema'; + +@Injectable() +export class ConversationRepository extends BaseRepositoryV2< + ConversationDBModel, + ConversationEntity, + EnforceEnvOrOrgIds +> { + constructor() { + super(Conversation, ConversationEntity); + } + + async findByPlatformThread( + environmentId: string, + organizationId: string, + platformThreadId: string + ): Promise { + return this.findOne( + { + _environmentId: environmentId, + _organizationId: organizationId, + 'channels.platformThreadId': platformThreadId, + }, + '*' + ); + } + + async findActiveByParticipant( + environmentId: string, + organizationId: string, + participantId: string, + participantType = ConversationParticipantTypeEnum.SUBSCRIBER + ): Promise { + return this.find( + { + _environmentId: environmentId, + _organizationId: organizationId, + participants: { $elemMatch: { id: participantId, type: participantType } }, + status: ConversationStatusEnum.ACTIVE, + }, + '*' + ); + } + + async updateStatus(environmentId: string, organizationId: string, id: string, status: ConversationStatusEnum): Promise { + await this.update( + { _id: id, _environmentId: environmentId, _organizationId: organizationId }, + { $set: { status } } + ); + } + + async updateMetadata(environmentId: string, organizationId: string, id: string, metadata: Record): Promise { + await this.update( + { _id: id, _environmentId: environmentId, _organizationId: organizationId }, + { $set: { metadata } } + ); + } + + async updateParticipants( + environmentId: string, + organizationId: string, + id: string, + participants: ConversationParticipant[] + ): Promise { + await this.update( + { _id: id, _environmentId: environmentId, _organizationId: organizationId }, + { $set: { participants } } + ); + } + + async touchActivity(environmentId: string, organizationId: string, id: string, messagePreview: string): Promise { + await this.update( + { _id: id, _environmentId: environmentId, _organizationId: organizationId }, + { + $set: { + lastActivityAt: new Date().toISOString(), + lastMessagePreview: messagePreview.slice(0, 200), + }, + $inc: { messageCount: 1 }, + } + ); + } + + async updateChannelThread( + environmentId: string, + organizationId: string, + id: string, + platformThreadId: string, + serializedThread: Record + ): Promise { + await this.update( + { + _id: id, + _environmentId: environmentId, + _organizationId: organizationId, + 'channels.platformThreadId': platformThreadId, + }, + { $set: { 'channels.$.serializedThread': serializedThread } } + ); + } +} diff --git a/libs/dal/src/repositories/conversation/conversation.schema.ts b/libs/dal/src/repositories/conversation/conversation.schema.ts new file mode 100644 index 00000000000..b46d77d6774 --- /dev/null +++ b/libs/dal/src/repositories/conversation/conversation.schema.ts @@ -0,0 +1,104 @@ +import mongoose, { Schema } from 'mongoose'; +import { schemaOptions } from '../schema-default.options'; +import { ConversationDBModel, ConversationParticipantTypeEnum, ConversationStatusEnum } from './conversation.entity'; + +const conversationSchema = new Schema( + { + identifier: { + type: Schema.Types.String, + required: true, + }, + _agentId: { + type: Schema.Types.ObjectId, + required: true, + }, + participants: { + type: [ + new Schema( + { + type: { + type: Schema.Types.String, + enum: Object.values(ConversationParticipantTypeEnum), + required: true, + }, + id: { + type: Schema.Types.String, + required: true, + }, + }, + { _id: false } + ), + ], + default: [], + }, + channels: { + type: [ + new Schema( + { + platform: { + type: Schema.Types.String, + required: true, + }, + _integrationId: { + type: Schema.Types.ObjectId, + ref: 'Integration', + required: true, + }, + platformThreadId: { + type: Schema.Types.String, + required: true, + }, + serializedThread: { + type: Schema.Types.Mixed, + }, + }, + { _id: false } + ), + ], + default: [], + }, + status: { + type: Schema.Types.String, + enum: Object.values(ConversationStatusEnum), + default: ConversationStatusEnum.ACTIVE, + required: true, + }, + title: { + type: Schema.Types.String, + required: true, + }, + metadata: { + type: Schema.Types.Mixed, + default: {}, + }, + messageCount: { + type: Schema.Types.Number, + default: 0, + }, + lastMessagePreview: { + type: Schema.Types.String, + }, + lastActivityAt: { + type: Schema.Types.String, + }, + _environmentId: { + type: Schema.Types.ObjectId, + ref: 'Environment', + required: true, + }, + _organizationId: { + type: Schema.Types.ObjectId, + ref: 'Organization', + required: true, + }, + }, + schemaOptions +); + +conversationSchema.index({ _environmentId: 1, identifier: 1 }, { unique: true }); +conversationSchema.index({ _environmentId: 1, 'channels.platformThreadId': 1 }); +conversationSchema.index({ _environmentId: 1, 'participants.id': 1, status: 1 }); + +export const Conversation = + (mongoose.models.Conversation as mongoose.Model) || + mongoose.model('Conversation', conversationSchema); diff --git a/libs/dal/src/repositories/conversation/index.ts b/libs/dal/src/repositories/conversation/index.ts new file mode 100644 index 00000000000..b105a1d5cb9 --- /dev/null +++ b/libs/dal/src/repositories/conversation/index.ts @@ -0,0 +1,3 @@ +export * from './conversation.entity'; +export * from './conversation.repository'; +export * from './conversation.schema'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 42600b84a2d..e451a4588c3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -311,6 +311,18 @@ importers: '@aws-sdk/client-secrets-manager': specifier: ^3.971.0 version: 3.996.0 + '@chat-adapter/slack': + specifier: ^4.25.0 + version: 4.25.0 + '@chat-adapter/state-redis': + specifier: ^4.25.0 + version: 4.25.0 + '@chat-adapter/teams': + specifier: ^4.25.0 + version: 4.25.0 + '@chat-adapter/whatsapp': + specifier: ^4.25.0 + version: 4.25.0 '@godaddy/terminus': specifier: ^4.12.1 version: 4.12.1 @@ -340,7 +352,7 @@ importers: version: 7.4.0(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) '@nestjs/terminus': specifier: 10.2.3 - version: 10.2.3(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/axios@3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1))(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.2.3(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/axios@3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1))(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/throttler': specifier: 6.2.1 version: 6.2.1(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(reflect-metadata@0.2.2) @@ -425,6 +437,9 @@ importers: bull: specifier: ^4.2.1 version: 4.10.4 + chat: + specifier: ^4.25.0 + version: 4.25.0 class-transformer: specifier: 0.5.1 version: 0.5.1 @@ -678,7 +693,7 @@ importers: version: 3.0.51(react@19.2.3)(zod@4.3.5) '@better-auth/sso': specifier: ^1.3.0 - version: 1.4.7(better-auth@1.5.6(f5eede22a65d6cd0c93a8bf1a87b70c5)) + version: 1.4.7(better-auth@1.5.6(14eb71b76c563af6394284c30ec93f47)) '@calcom/embed-react': specifier: 1.5.2 version: 1.5.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -885,7 +900,7 @@ importers: version: 6.2.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) better-auth: specifier: ^1.4.9 - version: 1.5.6(f5eede22a65d6cd0c93a8bf1a87b70c5) + version: 1.5.6(14eb71b76c563af6394284c30ec93f47) class-variance-authority: specifier: ^0.7.0 version: 0.7.1 @@ -1292,7 +1307,7 @@ importers: version: 10.4.18(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18) '@nestjs/terminus': specifier: 10.2.3 - version: 10.2.3(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/axios@3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1))(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.2.3(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/axios@3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1))(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))(reflect-metadata@0.2.2)(rxjs@7.8.1) '@novu/application-generic': specifier: workspace:* version: link:../../libs/application-generic @@ -1455,7 +1470,7 @@ importers: version: 7.4.0(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) '@nestjs/terminus': specifier: 10.2.3 - version: 10.2.3(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/axios@3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1))(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.2.3(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/axios@3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1))(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))(reflect-metadata@0.2.2)(rxjs@7.8.1) '@novu/application-generic': specifier: workspace:* version: link:../../libs/application-generic @@ -1688,7 +1703,7 @@ importers: version: 7.4.0(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) '@nestjs/terminus': specifier: 10.2.3 - version: 10.2.3(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/axios@3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1))(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.2.3(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/axios@3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1))(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/websockets': specifier: 10.4.18 version: 10.4.18(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(@nestjs/platform-socket.io@10.4.18)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -1854,7 +1869,7 @@ importers: version: 1.0.0(@langchain/core@1.1.18(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.212.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.17.0(ws@8.20.0)(zod@3.25.20))(ws@8.20.0)) '@langchain/langgraph-checkpoint-mongodb': specifier: ^1.1.6 - version: 1.1.6(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(@langchain/core@1.1.18(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.212.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.17.0(ws@8.20.0)(zod@3.25.20))(ws@8.20.0))(@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.18(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.212.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.17.0(ws@8.20.0)(zod@3.25.20))(ws@8.20.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) + version: 1.1.6(@aws-sdk/credential-providers@3.637.0)(@langchain/core@1.1.18(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.212.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.17.0(ws@8.20.0)(zod@3.25.20))(ws@8.20.0))(@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.18(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.212.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.17.0(ws@8.20.0)(zod@3.25.20))(ws@8.20.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) '@langchain/openai': specifier: ^1.2.4 version: 1.2.4(@langchain/core@1.1.18(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.212.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.17.0(ws@8.20.0)(zod@3.25.20))(ws@8.20.0))(ws@8.20.0) @@ -1976,6 +1991,9 @@ importers: '@types/express': specifier: 4.17.17 version: 4.17.17 + class-transformer: + specifier: 0.5.1 + version: 0.5.1 class-validator: specifier: 0.14.1 version: 0.14.1 @@ -2021,7 +2039,7 @@ importers: dependencies: '@better-auth/sso': specifier: ^1.4.9 - version: 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(better-auth@1.5.6(aac79383f71d2c0e811dbd569d97ebd4))(better-call@1.3.2(zod@4.3.6)) + version: 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(better-auth@1.5.6(b8a7a1c167ccbf2262fb279c12fb16c6))(better-call@1.3.2(zod@4.3.6)) '@clerk/backend': specifier: ^1.25.2 version: 1.25.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -2060,7 +2078,7 @@ importers: version: link:../../../packages/stateless better-auth: specifier: ^1.4.9 - version: 1.5.6(aac79383f71d2c0e811dbd569d97ebd4) + version: 1.5.6(b8a7a1c167ccbf2262fb279c12fb16c6) better-call: specifier: ^1.3.2 version: 1.3.2(zod@4.3.6) @@ -2075,7 +2093,7 @@ importers: version: 3.1.0 mongoose: specifier: ^8.9.5 - version: 8.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) + version: 8.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) passport: specifier: 0.7.0 version: 0.7.0 @@ -2175,7 +2193,7 @@ importers: version: 3.2.0(date-fns@4.1.0) mongoose: specifier: ^8.9.5 - version: 8.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) + version: 8.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) rxjs: specifier: 7.8.1 version: 7.8.1 @@ -2394,7 +2412,7 @@ importers: version: 7.4.0(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) '@nestjs/terminus': specifier: 10.2.3 - version: 10.2.3(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/axios@3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1))(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))(reflect-metadata@0.2.2)(rxjs@7.8.1) + version: 10.2.3(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/axios@3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1))(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/testing': specifier: 10.4.18 version: 10.4.18(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(@nestjs/platform-express@10.4.18) @@ -2475,7 +2493,7 @@ importers: version: 1.39.0 '@pulsecron/pulse': specifier: 1.6.8 - version: 1.6.8(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) + version: 1.6.8(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) '@segment/analytics-node': specifier: ^1.1.4 version: 1.1.4(encoding@0.1.13) @@ -5074,6 +5092,14 @@ packages: resolution: {integrity: sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==} engines: {node: '>=14.0.0'} + '@azure/msal-common@15.17.0': + resolution: {integrity: sha512-VQ5/gTLFADkwue+FohVuCqlzFPUq4xSrX8jeZe+iwZuY6moliNC8xt86qPVNYdtbQfELDf2Nu6LI+demFPHGgw==} + engines: {node: '>=0.8.0'} + + '@azure/msal-node@3.8.10': + resolution: {integrity: sha512-0Hz7Kx4hs70KZWep/Rd7aw/qOLUF92wUOhn7ZsOuB5xNR/06NL1E2RAI9+UKH1FtvN8nD6mFjH7UKSjv6vOWvQ==} + engines: {node: '>=16'} + '@azure/storage-blob@12.13.0': resolution: {integrity: sha512-t3Q2lvBMJucgTjQcP5+hvEJMAsJSk0qmAnjDLie2td017IiduZbbC9BOcFfmwzR6y6cJdZOuewLCNFmEx9IrXA==} engines: {node: '>=14.0.0'} @@ -6048,6 +6074,21 @@ packages: '@cfworker/json-schema@4.1.1': resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} + '@chat-adapter/shared@4.25.0': + resolution: {integrity: sha512-NoRjEqNN1fyOTwT3AXNXjoy88QHk7FxhQxRus9JdEZBBYsDEuP4B8ekN+BVLQD/rlPcDgQ5TN0UGuiOi9r5nZQ==} + + '@chat-adapter/slack@4.25.0': + resolution: {integrity: sha512-5jTLMzRUB3iyezaxNSxXAdJIX1xQFvMsVyOResrTWs72Mfa9Ae0K69iS7+X/Z2kbgj65w0wR54Oa5HXDt8HQWQ==} + + '@chat-adapter/state-redis@4.25.0': + resolution: {integrity: sha512-XHZ6Kv9vbKP19YaRKXaurnsxzizHpQOkfY0P4JPS8oEGVKSCJ5a9ZEnlsT3guLG8zcO6cFqNR2Br+PmX6fh1Dw==} + + '@chat-adapter/teams@4.25.0': + resolution: {integrity: sha512-oxRy6VgbGaLHvh7RMoxS5ZxJxZIwxJH57/2VUHiPXkmdJfvhFxfdYDJ3pKP3dVZKYKPr4gXAOR2no0MeQmyftQ==} + + '@chat-adapter/whatsapp@4.25.0': + resolution: {integrity: sha512-FOk3Y0BCS3Gbm2tgUEa+84EPe9nLM29bh4fV+6uuB5CVVfYsWYpsHlNNItes9/p+4SKHgkKuecg3Hh7z9i8KKA==} + '@chevrotain/cst-dts-gen@11.0.3': resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} @@ -7955,6 +7996,30 @@ packages: resolution: {integrity: sha512-fNiD3G55ZJGhPOBPMKD/enozj8yxJSYyVJWxRWdcUtw842rvthDHJgUWq9gXQTensFlMHv2wGuCjjivPv53j0A==} hasBin: true + '@microsoft/teams.api@2.0.7': + resolution: {integrity: sha512-SQu7d/alQ3ZKgBX2ur/0VbtxsDLMZb3HmGUVnzIWkvSzFkGcPQ8uPK//670gpEyFJVh2qqP0wFwOwH98/tO57w==} + engines: {node: '>=20'} + + '@microsoft/teams.apps@2.0.7': + resolution: {integrity: sha512-1y7mLrM/HZfRn8tHK/vInMZCpMXjRPQ6QawboNXttJqEQxvlwNRK9nzDjnzuIyBF32oTVt/ro7Id38oNnhaXeQ==} + engines: {node: '>=20'} + + '@microsoft/teams.cards@2.0.7': + resolution: {integrity: sha512-HUGw5OWKc6eCdinRLYqHgFyvScTplQs+PqUqHnf79wH1QNqAKCX+p7uF71YxTm383laJYOqDGYU6uvFEoTvOsA==} + engines: {node: '>=20'} + + '@microsoft/teams.common@2.0.7': + resolution: {integrity: sha512-O3qWC/RbLbiJSAHyk1j5Ybx3GAxmM7DhFbfLW5a2sebEQ+Sn/hB/8rr+IsxlG2FAaUgrcKkir8B55wuKTlZPYw==} + engines: {node: '>=20'} + + '@microsoft/teams.graph-endpoints@2.0.7': + resolution: {integrity: sha512-VYx2CeSqZnjsp8fvVgt0f5PahXk2OKBKUHo1ICPLX/pvzsxjB8+RYU/5dvXVzPweNRTbIJR5gAugzyZwL/1miQ==} + engines: {node: '>=20'} + + '@microsoft/teams.graph@2.0.7': + resolution: {integrity: sha512-hHX1gsCL7GFhAUz1CAT+PFar5U20/nA6sV4yJJaLygu0Wft10XgX3tJh1FckXBQlO1vCaDRtmcMJ9Eey0Z/wRg==} + engines: {node: '>=20'} + '@microsoft/tsdoc-config@0.17.0': resolution: {integrity: sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==} @@ -11336,6 +11401,39 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc + '@redis/bloom@5.11.0': + resolution: {integrity: sha512-KYiVilAhAFN3057afUb/tfYJpsEyTkQB+tQcn5gVVA7DgcNOAj8lLxe4j8ov8BF6I9C1Fe/kwlbuAICcTMX8Lw==} + engines: {node: '>= 18'} + peerDependencies: + '@redis/client': ^5.11.0 + + '@redis/client@5.11.0': + resolution: {integrity: sha512-GHoprlNQD51Xq2Ztd94HHV94MdFZQ3CVrpA04Fz8MVoHM0B7SlbmPEVIjwTbcv58z8QyjnrOuikS0rWF03k5dQ==} + engines: {node: '>= 18'} + peerDependencies: + '@node-rs/xxhash': ^1.1.0 + peerDependenciesMeta: + '@node-rs/xxhash': + optional: true + + '@redis/json@5.11.0': + resolution: {integrity: sha512-1iAy9kAtcD0quB21RbPTbUqqy+T2Uu2JxucwE+B4A+VaDbIRvpZR6DMqV8Iqaws2YxJYB3GC5JVNzPYio2ErUg==} + engines: {node: '>= 18'} + peerDependencies: + '@redis/client': ^5.11.0 + + '@redis/search@5.11.0': + resolution: {integrity: sha512-g1l7f3Rnyk/xI99oGHIgWHSKFl45Re5YTIcO8j/JE8olz389yUFyz2+A6nqVy/Zi031VgPDWscbbgOk8hlhZ3g==} + engines: {node: '>= 18'} + peerDependencies: + '@redis/client': ^5.11.0 + + '@redis/time-series@5.11.0': + resolution: {integrity: sha512-TWFeOcU4xkj0DkndnOyhtxvX1KWD+78UHT3XX3x3XRBUGWeQrKo3jqzDsZwxbggUgf9yLJr/akFHXru66X5UQA==} + engines: {node: '>= 18'} + peerDependencies: + '@redis/client': ^5.11.0 + '@reduxjs/toolkit@2.8.2': resolution: {integrity: sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A==} peerDependencies: @@ -11923,6 +12021,18 @@ packages: Deprecated: no longer maintained and no longer used by Sinon packages. See https://github.com/sinonjs/nise/issues/243 for replacement details. + '@slack/logger@4.0.1': + resolution: {integrity: sha512-6cmdPrV/RYfd2U0mDGiMK8S7OJqpCTm7enMLRR3edccsPX8j7zXTLnaEF4fhxxJJTAIOil6+qZrnUPTuaLvwrQ==} + engines: {node: '>= 18', npm: '>= 8.6.0'} + + '@slack/types@2.20.1': + resolution: {integrity: sha512-eWX2mdt1ktpn8+40iiMc404uGrih+2fxiky3zBcPjtXKj6HLRdYlmhrPkJi7JTJm8dpXR6BWVWEDBXtaWMKD6A==} + engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} + + '@slack/web-api@7.15.0': + resolution: {integrity: sha512-va7zYIt3QHG1x9M/jqXXRPFMoOVlVSSRHC5YH+DzKYsrz5xUKOA3lR4THsu/Zxha9N1jOndbKFKLtr0WOPW1Vw==} + engines: {node: '>= 18', npm: '>= 8.6.0'} + '@smithy/abort-controller@3.1.1': resolution: {integrity: sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==} engines: {node: '>=16.0.0'} @@ -14565,6 +14675,9 @@ packages: webpack-dev-server: optional: true + '@workflow/serde@4.1.0-beta.2': + resolution: {integrity: sha512-8kkeoQKLDaKXefjV5dbhBj2aErfKp1Mc4pb6tj8144cF+Em5SPbyMbyLCHp+BVrFfFVCBluCtMx+jjvaFVZGww==} + '@wry/context@0.4.4': resolution: {integrity: sha512-LrKVLove/zw6h2Md/KZyWxIkFM6AoyKp71OqpH9Hiip1csjPVoD3tPxlbQUNxEnHENks3UGgNpSBCAfq9KWuag==} @@ -15844,6 +15957,9 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + chat@4.25.0: + resolution: {integrity: sha512-QM8ex4Gpn8zYIPyQXh41Who6R9Wq3WcQeOjAy4EuR1m1ha0tASuzHkLQfjaTAGLgrgrThV0Zh5KKoH0S92iwNA==} + check-disk-space@3.4.0: resolution: {integrity: sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==} engines: {node: '>=16'} @@ -19125,6 +19241,9 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} hasBin: true + is-electron@2.2.2: + resolution: {integrity: sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==} + is-extendable@0.1.1: resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} engines: {node: '>=0.10.0'} @@ -20097,6 +20216,10 @@ packages: resolution: {integrity: sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==} engines: {node: '>=14'} + jwks-rsa@3.2.2: + resolution: {integrity: sha512-BqTyEDV+lS8F2trk3A+qJnxV5Q9EqKCBJOPti3W97r7qTympCZjb7h2X6f2kc+0K3rsSTY1/6YG2eaXKoj497w==} + engines: {node: '>=14'} + jws@3.2.3: resolution: {integrity: sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==} @@ -20109,6 +20232,10 @@ packages: jwt-decode@3.1.2: resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==} + jwt-decode@4.0.0: + resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} + engines: {node: '>=18'} + kareem@2.6.3: resolution: {integrity: sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==} engines: {node: '>=12.0.0'} @@ -21055,15 +21182,9 @@ packages: micromark-factory-whitespace@2.0.0: resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==} - micromark-util-character@2.1.0: - resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} - micromark-util-character@2.1.1: resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - micromark-util-chunked@2.0.0: - resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==} - micromark-util-chunked@2.0.1: resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} @@ -21088,9 +21209,6 @@ packages: micromark-util-normalize-identifier@2.0.0: resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==} - micromark-util-resolve-all@2.0.0: - resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==} - micromark-util-resolve-all@2.0.1: resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} @@ -21100,9 +21218,6 @@ packages: micromark-util-subtokenize@2.0.1: resolution: {integrity: sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==} - micromark-util-symbol@2.0.0: - resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} - micromark-util-symbol@2.0.1: resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} @@ -23703,6 +23818,10 @@ packages: resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} engines: {node: '>=4'} + redis@5.11.0: + resolution: {integrity: sha512-YwXjATVDT+AuxcyfOwZn046aml9jMlQPvU1VXIlLDVAExe0u93aTfPYSeRgG4p9Q/Jlkj+LXJ1XEoFV+j2JKcQ==} + engines: {node: '>= 18'} + reduce-flatten@2.0.0: resolution: {integrity: sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==} engines: {node: '>=6'} @@ -23810,6 +23929,9 @@ packages: remend@1.1.0: resolution: {integrity: sha512-JENGyuIhTwzUfCarW43X4r9cehoqTo9QyYxfNDZSud2AmqeuWjZ5pfybasTa4q0dxTJAj5m8NB+wR+YueAFpxQ==} + remend@1.3.0: + resolution: {integrity: sha512-iIhggPkhW3hFImKtB10w0dz4EZbs28mV/dmbcYVonWEJ6UGHHpP+bFZnTh6GNWJONg5m+U56JrL+8IxZRdgWjw==} + remote-content@4.0.1: resolution: {integrity: sha512-W2lDnjK4k1vAJg7UZArH/rkNYJqZuteHkX1jS7tO9TJUiLhDcu2Ejvj97gK/XbZNDhzld0sn11OW8vihin4cAg==} @@ -27444,8 +27566,8 @@ snapshots: '@aws-crypto/sha1-browser': 3.0.0 '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sso-oidc': 3.575.0 - '@aws-sdk/client-sts': 3.575.0(@aws-sdk/client-sso-oidc@3.575.0) + '@aws-sdk/client-sso-oidc': 3.575.0(@aws-sdk/client-sts@3.575.0) + '@aws-sdk/client-sts': 3.575.0 '@aws-sdk/core': 3.575.0 '@aws-sdk/credential-provider-node': 3.575.0(@aws-sdk/client-sso-oidc@3.575.0)(@aws-sdk/client-sts@3.575.0) '@aws-sdk/middleware-bucket-endpoint': 3.575.0 @@ -27741,11 +27863,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.575.0': + '@aws-sdk/client-sso-oidc@3.575.0(@aws-sdk/client-sts@3.575.0)': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sts': 3.575.0(@aws-sdk/client-sso-oidc@3.575.0) + '@aws-sdk/client-sts': 3.575.0 '@aws-sdk/core': 3.575.0 '@aws-sdk/credential-provider-node': 3.575.0(@aws-sdk/client-sso-oidc@3.575.0)(@aws-sdk/client-sts@3.575.0) '@aws-sdk/middleware-host-header': 3.575.0 @@ -27784,6 +27906,7 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.8.1 transitivePeerDependencies: + - '@aws-sdk/client-sts' - aws-crt '@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)': @@ -27962,11 +28085,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sts@3.575.0(@aws-sdk/client-sso-oidc@3.575.0)': + '@aws-sdk/client-sts@3.575.0': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sso-oidc': 3.575.0 + '@aws-sdk/client-sso-oidc': 3.575.0(@aws-sdk/client-sts@3.575.0) '@aws-sdk/core': 3.575.0 '@aws-sdk/credential-provider-node': 3.575.0(@aws-sdk/client-sso-oidc@3.575.0)(@aws-sdk/client-sts@3.575.0) '@aws-sdk/middleware-host-header': 3.575.0 @@ -28005,7 +28128,6 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.8.1 transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - aws-crt '@aws-sdk/client-sts@3.637.0': @@ -28210,7 +28332,7 @@ snapshots: '@aws-sdk/credential-provider-ini@3.575.0(@aws-sdk/client-sso-oidc@3.575.0)(@aws-sdk/client-sts@3.575.0)': dependencies: - '@aws-sdk/client-sts': 3.575.0(@aws-sdk/client-sso-oidc@3.575.0) + '@aws-sdk/client-sts': 3.575.0 '@aws-sdk/credential-provider-env': 3.575.0 '@aws-sdk/credential-provider-process': 3.575.0 '@aws-sdk/credential-provider-sso': 3.575.0(@aws-sdk/client-sso-oidc@3.575.0) @@ -28263,6 +28385,25 @@ snapshots: - aws-crt optional: true + '@aws-sdk/credential-provider-ini@3.637.0(@aws-sdk/client-sts@3.637.0)': + dependencies: + '@aws-sdk/client-sts': 3.637.0 + '@aws-sdk/credential-provider-env': 3.620.1 + '@aws-sdk/credential-provider-http': 3.635.0 + '@aws-sdk/credential-provider-process': 3.620.1 + '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.575.0) + '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/types': 3.609.0 + '@smithy/credential-provider-imds': 3.2.0 + '@smithy/property-provider': 3.1.3 + '@smithy/shared-ini-file-loader': 3.1.4 + '@smithy/types': 3.3.0 + tslib: 2.8.1 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + optional: true + '@aws-sdk/credential-provider-ini@3.972.10': dependencies: '@aws-sdk/core': 3.973.26 @@ -28386,6 +28527,26 @@ snapshots: - aws-crt optional: true + '@aws-sdk/credential-provider-node@3.637.0(@aws-sdk/client-sts@3.637.0)': + dependencies: + '@aws-sdk/credential-provider-env': 3.620.1 + '@aws-sdk/credential-provider-http': 3.635.0 + '@aws-sdk/credential-provider-ini': 3.637.0(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-process': 3.620.1 + '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.575.0) + '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/types': 3.609.0 + '@smithy/credential-provider-imds': 3.2.0 + '@smithy/property-provider': 3.1.3 + '@smithy/shared-ini-file-loader': 3.1.4 + '@smithy/types': 3.3.0 + tslib: 2.8.1 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - '@aws-sdk/client-sts' + - aws-crt + optional: true + '@aws-sdk/credential-provider-node@3.972.11': dependencies: '@aws-sdk/credential-provider-env': 3.972.10 @@ -28524,7 +28685,7 @@ snapshots: '@aws-sdk/credential-provider-web-identity@3.575.0(@aws-sdk/client-sts@3.575.0)': dependencies: - '@aws-sdk/client-sts': 3.575.0(@aws-sdk/client-sso-oidc@3.575.0) + '@aws-sdk/client-sts': 3.575.0 '@aws-sdk/types': 3.575.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 @@ -28563,7 +28724,7 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.575.0)': + '@aws-sdk/credential-providers@3.637.0': dependencies: '@aws-sdk/client-cognito-identity': 3.637.0 '@aws-sdk/client-sso': 3.637.0 @@ -28571,8 +28732,8 @@ snapshots: '@aws-sdk/credential-provider-cognito-identity': 3.637.0 '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-ini': 3.637.0(@aws-sdk/client-sso-oidc@3.575.0)(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.575.0)(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-ini': 3.637.0(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/credential-provider-process': 3.620.1 '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.575.0) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) @@ -28586,7 +28747,7 @@ snapshots: - aws-crt optional: true - '@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))': + '@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.575.0)': dependencies: '@aws-sdk/client-cognito-identity': 3.637.0 '@aws-sdk/client-sso': 3.637.0 @@ -28594,10 +28755,10 @@ snapshots: '@aws-sdk/credential-provider-cognito-identity': 3.637.0 '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-ini': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-ini': 3.637.0(@aws-sdk/client-sso-oidc@3.575.0)(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.575.0)(@aws-sdk/client-sts@3.637.0) '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) + '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.575.0) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -29067,7 +29228,7 @@ snapshots: '@aws-sdk/token-providers@3.575.0(@aws-sdk/client-sso-oidc@3.575.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.575.0 + '@aws-sdk/client-sso-oidc': 3.575.0(@aws-sdk/client-sts@3.575.0) '@aws-sdk/types': 3.575.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 @@ -29076,7 +29237,7 @@ snapshots: '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.575.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.575.0 + '@aws-sdk/client-sso-oidc': 3.575.0(@aws-sdk/client-sts@3.575.0) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 @@ -29405,6 +29566,14 @@ snapshots: dependencies: tslib: 2.8.1 + '@azure/msal-common@15.17.0': {} + + '@azure/msal-node@3.8.10': + dependencies: + '@azure/msal-common': 15.17.0 + jsonwebtoken: 9.0.3 + uuid: 8.3.2 + '@azure/storage-blob@12.13.0(encoding@0.1.13)': dependencies: '@azure/abort-controller': 1.1.0 @@ -30476,7 +30645,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.39.0 '@standard-schema/spec': 1.1.0 - better-call: 1.3.2(zod@4.3.5) + better-call: 1.3.2(zod@4.3.6) jose: 6.1.3 kysely: 0.28.14 nanostores: 1.2.0 @@ -30499,33 +30668,33 @@ snapshots: '@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0) '@better-auth/utils': 0.3.1 - '@better-auth/mongo-adapter@1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(mongodb@6.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))': + '@better-auth/mongo-adapter@1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(mongodb@6.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))': dependencies: '@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0) '@better-auth/utils': 0.3.1 optionalDependencies: - mongodb: 6.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) + mongodb: 6.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) '@better-auth/prisma-adapter@1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)': dependencies: '@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0) '@better-auth/utils': 0.3.1 - '@better-auth/sso@1.4.7(better-auth@1.5.6(f5eede22a65d6cd0c93a8bf1a87b70c5))': + '@better-auth/sso@1.4.7(better-auth@1.5.6(14eb71b76c563af6394284c30ec93f47))': dependencies: '@better-fetch/fetch': 1.1.21 - better-auth: 1.5.6(f5eede22a65d6cd0c93a8bf1a87b70c5) + better-auth: 1.5.6(14eb71b76c563af6394284c30ec93f47) fast-xml-parser: 5.5.8 jose: 6.1.3 samlify: 2.10.2 zod: 4.3.5 - '@better-auth/sso@1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(better-auth@1.5.6(aac79383f71d2c0e811dbd569d97ebd4))(better-call@1.3.2(zod@4.3.6))': + '@better-auth/sso@1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(better-auth@1.5.6(b8a7a1c167ccbf2262fb279c12fb16c6))(better-call@1.3.2(zod@4.3.6))': dependencies: '@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0) '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 - better-auth: 1.5.6(aac79383f71d2c0e811dbd569d97ebd4) + better-auth: 1.5.6(b8a7a1c167ccbf2262fb279c12fb16c6) better-call: 1.3.2(zod@4.3.6) fast-xml-parser: 5.5.8 jose: 6.1.3 @@ -30599,6 +30768,48 @@ snapshots: '@cfworker/json-schema@4.1.1': {} + '@chat-adapter/shared@4.25.0': + dependencies: + chat: 4.25.0 + transitivePeerDependencies: + - supports-color + + '@chat-adapter/slack@4.25.0': + dependencies: + '@chat-adapter/shared': 4.25.0 + '@slack/web-api': 7.15.0 + chat: 4.25.0 + transitivePeerDependencies: + - debug + - supports-color + + '@chat-adapter/state-redis@4.25.0': + dependencies: + chat: 4.25.0 + redis: 5.11.0 + transitivePeerDependencies: + - '@node-rs/xxhash' + - supports-color + + '@chat-adapter/teams@4.25.0': + dependencies: + '@chat-adapter/shared': 4.25.0 + '@microsoft/teams.api': 2.0.7 + '@microsoft/teams.apps': 2.0.7 + '@microsoft/teams.cards': 2.0.7 + '@microsoft/teams.graph-endpoints': 2.0.7 + chat: 4.25.0 + transitivePeerDependencies: + - debug + - supports-color + + '@chat-adapter/whatsapp@4.25.0': + dependencies: + '@chat-adapter/shared': 4.25.0 + chat: 4.25.0 + transitivePeerDependencies: + - supports-color + '@chevrotain/cst-dts-gen@11.0.3': dependencies: '@chevrotain/gast': 11.0.3 @@ -32909,11 +33120,11 @@ snapshots: - ws optional: true - '@langchain/langgraph-checkpoint-mongodb@1.1.6(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(@langchain/core@1.1.18(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.212.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.17.0(ws@8.20.0)(zod@3.25.20))(ws@8.20.0))(@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.18(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.212.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.17.0(ws@8.20.0)(zod@3.25.20))(ws@8.20.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7)': + '@langchain/langgraph-checkpoint-mongodb@1.1.6(@aws-sdk/credential-providers@3.637.0)(@langchain/core@1.1.18(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.212.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.17.0(ws@8.20.0)(zod@3.25.20))(ws@8.20.0))(@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.18(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.212.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.17.0(ws@8.20.0)(zod@3.25.20))(ws@8.20.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7)': dependencies: '@langchain/core': 1.1.18(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.212.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.17.0(ws@8.20.0)(zod@3.25.20))(ws@8.20.0) '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.18(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.212.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.17.0(ws@8.20.0)(zod@3.25.20))(ws@8.20.0)) - mongodb: 6.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) + mongodb: 6.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) transitivePeerDependencies: - '@aws-sdk/credential-providers' - '@mongodb-js/zstd' @@ -33194,6 +33405,48 @@ snapshots: - '@types/node' optional: true + '@microsoft/teams.api@2.0.7': + dependencies: + '@microsoft/teams.cards': 2.0.7 + '@microsoft/teams.common': 2.0.7 + jwt-decode: 4.0.0 + qs: 6.15.0 + transitivePeerDependencies: + - debug + + '@microsoft/teams.apps@2.0.7': + dependencies: + '@azure/msal-node': 3.8.10 + '@microsoft/teams.api': 2.0.7 + '@microsoft/teams.common': 2.0.7 + '@microsoft/teams.graph': 2.0.7 + axios: 1.15.0 + cors: 2.8.5 + express: 5.0.1 + jsonwebtoken: 9.0.3 + jwks-rsa: 3.2.2 + reflect-metadata: 0.2.2 + transitivePeerDependencies: + - debug + - supports-color + + '@microsoft/teams.cards@2.0.7': {} + + '@microsoft/teams.common@2.0.7': + dependencies: + axios: 1.15.0 + transitivePeerDependencies: + - debug + + '@microsoft/teams.graph-endpoints@2.0.7': {} + + '@microsoft/teams.graph@2.0.7': + dependencies: + '@microsoft/teams.common': 2.0.7 + qs: 6.15.0 + transitivePeerDependencies: + - debug + '@microsoft/tsdoc-config@0.17.0': dependencies: '@microsoft/tsdoc': 0.15.0 @@ -33505,7 +33758,7 @@ snapshots: class-transformer: 0.5.1 class-validator: 0.14.1 - '@nestjs/terminus@10.2.3(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/axios@3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1))(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))(reflect-metadata@0.2.2)(rxjs@7.8.1)': + '@nestjs/terminus@10.2.3(@grpc/grpc-js@1.14.3)(@grpc/proto-loader@0.8.0)(@nestjs/axios@3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1))(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7))(reflect-metadata@0.2.2)(rxjs@7.8.1)': dependencies: '@nestjs/common': 10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.18(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.18)(@nestjs/websockets@10.4.18)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -33517,7 +33770,7 @@ snapshots: '@grpc/grpc-js': 1.14.3 '@grpc/proto-loader': 0.8.0 '@nestjs/axios': 3.0.3(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.15.0)(rxjs@7.8.1) - mongoose: 8.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) + mongoose: 8.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) '@nestjs/testing@10.4.18(@nestjs/common@10.4.18(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.18)(@nestjs/platform-express@10.4.18)': dependencies: @@ -35850,14 +36103,14 @@ snapshots: '@protobufjs/utf8@1.1.0': {} - '@pulsecron/pulse@1.6.8(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7)': + '@pulsecron/pulse@1.6.8(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7)': dependencies: cron-parser: 4.9.0 date.js: 0.3.3 debug: 4.3.6 human-interval: 2.0.1 moment-timezone: 0.5.48 - mongodb: 6.8.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) + mongodb: 6.8.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) transitivePeerDependencies: - '@aws-sdk/credential-providers' - '@mongodb-js/zstd' @@ -38185,6 +38438,26 @@ snapshots: dependencies: react: 19.2.3 + '@redis/bloom@5.11.0(@redis/client@5.11.0)': + dependencies: + '@redis/client': 5.11.0 + + '@redis/client@5.11.0': + dependencies: + cluster-key-slot: 1.1.2 + + '@redis/json@5.11.0(@redis/client@5.11.0)': + dependencies: + '@redis/client': 5.11.0 + + '@redis/search@5.11.0(@redis/client@5.11.0)': + dependencies: + '@redis/client': 5.11.0 + + '@redis/time-series@5.11.0(@redis/client@5.11.0)': + dependencies: + '@redis/client': 5.11.0 + '@reduxjs/toolkit@2.8.2(react-redux@9.2.0(@types/react@19.2.8)(react@19.2.3)(redux@5.0.1))(react@19.2.3)': dependencies: '@standard-schema/spec': 1.1.0 @@ -38889,6 +39162,29 @@ snapshots: '@sinonjs/text-encoding@0.7.2': {} + '@slack/logger@4.0.1': + dependencies: + '@types/node': 22.15.13 + + '@slack/types@2.20.1': {} + + '@slack/web-api@7.15.0': + dependencies: + '@slack/logger': 4.0.1 + '@slack/types': 2.20.1 + '@types/node': 22.15.13 + '@types/retry': 0.12.0 + axios: 1.15.0 + eventemitter3: 5.0.4 + form-data: 4.0.5 + is-electron: 2.2.2 + is-stream: 2.0.1 + p-queue: 6.6.2 + p-retry: 4.6.2 + retry: 0.13.1 + transitivePeerDependencies: + - debug + '@smithy/abort-controller@3.1.1': dependencies: '@smithy/types': 3.3.0 @@ -42667,6 +42963,8 @@ snapshots: webpack: 5.105.4(@swc/core@1.7.26(@swc/helpers@0.5.15))(esbuild@0.27.3)(webpack-cli@5.1.4) webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.0)(webpack@5.105.4) + '@workflow/serde@4.1.0-beta.2': {} + '@wry/context@0.4.4': dependencies: '@types/node': 22.15.13 @@ -43640,13 +43938,13 @@ snapshots: jsonpointer: 5.0.1 leven: 3.1.0 - better-auth@1.5.6(aac79383f71d2c0e811dbd569d97ebd4): + better-auth@1.5.6(14eb71b76c563af6394284c30ec93f47): dependencies: '@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0) '@better-auth/drizzle-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1) '@better-auth/kysely-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(kysely@0.28.14) '@better-auth/memory-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1) - '@better-auth/mongo-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(mongodb@6.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7)) + '@better-auth/mongo-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(mongodb@6.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7)) '@better-auth/prisma-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1) '@better-auth/telemetry': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0)) '@better-auth/utils': 0.3.1 @@ -43660,59 +43958,50 @@ snapshots: nanostores: 1.2.0 zod: 4.3.6 optionalDependencies: - '@sveltejs/kit': 2.57.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@2.5.3(svelte@5.53.5)(vite@6.4.2(@types/node@22.15.13)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.77.8)(sugarss@4.0.1(postcss@8.5.8))(terser@5.31.6)(tsx@4.19.0)(yaml@2.8.3)))(svelte@5.53.5)(typescript@5.6.2)(vite@6.4.2(@types/node@22.15.13)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.77.8)(sugarss@4.0.1(postcss@8.5.8))(terser@5.31.6)(tsx@4.19.0)(yaml@2.8.3)) - mongodb: 6.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) + '@sveltejs/kit': 2.57.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@2.5.3(svelte@5.53.5)(vite@6.4.2(@types/node@22.15.13)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.47))(terser@5.31.6)(tsx@4.19.0)(yaml@2.8.3)))(svelte@5.53.5)(typescript@5.6.2)(vite@6.4.2(@types/node@22.15.13)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.47))(terser@5.31.6)(tsx@4.19.0)(yaml@2.8.3)) + mongodb: 6.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) next: 16.2.3(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(babel-plugin-macros@3.1.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.77.8) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) solid-js: 1.9.6 svelte: 5.53.5 - vitest: 2.1.9(@edge-runtime/vm@4.0.2)(@types/node@22.15.13)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.0)(less@4.2.0)(lightningcss@1.30.2)(sass@1.77.8)(sugarss@4.0.1(postcss@8.5.8))(terser@5.31.6)(tsx@4.19.0)(yaml@2.8.3) + vitest: 2.1.9(@edge-runtime/vm@4.0.2)(@types/node@22.15.13)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.0)(less@4.2.0)(lightningcss@1.30.2)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.47))(terser@5.31.6)(tsx@4.19.0)(yaml@2.8.3) transitivePeerDependencies: - '@cloudflare/workers-types' - '@opentelemetry/api' - better-auth@1.5.6(f5eede22a65d6cd0c93a8bf1a87b70c5): + better-auth@1.5.6(b8a7a1c167ccbf2262fb279c12fb16c6): dependencies: '@better-auth/core': 1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0) '@better-auth/drizzle-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1) '@better-auth/kysely-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(kysely@0.28.14) '@better-auth/memory-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1) - '@better-auth/mongo-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(mongodb@6.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7)) + '@better-auth/mongo-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1)(mongodb@6.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7)) '@better-auth/prisma-adapter': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0))(@better-auth/utils@0.3.1) '@better-auth/telemetry': 1.5.6(@better-auth/core@1.5.6(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.0)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.14)(nanostores@1.2.0)) '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 '@noble/ciphers': 2.1.1 '@noble/hashes': 2.0.1 - better-call: 1.3.2(zod@4.3.5) + better-call: 1.3.2(zod@4.3.6) defu: 6.1.6 jose: 6.1.3 kysely: 0.28.14 nanostores: 1.2.0 zod: 4.3.6 optionalDependencies: - '@sveltejs/kit': 2.57.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@2.5.3(svelte@5.53.5)(vite@6.4.2(@types/node@22.15.13)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.47))(terser@5.31.6)(tsx@4.19.0)(yaml@2.8.3)))(svelte@5.53.5)(typescript@5.6.2)(vite@6.4.2(@types/node@22.15.13)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.47))(terser@5.31.6)(tsx@4.19.0)(yaml@2.8.3)) - mongodb: 6.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) + '@sveltejs/kit': 2.57.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@2.5.3(svelte@5.53.5)(vite@6.4.2(@types/node@22.15.13)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.77.8)(sugarss@4.0.1(postcss@8.5.8))(terser@5.31.6)(tsx@4.19.0)(yaml@2.8.3)))(svelte@5.53.5)(typescript@5.6.2)(vite@6.4.2(@types/node@22.15.13)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.30.2)(sass@1.77.8)(sugarss@4.0.1(postcss@8.5.8))(terser@5.31.6)(tsx@4.19.0)(yaml@2.8.3)) + mongodb: 6.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) next: 16.2.3(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(babel-plugin-macros@3.1.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.77.8) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) solid-js: 1.9.6 svelte: 5.53.5 - vitest: 2.1.9(@edge-runtime/vm@4.0.2)(@types/node@22.15.13)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.0)(less@4.2.0)(lightningcss@1.30.2)(sass@1.77.8)(sugarss@4.0.1(postcss@8.4.47))(terser@5.31.6)(tsx@4.19.0)(yaml@2.8.3) + vitest: 2.1.9(@edge-runtime/vm@4.0.2)(@types/node@22.15.13)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.0)(less@4.2.0)(lightningcss@1.30.2)(sass@1.77.8)(sugarss@4.0.1(postcss@8.5.8))(terser@5.31.6)(tsx@4.19.0)(yaml@2.8.3) transitivePeerDependencies: - '@cloudflare/workers-types' - '@opentelemetry/api' - better-call@1.3.2(zod@4.3.5): - dependencies: - '@better-auth/utils': 0.3.1 - '@better-fetch/fetch': 1.1.21 - rou3: 0.7.12 - set-cookie-parser: 3.1.0 - optionalDependencies: - zod: 4.3.5 - better-call@1.3.2(zod@4.3.6): dependencies: '@better-auth/utils': 0.3.1 @@ -44162,6 +44451,18 @@ snapshots: chardet@0.7.0: {} + chat@4.25.0: + dependencies: + '@workflow/serde': 4.1.0-beta.2 + mdast-util-to-string: 4.0.0 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + remend: 1.3.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + check-disk-space@3.4.0: {} check-error@1.0.3: @@ -45312,7 +45613,6 @@ snapshots: ms: 2.1.3 optionalDependencies: supports-color: 8.1.1 - optional: true debug@4.3.1(supports-color@8.1.1): dependencies: @@ -48249,6 +48549,8 @@ snapshots: is-docker@3.0.0: {} + is-electron@2.2.2: {} + is-extendable@0.1.1: {} is-extglob@1.0.0: {} @@ -49880,6 +50182,16 @@ snapshots: transitivePeerDependencies: - supports-color + jwks-rsa@3.2.2: + dependencies: + '@types/jsonwebtoken': 9.0.5 + debug: 4.4.3(supports-color@8.1.1) + jose: 4.15.5 + limiter: 1.1.5 + lru-memoizer: 2.2.0 + transitivePeerDependencies: + - supports-color + jws@3.2.3: dependencies: jwa: 1.4.2 @@ -49894,6 +50206,8 @@ snapshots: jwt-decode@3.1.2: {} + jwt-decode@4.0.0: {} + kareem@2.6.3: {} katex@0.16.28: @@ -50674,7 +50988,7 @@ snapshots: micromark-util-decode-numeric-character-reference: 2.0.1 micromark-util-decode-string: 2.0.0 micromark-util-normalize-identifier: 2.0.0 - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 unist-util-stringify-position: 4.0.0 transitivePeerDependencies: @@ -50686,7 +51000,7 @@ snapshots: ccount: 2.0.1 devlop: 1.1.0 mdast-util-find-and-replace: 3.0.1 - micromark-util-character: 2.1.0 + micromark-util-character: 2.1.1 mdast-util-gfm-footnote@2.0.0: dependencies: @@ -50951,14 +51265,14 @@ snapshots: micromark-factory-space: 2.0.0 micromark-factory-title: 2.0.0 micromark-factory-whitespace: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-chunked: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 micromark-util-classify-character: 2.0.0 micromark-util-html-tag-name: 2.0.0 micromark-util-normalize-identifier: 2.0.0 - micromark-util-resolve-all: 2.0.0 + micromark-util-resolve-all: 2.0.1 micromark-util-subtokenize: 2.0.1 - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 micromark-extension-cjk-friendly-gfm-strikethrough@1.2.3(micromark-util-types@2.0.0)(micromark@4.0.0): @@ -50995,9 +51309,9 @@ snapshots: micromark-extension-gfm-autolink-literal@2.1.0: dependencies: - micromark-util-character: 2.1.0 + micromark-util-character: 2.1.1 micromark-util-sanitize-uri: 2.0.0 - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 micromark-extension-gfm-footnote@2.1.0: @@ -51005,27 +51319,27 @@ snapshots: devlop: 1.1.0 micromark-core-commonmark: 2.0.1 micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 + micromark-util-character: 2.1.1 micromark-util-normalize-identifier: 2.0.0 micromark-util-sanitize-uri: 2.0.0 - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 micromark-extension-gfm-strikethrough@2.1.0: dependencies: devlop: 1.1.0 - micromark-util-chunked: 2.0.0 + micromark-util-chunked: 2.0.1 micromark-util-classify-character: 2.0.0 - micromark-util-resolve-all: 2.0.0 - micromark-util-symbol: 2.0.0 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 micromark-extension-gfm-table@2.1.0: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 micromark-extension-gfm-tagfilter@2.0.0: @@ -51036,8 +51350,8 @@ snapshots: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 micromark-extension-gfm@3.0.0: @@ -51057,45 +51371,40 @@ snapshots: devlop: 1.1.0 katex: 0.16.28 micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 micromark-factory-destination@2.0.0: dependencies: - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 micromark-factory-label@2.0.0: dependencies: devlop: 1.1.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 micromark-factory-space@2.0.0: dependencies: - micromark-util-character: 2.1.0 + micromark-util-character: 2.1.1 micromark-util-types: 2.0.0 micromark-factory-title@2.0.0: dependencies: micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 micromark-factory-whitespace@2.0.0: dependencies: micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 - micromark-util-types: 2.0.0 - - micromark-util-character@2.1.0: - dependencies: - micromark-util-symbol: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 micromark-util-character@2.1.1: @@ -51103,35 +51412,31 @@ snapshots: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 - micromark-util-chunked@2.0.0: - dependencies: - micromark-util-symbol: 2.0.0 - micromark-util-chunked@2.0.1: dependencies: micromark-util-symbol: 2.0.1 micromark-util-classify-character@2.0.0: dependencies: - micromark-util-character: 2.1.0 - micromark-util-symbol: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 micromark-util-combine-extensions@2.0.0: dependencies: - micromark-util-chunked: 2.0.0 + micromark-util-chunked: 2.0.1 micromark-util-types: 2.0.0 micromark-util-decode-numeric-character-reference@2.0.1: dependencies: - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 micromark-util-decode-string@2.0.0: dependencies: decode-named-character-reference: 1.0.2 - micromark-util-character: 2.1.0 + micromark-util-character: 2.1.1 micromark-util-decode-numeric-character-reference: 2.0.1 - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 micromark-util-encode@2.0.0: {} @@ -51139,11 +51444,7 @@ snapshots: micromark-util-normalize-identifier@2.0.0: dependencies: - micromark-util-symbol: 2.0.0 - - micromark-util-resolve-all@2.0.0: - dependencies: - micromark-util-types: 2.0.0 + micromark-util-symbol: 2.0.1 micromark-util-resolve-all@2.0.1: dependencies: @@ -51151,19 +51452,17 @@ snapshots: micromark-util-sanitize-uri@2.0.0: dependencies: - micromark-util-character: 2.1.0 + micromark-util-character: 2.1.1 micromark-util-encode: 2.0.0 - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 micromark-util-subtokenize@2.0.1: dependencies: devlop: 1.1.0 - micromark-util-chunked: 2.0.0 - micromark-util-symbol: 2.0.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 - micromark-util-symbol@2.0.0: {} - micromark-util-symbol@2.0.1: {} micromark-util-types@2.0.0: {} @@ -51176,16 +51475,16 @@ snapshots: devlop: 1.1.0 micromark-core-commonmark: 2.0.1 micromark-factory-space: 2.0.0 - micromark-util-character: 2.1.0 - micromark-util-chunked: 2.0.0 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 micromark-util-combine-extensions: 2.0.0 micromark-util-decode-numeric-character-reference: 2.0.1 micromark-util-encode: 2.0.0 micromark-util-normalize-identifier: 2.0.0 - micromark-util-resolve-all: 2.0.0 + micromark-util-resolve-all: 2.0.1 micromark-util-sanitize-uri: 2.0.0 micromark-util-subtokenize: 2.0.1 - micromark-util-symbol: 2.0.0 + micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.0 transitivePeerDependencies: - supports-color @@ -51446,33 +51745,33 @@ snapshots: gcp-metadata: 5.3.0(encoding@0.1.13) socks: 2.8.7 - mongodb@6.20.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7): + mongodb@6.20.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7): dependencies: '@mongodb-js/saslprep': 1.4.4 bson: 6.10.4 mongodb-connection-string-url: 3.0.2 optionalDependencies: - '@aws-sdk/credential-providers': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) + '@aws-sdk/credential-providers': 3.637.0 gcp-metadata: 5.3.0(encoding@0.1.13) socks: 2.8.7 - mongodb@6.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7): + mongodb@6.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7): dependencies: '@mongodb-js/saslprep': 1.4.4 bson: 6.10.4 mongodb-connection-string-url: 3.0.2 optionalDependencies: - '@aws-sdk/credential-providers': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) + '@aws-sdk/credential-providers': 3.637.0 gcp-metadata: 5.3.0(encoding@0.1.13) socks: 2.8.7 - mongodb@6.8.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7): + mongodb@6.8.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7): dependencies: '@mongodb-js/saslprep': 1.1.8 bson: 6.8.0 mongodb-connection-string-url: 3.0.1 optionalDependencies: - '@aws-sdk/credential-providers': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) + '@aws-sdk/credential-providers': 3.637.0 gcp-metadata: 5.3.0(encoding@0.1.13) socks: 2.8.7 @@ -51499,11 +51798,11 @@ snapshots: - socks - supports-color - mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7): + mongoose@8.21.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7): dependencies: bson: 6.10.4 kareem: 2.6.3 - mongodb: 6.20.0(@aws-sdk/credential-providers@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)))(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) + mongodb: 6.20.0(@aws-sdk/credential-providers@3.637.0)(gcp-metadata@5.3.0(encoding@0.1.13))(socks@2.8.7) mpath: 0.9.0 mquery: 5.0.0 ms: 2.1.3 @@ -51636,7 +51935,7 @@ snapshots: needle@3.2.0: dependencies: - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) iconv-lite: 0.6.3 sax: 1.5.0 transitivePeerDependencies: @@ -52966,7 +53265,7 @@ snapshots: portfinder@1.0.32: dependencies: async: 2.6.4 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) mkdirp: 0.5.6 transitivePeerDependencies: - supports-color @@ -54301,6 +54600,16 @@ snapshots: dependencies: redis-errors: 1.2.0 + redis@5.11.0: + dependencies: + '@redis/bloom': 5.11.0(@redis/client@5.11.0) + '@redis/client': 5.11.0 + '@redis/json': 5.11.0(@redis/client@5.11.0) + '@redis/search': 5.11.0(@redis/client@5.11.0) + '@redis/time-series': 5.11.0(@redis/client@5.11.0) + transitivePeerDependencies: + - '@node-rs/xxhash' + reduce-flatten@2.0.0: {} redux-thunk@3.1.0(redux@5.0.1): @@ -54451,6 +54760,8 @@ snapshots: remend@1.1.0: {} + remend@1.3.0: {} + remote-content@4.0.1: dependencies: axios: 1.15.0