UNPKG

n8n

Version:

n8n Workflow Automation Tool

275 lines 12.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AgentChatBridge = void 0; const di_1 = require("@n8n/di"); const agent_memory_scope_1 = require("../utils/agent-memory-scope"); const agent_chat_integration_1 = require("./agent-chat-integration"); const agent_chat_hitl_resume_handler_1 = require("./agent-chat-hitl-resume-handler"); const agent_chat_message_context_1 = require("./agent-chat-message-context"); const agent_chat_stream_consumer_1 = require("./agent-chat-stream-consumer"); const agent_chat_suspension_cards_1 = require("./agent-chat-suspension-cards"); const callback_store_1 = require("./callback-store"); const integration_message_context_service_1 = require("./integration-message-context.service"); const types_1 = require("./types"); class AgentChatBridge { constructor(chat, agentId, agentService, componentMapper, logger, n8nProjectId, integration, messageContextStore) { this.chat = chat; this.agentId = agentId; this.agentService = agentService; this.componentMapper = componentMapper; this.logger = logger; this.n8nProjectId = n8nProjectId; this.integration = integration; this.integrationImpl = di_1.Container.get(agent_chat_integration_1.ChatIntegrationRegistry).get(integration.type); this.messageContextBridge = new agent_chat_message_context_1.AgentChatMessageContextBridge(messageContextStore, integration, agentId, logger); if (this.integrationImpl?.needsShortCallbackData) { this.callbackStore = new callback_store_1.CallbackStore(); } const disableStreaming = this.integrationImpl?.disableStreaming ?? false; this.streamConsumer = new agent_chat_stream_consumer_1.AgentChatStreamConsumer({ disableStreaming, logger: this.logger, postErrorToThread: this.postErrorToThread.bind(this), handleSuspension: this.handleSuspension.bind(this), handleMessage: this.handleMessage.bind(this), }); this.hitlResumeHandler = new agent_chat_hitl_resume_handler_1.AgentChatHitlResumeHandler({ agentId, projectId: n8nProjectId, integration, agentService, logger, callbackStore: this.callbackStore, resolvePlatformThreadId: this.resolvePlatformThreadId.bind(this), toAgentThreadId: this.toAgentThreadId.bind(this), getPlatformAgentContext: this.getPlatformAgentContext.bind(this), messageContextBridge: this.messageContextBridge, streamConsumer: this.streamConsumer, createResumeExecutionContext: async (thread) => { const params = { chat: this.chat, thread, logger: this.logger, agentId: this.agentId, }; const resumeExecutionContext = await this.integrationImpl?.createResumeExecutionContext?.(params); if (resumeExecutionContext) return resumeExecutionContext; return {}; }, }); this.registerHandlers(); } static create(chat, agentId, agentService, componentMapper, logger, n8nProjectId, integration) { const agentExecutor = { async *executeForChatPublished({ memory, agentId: aid, message, integrationType }) { yield* agentService.executeForChatPublished({ agentId: aid, projectId: n8nProjectId, message, memory: { threadId: memory.threadId.id, resourceId: memory.resourceId, ...(memory.resourceId !== undefined && { resourceId: memory.resourceId, }), }, integrationType, }); }, async *resumeForChat(config) { yield* agentService.resumeForChat(config); }, }; return new AgentChatBridge(chat, agentId, agentExecutor, componentMapper, logger, n8nProjectId, integration, di_1.Container.get(integration_message_context_service_1.IntegrationMessageContextService)); } registerHandlers() { this.chat.onNewMention(async (thread, message) => { try { if (!this.canUserAccess(message.author)) return; await thread.subscribe(); await this.executeAndStream(thread, message, { isNewMention: true }); } catch (error) { await this.postErrorToThread(thread, error); } }); this.chat.onSubscribedMessage(async (thread, message) => { try { if (!this.canUserAccess(message.author)) return; await this.executeAndStream(thread, message, { isNewMention: false }); } catch (error) { await this.postErrorToThread(thread, error); } }); this.chat.onAction(async (event) => { try { if (!this.canUserAccess(event.user)) return; await this.hitlResumeHandler.handleAction(event); } catch (error) { await this.postErrorToThread(event.thread, error); } }); } dispose() { this.callbackStore?.dispose(); } canUserAccess(author) { return this.integrationImpl?.isUserAllowed?.(author, this.integration) ?? true; } resolvePlatformThreadId(thread) { return this.integrationImpl?.formatThreadId?.fromSdk(thread) ?? thread.id; } toAgentThreadId(platformThreadId) { return (0, types_1.toInternalThreadId)(`${this.agentId}:${platformThreadId}`); } getShortenCallback() { if (!this.callbackStore) return undefined; const store = this.callbackStore; return async (actionId, value) => { const key = await store.store(actionId, value); return { id: key, value: '' }; }; } async executeAndStream(thread, message, options) { const { isNewMention } = options; const platformAgentContext = this.getPlatformAgentContext(); const text = this.prepareInboundText(message.text, platformAgentContext).trim(); if (!text) return; const platformThreadId = this.resolvePlatformThreadId(thread); const threadId = this.toAgentThreadId(platformThreadId); const statusRetry = new AbortController(); const [bridgeExecutionContext, subject] = await Promise.all([ this.resolveBridgeExecutionContext(thread, message, platformAgentContext, statusRetry, isNewMention), this.messageContextBridge.resolveSubject(message), ]); const statusHandle = (0, agent_chat_integration_1.onceStatusHandle)(bridgeExecutionContext.statusHandle); try { await this.messageContextBridge.updateLatest(threadId.id, message.author.userId, thread, { messageId: message.id, interactingUserId: message.author.userId, ...bridgeExecutionContext.platformAgentContext, subject, }); const agentInput = bridgeExecutionContext.historyContext ? `${bridgeExecutionContext.historyContext}\n\n${text}` : text; const stream = this.agentService.executeForChatPublished({ agentId: this.agentId, projectId: this.n8nProjectId, message: agentInput, memory: { threadId, resourceId: (0, agent_memory_scope_1.integrationMemoryResourceId)(this.integration.type, message.author.userId), }, integrationType: this.integration.type, }); await this.streamConsumer.consume(stream, thread, { forceBuffered: bridgeExecutionContext.forceBuffered, statusHandle, }); } finally { statusRetry.abort(); await statusHandle?.clearBeforeResponse(); } } async resolveBridgeExecutionContext(thread, message, platformAgentContext, statusRetry, isNewMention) { return ((await this.integrationImpl?.createBridgeExecutionContext?.({ chat: this.chat, thread, message, logger: this.logger, agentId: this.agentId, statusRetry, isNewMention, })) ?? { platformAgentContext }); } async handleSuspension(chunk, thread) { const { runId, toolCallId, suspendPayload } = chunk; if (!runId || !toolCallId) { this.logger.warn('[AgentChatBridge] Suspended chunk missing runId or toolCallId'); return 'failed'; } const cardPayload = (0, agent_chat_suspension_cards_1.buildSuspendCardPayload)(suspendPayload); if (!cardPayload) return 'skipped'; try { const card = await this.componentMapper.toCard(cardPayload, runId, toolCallId, chunk.resumeSchema, this.getShortenCallback(), this.integration.type); await thread.post({ card }); return 'posted'; } catch (error) { this.logger.error('[AgentChatBridge] Failed to post suspension card', { agentId: this.agentId, runId, toolCallId, error: error instanceof Error ? error.message : String(error), }); return 'failed'; } } async handleMessage(chunk, thread) { const agentMessage = chunk.message; if (!('content' in agentMessage) || !Array.isArray(agentMessage.content)) return false; const textParts = agentMessage.content .filter((part) => part.type === 'text' && 'text' in part) .map((part) => part.text); const textToPost = textParts.join(''); if (!textToPost.trim()) return false; try { await thread.post(textToPost); return true; } catch (error) { this.logger.error('[AgentChatBridge] Failed to post message chunk', { agentId: this.agentId, threadId: thread.id, error: error instanceof Error ? error.message : String(error), }); return false; } } getPlatformAgentContext() { return this.integrationImpl?.getPlatformAgentContext?.(this.chat) ?? {}; } prepareInboundText(text, context) { const trimmed = text?.trim() ?? ''; return this.integrationImpl?.prepareInboundText?.(trimmed, context) ?? trimmed; } async postErrorToThread(thread, error) { const message = error instanceof Error ? error.message : 'An unexpected error occurred'; this.logger.error('[AgentChatBridge] Error in handler', { agentId: this.agentId, threadId: thread?.id, error: message, }); try { if (!thread) { this.logger.warn("[AgentChatBridge] Couldn't post error message because thread is not set", { agentId: this.agentId, error: message, }); return; } await thread.post('⚠️ Something went wrong while processing your request. Please try again.'); } catch (postError) { this.logger.error('[AgentChatBridge] Failed to post error message', { agentId: this.agentId, error: postError instanceof Error ? postError.message : String(postError), }); } } } exports.AgentChatBridge = AgentChatBridge; //# sourceMappingURL=agent-chat-bridge.js.map