n8n
Version:
n8n Workflow Automation Tool
117 lines • 4.87 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SuspendedRunRestorer = void 0;
const instance_ai_1 = require("@n8n/instance-ai");
const n8n_workflow_1 = require("n8n-workflow");
function getErrorMessage(error) {
return error instanceof Error ? error.message : String(error);
}
class SuspendedRunRestorer {
constructor(options) {
this.logger = options.logger;
this.pendingConfirmationRepo = options.pendingConfirmationRepo;
this.runState = options.runState;
this.dbSnapshotStorage = options.dbSnapshotStorage;
this.eventBus = options.eventBus;
this.rebuilder = options.rebuilder;
}
async resolveOrphanedConfirmation(userId, requestId, data) {
let orphan;
try {
orphan = await this.pendingConfirmationRepo.claim(requestId, userId);
}
catch (error) {
this.logger.warn('Failed to claim orphaned pending confirmation', {
requestId,
error: getErrorMessage(error),
});
return null;
}
if (!orphan)
return null;
this.logger.info('Reclaiming pending confirmation orphaned by a process restart', {
requestId,
threadId: orphan.threadId,
runId: orphan.runId,
kind: orphan.kind,
hasCheckpoint: Boolean(orphan.checkpointKey),
});
if (orphan.kind === 'suspended' && this.canResumeOrphan(orphan)) {
const resumed = await this.tryResumeFromOrphan(orphan, data);
if (resumed) {
return resumed;
}
}
this.finalizeUnresumableOrphan(orphan);
throw new n8n_workflow_1.UserError('This confirmation was lost when the assistant restarted. Send a new message to continue.');
}
canResumeOrphan(orphan) {
return Boolean(orphan.toolCallId && orphan.checkpointKey);
}
finalizeUnresumableOrphan(orphan) {
try {
this.publishRunFinish(orphan.threadId, orphan.runId, 'restart_lost_confirmation');
void this.dbSnapshotStorage
.markRunCancelled(orphan.threadId, orphan.runId)
.catch((error) => {
this.logger.warn('Failed to mark orphan snapshot as cancelled', {
requestId: orphan.requestId,
threadId: orphan.threadId,
runId: orphan.runId,
error: getErrorMessage(error),
});
});
}
catch (error) {
this.logger.warn('Failed to finalize orphaned confirmation snapshot', {
requestId: orphan.requestId,
error: getErrorMessage(error),
});
}
}
publishRunFinish(threadId, runId, reason) {
this.eventBus.publish(threadId, {
type: 'run-finish',
runId,
agentId: (0, instance_ai_1.orchestratorAgentId)(runId),
payload: { status: 'cancelled', reason },
});
}
async tryResumeFromOrphan(orphan, data) {
const outcome = await this.rebuilder.rebuildSuspendedRun(orphan);
switch (outcome.kind) {
case 'ready':
this.runState.suspendRun(orphan.threadId, outcome.state);
return await this.rebuilder.resumeSuspendedRun(orphan.userId, orphan.requestId, data);
case 'no-user':
this.logger.warn('Cannot resume orphaned run: user no longer authorized', {
requestId: orphan.requestId,
userId: orphan.userId,
});
return null;
case 'no-checkpoint':
this.logger.warn('Cannot resume orphaned run: checkpoint missing or unavailable', {
requestId: orphan.requestId,
checkpointKey: orphan.checkpointKey,
...(outcome.error ? { error: getErrorMessage(outcome.error) } : {}),
});
return null;
case 'env-failure':
this.logger.warn('Cannot resume orphaned run: failed to build execution environment', {
requestId: orphan.requestId,
threadId: orphan.threadId,
error: getErrorMessage(outcome.error),
});
return null;
case 'agent-failure':
this.logger.warn('Cannot resume orphaned run: failed to build agent', {
requestId: orphan.requestId,
threadId: orphan.threadId,
error: getErrorMessage(outcome.error),
});
return null;
}
}
}
exports.SuspendedRunRestorer = SuspendedRunRestorer;
//# sourceMappingURL=suspended-run-restorer.service.js.map