n8n
Version:
n8n Workflow Automation Tool
423 lines • 23 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentRuntimeReconstructionService = void 0;
const agents_1 = require("@n8n/agents");
const http_proxy_agent_1 = require("@n8n/ai-utilities/http-proxy-agent");
const api_types_1 = require("@n8n/api-types");
const backend_common_1 = require("@n8n/backend-common");
const backend_network_1 = require("@n8n/backend-network");
const config_1 = require("@n8n/config");
const db_1 = require("@n8n/db");
const di_1 = require("@n8n/di");
const n8n_workflow_1 = require("n8n-workflow");
const nanoid_1 = require("nanoid");
const active_executions_1 = require("../../active-executions");
const constants_1 = require("../../constants");
const credentials_finder_service_1 = require("../../credentials/credentials-finder.service");
const node_execution_1 = require("../../node-execution");
const oauth_service_1 = require("../../oauth/oauth.service");
const check_access_1 = require("../../permissions.ee/check-access");
const ai_service_1 = require("../../services/ai.service");
const proxy_token_manager_1 = require("../../services/proxy-token-manager");
const url_service_1 = require("../../services/url.service");
const ai_proxy_fetch_1 = require("../../utils/ai-proxy-fetch");
const workflow_finder_service_1 = require("../../workflows/workflow-finder.service");
const agent_knowledge_gate_1 = require("./agent-knowledge-gate");
const agent_knowledge_sandbox_service_1 = require("./agent-knowledge-sandbox.service");
const agent_chat_integration_1 = require("./integrations/agent-chat-integration");
const integration_tools_1 = require("./integrations/integration-tools");
const n8n_checkpoint_storage_1 = require("./integrations/n8n-checkpoint-storage");
const n8n_memory_1 = require("./integrations/n8n-memory");
const from_json_config_1 = require("./json-config/from-json-config");
const mcp_client_factory_1 = require("./json-config/mcp-client-factory");
const model_config_1 = require("./json-config/model-config");
const agent_file_repository_1 = require("./repositories/agent-file.repository");
const agent_repository_1 = require("./repositories/agent.repository");
const agent_secure_runtime_1 = require("./runtime/agent-secure-runtime");
const delegate_sub_agent_tool_1 = require("./sub-agents/delegate-sub-agent-tool");
const sub_agent_foreground_runner_1 = require("./sub-agents/sub-agent-foreground-runner");
const tool_registry_1 = require("./tool-registry");
const environment_tool_1 = require("./tools/environment-tool");
const workflow_tool_workflow_resolver_1 = require("./tools/workflow-tool-workflow-resolver");
const sub_agent_resolver_1 = require("./utils/sub-agent-resolver");
async function getChatIntegrationToolServices() {
const { IntegrationMessageContextService } = await import('./integrations/integration-message-context.service.js');
const { ChatIntegrationActionExecutor } = await import('./integrations/integration-action-executor.js');
const { ChatIntegrationContextQueryExecutor } = await import('./integrations/integration-context-query-executor.js');
return {
messageContextStore: di_1.Container.get(IntegrationMessageContextService),
actionExecutor: di_1.Container.get(ChatIntegrationActionExecutor),
queryExecutor: di_1.Container.get(ChatIntegrationContextQueryExecutor),
};
}
async function getWorkflowRunner() {
const { WorkflowRunner } = await import('../../workflow-runner.js');
return di_1.Container.get(WorkflowRunner);
}
let AgentRuntimeReconstructionService = class AgentRuntimeReconstructionService {
constructor(logger, agentRepository, agentFileRepository, activeExecutions, workflowRepository, urlService, n8nCheckpointStorage, secureRuntime, ephemeralNodeExecutor, n8nMemory, oauthService, agentsConfig, aiService, outboundHttp, agentKnowledgeSandboxService, ssrfConfig, ssrfProtectionService, credentialsFinderService, workflowFinderService) {
this.logger = logger;
this.agentRepository = agentRepository;
this.agentFileRepository = agentFileRepository;
this.activeExecutions = activeExecutions;
this.workflowRepository = workflowRepository;
this.urlService = urlService;
this.n8nCheckpointStorage = n8nCheckpointStorage;
this.secureRuntime = secureRuntime;
this.ephemeralNodeExecutor = ephemeralNodeExecutor;
this.n8nMemory = n8nMemory;
this.oauthService = oauthService;
this.agentsConfig = agentsConfig;
this.aiService = aiService;
this.outboundHttp = outboundHttp;
this.agentKnowledgeSandboxService = agentKnowledgeSandboxService;
this.ssrfConfig = ssrfConfig;
this.ssrfProtectionService = ssrfProtectionService;
this.credentialsFinderService = credentialsFinderService;
this.workflowFinderService = workflowFinderService;
}
async reconstructFromAgentEntity(agentEntity, credentialProvider, integrationType, user, instrumentation) {
let config = agentEntity.schema;
if (!config) {
throw new n8n_workflow_1.UserError('Agent has no JSON config.');
}
if (user && config.tools?.length) {
config = {
...config,
tools: await this.filterToolsForUser(config.tools, agentEntity.projectId, user),
};
}
const toolsByName = {};
const toolDescriptors = {};
for (const [_toolId, toolEntry] of Object.entries(agentEntity.tools ?? {})) {
toolsByName[toolEntry.descriptor.name] = toolEntry.code;
toolDescriptors[_toolId] = toolEntry.descriptor;
}
const subAgentDelegation = await this.createSubAgentDelegationConfig(config, agentEntity.projectId);
return await this.reconstructRuntime({
config,
memoryOwnerAgentId: agentEntity.id,
projectId: agentEntity.projectId,
credentialProvider,
toolDescriptors,
toolCodeByName: toolsByName,
skills: agentEntity.skills ?? {},
runtimeProfile: 'top-level',
parentAgentIdForDelegation: agentEntity.id,
integrationType,
credentialIntegrations: agentEntity.integrations ?? [],
subAgentDelegation,
user,
instrumentation,
});
}
async filterToolsForUser(tools, projectId, user) {
const canExecute = await (0, check_access_1.userHasScopes)(user, ['workflow:execute'], false, { projectId });
const filtered = [];
for (const ref of tools) {
if (ref.type === 'custom') {
filtered.push(ref);
continue;
}
if (!canExecute)
continue;
if (ref.type === 'node') {
const credentialIds = Object.values(ref.node.credentials ?? {})
.map((credential) => credential.id)
.filter((id) => Boolean(id));
const accessibleCredentials = await Promise.all(credentialIds.map(async (id) => await this.credentialsFinderService.findCredentialForUser(id, user, [
'credential:read',
])));
if (accessibleCredentials.some((credential) => credential === null))
continue;
filtered.push(ref);
continue;
}
const workflow = await (0, workflow_tool_workflow_resolver_1.findWorkflowToolWorkflow)(this.workflowRepository, ref.workflow, projectId);
if (!workflow)
continue;
const accessibleWorkflow = await this.workflowFinderService.findWorkflowForUser(workflow.id, user, ['workflow:execute']);
if (!accessibleWorkflow)
continue;
filtered.push(ref);
}
return filtered;
}
async reconstructFromResolvedSource(params) {
let config = params.config;
if (params.user && config.tools?.length) {
config = {
...config,
tools: await this.filterToolsForUser(config.tools, params.projectId, params.user),
};
}
const subAgentDelegation = await this.createSubAgentDelegationConfig(config, params.projectId);
return await this.reconstructRuntime({
...params,
config,
credentialIntegrations: [],
subAgentDelegation,
});
}
async reconstructRuntime(options) {
const { config, memoryOwnerAgentId, projectId, credentialProvider, toolDescriptors, toolCodeByName, skills, runtimeProfile, parentAgentIdForDelegation, integrationType, credentialIntegrations, subAgentDelegation, user, instrumentation, } = options;
const toolExecutor = this.secureRuntime.createToolExecutor(toolCodeByName);
const toolResolver = this.makeToolResolver(projectId, instrumentation);
const resolvedTools = [];
const aiProxyFetch = (0, ai_proxy_fetch_1.createAiProxyFetch)(this.outboundHttp);
const aiMcpFetch = instrumentation?.mcpFetch ??
(0, ai_proxy_fetch_1.createAiMcpFetch)(this.outboundHttp, this.ssrfConfig, this.ssrfProtectionService);
const webSearchFetch = (0, ai_proxy_fetch_1.createWebSearchFetch)(this.outboundHttp, this.ssrfConfig, this.ssrfProtectionService);
const buildMcpClient = async (server) => await (0, mcp_client_factory_1.buildMcpClientForServer)(server, {
credentialProvider,
oauthService: this.oauthService,
projectId,
proxyFetch: aiMcpFetch,
onConnectionFailed: (event) => {
this.logger.warn('Skipped MCP server that failed to connect', {
agentId: memoryOwnerAgentId,
serverName: event.server,
error: event.error,
});
},
...(instrumentation?.onMcpToolCallSettled !== undefined && {
onToolCallSettled: async (event) => await instrumentation.onMcpToolCallSettled?.({
serverName: server.name,
...event,
}),
}),
});
const reconstructed = await (0, from_json_config_1.buildFromJson)(config, toolDescriptors, {
toolExecutor,
credentialProvider,
resolveTool: async (ref) => {
const resolved = await toolResolver(ref);
if (resolved)
resolvedTools.push(resolved);
return resolved;
},
skills,
memoryFactory: this.getMemoryFactory(memoryOwnerAgentId),
buildMcpClient,
resolveManagedEmbeddingProviderOptions: async () => await this.resolveManagedEmbeddingProviderOptions(projectId),
modelFetch: instrumentation?.modelFetch ?? aiProxyFetch,
fallbackWebSearch: instrumentation?.webSearch,
attachAuthPendingMcpServers: instrumentation?.mcpFetch !== undefined,
webSearchFetch,
});
await this.injectRuntimeDependencies({
agent: reconstructed,
agentId: memoryOwnerAgentId,
projectId,
credentialProvider,
runtimeProfile,
config,
subAgentDelegation,
parentAgentIdForDelegation: parentAgentIdForDelegation ?? memoryOwnerAgentId,
integrationType,
credentialIntegrations,
user,
instrumentation,
});
return { agent: reconstructed, toolRegistry: (0, tool_registry_1.buildToolRegistry)(resolvedTools) };
}
async createSubAgentDelegationConfig(config, projectId) {
const configuredAgents = config.subAgents?.agents ?? [];
const sourcesById = {};
const availableSubAgents = [];
for (const { agentId, agent, useWhen } of await (0, sub_agent_resolver_1.resolveUniqueSubAgents)({
refs: configuredAgents,
projectId,
agentRepository: this.agentRepository,
})) {
if (!agent?.activeVersionId)
continue;
sourcesById[agentId] = { agentId };
availableSubAgents.push({
id: agentId,
name: agent.name,
...(useWhen ? { useWhen } : {}),
});
}
return { sourcesById, availableSubAgents };
}
getMemoryFactory(agentId) {
return (_params) => this.n8nMemory.getImplementation(agentId);
}
async resolveManagedEmbeddingProviderOptions(ownerId) {
if (!this.aiService.isProxyEnabled())
return null;
const client = await this.aiService.getClient();
const baseURL = client.getApiProxyBaseUrl().replace(/\/$/, '') + '/openai/';
const tokenManager = new proxy_token_manager_1.ProxyTokenManager(async () => {
return await client.getBuilderApiProxyToken({ id: ownerId }, { userMessageId: (0, nanoid_1.nanoid)() });
});
return {
baseURL,
apiKey: 'proxy-managed',
fetch: async (input, init) => {
const headers = new Headers(init?.headers);
const auth = await tokenManager.getAuthHeaders();
for (const [key, value] of Object.entries(auth)) {
headers.set(key, value);
}
for (const [key, value] of Object.entries((0, api_types_1.buildProxyHeaders)({ feature: 'agent-builder', n8nVersion: constants_1.N8N_VERSION }))) {
headers.set(key, value);
}
return await (0, http_proxy_agent_1.proxyFetch)(input, { ...init, headers });
},
};
}
makeToolResolver(projectId, instrumentation) {
const instrumentToolAdditionalData = instrumentation?.configureToolAdditionalData;
return async (ref) => {
if (ref.type === 'workflow') {
const { resolveWorkflowTool } = await import('./tools/workflow-tool-factory.js');
return await resolveWorkflowTool(ref, {
workflowRepository: this.workflowRepository,
workflowRunner: await getWorkflowRunner(),
activeExecutions: this.activeExecutions,
projectId,
webhookBaseUrl: this.urlService.getWebhookBaseUrl(),
instrumentToolAdditionalData,
});
}
if (ref.type === 'node') {
const { resolveNodeTool } = await import('./tools/node-tool-factory.js');
return await resolveNodeTool(ref, {
executor: this.ephemeralNodeExecutor,
projectId,
instrumentToolAdditionalData,
});
}
return null;
};
}
async injectRuntimeDependencies(params) {
const { agent, agentId, projectId, credentialProvider, runtimeProfile, config, subAgentDelegation, parentAgentIdForDelegation, integrationType, credentialIntegrations, user, instrumentation, } = params;
agent.tool((0, environment_tool_1.createGetEnvironmentTool)());
if (runtimeProfile !== 'inline' &&
(0, agent_knowledge_gate_1.isAgentKnowledgeBaseEnabled)(this.agentsConfig, this.aiService.isProxyEnabled()) &&
(await this.agentFileRepository.hasFilesForAgent(agentId))) {
const { createKnowledgeRetrievalTools } = await import('./tools/knowledge/search-knowledge.tool.js');
agent.tool(createKnowledgeRetrievalTools({
projectId,
agentId,
sandboxService: this.agentKnowledgeSandboxService,
}));
}
if (runtimeProfile === 'top-level') {
const includeN8nChat = integrationType === api_types_1.N8N_CHAT_INTEGRATION_TYPE;
if (credentialIntegrations.length > 0 || includeN8nChat) {
const integrationRegistry = di_1.Container.get(agent_chat_integration_1.ChatIntegrationRegistry);
const { messageContextStore, actionExecutor, queryExecutor } = await getChatIntegrationToolServices();
const descriptors = (0, integration_tools_1.getIntegrationToolConnectionDescriptors)(credentialIntegrations, agentId, (integrationConfig) => {
const integrationDef = integrationRegistry.get(integrationConfig.type);
return {
contextToolDefinitions: integrationDef?.contextToolDefinitions,
actionToolDefinitions: integrationDef?.actionToolDefinitions,
contextQueries: integrationDef?.contextQueries,
actions: integrationDef?.actions,
contextToolGuidance: integrationDef?.contextToolGuidance,
actionToolGuidance: integrationDef?.actionToolGuidance,
};
});
if (includeN8nChat) {
const n8nChat = integrationRegistry.require(api_types_1.N8N_CHAT_INTEGRATION_TYPE);
const n8nChatIntegration = {
type: api_types_1.N8N_CHAT_INTEGRATION_TYPE,
};
descriptors.push({
agentId,
integration: n8nChatIntegration,
integrationConnectionId: api_types_1.N8N_CHAT_INTEGRATION_TYPE,
contextToolName: api_types_1.N8N_CHAT_CONTEXT_TOOL_NAME,
actionToolName: api_types_1.N8N_CHAT_ACTION_TOOL_NAME,
contextQueries: [...n8nChat.contextQueries],
actions: [...n8nChat.actions],
contextToolDefinitions: [...n8nChat.contextToolDefinitions],
actionToolDefinitions: [...n8nChat.actionToolDefinitions],
contextToolGuidance: n8nChat.contextToolGuidance,
actionToolGuidance: n8nChat.actionToolGuidance,
});
}
for (const descriptor of descriptors) {
agent.tool((0, integration_tools_1.createIntegrationContextTool)({ descriptor, messageContextStore, queryExecutor }));
agent.tool((0, integration_tools_1.createIntegrationActionTool)({ descriptor, messageContextStore, actionExecutor }));
}
}
}
if (runtimeProfile === 'top-level') {
await this.attachSubAgentDelegationTool({
agent,
config,
parentAgentId: parentAgentIdForDelegation,
projectId,
credentialProvider,
delegation: subAgentDelegation,
user,
instrumentation,
});
this.attachWriteTodosTool(agent, agentId);
}
if (runtimeProfile !== 'inline' && !agent.hasCheckpointStorage()) {
agent.checkpoint(this.n8nCheckpointStorage.getStorage(agentId));
}
}
async attachSubAgentDelegationTool(params) {
const { agent, config, parentAgentId, projectId, credentialProvider, delegation, user, instrumentation, } = params;
const inlineSubAgentModelsByDifficulty = await this.resolveInlineSubAgentModelsByDifficulty(config, credentialProvider);
agent.tool((0, delegate_sub_agent_tool_1.createN8nDelegateSubAgentTool)({
runner: di_1.Container.get(sub_agent_foreground_runner_1.SubAgentForegroundRunner),
...delegation,
projectId,
parentAgentId,
credentialProvider,
user,
instrumentation,
policy: this.buildSubAgentPolicy(config),
...(inlineSubAgentModelsByDifficulty !== undefined
? { inlineSubAgentModelsByDifficulty }
: {}),
resolveInlineSubAgentProviderTools: (modelConfig) => (0, from_json_config_1.buildProviderToolsForModel)(config, modelConfig),
}));
this.logger.debug('Injected delegate_subagent tool', { agentId: parentAgentId });
}
async resolveInlineSubAgentModelsByDifficulty(config, credentialProvider) {
const mappings = config.subAgents?.modelsByDifficulty;
if (!mappings)
return undefined;
const resolved = {};
for (const difficulty of api_types_1.SUB_AGENT_TASK_DIFFICULTIES) {
const mapping = mappings[difficulty];
if (!mapping)
continue;
resolved[difficulty] = await (0, model_config_1.resolveCredentialAwareModelConfig)(mapping.model, mapping.credential, credentialProvider);
}
return Object.keys(resolved).length > 0 ? resolved : undefined;
}
attachWriteTodosTool(agent, agentId) {
agent.tool((0, agents_1.createWriteTodosTool)());
this.logger.debug('Injected write_todos tool', { agentId });
}
buildSubAgentPolicy(config) {
return {
maxChildren: config.subAgents?.maxChildren ?? api_types_1.SUB_AGENT_MAX_CHILDREN_DEFAULT,
};
}
};
exports.AgentRuntimeReconstructionService = AgentRuntimeReconstructionService;
exports.AgentRuntimeReconstructionService = AgentRuntimeReconstructionService = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [backend_common_1.Logger, agent_repository_1.AgentRepository, agent_file_repository_1.AgentFileRepository, active_executions_1.ActiveExecutions, db_1.WorkflowRepository, url_service_1.UrlService, n8n_checkpoint_storage_1.N8NCheckpointStorage, agent_secure_runtime_1.AgentSecureRuntime, node_execution_1.EphemeralNodeExecutor, n8n_memory_1.N8nMemory, oauth_service_1.OauthService, config_1.AgentsConfig, ai_service_1.AiService, backend_network_1.OutboundHttp, agent_knowledge_sandbox_service_1.AgentKnowledgeSandboxService, config_1.SsrfProtectionConfig, backend_network_1.SsrfProtectionService, credentials_finder_service_1.CredentialsFinderService, workflow_finder_service_1.WorkflowFinderService])
], AgentRuntimeReconstructionService);
//# sourceMappingURL=agent-runtime-reconstruction.service.js.map