UNPKG

n8n

Version:

n8n Workflow Automation Tool

288 lines • 11.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.executeContextToolOperation = executeContextToolOperation; exports.executeActionToolBatch = executeActionToolBatch; exports.executeActionToolOperation = executeActionToolOperation; const is_record_1 = require("@n8n/utils/is-record"); const integration_tool_definitions_1 = require("./integration-tool-definitions"); const integration_error_codes_1 = require("./integration-error-codes"); async function executeContextToolOperation(params) { const { operation, descriptor, messageContextStore, queryExecutor, persistence } = params; if (isCurrentContextQuery(operation.query)) { if (!persistence) { return { ok: false, error: { code: integration_error_codes_1.INTEGRATION_ERROR_CODES.NO_THREAD_CONTEXT, message: 'There is no current agent thread context.', }, }; } const context = await messageContextStore.getLatest(persistence.threadId); if (!context || context.integrationConnectionId !== descriptor.integrationConnectionId) { if (operation.query === 'get_current_message_context') { return { ok: true, context: null }; } if (operation.query === 'get_current_subject') { return { ok: true, subject: null }; } return { ok: false, error: { code: integration_error_codes_1.INTEGRATION_ERROR_CODES.NO_MESSAGE_CONTEXT, message: 'There is no current message context for this integration connection.', }, }; } if (operation.query === 'get_current_message_context') { return { ok: true, context }; } if (operation.query === 'get_current_subject') { return { ok: true, subject: context.subject ?? null }; } if (operation.query === 'get_current_user') { if (!context.interactingUserId) { return { ok: false, error: { code: integration_error_codes_1.INTEGRATION_ERROR_CODES.NO_INTERACTING_USER_CONTEXT, message: 'The latest message context does not include an interacting user ID.', }, }; } return await queryExecutor.execute({ descriptor, query: 'get_user', input: { userId: context.interactingUserId }, persistence, }); } const channelId = getTargetChannelId(context.target); if (!channelId) { return { ok: false, error: { code: integration_error_codes_1.INTEGRATION_ERROR_CODES.NO_CHANNEL_CONTEXT, message: 'The latest message context does not include a channel ID.', }, }; } return await queryExecutor.execute({ descriptor, query: 'get_channel_info', input: { channelId }, persistence, }); } return await queryExecutor.execute({ descriptor, query: operation.query, input: operation.input, persistence, }); } async function executeActionToolBatch(params) { const { operations, descriptor, messageContextStore, actionExecutor, ctx } = params; let currentMessageContext = ctx.persistence !== undefined ? await getOptionalCurrentContext({ descriptor, messageContextStore, threadId: ctx.persistence.threadId, }) : undefined; const results = []; for (const operation of operations) { const result = await executeActionToolOperation({ operation, descriptor, messageContextStore, actionExecutor, ctx, currentMessageContext, allowSuspend: false, }); const nextMessageContext = extractSuccessfulMessageContext(result); if (nextMessageContext) currentMessageContext = nextMessageContext; results.push({ action: operation.action, result }); } return { ok: true, results }; } async function executeActionToolOperation(params) { const { operation, descriptor, messageContextStore, actionExecutor, ctx, interruptCtx, allowSuspend, } = params; const persistence = ctx.persistence; const message = parseMessage(operation.input.message); const actionInput = message === undefined ? operation.input : { ...operation.input, message }; const awaitsResponse = shouldAwaitResponse(message); if (awaitsResponse && !allowSuspend) { return { ok: false, error: { code: integration_error_codes_1.INTEGRATION_ERROR_CODES.ACTION_FAILED, message: 'Batch actions cannot include cards that wait for a user response. Send that action separately.', }, }; } let currentMessageContext = params.currentMessageContext; if (operation.action === 'respond') { if (!currentMessageContext) { const contextResult = await getRespondContext({ descriptor, messageContextStore, persistence, }); if (!contextResult.ok) { return contextResult; } currentMessageContext = contextResult.context; } } else if (!currentMessageContext && persistence) { currentMessageContext = await getOptionalCurrentContext({ descriptor, messageContextStore, threadId: persistence.threadId, }); } const result = await actionExecutor.execute({ descriptor, action: operation.action, input: actionInput, awaitResponse: awaitsResponse, runId: ctx.runId, toolCallId: ctx.toolCallId, currentMessageContext, }); if (!result.ok) return result; let actionResult = result; if (result.messageContext && persistence) { const messageContext = withPreviousSubject(result.messageContext, currentMessageContext); await messageContextStore.setLatest(persistence.threadId, persistence.resourceId, messageContext); actionResult = { ...result, messageContext }; } if (!awaitsResponse) return actionResult; return await interruptCtx?.suspend({ type: 'integration_action', action: operation.action, integrationConnectionId: descriptor.integrationConnectionId, messageContext: actionResult.messageContext, }); } function isCurrentContextQuery(query) { return (query === 'get_current_message_context' || query === 'get_current_subject' || query === 'get_current_user' || query === 'get_current_channel_info'); } function parseMessage(value) { const result = integration_tool_definitions_1.messageSchema.safeParse(value); return result.success ? result.data : undefined; } function shouldAwaitResponse(message) { const card = message?.card; if (card?.awaitResponse === true) return true; return card?.components.some(isInteractiveCardComponent) ?? false; } function isInteractiveCardComponent(component) { switch (component.type) { case 'button': case 'select': case 'radio_select': return true; case 'section': return component.button !== undefined; default: return false; } } function withPreviousSubject(context, previousContext) { if (!previousContext) return context; if (context.integrationConnectionId !== previousContext.integrationConnectionId) return context; const replyExpectation = context.replyExpectation ?? previousContext.replyExpectation; const replyTarget = context.replyTarget ?? previousContext.replyTarget ?? (replyExpectation ? previousContext.target : undefined); const replyMessageId = context.replyMessageId ?? previousContext.replyMessageId ?? (replyExpectation ? previousContext.messageId : undefined); return { ...context, ...(!context.subject && previousContext.subject ? { subject: previousContext.subject } : {}), ...(!context.agentUserId && previousContext.agentUserId ? { agentUserId: previousContext.agentUserId } : {}), ...(replyExpectation ? { replyExpectation } : {}), ...(replyTarget ? { replyTarget } : {}), ...(replyMessageId ? { replyMessageId } : {}), }; } function extractSuccessfulMessageContext(result) { if (!(0, is_record_1.isRecord)(result) || result.ok !== true) return undefined; const messageContext = result.messageContext; return isIntegrationMessageContext(messageContext) ? messageContext : undefined; } function isIntegrationMessageContext(value) { return ((0, is_record_1.isRecord)(value) && typeof value.integrationConnectionId === 'string' && typeof value.platform === 'string' && (0, is_record_1.isRecord)(value.target) && (value.agentUserId === undefined || typeof value.agentUserId === 'string') && typeof value.updatedAt === 'string'); } async function getOptionalCurrentContext(params) { try { const context = await params.messageContextStore.getLatest(params.threadId); if (context?.integrationConnectionId !== params.descriptor.integrationConnectionId) { return undefined; } return context; } catch { return undefined; } } async function getRespondContext(params) { const { descriptor, messageContextStore, persistence } = params; if (!persistence) { return { ok: false, error: { code: integration_error_codes_1.INTEGRATION_ERROR_CODES.NO_MESSAGE_CONTEXT, message: 'There is no current message context. Use an explicit send action.', }, }; } const context = await messageContextStore.getLatest(persistence.threadId); if (!context) { return { ok: false, error: { code: integration_error_codes_1.INTEGRATION_ERROR_CODES.NO_MESSAGE_CONTEXT, message: 'There is no current message context. Use an explicit send action.', }, }; } if (context.integrationConnectionId !== descriptor.integrationConnectionId) { return { ok: false, error: { code: integration_error_codes_1.INTEGRATION_ERROR_CODES.NO_MESSAGE_CONTEXT_FOR_INTEGRATION, message: 'The latest message context belongs to another integration connection.', }, }; } return { ok: true, context }; } function getTargetChannelId(target) { if (target.type === 'thread' || target.type === 'channel') { return target.channelId; } return undefined; } //# sourceMappingURL=integration-tool-execution.js.map