UNPKG

n8n

Version:

n8n Workflow Automation Tool

240 lines • 11.2 kB
"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.AgentTestRunService = exports.InvalidAgentTestRunCheckpointError = exports.agentTestRunContinuationSchema = void 0; exports.parseStandardApprovalSuspension = parseStandardApprovalSuspension; exports.collectStandardApprovals = collectStandardApprovals; const node_crypto_1 = require("node:crypto"); const node_util_1 = require("node:util"); const agents_1 = require("@n8n/agents"); const tool_1 = require("@n8n/agents/tool"); const api_types_1 = require("@n8n/api-types"); const di_1 = require("@n8n/di"); const n8n_workflow_1 = require("n8n-workflow"); const zod_1 = require("zod"); const agent_execution_orchestrator_service_1 = require("./agent-execution-orchestrator.service"); const agent_execution_service_1 = require("./agent-execution.service"); const agent_validation_service_1 = require("./agent-validation.service"); const n8n_checkpoint_storage_1 = require("./integrations/n8n-checkpoint-storage"); const agent_memory_scope_1 = require("./utils/agent-memory-scope"); exports.agentTestRunContinuationSchema = zod_1.z .object({ runId: zod_1.z.string(), toolCallId: zod_1.z.string(), sessionId: zod_1.z.string(), response: zod_1.z.string(), }) .strict(); const expectedApprovalResumeJsonSchema = (0, agents_1.zodToJsonSchema)(tool_1.APPROVAL_RESUME_SCHEMA); class InvalidAgentTestRunCheckpointError extends n8n_workflow_1.UserError { constructor() { super('This test run can no longer be resumed.'); this.code = 'invalid_checkpoint'; } } exports.InvalidAgentTestRunCheckpointError = InvalidAgentTestRunCheckpointError; function parseStandardApprovalSuspension(suspension) { const payload = tool_1.APPROVAL_SUSPEND_SCHEMA.safeParse(suspension.suspendPayload); if (!payload.success || !(0, node_util_1.isDeepStrictEqual)(suspension.resumeSchema, expectedApprovalResumeJsonSchema)) { return undefined; } return payload.data; } function collectStandardApprovals(result) { const approvals = []; for (const suspension of result.suspensions) { const approval = parseStandardApprovalSuspension(suspension); if (!approval) return undefined; approvals.push({ ...approval, continuation: { runId: suspension.runId, toolCallId: suspension.toolCallId, sessionId: result.sessionId, response: result.response, }, }); } return approvals; } let AgentTestRunService = class AgentTestRunService { constructor(agentExecutionService, agentValidationService, agentExecutionOrchestratorService, n8nCheckpointStorage) { this.agentExecutionService = agentExecutionService; this.agentValidationService = agentValidationService; this.agentExecutionOrchestratorService = agentExecutionOrchestratorService; this.n8nCheckpointStorage = n8nCheckpointStorage; } async prepareDraftRun({ agentId, projectId, sessionId, credentialProvider, }) { if (sessionId) { const existing = await this.agentExecutionService.findThreadById(sessionId); if (existing && !(0, agent_execution_service_1.threadBelongsTo)(existing, projectId, agentId)) { return { status: 'session_not_found' }; } } const { missing } = await this.agentValidationService.validateAgentIsRunnable(agentId, projectId, credentialProvider); if (missing.length > 0) return { status: 'agent_misconfigured', missing }; return { status: 'ready', sessionId: sessionId ?? (0, node_crypto_1.randomUUID)() }; } streamDraftRun({ agentId, projectId, message, user, sessionId, attachments, source, onExecutionRecorded, abortSignal, }) { return this.agentExecutionOrchestratorService.executeForChat({ agentId, projectId, message, user, memory: { threadId: sessionId, resourceId: (0, agent_memory_scope_1.draftChatMemoryResourceId)(user.id), }, attachments, source, onExecutionRecorded, abortSignal, }); } async executeDraftRun(input) { const prepared = await this.prepareDraftRun(input); if (prepared.status !== 'ready') return prepared; let executionId; const stream = this.streamDraftRun({ ...input, sessionId: prepared.sessionId, onExecutionRecorded: (id) => { executionId = id; }, }); return await this.collectDraftRun(stream, prepared.sessionId, '', () => executionId); } async resumeDraftRun(input) { const existing = await this.agentExecutionService.findThreadById(input.sessionId); if (existing && !(0, agent_execution_service_1.threadBelongsTo)(existing, input.projectId, input.agentId)) { return { status: 'session_not_found' }; } let executionId; const stream = this.agentExecutionOrchestratorService.resumeForChat({ agentId: input.agentId, projectId: input.projectId, runId: input.runId, toolCallId: input.toolCallId, resumeData: input.resumeData, user: input.user, usePublishedVersion: false, integrationType: api_types_1.N8N_CHAT_INTEGRATION_TYPE, expectedMemory: { threadId: input.sessionId, resourceId: (0, agent_memory_scope_1.draftChatMemoryResourceId)(input.user.id), }, source: input.source, onExecutionRecorded: (id) => { executionId = id; }, ...(input.abortSignal ? { abortSignal: input.abortSignal } : {}), }); return await this.collectDraftRun(stream, input.sessionId, input.response, () => executionId); } async resumeDraftApproval(input) { const continuation = exports.agentTestRunContinuationSchema.safeParse(input.continuation); if (!continuation.success) throw new InvalidAgentTestRunCheckpointError(); let checkpoint; try { checkpoint = await this.n8nCheckpointStorage.load(continuation.data.runId, input.agentId); } catch (error) { if (error instanceof n8n_workflow_1.UserError) throw new InvalidAgentTestRunCheckpointError(); throw error; } const pendingToolCall = checkpoint?.pendingToolCalls[continuation.data.toolCallId]; const expectedResourceId = (0, agent_memory_scope_1.draftChatMemoryResourceId)(input.user.id); if (checkpoint?.status !== 'suspended' || checkpoint.persistence?.delegated === true || checkpoint.persistence?.threadId !== continuation.data.sessionId || checkpoint.persistence?.resourceId !== expectedResourceId || !pendingToolCall?.suspended || pendingToolCall.runId !== continuation.data.runId || !parseStandardApprovalSuspension({ runId: pendingToolCall.runId, toolCallId: pendingToolCall.toolCallId, toolName: pendingToolCall.toolName, suspendPayload: pendingToolCall.suspendPayload, resumeSchema: pendingToolCall.resumeSchema, })) { throw new InvalidAgentTestRunCheckpointError(); } return await this.resumeDraftRun({ agentId: input.agentId, projectId: input.projectId, sessionId: continuation.data.sessionId, runId: continuation.data.runId, toolCallId: continuation.data.toolCallId, resumeData: { approved: input.approved }, user: input.user, source: input.source, response: continuation.data.response, ...(input.abortSignal ? { abortSignal: input.abortSignal } : {}), }); } async cancelSuspendedRuns({ agentId, suspensions, userId, }) { const runIds = [...new Set(suspensions.map(({ runId }) => runId))]; try { const cancellations = await Promise.all(runIds.map(async (runId) => await this.agentExecutionOrchestratorService.cancelChatRun({ agentId, runId, resourceId: (0, agent_memory_scope_1.draftChatMemoryResourceId)(userId), }))); return cancellations.every(Boolean); } catch { return false; } } async collectDraftRun(stream, sessionId, initialResponse, getExecutionId) { let response = initialResponse; const suspensions = []; for await (const chunk of stream) { if (chunk.type === 'error') { throw chunk.error; } if (chunk.type === 'text-delta') { response += chunk.delta; } else if (chunk.type === 'tool-call-suspended') { suspensions.push({ runId: chunk.runId, toolCallId: chunk.toolCallId, toolName: chunk.toolName, ...(chunk.input !== undefined ? { input: chunk.input } : {}), ...(chunk.suspendPayload !== undefined ? { suspendPayload: chunk.suspendPayload } : {}), ...(chunk.resumeSchema !== undefined ? { resumeSchema: chunk.resumeSchema } : {}), }); } } const executionId = getExecutionId(); const metadata = { response, sessionId, ...(executionId ? { executionId } : {}), }; return suspensions.length > 0 ? { status: 'suspended', ...metadata, suspensions } : { status: 'completed', ...metadata }; } }; exports.AgentTestRunService = AgentTestRunService; exports.AgentTestRunService = AgentTestRunService = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [agent_execution_service_1.AgentExecutionService, agent_validation_service_1.AgentValidationService, agent_execution_orchestrator_service_1.AgentExecutionOrchestratorService, n8n_checkpoint_storage_1.N8NCheckpointStorage]) ], AgentTestRunService); //# sourceMappingURL=agent-test-run.service.js.map