Files
openclaw/extensions/memory-core/src/memory-tool-manager-mock.ts
Tak Hoffman f74983e442 fix(memory): preserve active recall tool agent context (#76380)
Summary:
- The PR threads the embedded run's trusted requester agent id into plugin tool context and memory-core tool availability/execution, adds regression tests, and records an Active Memory changelog fix.
- Reproducibility: yes. Current main shows Active Memory passing a synthetic `:active-memory:` session key plu ... ently derive memory scope from the session key; I did not run the regression test in this read-only review.

Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.

Validation:
- ClawSweeper review passed for head 33ab3d7fc7.
- Required merge gates passed before the squash merge.

Prepared head SHA: 33ab3d7fc7
Review: https://github.com/openclaw/openclaw/pull/76380#issuecomment-4365186657

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-05-03 02:16:48 +00:00

128 lines
3.7 KiB
TypeScript

import type { MemorySearchRuntimeDebug } from "openclaw/plugin-sdk/memory-core-host-runtime-files";
import { vi } from "vitest";
type SearchImpl = (opts?: {
maxResults?: number;
minScore?: number;
sessionKey?: string;
qmdSearchModeOverride?: "query" | "search" | "vsearch";
onDebug?: (debug: MemorySearchRuntimeDebug) => void;
}) => Promise<unknown[]>;
export type MemoryReadParams = { relPath: string; from?: number; lines?: number };
type MemoryReadResult = {
text: string;
path: string;
truncated?: boolean;
from?: number;
lines?: number;
nextFrom?: number;
};
type MemoryBackend = "builtin" | "qmd";
let backend: MemoryBackend = "builtin";
let workspaceDir = "/workspace";
let customStatus: Record<string, unknown> | undefined;
let searchImpl: SearchImpl = async () => [];
let readFileImpl: (params: MemoryReadParams) => Promise<MemoryReadResult> = async (params) => ({
text: "",
path: params.relPath,
from: params.from ?? 1,
lines: params.lines ?? 120,
});
const stubManager = {
search: vi.fn(async (_query: string, opts?: Parameters<SearchImpl>[0]) => await searchImpl(opts)),
readFile: vi.fn(async (params: MemoryReadParams) => await readFileImpl(params)),
status: () => ({
backend,
files: 1,
chunks: 1,
dirty: false,
workspaceDir,
dbPath: "/workspace/.memory/index.sqlite",
provider: "builtin",
model: "builtin",
requestedProvider: "builtin",
sources: ["memory" as const],
sourceCounts: [{ source: "memory" as const, files: 1, chunks: 1 }],
custom: customStatus,
}),
sync: vi.fn(),
probeVectorAvailability: vi.fn(async () => true),
close: vi.fn(),
};
const getMemorySearchManagerMock = vi.fn(async (_params: { cfg?: unknown; agentId?: string }) => ({
manager: stubManager,
}));
const readAgentMemoryFileMock = vi.fn(
async (params: MemoryReadParams) => await readFileImpl(params),
);
vi.mock("./tools.runtime.js", () => ({
resolveMemoryBackendConfig: ({
cfg,
}: {
cfg?: { memory?: { backend?: string; qmd?: unknown } };
}) => ({
backend,
qmd: cfg?.memory?.qmd,
}),
getMemorySearchManager: getMemorySearchManagerMock,
readAgentMemoryFile: readAgentMemoryFileMock,
}));
export function setMemoryBackend(next: MemoryBackend): void {
backend = next;
}
export function setMemoryWorkspaceDir(next: string): void {
workspaceDir = next;
}
export function setMemorySearchImpl(next: SearchImpl): void {
searchImpl = next;
}
export function setMemoryReadFileImpl(
next: (params: MemoryReadParams) => Promise<MemoryReadResult>,
): void {
readFileImpl = next;
}
export function resetMemoryToolMockState(overrides?: {
backend?: MemoryBackend;
searchImpl?: SearchImpl;
readFileImpl?: (params: MemoryReadParams) => Promise<MemoryReadResult>;
}): void {
backend = overrides?.backend ?? "builtin";
workspaceDir = "/workspace";
customStatus = undefined;
searchImpl = overrides?.searchImpl ?? (async () => []);
readFileImpl =
overrides?.readFileImpl ??
(async (params: MemoryReadParams) => ({
text: "",
path: params.relPath,
from: params.from ?? 1,
lines: params.lines ?? 120,
}));
vi.clearAllMocks();
}
export function getMemorySearchManagerMockCalls(): number {
return getMemorySearchManagerMock.mock.calls.length;
}
export function getMemorySearchManagerMockConfigs(): unknown[] {
return getMemorySearchManagerMock.mock.calls.map(([params]) => params.cfg);
}
export function getMemorySearchManagerMockParams(): Array<{ cfg?: unknown; agentId?: string }> {
return getMemorySearchManagerMock.mock.calls.map(([params]) => params);
}
export function getReadAgentMemoryFileMockCalls(): number {
return readAgentMemoryFileMock.mock.calls.length;
}