openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
121 lines (120 loc) • 5.76 kB
JavaScript
import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs";
import { i as createAbortError } from "./src-D1BHGNTj.mjs";
import "./abort-signal-QqAmLWTl.mjs";
import { l as isAgentRunDirectAbortReason } from "./run-termination-5c-0T0hN.mjs";
import { AsyncLocalStorage } from "node:async_hooks";
//#region src/infra/exec-request-context.ts
const state = resolveGlobalSingleton(Symbol.for("openclaw.execRequestContext"), () => ({
context: new AsyncLocalStorage(),
turns: /* @__PURE__ */ new Set()
}));
const EVENT_OWNERS = Symbol.for("openclaw.execRequestEventOwners");
function createExecRequestOwner(identity) {
const controller = new AbortController();
return {
identity: Object.freeze({ ...identity }),
turnRunIds: new Set(identity.runId ? [identity.runId] : []),
controller,
signal: controller.signal,
pendingProcesses: /* @__PURE__ */ new Set(),
cleanupUncertain: false
};
}
function cancelExecRequestOwners(owners) {
for (const owner of owners) if (!owner.controller.signal.aborted) {
const reason = createAbortError("The request that owns this command was stopped");
owner.controller.abort(reason);
}
}
function execRequestMatches(owner, target) {
return (target.runId === void 0 || owner.turnRunIds.has(target.runId)) && (target.sessionKey === void 0 || owner.identity.sessionKey === target.sessionKey) && (target.sessionId === void 0 || owner.identity.sessionId === target.sessionId) && (target.agentId === void 0 || owner.identity.agentId === target.agentId);
}
/** Capture at tool construction: Code Mode invokes the bound tool from a later callback. */
function captureExecRequestOwners(identity) {
const turn = state.context.getStore();
return turn && turn.identity.runId === identity.runId && turn.identity.sessionId === identity.sessionId ? turn.owners : void 0;
}
function activeExecRequestOwners(target, accept) {
return Array.from(new Set([...state.turns].flatMap(({ identity, owners }) => {
const active = (target.runId === void 0 || identity.runId === target.runId) && (target.sessionKey === void 0 || identity.sessionKey === target.sessionKey) && (target.sessionId === void 0 || identity.sessionId === target.sessionId) && (target.agentId === void 0 || identity.agentId === target.agentId);
const hasBoundActor = Boolean(identity.ownerConnId?.trim() || identity.ownerDeviceId?.trim());
return owners.filter((owner) => {
const current = hasBoundActor ? identity : {
...owner.identity,
...identity
};
return active && accept(current) || execRequestMatches(owner, target) && accept(owner.identity);
});
})));
}
/** Only the accepted session binding or compaction owner updates request identity. */
function adoptExecRequestSession(params) {
const active = state.context.getStore();
if (active?.identity.runId !== params.runId || active.identity.sessionId !== params.previousSessionId) return;
for (const turn of state.turns) if (turn.owners === active.owners && turn.identity.runId === params.runId && turn.identity.sessionId === params.previousSessionId) turn.identity = {
...turn.identity,
sessionId: params.sessionId
};
for (const owner of active.owners) if (owner.identity.sessionId === params.previousSessionId) owner.identity = Object.freeze({
...owner.identity,
sessionId: params.sessionId
});
}
/** Native process finalization owns settlement; normal turn completion does not join it. */
function retainExecRequestProcess(owners, settlement) {
if (!owners?.length) return;
for (const owner of owners) owner.pendingProcesses.add(settlement);
const release = () => {
for (const owner of owners) owner.pendingProcesses.delete(settlement);
};
settlement.then(release, release);
}
/** A continuation inherits only its selected events; ordinary human turns start fresh. */
async function withExecRequestTurn(params, run) {
const outer = state.context.getStore();
const sameTurn = outer?.identity.runId === params.identity.runId && outer?.identity.sessionId === params.identity.sessionId ? outer : void 0;
const identity = sameTurn?.identity ?? params.identity;
const inherited = params.owners ?? sameTurn?.owners;
const owners = inherited?.length ? inherited : [createExecRequestOwner(identity)];
for (const owner of owners) {
owner.signal.throwIfAborted();
if (params.identity.runId) owner.turnRunIds.add(params.identity.runId);
}
const turn = {
identity,
owners
};
const stop = () => {
if (isAgentRunDirectAbortReason(params.abortSignal?.reason)) cancelExecRequestOwners(owners);
};
state.turns.add(turn);
params.abortSignal?.addEventListener("abort", stop, { once: true });
if (params.abortSignal?.aborted) stop();
try {
return await state.context.run(turn, run);
} finally {
try {
await Promise.all(owners.flatMap((owner) => owner.signal.aborted ? Array.from(owner.pendingProcesses) : []));
} finally {
state.turns.delete(turn);
params.abortSignal?.removeEventListener("abort", stop);
}
}
}
/** Enumerable symbol metadata survives internal option/event copies, but never JSON. */
function withExecRequestOwners(value, owners) {
return owners?.length ? {
...value,
[EVENT_OWNERS]: owners
} : value;
}
function readExecRequestOwners(value) {
return value[EVENT_OWNERS];
}
function execRequestAbortSignal(owners, signal) {
if (!owners?.length) return signal;
const signals = owners.map((owner) => owner.signal);
return AbortSignal.any(signal ? [signal, ...signals] : signals);
}
//#endregion
export { execRequestAbortSignal as a, retainExecRequestProcess as c, captureExecRequestOwners as i, withExecRequestOwners as l, adoptExecRequestSession as n, execRequestMatches as o, cancelExecRequestOwners as r, readExecRequestOwners as s, activeExecRequestOwners as t, withExecRequestTurn as u };