Files
boss/local-agent/master-task-timeout.mjs

69 lines
1.9 KiB
JavaScript

const DEFAULT_PROJECT_UNDERSTANDING_TIMEOUT_MS = 8 * 60 * 1000;
const DEFAULT_MASTER_REPLY_TIMEOUT_MS = 15 * 60 * 1000;
const DEFAULT_THREAD_EXECUTION_TIMEOUT_MS = 30 * 60 * 1000;
function normalizeTimeoutMs(value) {
const parsed = Number(value);
if (!Number.isFinite(parsed) || parsed <= 0) {
return null;
}
return Math.round(parsed);
}
export function resolveMasterAgentTaskTimeoutMs(config = {}, task = {}) {
if (task?.projectUnderstandingTargetProjectId) {
return (
normalizeTimeoutMs(config.projectUnderstandingTaskTimeoutMs) ??
DEFAULT_PROJECT_UNDERSTANDING_TIMEOUT_MS
);
}
if (
task?.taskType === "dispatch_execution" ||
(task?.taskType === "conversation_reply" && task?.projectId !== "master-agent")
) {
return (
normalizeTimeoutMs(config.threadExecutionTaskTimeoutMs) ??
DEFAULT_THREAD_EXECUTION_TIMEOUT_MS
);
}
return normalizeTimeoutMs(config.masterAgentReplyTimeoutMs) ?? DEFAULT_MASTER_REPLY_TIMEOUT_MS;
}
export async function runWithTaskTimeout({ timeoutMs, label, onTimeout }, operation) {
const normalizedTimeoutMs = normalizeTimeoutMs(timeoutMs);
if (!normalizedTimeoutMs) {
return await operation();
}
return await new Promise((resolve, reject) => {
let settled = false;
const finish = (callback, value) => {
if (settled) {
return;
}
settled = true;
clearTimeout(timer);
callback(value);
};
const timer = setTimeout(async () => {
try {
await onTimeout?.();
} catch {
// Timeout cleanup is best-effort. The caller still gets a timeout error.
}
finish(
reject,
new Error(`${label || "master_agent_task"} exceeded timeout after ${normalizedTimeoutMs}ms`),
);
}, normalizedTimeoutMs);
Promise.resolve()
.then(operation)
.then((value) => finish(resolve, value))
.catch((error) => finish(reject, error));
});
}