n8n
Version:
n8n Workflow Automation Tool
240 lines • 11.2 kB
JavaScript
;
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