mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-11 04:54:33 +00:00
refactor(reply): split abort cutoff and timeout policy modules
This commit is contained in:
@@ -1,52 +1,20 @@
|
||||
import { abortEmbeddedPiRun } from "../../agents/pi-embedded.js";
|
||||
import { parseDurationMs } from "../../cli/parse-duration.js";
|
||||
import { isRestartEnabled } from "../../config/commands.js";
|
||||
import type { SessionEntry } from "../../config/sessions.js";
|
||||
import { updateSessionStore } from "../../config/sessions.js";
|
||||
import {
|
||||
formatThreadBindingTtlLabel,
|
||||
getThreadBindingManager,
|
||||
setThreadBindingTtlBySessionKey,
|
||||
} from "../../discord/monitor/thread-bindings.js";
|
||||
import { logVerbose } from "../../globals.js";
|
||||
import { createInternalHookEvent, triggerInternalHook } from "../../hooks/internal-hooks.js";
|
||||
import { scheduleGatewaySigusr1Restart, triggerOpenClawRestart } from "../../infra/restart.js";
|
||||
import { loadCostUsageSummary, loadSessionCostSummary } from "../../infra/session-cost-usage.js";
|
||||
import { formatTokenCount, formatUsd } from "../../utils/usage-format.js";
|
||||
import { parseActivationCommand } from "../group-activation.js";
|
||||
import { parseSendPolicyCommand } from "../send-policy.js";
|
||||
import { normalizeUsageDisplay, resolveResponseUsageMode } from "../thinking.js";
|
||||
import {
|
||||
formatAbortReplyText,
|
||||
isAbortTrigger,
|
||||
resolveAbortCutoffFromContext,
|
||||
resolveSessionEntryForKey,
|
||||
setAbortMemory,
|
||||
stopSubagentsForRequester,
|
||||
} from "./abort.js";
|
||||
import { handleAbortTrigger, handleStopCommand } from "./commands-session-abort.js";
|
||||
import { persistSessionEntry } from "./commands-session-store.js";
|
||||
import type { CommandHandler } from "./commands-types.js";
|
||||
import { clearSessionQueues } from "./queue.js";
|
||||
|
||||
function resolveAbortTarget(params: {
|
||||
ctx: { CommandTargetSessionKey?: string | null };
|
||||
sessionKey?: string;
|
||||
sessionEntry?: SessionEntry;
|
||||
sessionStore?: Record<string, SessionEntry>;
|
||||
}) {
|
||||
const targetSessionKey = params.ctx.CommandTargetSessionKey?.trim() || params.sessionKey;
|
||||
const { entry, key } = resolveSessionEntryForKey(params.sessionStore, targetSessionKey);
|
||||
if (entry && key) {
|
||||
return { entry, key, sessionId: entry.sessionId };
|
||||
}
|
||||
if (params.sessionEntry && params.sessionKey) {
|
||||
return {
|
||||
entry: params.sessionEntry,
|
||||
key: params.sessionKey,
|
||||
sessionId: params.sessionEntry.sessionId,
|
||||
};
|
||||
}
|
||||
return { entry: undefined, key: targetSessionKey, sessionId: undefined };
|
||||
}
|
||||
|
||||
const SESSION_COMMAND_PREFIX = "/session";
|
||||
const SESSION_TTL_OFF_VALUES = new Set(["off", "disable", "disabled", "none", "0"]);
|
||||
@@ -95,53 +63,6 @@ function formatSessionExpiry(expiresAt: number) {
|
||||
return new Date(expiresAt).toISOString();
|
||||
}
|
||||
|
||||
async function applyAbortTarget(params: {
|
||||
abortTarget: ReturnType<typeof resolveAbortTarget>;
|
||||
sessionStore?: Record<string, SessionEntry>;
|
||||
storePath?: string;
|
||||
abortKey?: string;
|
||||
abortCutoff?: { messageSid?: string; timestamp?: number };
|
||||
}) {
|
||||
const { abortTarget } = params;
|
||||
if (abortTarget.sessionId) {
|
||||
abortEmbeddedPiRun(abortTarget.sessionId);
|
||||
}
|
||||
if (abortTarget.entry && params.sessionStore && abortTarget.key) {
|
||||
abortTarget.entry.abortedLastRun = true;
|
||||
abortTarget.entry.abortCutoffMessageSid = params.abortCutoff?.messageSid;
|
||||
abortTarget.entry.abortCutoffTimestamp = params.abortCutoff?.timestamp;
|
||||
abortTarget.entry.updatedAt = Date.now();
|
||||
params.sessionStore[abortTarget.key] = abortTarget.entry;
|
||||
if (params.storePath) {
|
||||
await updateSessionStore(params.storePath, (store) => {
|
||||
store[abortTarget.key] = {
|
||||
...abortTarget.entry,
|
||||
abortedLastRun: true,
|
||||
abortCutoffMessageSid: params.abortCutoff?.messageSid,
|
||||
abortCutoffTimestamp: params.abortCutoff?.timestamp,
|
||||
updatedAt: Date.now(),
|
||||
};
|
||||
});
|
||||
}
|
||||
} else if (params.abortKey) {
|
||||
setAbortMemory(params.abortKey, true);
|
||||
}
|
||||
}
|
||||
|
||||
async function persistSessionEntry(params: Parameters<CommandHandler>[0]): Promise<boolean> {
|
||||
if (!params.sessionEntry || !params.sessionStore || !params.sessionKey) {
|
||||
return false;
|
||||
}
|
||||
params.sessionEntry.updatedAt = Date.now();
|
||||
params.sessionStore[params.sessionKey] = params.sessionEntry;
|
||||
if (params.storePath) {
|
||||
await updateSessionStore(params.storePath, (store) => {
|
||||
store[params.sessionKey] = params.sessionEntry as SessionEntry;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export const handleActivationCommand: CommandHandler = async (params, allowTextCommands) => {
|
||||
if (!allowTextCommands) {
|
||||
return null;
|
||||
@@ -483,90 +404,4 @@ export const handleRestartCommand: CommandHandler = async (params, allowTextComm
|
||||
};
|
||||
};
|
||||
|
||||
export const handleStopCommand: CommandHandler = async (params, allowTextCommands) => {
|
||||
if (!allowTextCommands) {
|
||||
return null;
|
||||
}
|
||||
if (params.command.commandBodyNormalized !== "/stop") {
|
||||
return null;
|
||||
}
|
||||
if (!params.command.isAuthorizedSender) {
|
||||
logVerbose(
|
||||
`Ignoring /stop from unauthorized sender: ${params.command.senderId || "<unknown>"}`,
|
||||
);
|
||||
return { shouldContinue: false };
|
||||
}
|
||||
const abortTarget = resolveAbortTarget({
|
||||
ctx: params.ctx,
|
||||
sessionKey: params.sessionKey,
|
||||
sessionEntry: params.sessionEntry,
|
||||
sessionStore: params.sessionStore,
|
||||
});
|
||||
const cleared = clearSessionQueues([abortTarget.key, abortTarget.sessionId]);
|
||||
if (cleared.followupCleared > 0 || cleared.laneCleared > 0) {
|
||||
logVerbose(
|
||||
`stop: cleared followups=${cleared.followupCleared} lane=${cleared.laneCleared} keys=${cleared.keys.join(",")}`,
|
||||
);
|
||||
}
|
||||
await applyAbortTarget({
|
||||
abortTarget,
|
||||
sessionStore: params.sessionStore,
|
||||
storePath: params.storePath,
|
||||
abortKey: params.command.abortKey,
|
||||
abortCutoff:
|
||||
params.sessionKey?.trim() &&
|
||||
abortTarget.key?.trim() &&
|
||||
params.sessionKey.trim() === abortTarget.key.trim()
|
||||
? resolveAbortCutoffFromContext(params.ctx)
|
||||
: undefined,
|
||||
});
|
||||
|
||||
// Trigger internal hook for stop command
|
||||
const hookEvent = createInternalHookEvent(
|
||||
"command",
|
||||
"stop",
|
||||
abortTarget.key ?? params.sessionKey ?? "",
|
||||
{
|
||||
sessionEntry: abortTarget.entry ?? params.sessionEntry,
|
||||
sessionId: abortTarget.sessionId,
|
||||
commandSource: params.command.surface,
|
||||
senderId: params.command.senderId,
|
||||
},
|
||||
);
|
||||
await triggerInternalHook(hookEvent);
|
||||
|
||||
const { stopped } = stopSubagentsForRequester({
|
||||
cfg: params.cfg,
|
||||
requesterSessionKey: abortTarget.key ?? params.sessionKey,
|
||||
});
|
||||
|
||||
return { shouldContinue: false, reply: { text: formatAbortReplyText(stopped) } };
|
||||
};
|
||||
|
||||
export const handleAbortTrigger: CommandHandler = async (params, allowTextCommands) => {
|
||||
if (!allowTextCommands) {
|
||||
return null;
|
||||
}
|
||||
if (!isAbortTrigger(params.command.rawBodyNormalized)) {
|
||||
return null;
|
||||
}
|
||||
const abortTarget = resolveAbortTarget({
|
||||
ctx: params.ctx,
|
||||
sessionKey: params.sessionKey,
|
||||
sessionEntry: params.sessionEntry,
|
||||
sessionStore: params.sessionStore,
|
||||
});
|
||||
await applyAbortTarget({
|
||||
abortTarget,
|
||||
sessionStore: params.sessionStore,
|
||||
storePath: params.storePath,
|
||||
abortKey: params.command.abortKey,
|
||||
abortCutoff:
|
||||
params.sessionKey?.trim() &&
|
||||
abortTarget.key?.trim() &&
|
||||
params.sessionKey.trim() === abortTarget.key.trim()
|
||||
? resolveAbortCutoffFromContext(params.ctx)
|
||||
: undefined,
|
||||
});
|
||||
return { shouldContinue: false, reply: { text: "⚙️ Agent was aborted." } };
|
||||
};
|
||||
export { handleAbortTrigger, handleStopCommand };
|
||||
|
||||
Reference in New Issue
Block a user