openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
91 lines (90 loc) • 4 kB
JavaScript
import { t as createAbortError } from "./abort-signal-D2k14JsD.js";
import { a as attachInternalToolExecutionPreparer, p as getInternalToolExecutionPreparer } from "./internal-hooks-CgPsiqhr.js";
import { s as isCodeModeControlTool } from "./code-mode-control-tools-CRK5FQqM.js";
import { r as copyAgentToolMetadata } from "./agent-tool-metadata-CZvIgxro.js";
import { c as registerTrustedToolNoStartError } from "./tool-result-error-CbDLJzG-.js";
//#region src/agents/agent-tools.abort.ts
/**
* Abort-signal wrapping for agent tools.
* Combines per-call cancellation with run-level aborts while preserving
* identity-backed metadata on wrapped tools.
*/
function throwAbortError() {
throw registerTrustedToolNoStartError(createAbortError("Aborted"));
}
/**
* Races a tool execute promise against the combined abort signal so an abort
* settles the wrapped call immediately instead of awaiting the tool forever.
* JavaScript cannot cancel a running promise: a tool that never observes the
* signal keeps executing in the background and may settle later, but its late
* settlement is detached here so the result never lands in an aborted run.
* Tool settlements pass through untouched to preserve tool error semantics,
* including non-Error rejections.
*/
function raceWithAbortSignal(promise, signal, yieldRunSignal) {
return new Promise((resolve, reject) => {
const onAbort = () => {
signal.removeEventListener("abort", onAbort);
const reason = yieldRunSignal?.reason;
if (yieldRunSignal?.aborted && signal.reason === reason && reason?.code === "sessions_yield" && reason.turnHandoff === true) return;
reject(createAbortError("Aborted"));
};
signal.addEventListener("abort", onAbort, { once: true });
promise.then((value) => {
signal.removeEventListener("abort", onAbort);
resolve(value);
}, (error) => {
signal.removeEventListener("abort", onAbort);
reject(error);
});
if (signal.aborted) onAbort();
});
}
/** Wrap a tool so every execute call observes the supplied run abort signal. */
function wrapToolWithAbortSignal(tool, abortSignal) {
if (!abortSignal) return tool;
const execute = tool.execute;
if (!execute) return tool;
const ownsCancellationOutcome = isCodeModeControlTool(tool);
const wrappedTool = {
...tool,
execute: async (toolCallId, params, signal, onUpdate) => {
const combinedSignal = signal ? AbortSignal.any([signal, abortSignal]) : abortSignal;
if (combinedSignal.aborted) throwAbortError();
const execution = execute(toolCallId, params, combinedSignal, onUpdate);
return ownsCancellationOutcome ? await execution : await raceWithAbortSignal(execution, combinedSignal, tool.name === "sessions_yield" ? abortSignal : void 0);
}
};
copyAgentToolMetadata(tool, wrappedTool);
const sourcePreparer = getInternalToolExecutionPreparer(tool);
if (sourcePreparer) attachInternalToolExecutionPreparer(wrappedTool, async (params) => {
const combinedSignal = params.signal ? AbortSignal.any([params.signal, abortSignal]) : abortSignal;
if (combinedSignal.aborted) throwAbortError();
const yieldRunSignal = tool.name === "sessions_yield" ? abortSignal : void 0;
const sourcePreparation = sourcePreparer({
...params,
signal: combinedSignal
});
let prepared;
try {
prepared = await raceWithAbortSignal(sourcePreparation, combinedSignal, yieldRunSignal);
} catch (error) {
sourcePreparation.then((latePreparation) => latePreparation.dispose(), () => void 0);
throw error;
}
if (prepared.kind === "immediate") return prepared;
return {
kind: "ready",
args: prepared.args,
execute: (onImplementationStart) => {
if (combinedSignal.aborted) throwAbortError();
const execution = prepared.execute(onImplementationStart);
return ownsCancellationOutcome ? execution : raceWithAbortSignal(execution, combinedSignal, yieldRunSignal);
},
dispose: prepared.dispose
};
});
return wrappedTool;
}
//#endregion
export { wrapToolWithAbortSignal as n, raceWithAbortSignal as t };