openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
296 lines (295 loc) • 12.7 kB
JavaScript
import { c as normalizeOptionalLowercaseString, l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { S as isAcpSessionKey, T as isSubagentSessionKey } from "./session-key-BnWWjqNc.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { o as resolveSessionStorePathCore } from "./paths-CXdaYWF_.js";
import { _ as resolveSessionAgentId } from "./agent-scope-DbtJyKUL.js";
import { r as logVerbose } from "./globals-CTaGxEqj.js";
import { r as resolveCommandAuthorization } from "./command-auth-DRYzLgCh.js";
import { bt as resolveSessionAbortTarget, yt as markSessionAbortTarget } from "./session-accessor-YsytfDtG.js";
import { l as loadSessionEntry } from "./session-accessor.sqlite-entry-CWk3jL7s.js";
import { t as getAcpSessionManager } from "./manager-Bbp2Ogkb.js";
import { i as setAbortMemory, n as isAbortRequestText } from "./abort-primitives-pmK4VR6Y.js";
import { h as replyRunRegistry } from "./reply-run-registry.registry-BMo0d96L.js";
import "./reply-run-registry-BlmBKV23.js";
import { n as abortEmbeddedAgentRun } from "./runs-Cb42qain.js";
import { i as resolveActiveEmbeddedRunSessionId } from "./active-run-projections-C4mdt8WG.js";
import "./sessions-9nxpeTwt.js";
import { i as resolveConversationBindingContextFromMessage } from "./conversation-binding-input-BzjT8SWv.js";
import { m as resolveMainSessionAlias, p as resolveInternalSessionKey } from "./sessions-helpers-B2gzzr60.js";
import { h as listSubagentRunsForController } from "./subagent-registry-read-DeFf_9Nz.js";
import { t as killAllControlledSubagentRuns } from "./subagent-control-Cm3eFdYX.js";
import { t as clearSessionQueues } from "./cleanup-DKkilV3a.js";
import "./queue-BpaiO3X2.js";
import { a as stripMentions, o as stripStructuralPrefixes } from "./mentions-BXIDEOtX.js";
import { a as shouldPersistAbortCutoff, i as resolveAbortCutoffFromContext } from "./abort-cutoff-BxM8jmK3.js";
import { t as resolveEffectiveResetTargetSessionKey } from "./acp-reset-target-Cuv8dYQz.js";
//#region src/auto-reply/reply/abort.ts
function abortSessionRunTargetWithOutcome(params) {
const sessionIds = /* @__PURE__ */ new Set();
const key = normalizeOptionalString(params.key);
let active = key ? replyRunRegistry.isActive(key) : false;
if (key) {
const activeSessionId = resolveActiveEmbeddedRunSessionId(key);
if (activeSessionId) {
active = true;
sessionIds.add(activeSessionId);
}
}
const explicitSessionId = normalizeOptionalString(params.sessionId);
if (explicitSessionId) sessionIds.add(explicitSessionId);
let aborted = key ? replyRunRegistry.abort(key) : false;
for (const sessionId of sessionIds) aborted = abortEmbeddedAgentRun(sessionId) || aborted;
return {
active,
aborted
};
}
function formatAbortReplyText(stoppedSubagents, rejectionReason, failedSubagents) {
const failureSuffix = typeof failedSubagents === "number" && failedSubagents > 0 ? ` Cancellation was incomplete for ${failedSubagents} sub-agent${failedSubagents === 1 ? "" : "s"}. Retry /stop.` : "";
if (rejectionReason === "finalizing") {
const base = "Agent reply is already finalizing and can no longer be aborted.";
if (typeof stoppedSubagents !== "number" || stoppedSubagents <= 0) return `${base}${failureSuffix}`;
return `${base} Stopped ${stoppedSubagents} ${stoppedSubagents === 1 ? "sub-agent" : "sub-agents"}.${failureSuffix}`;
}
if (typeof stoppedSubagents !== "number" || stoppedSubagents <= 0) return `⚙️ Agent was aborted.${failureSuffix}`;
return `⚙️ Agent was aborted. Stopped ${stoppedSubagents} ${stoppedSubagents === 1 ? "sub-agent" : "sub-agents"}.${failureSuffix}`;
}
function resolveStoredSessionId(params) {
const agentId = resolveSessionAgentId({
sessionKey: params.sessionKey,
config: params.cfg
});
const storePath = resolveSessionStorePathCore(params.cfg.session?.store, { agentId });
try {
return loadSessionEntry({
agentId,
clone: false,
sessionKey: params.sessionKey,
storePath
})?.sessionId;
} catch {
return;
}
}
function resolveBoundAcpAbortTargetSessionKey(params) {
const bindingContext = resolveConversationBindingContextFromMessage({
cfg: params.cfg,
ctx: params.ctx
});
if (!bindingContext) return;
return resolveEffectiveResetTargetSessionKey({
cfg: params.cfg,
channel: bindingContext.channel,
accountId: bindingContext.accountId,
conversationId: bindingContext.conversationId,
parentConversationId: bindingContext.parentConversationId,
activeSessionKey: params.activeSessionKey,
skipConfiguredFallbackWhenActiveSessionNonAcp: false,
fallbackToActiveAcpWhenUnbound: false
});
}
function normalizeRequesterSessionKey(cfg, key) {
const cleaned = normalizeOptionalString(key);
if (!cleaned) return;
const { mainKey, alias } = resolveMainSessionAlias(cfg);
return resolveInternalSessionKey({
key: cleaned,
alias,
mainKey
});
}
async function stopSubagentsForRequester(params) {
const requesterKey = normalizeRequesterSessionKey(params.cfg, params.requesterSessionKey);
if (!requesterKey) {
await params.beforeKill?.();
return {
stopped: 0,
failed: 0
};
}
const controllerAgentId = resolveSessionAgentId({
config: params.cfg,
sessionKey: requesterKey,
fallbackAgentId: params.requesterAgentId
});
const result = await killAllControlledSubagentRuns({
cfg: params.cfg,
controller: {
controllerSessionKey: requesterKey,
controllerAgentId,
callerSessionKey: requesterKey,
callerIsSubagent: isSubagentSessionKey(requesterKey),
controlScope: "children"
},
runs: listSubagentRunsForController(requesterKey),
suppressTaskDelivery: true,
beforeKill: params.beforeKill
});
if (result.status === "error") logVerbose(`abort: failed to stop subagents for ${requesterKey}: ${result.error}`);
if (result.killed > 0) logVerbose(`abort: stopped ${result.killed} subagent run(s) for ${requesterKey}`);
return {
stopped: result.killed,
failed: result.status === "error" ? result.failed : 0
};
}
async function tryFastAbortFromMessage(params) {
const { ctx, cfg } = params;
const commandSessionKey = normalizeOptionalString(ctx.SessionKey) ?? normalizeOptionalString(ctx.ParentSessionKey);
const targetKey = normalizeOptionalString(ctx.CommandTargetSessionKey) ?? commandSessionKey;
const raw = stripStructuralPrefixes(ctx.commandText);
const stripped = normalizeOptionalLowercaseString(ctx.ChatType) === "group" ? stripMentions(raw, ctx, cfg, resolveSessionAgentId({
sessionKey: targetKey ?? ctx.SessionKey ?? "",
config: cfg
})) : raw;
if (!isAbortRequestText(stripped)) return {
handled: false,
aborted: false
};
const commandAuthorized = ctx.CommandAuthorized;
const auth = resolveCommandAuthorization({
ctx,
cfg,
commandAuthorized
});
if (!auth.isAuthorizedSender) return {
handled: false,
aborted: false
};
const agentId = resolveSessionAgentId({
sessionKey: targetKey ?? ctx.SessionKey ?? "",
config: cfg,
fallbackAgentId: ctx.AgentId
});
const abortKey = targetKey ?? auth.from ?? auth.to;
const requesterSessionKey = targetKey ?? ctx.SessionKey ?? abortKey;
if (targetKey) {
const storePath = resolveSessionStorePathCore(cfg.session?.store, { agentId });
const abortCutoffForTarget = (target) => shouldPersistAbortCutoff({
commandSessionKey,
targetSessionKey: target.sessionKey
}) ? resolveAbortCutoffFromContext(ctx) : void 0;
let resolvedAbortTarget = null;
try {
resolvedAbortTarget = resolveSessionAbortTarget({
agentId,
sessionKey: targetKey,
storePath
});
} catch (error) {
logVerbose(`abort: failed to resolve abort metadata for ${targetKey}: ${formatErrorMessage(error)}`);
}
const resolvedTargetKey = resolvedAbortTarget?.sessionKey ?? targetKey;
const conversationBoundAcpTargetKey = commandSessionKey ? resolveBoundAcpAbortTargetSessionKey({
ctx,
cfg,
activeSessionKey: commandSessionKey
}) : void 0;
const boundAcpTargetKey = !isAcpSessionKey(resolvedTargetKey) ? conversationBoundAcpTargetKey : void 0;
const abortTargetKeys = [resolvedTargetKey];
if (boundAcpTargetKey && boundAcpTargetKey !== resolvedTargetKey) abortTargetKeys.push(boundAcpTargetKey);
let aborted = false;
let activeAbortRejected = false;
const acpCancellations = [];
try {
const { stopped, failed } = await stopSubagentsForRequester({
cfg,
requesterSessionKey,
requesterAgentId: agentId,
beforeKill: () => {
if (params.isCommandTargetCurrent?.() === false) throw new Error("The selected session changed before it could be stopped.");
try {
const sourceAbortKey = commandSessionKey && !abortTargetKeys.includes(commandSessionKey) && conversationBoundAcpTargetKey && abortTargetKeys.includes(conversationBoundAcpTargetKey) ? commandSessionKey : void 0;
const sessionIdsByKey = new Map(abortTargetKeys.map((abortTargetKey) => [abortTargetKey, replyRunRegistry.resolveSessionId(abortTargetKey) ?? (abortTargetKey === resolvedTargetKey ? resolvedAbortTarget?.sessionId : resolveStoredSessionId({
cfg,
sessionKey: abortTargetKey
}))]));
for (const abortTargetKey of abortTargetKeys) {
const outcome = abortSessionRunTargetWithOutcome({
key: abortTargetKey,
sessionId: sessionIdsByKey.get(abortTargetKey)
});
activeAbortRejected ||= outcome.active && !outcome.aborted;
aborted = outcome.aborted || aborted;
}
const sourceSessionId = sourceAbortKey ? replyRunRegistry.resolveSessionId(sourceAbortKey) ?? resolveStoredSessionId({
cfg,
sessionKey: sourceAbortKey
}) : void 0;
if (sourceAbortKey) {
const outcome = abortSessionRunTargetWithOutcome({
key: sourceAbortKey,
sessionId: sourceSessionId
});
activeAbortRejected ||= outcome.active && !outcome.aborted;
aborted = outcome.aborted || aborted;
}
const cleared = clearSessionQueues(abortTargetKeys.flatMap((abortTargetKey) => [abortTargetKey, sessionIdsByKey.get(abortTargetKey)]).concat(sourceAbortKey, sourceSessionId));
if (cleared.followupCleared > 0 || cleared.laneCleared > 0) logVerbose(`abort: cleared followups=${cleared.followupCleared} lane=${cleared.laneCleared} keys=${cleared.keys.join(",")}`);
} finally {
const acpManager = getAcpSessionManager();
for (const acpTargetKey of abortTargetKeys) {
const resolution = acpManager.resolveSession({
cfg,
sessionKey: acpTargetKey,
agentId: acpTargetKey === resolvedTargetKey ? agentId : void 0
});
if (resolution.kind === "none") continue;
acpCancellations.push(acpManager.cancelSession({
cfg,
sessionKey: resolution.sessionKey,
agentId: resolution.agentId,
reason: "fast-abort"
}).catch((error) => {
logVerbose(`abort: ACP cancel failed for ${acpTargetKey}: ${formatErrorMessage(error)}`);
}));
}
}
return true;
}
});
const rejectionReason = activeAbortRejected && !aborted ? "finalizing" : void 0;
if (!rejectionReason) {
let persistedAbortTarget = null;
try {
persistedAbortTarget = await markSessionAbortTarget({
isCurrent: params.isCommandTargetCurrent,
scope: {
agentId,
sessionKey: targetKey,
storePath
},
resolveAbortCutoff: abortCutoffForTarget
});
} catch (error) {
logVerbose(`abort: failed to persist abort metadata for ${targetKey}: ${formatErrorMessage(error)}`);
}
if (persistedAbortTarget?.persisted === false) logVerbose(`abort: failed to persist abort metadata for ${targetKey}: ${persistedAbortTarget.persistenceError ?? "unknown error"}`);
const abortMemoryKey = persistedAbortTarget?.sessionKey ?? resolvedAbortTarget?.sessionKey ?? abortKey;
const hasAbortTargetEntry = Boolean(persistedAbortTarget?.entry ?? resolvedAbortTarget?.entry);
if (persistedAbortTarget?.persisted !== true && abortMemoryKey && !hasAbortTargetEntry && params.isCommandTargetCurrent?.() !== false) setAbortMemory(abortMemoryKey, true);
}
return {
handled: true,
aborted,
...rejectionReason ? { rejectionReason } : {},
stoppedSubagents: stopped,
failedSubagents: failed
};
} finally {
await Promise.all(acpCancellations);
}
}
if (abortKey) setAbortMemory(abortKey, true);
const { stopped, failed } = await stopSubagentsForRequester({
cfg,
requesterSessionKey
});
return {
handled: true,
aborted: false,
stoppedSubagents: stopped,
failedSubagents: failed
};
}
//#endregion
export { tryFastAbortFromMessage as i, formatAbortReplyText as n, stopSubagentsForRequester as r, abortSessionRunTargetWithOutcome as t };