mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
Auto-reply: bound abort memory map growth
This commit is contained in:
@@ -1,9 +1,16 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { isAbortTrigger, tryFastAbortFromMessage } from "./abort.js";
|
||||
import {
|
||||
getAbortMemory,
|
||||
getAbortMemorySizeForTest,
|
||||
isAbortTrigger,
|
||||
resetAbortMemoryForTest,
|
||||
setAbortMemory,
|
||||
tryFastAbortFromMessage,
|
||||
} from "./abort.js";
|
||||
import { enqueueFollowupRun, getFollowupQueueDepth, type FollowupRun } from "./queue.js";
|
||||
import { initSessionState } from "./session.js";
|
||||
import { buildTestCtx } from "./test-ctx.js";
|
||||
@@ -28,6 +35,10 @@ vi.mock("../../agents/subagent-registry.js", () => ({
|
||||
}));
|
||||
|
||||
describe("abort detection", () => {
|
||||
afterEach(() => {
|
||||
resetAbortMemoryForTest();
|
||||
});
|
||||
|
||||
it("triggerBodyNormalized extracts /stop from RawBody for abort detection", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-abort-"));
|
||||
const storePath = path.join(root, "sessions.json");
|
||||
@@ -62,6 +73,24 @@ describe("abort detection", () => {
|
||||
expect(isAbortTrigger("/stop")).toBe(false);
|
||||
});
|
||||
|
||||
it("removes abort memory entry when flag is reset", () => {
|
||||
setAbortMemory("session-1", true);
|
||||
expect(getAbortMemory("session-1")).toBe(true);
|
||||
|
||||
setAbortMemory("session-1", false);
|
||||
expect(getAbortMemory("session-1")).toBeUndefined();
|
||||
expect(getAbortMemorySizeForTest()).toBe(0);
|
||||
});
|
||||
|
||||
it("caps abort memory tracking to a bounded max size", () => {
|
||||
for (let i = 0; i < 2105; i += 1) {
|
||||
setAbortMemory(`session-${i}`, true);
|
||||
}
|
||||
expect(getAbortMemorySizeForTest()).toBe(2000);
|
||||
expect(getAbortMemory("session-0")).toBeUndefined();
|
||||
expect(getAbortMemory("session-2104")).toBe(true);
|
||||
});
|
||||
|
||||
it("fast-aborts even when text commands are disabled", async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-abort-"));
|
||||
const storePath = path.join(root, "sessions.json");
|
||||
|
||||
@@ -22,6 +22,7 @@ import { clearSessionQueues } from "./queue.js";
|
||||
|
||||
const ABORT_TRIGGERS = new Set(["stop", "esc", "abort", "wait", "exit", "interrupt"]);
|
||||
const ABORT_MEMORY = new Map<string, boolean>();
|
||||
const ABORT_MEMORY_MAX = 2000;
|
||||
|
||||
export function isAbortTrigger(text?: string): boolean {
|
||||
if (!text) {
|
||||
@@ -32,11 +33,51 @@ export function isAbortTrigger(text?: string): boolean {
|
||||
}
|
||||
|
||||
export function getAbortMemory(key: string): boolean | undefined {
|
||||
return ABORT_MEMORY.get(key);
|
||||
const normalized = key.trim();
|
||||
if (!normalized) {
|
||||
return undefined;
|
||||
}
|
||||
return ABORT_MEMORY.get(normalized);
|
||||
}
|
||||
|
||||
function pruneAbortMemory(): void {
|
||||
if (ABORT_MEMORY.size <= ABORT_MEMORY_MAX) {
|
||||
return;
|
||||
}
|
||||
const excess = ABORT_MEMORY.size - ABORT_MEMORY_MAX;
|
||||
let removed = 0;
|
||||
for (const entryKey of ABORT_MEMORY.keys()) {
|
||||
ABORT_MEMORY.delete(entryKey);
|
||||
removed += 1;
|
||||
if (removed >= excess) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function setAbortMemory(key: string, value: boolean): void {
|
||||
ABORT_MEMORY.set(key, value);
|
||||
const normalized = key.trim();
|
||||
if (!normalized) {
|
||||
return;
|
||||
}
|
||||
if (!value) {
|
||||
ABORT_MEMORY.delete(normalized);
|
||||
return;
|
||||
}
|
||||
// Refresh insertion order so active keys are less likely to be evicted.
|
||||
if (ABORT_MEMORY.has(normalized)) {
|
||||
ABORT_MEMORY.delete(normalized);
|
||||
}
|
||||
ABORT_MEMORY.set(normalized, true);
|
||||
pruneAbortMemory();
|
||||
}
|
||||
|
||||
export function getAbortMemorySizeForTest(): number {
|
||||
return ABORT_MEMORY.size;
|
||||
}
|
||||
|
||||
export function resetAbortMemoryForTest(): void {
|
||||
ABORT_MEMORY.clear();
|
||||
}
|
||||
|
||||
export function formatAbortReplyText(stoppedSubagents?: number): string {
|
||||
|
||||
Reference in New Issue
Block a user