refactor(reply): split abort cutoff and timeout policy modules

This commit is contained in:
Peter Steinberger
2026-02-26 14:00:31 +01:00
parent f53e4e9ffb
commit b402770f63
9 changed files with 476 additions and 309 deletions

View File

@@ -5,7 +5,6 @@ import { applyOwnerOnlyToolPolicy } from "../../agents/tool-policy.js";
import { getChannelDock } from "../../channels/dock.js";
import type { OpenClawConfig } from "../../config/config.js";
import type { SessionEntry } from "../../config/sessions.js";
import { updateSessionStore } from "../../config/sessions.js";
import { logVerbose } from "../../globals.js";
import { generateSecureToken } from "../../infra/secure-random.js";
import { resolveGatewayMessageChannel } from "../../utils/message-channel.js";
@@ -17,7 +16,13 @@ import {
import type { MsgContext, TemplateContext } from "../templating.js";
import type { ElevatedLevel, ReasoningLevel, ThinkLevel, VerboseLevel } from "../thinking.js";
import type { GetReplyOptions, ReplyPayload } from "../types.js";
import { getAbortMemory, isAbortRequestText, shouldSkipMessageByAbortCutoff } from "./abort.js";
import {
clearAbortCutoffInSession,
readAbortCutoffFromSessionEntry,
resolveAbortCutoffFromContext,
shouldSkipMessageByAbortCutoff,
} from "./abort-cutoff.js";
import { getAbortMemory, isAbortRequestText } from "./abort.js";
import { buildStatusReply, handleCommands } from "./commands.js";
import type { InlineDirectives } from "./directive-handling.js";
import { isDirectiveOnly } from "./directive-handling.js";
@@ -253,54 +258,29 @@ export async function handleInlineActions(params: {
await opts.onBlockReply(reply);
};
const clearAbortCutoff = async () => {
if (!sessionEntry || !sessionStore || !sessionKey) {
return;
}
if (
sessionEntry.abortCutoffMessageSid === undefined &&
sessionEntry.abortCutoffTimestamp === undefined
) {
return;
}
sessionEntry.abortCutoffMessageSid = undefined;
sessionEntry.abortCutoffTimestamp = undefined;
sessionEntry.updatedAt = Date.now();
sessionStore[sessionKey] = sessionEntry;
if (storePath) {
await updateSessionStore(storePath, (store) => {
const existing = store[sessionKey] ?? sessionEntry;
if (!existing) {
return;
}
existing.abortCutoffMessageSid = undefined;
existing.abortCutoffTimestamp = undefined;
existing.updatedAt = Date.now();
store[sessionKey] = existing;
});
}
};
const isStopLikeInbound = isAbortRequestText(command.rawBodyNormalized);
if (!isStopLikeInbound && sessionEntry) {
const shouldSkip = shouldSkipMessageByAbortCutoff({
cutoffMessageSid: sessionEntry.abortCutoffMessageSid,
cutoffTimestamp: sessionEntry.abortCutoffTimestamp,
messageSid:
(typeof ctx.MessageSidFull === "string" && ctx.MessageSidFull.trim()) ||
(typeof ctx.MessageSid === "string" && ctx.MessageSid.trim()) ||
undefined,
timestamp: typeof ctx.Timestamp === "number" ? ctx.Timestamp : undefined,
});
const cutoff = readAbortCutoffFromSessionEntry(sessionEntry);
const incoming = resolveAbortCutoffFromContext(ctx);
const shouldSkip = cutoff
? shouldSkipMessageByAbortCutoff({
cutoffMessageSid: cutoff.messageSid,
cutoffTimestamp: cutoff.timestamp,
messageSid: incoming?.messageSid,
timestamp: incoming?.timestamp,
})
: false;
if (shouldSkip) {
typing.cleanup();
return { kind: "reply", reply: undefined };
}
if (
sessionEntry.abortCutoffMessageSid !== undefined ||
sessionEntry.abortCutoffTimestamp !== undefined
) {
await clearAbortCutoff();
if (cutoff) {
await clearAbortCutoffInSession({
sessionEntry,
sessionStore,
sessionKey,
storePath,
});
}
}