n8n
Version:
n8n Workflow Automation Tool
275 lines • 12.1 kB
JavaScript
"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