@sema-agent/core
Version:
Stateless, task-oriented AI agent core
84 lines (83 loc) • 2.74 kB
JavaScript
export const DEFAULT_MOD_BUDGET_MS = 10_000;
export const CATCH_BUDGET_MS = 1_000;
export const RUNAWAY_AFTER_MS = 5_000;
export const POST_ABORT_GRACE_MS = 1_000;
export const MAX_TIMER_MS = 2_147_483_647;
export function resolveModBudgetMs(supplied, report, name) {
if (supplied === undefined)
return DEFAULT_MOD_BUDGET_MS;
if (typeof supplied === "number" && Number.isFinite(supplied) && supplied >= 0 && supplied <= MAX_TIMER_MS)
return supplied;
const got = typeof supplied === "number" ? String(supplied) : typeof supplied;
report(new Error(`${name}: budgetMs must be a non-negative finite number no greater than ${MAX_TIMER_MS} (got ${got}) — ` +
`every handler of this registration falls back to the ${DEFAULT_MOD_BUDGET_MS}ms default`));
return DEFAULT_MOD_BUDGET_MS;
}
export function createSelfBudget(ms, onExpire) {
let remaining = ms;
let runningSince;
let timer;
let pauses = 0;
let done = false;
let expired = false;
let fire = () => { };
const expiredP = new Promise((res) => (fire = res));
const left = () => (runningSince === undefined ? remaining : Math.max(0, remaining - (Date.now() - runningSince)));
const arm = () => {
runningSince = Date.now();
timer = setTimeout(() => {
if (done)
return;
expired = true;
done = true;
remaining = 0;
runningSince = undefined;
fire("expired");
onExpire();
}, remaining);
};
arm();
return {
expired: expiredP,
isExpired: () => expired,
pause() {
if (pauses++ !== 0 || done)
return;
clearTimeout(timer);
remaining = left();
runningSince = undefined;
},
resume() {
if (pauses === 0)
return;
if (--pauses !== 0 || done)
return;
arm();
},
clear() {
if (done)
return;
remaining = left();
runningSince = undefined;
done = true;
clearTimeout(timer);
},
usedMs: () => ms - left(),
reading: () => Object.freeze({ ms, remainingMs: left() }),
};
}
export function watchRunaway(settled, onRunaway) {
let over = false;
const mark = () => {
over = true;
};
settled.then(mark, mark);
unrefTimer(setTimeout(() => {
if (!over)
onRunaway();
}, RUNAWAY_AFTER_MS));
}
export function unrefTimer(timer) {
if (typeof timer === "object" && timer !== null && "unref" in timer && typeof timer.unref === "function")
timer.unref();
}