openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
77 lines (76 loc) • 3.24 kB
JavaScript
import { d as toErrorObject } from "./error-coercion-W9BtigRm.mjs";
import "./errors-BvOkmQM5.mjs";
//#region src/agents/embedded-agent-runner/run/abortable.ts
/** Marks AbortErrors produced by abortable() so provider aborts stay retryable. */
const OPENCLAW_ABORTABLE_WRAPPER = Symbol.for("openclaw.abortable.wrapper");
function isOpenClawAbortableWrapper(err) {
return err !== null && typeof err === "object" && OPENCLAW_ABORTABLE_WRAPPER in err;
}
function createAbortableError(signal) {
const reason = signal.reason;
const err = new Error(reason instanceof Error ? reason.message : "aborted", reason ? { cause: reason } : void 0);
return Object.assign(err, {
name: "AbortError",
[OPENCLAW_ABORTABLE_WRAPPER]: true
});
}
const RUN_LIVENESS_JOIN_TIMEOUT_MS = 12e4;
function finishRunLivenessJoin(completion) {
completion.finish?.();
}
/**
* Awaits post-turn work that must never dead-end the run: races the joined
* promise against the run-abort signal and a liveness deadline. Timeout and
* abort RESOLVE (timeout after `onTimeout`) instead of rejecting so settlement
* still produces a visible terminal outcome; rejections also resolve because
* the joined chains own their error logging.
*/
function joinWithRunLivenessDeadline(input) {
return new Promise((resolve) => {
const finish = (reason) => {
if (!completion.finish) return;
completion.finish = void 0;
clearTimeout(timer);
input.runAbortSignal?.removeEventListener("abort", onAbort);
if (reason === "timeout") input.onTimeout();
resolve();
};
const completion = { finish: () => finish("settled") };
const onSettled = finishRunLivenessJoin.bind(void 0, completion);
const onAbort = () => finish("abort");
const timer = setTimeout(() => finish("timeout"), input.timeoutMs ?? 12e4);
timer.unref?.();
if (input.runAbortSignal?.aborted) {
finish("abort");
return;
}
input.runAbortSignal?.addEventListener("abort", onAbort, { once: true });
Promise.resolve().then(() => input.joinWork()).then(onSettled, onSettled);
});
}
/**
* Races a promise against an AbortSignal while preserving normal promise
* settlement. Abort wins immediately and rejected non-Error payloads are
* normalized so callers can safely log/inspect them as Error objects.
*/
function abortable(signal, promise) {
if (signal.aborted) {
promise.catch(() => {});
return Promise.reject(createAbortableError(signal));
}
return withAbortListener(signal, () => promise, createAbortableError);
}
/** Start work under an already-checked signal, preserving the caller's abort error contract. */
function withAbortListener(signal, start, createError) {
return new Promise((resolve, reject) => {
const settle = (complete) => {
signal.removeEventListener("abort", onAbort);
complete();
};
const onAbort = () => settle(() => reject(createError(signal)));
signal.addEventListener("abort", onAbort, { once: true });
start().then((value) => settle(() => resolve(value)), (err) => settle(() => reject(toErrorObject(err, "Non-Error rejection"))));
});
}
//#endregion
export { joinWithRunLivenessDeadline as a, isOpenClawAbortableWrapper as i, abortable as n, withAbortListener as o, createAbortableError as r, RUN_LIVENESS_JOIN_TIMEOUT_MS as t };