n8n
Version:
n8n Workflow Automation Tool
123 lines • 5.02 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InstanceAiLivenessService = exports.INSTANCE_AI_RUN_TIMEOUT_REASON = void 0;
const constants_1 = require("@n8n/constants");
const ORCHESTRATOR_AGENT_ID = 'agent-001';
exports.INSTANCE_AI_RUN_TIMEOUT_REASON = 'timeout';
const RUN_TIMEOUT_MESSAGE = 'The run stopped making progress, so I cancelled it. You can retry or adjust the request.';
function getErrorMessage(error) {
return error instanceof Error ? error.message : String(error);
}
class InstanceAiLivenessService {
constructor(options) {
this.options = options;
this.timedOutRunIds = new Set();
this.timedOutActiveRunThreads = new Set();
}
get backgroundTaskIdleTimeoutMs() {
return this.options.backgroundTaskIdleTimeoutMs;
}
start() {
if (!this.options.policy.hasEnabledTimeouts())
return;
this.timeoutInterval = setInterval(() => {
void this.sweepTimedOutWork();
}, constants_1.Time.minutes.toMilliseconds);
}
shutdown() {
if (this.timeoutInterval) {
clearInterval(this.timeoutInterval);
this.timeoutInterval = undefined;
}
this.timedOutRunIds.clear();
this.timedOutActiveRunThreads.clear();
}
clearThreadState(threadId) {
this.timedOutActiveRunThreads.delete(threadId);
}
markRunTimedOut(runId) {
this.timedOutRunIds.add(runId);
}
consumeRunTimedOut(runId) {
const timedOut = this.timedOutRunIds.has(runId);
if (timedOut)
this.timedOutRunIds.delete(runId);
return timedOut;
}
hasTimedOutActiveRunThread(threadId) {
return this.timedOutActiveRunThreads.has(threadId);
}
async sweepTimedOutWork(now = Date.now()) {
const { activeThreadIds, suspendedThreadIds, confirmationRequestIds } = this.options.runState.sweepTimedOut(this.options.policy, now);
for (const threadId of activeThreadIds) {
this.options.logger.debug('Cancelling timed-out active run', { threadId });
this.cancelTimedOutActiveRun(threadId);
}
for (const threadId of suspendedThreadIds) {
this.options.logger.debug('Auto-rejecting timed-out suspended run', { threadId });
this.cancelTimedOutSuspendedRun(threadId);
}
for (const reqId of confirmationRequestIds) {
this.options.logger.debug('Auto-rejecting timed-out sub-agent confirmation', {
requestId: reqId,
});
const pending = this.options.runState.getPendingConfirmation(reqId);
if (pending) {
const runId = this.options.runState.getActiveRunId(pending.threadId);
if (runId)
this.publishRunTimeoutNotice(pending.threadId, runId);
}
this.options.runState.rejectPendingConfirmation(reqId);
}
try {
const timedOutTasks = await this.options.backgroundTasks.timeoutTimedOutTasks(this.options.policy, now);
for (const task of timedOutTasks) {
this.options.logger.debug('Timed out background task', {
threadId: task.threadId,
taskId: task.taskId,
role: task.role,
timeoutReason: task.timeoutReason,
});
}
}
catch (error) {
this.options.logger.warn('Failed to sweep timed-out background tasks', {
error: getErrorMessage(error),
});
}
}
cancelTimedOutActiveRun(threadId) {
const active = this.options.runState.cancelActiveRun(threadId);
if (!active)
return;
this.markRunTimedOut(active.runId);
this.timedOutActiveRunThreads.add(threadId);
this.publishRunTimeoutNotice(threadId, active.runId);
active.abortController.abort();
}
cancelTimedOutSuspendedRun(threadId) {
const suspended = this.options.runState.cancelSuspendedRun(threadId);
if (!suspended)
return;
this.markRunTimedOut(suspended.runId);
suspended.abortController.abort();
this.options.finalizeCancelledSuspendedRun(suspended, exports.INSTANCE_AI_RUN_TIMEOUT_REASON);
}
publishRunTimeoutNotice(threadId, runId) {
const responseId = `run-timeout:${runId}`;
const alreadyPublished = this.options.eventBus
.getEventsForRun(threadId, runId)
.some((event) => event.responseId === responseId);
if (alreadyPublished)
return;
this.options.eventBus.publish(threadId, {
type: 'text-delta',
runId,
agentId: ORCHESTRATOR_AGENT_ID,
responseId,
payload: { text: RUN_TIMEOUT_MESSAGE },
});
}
}
exports.InstanceAiLivenessService = InstanceAiLivenessService;
//# sourceMappingURL=instance-ai-liveness.service.js.map