mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
refactor(memory): inject local service host hooks
This commit is contained in:
@@ -9,7 +9,9 @@ export default definePluginEntry({
|
||||
api.registerCli(
|
||||
async ({ program }) => {
|
||||
const { registerMemoryCli } = await import("./cli.js");
|
||||
registerMemoryCli(program);
|
||||
registerMemoryCli(program, {
|
||||
acquireLocalService: api.runtime.llm?.acquireLocalService,
|
||||
});
|
||||
},
|
||||
{
|
||||
descriptors: [
|
||||
|
||||
@@ -13,12 +13,21 @@ import {
|
||||
import { buildPromptSection } from "./src/prompt-section.js";
|
||||
|
||||
const closeMemorySearchManagerMock = vi.hoisted(() => vi.fn(async () => {}));
|
||||
const getMemorySearchManagerMock = vi.hoisted(() => vi.fn(async () => null));
|
||||
const createMemoryRuntimeMock = vi.hoisted(() =>
|
||||
vi.fn(() => ({
|
||||
closeAllMemorySearchManagers: vi.fn(async () => {}),
|
||||
closeMemorySearchManager: closeMemorySearchManagerMock,
|
||||
getMemorySearchManager: getMemorySearchManagerMock,
|
||||
})),
|
||||
);
|
||||
|
||||
vi.mock("./src/runtime-provider.js", () => ({
|
||||
createMemoryRuntime: createMemoryRuntimeMock,
|
||||
memoryRuntime: {
|
||||
closeAllMemorySearchManagers: vi.fn(async () => {}),
|
||||
closeMemorySearchManager: closeMemorySearchManagerMock,
|
||||
getMemorySearchManager: vi.fn(async () => null),
|
||||
getMemorySearchManager: getMemorySearchManagerMock,
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -121,6 +130,15 @@ describe("memory-core plugin runtime registration", () => {
|
||||
|
||||
expect(closeMemorySearchManagerMock).toHaveBeenCalledWith({ cfg, agentId: "main" });
|
||||
});
|
||||
|
||||
it("binds the host local-service hook to the registered memory runtime", async () => {
|
||||
const runtime = registerMemoryCoreRuntime();
|
||||
const cfg = {} as OpenClawConfig;
|
||||
|
||||
await runtime.getMemorySearchManager({ cfg, agentId: "main" });
|
||||
|
||||
expect(createMemoryRuntimeMock).toHaveBeenCalledWith(hostRuntime.llm.acquireLocalService);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildMemoryFlushPlan", () => {
|
||||
|
||||
@@ -18,7 +18,7 @@ import type { TSchema } from "typebox";
|
||||
import { configureMemoryCoreDreamingState } from "./src/dreaming-state.js";
|
||||
import { registerShortTermPromotionDreaming } from "./src/dreaming.js";
|
||||
import { buildMemoryFlushPlan } from "./src/flush-plan.js";
|
||||
import { configureMemoryCoreEmbeddingLocalService } from "./src/memory/embedding-local-service.js";
|
||||
import type { MemoryCoreAcquireLocalService } from "./src/memory/embedding-local-service.js";
|
||||
import { buildPromptSection } from "./src/prompt-section.js";
|
||||
|
||||
type MemoryToolsModule = typeof import("./src/tools.js");
|
||||
@@ -30,6 +30,7 @@ type MemoryToolOptions = {
|
||||
agentSessionKey?: string;
|
||||
sandboxed?: boolean;
|
||||
oneShotCliRun?: boolean;
|
||||
acquireLocalService?: MemoryCoreAcquireLocalService;
|
||||
};
|
||||
|
||||
const loadMemoryToolsModule = createLazyRuntimeModule(() => import("./src/tools.js"));
|
||||
@@ -140,7 +141,10 @@ function createLazyMemoryGetTool(options: MemoryToolOptions): AnyAgentTool | nul
|
||||
});
|
||||
}
|
||||
|
||||
function resolveMemoryToolOptions(ctx: OpenClawPluginToolContext): MemoryToolOptions {
|
||||
function resolveMemoryToolOptions(
|
||||
ctx: OpenClawPluginToolContext,
|
||||
acquireLocalService?: MemoryCoreAcquireLocalService,
|
||||
): MemoryToolOptions {
|
||||
const getConfig = () => ctx.getRuntimeConfig?.() ?? ctx.runtimeConfig ?? ctx.config;
|
||||
return {
|
||||
config: getConfig(),
|
||||
@@ -149,33 +153,39 @@ function resolveMemoryToolOptions(ctx: OpenClawPluginToolContext): MemoryToolOpt
|
||||
agentSessionKey: ctx.sessionKey,
|
||||
sandboxed: ctx.sandboxed,
|
||||
oneShotCliRun: ctx.oneShotCliRun,
|
||||
...(acquireLocalService ? { acquireLocalService } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function createLazyMemoryRuntime(
|
||||
acquireLocalService?: MemoryCoreAcquireLocalService,
|
||||
): MemoryPluginRuntime {
|
||||
return {
|
||||
async getMemorySearchManager(params) {
|
||||
const { createMemoryRuntime } = await loadRuntimeProviderModule();
|
||||
return await createMemoryRuntime(acquireLocalService).getMemorySearchManager(params);
|
||||
},
|
||||
resolveMemoryBackendConfig(params) {
|
||||
return resolveMemoryBackendConfig(params);
|
||||
},
|
||||
async closeAllMemorySearchManagers() {
|
||||
const { memoryRuntime: runtime } = await loadRuntimeProviderModule();
|
||||
await runtime.closeAllMemorySearchManagers?.();
|
||||
},
|
||||
async closeMemorySearchManager(params) {
|
||||
const { memoryRuntime: runtime } = await loadRuntimeProviderModule();
|
||||
await runtime.closeMemorySearchManager?.(params);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const memoryRuntime: MemoryPluginRuntime = {
|
||||
async getMemorySearchManager(params) {
|
||||
const { memoryRuntime: runtime } = await loadRuntimeProviderModule();
|
||||
return await runtime.getMemorySearchManager(params);
|
||||
},
|
||||
resolveMemoryBackendConfig(params) {
|
||||
return resolveMemoryBackendConfig(params);
|
||||
},
|
||||
async closeAllMemorySearchManagers() {
|
||||
const { memoryRuntime: runtime } = await loadRuntimeProviderModule();
|
||||
await runtime.closeAllMemorySearchManagers?.();
|
||||
},
|
||||
async closeMemorySearchManager(params) {
|
||||
const { memoryRuntime: runtime } = await loadRuntimeProviderModule();
|
||||
await runtime.closeMemorySearchManager?.(params);
|
||||
},
|
||||
};
|
||||
export default definePluginEntry({
|
||||
id: "memory-core",
|
||||
name: "Memory (Core)",
|
||||
description: "File-backed memory search tools and CLI",
|
||||
kind: "memory",
|
||||
register(api) {
|
||||
configureMemoryCoreEmbeddingLocalService(api.runtime.llm?.acquireLocalService);
|
||||
const acquireLocalService = api.runtime.llm?.acquireLocalService;
|
||||
configureMemoryCoreDreamingState(<T>(options: OpenKeyedStoreOptions) =>
|
||||
api.runtime.state.openKeyedStore<T>(options),
|
||||
);
|
||||
@@ -183,7 +193,7 @@ export default definePluginEntry({
|
||||
api.registerMemoryCapability({
|
||||
promptBuilder: buildPromptSection,
|
||||
flushPlanResolver: buildMemoryFlushPlan,
|
||||
runtime: memoryRuntime,
|
||||
runtime: createLazyMemoryRuntime(acquireLocalService),
|
||||
publicArtifacts: {
|
||||
async listArtifacts(params) {
|
||||
const { listMemoryCorePublicArtifacts } = await import("./src/public-artifacts.js");
|
||||
@@ -192,13 +202,19 @@ export default definePluginEntry({
|
||||
},
|
||||
});
|
||||
|
||||
api.registerTool((ctx) => createLazyMemorySearchTool(resolveMemoryToolOptions(ctx)), {
|
||||
names: ["memory_search"],
|
||||
});
|
||||
api.registerTool(
|
||||
(ctx) => createLazyMemorySearchTool(resolveMemoryToolOptions(ctx, acquireLocalService)),
|
||||
{
|
||||
names: ["memory_search"],
|
||||
},
|
||||
);
|
||||
|
||||
api.registerTool((ctx) => createLazyMemoryGetTool(resolveMemoryToolOptions(ctx)), {
|
||||
names: ["memory_get"],
|
||||
});
|
||||
api.registerTool(
|
||||
(ctx) => createLazyMemoryGetTool(resolveMemoryToolOptions(ctx, acquireLocalService)),
|
||||
{
|
||||
names: ["memory_get"],
|
||||
},
|
||||
);
|
||||
|
||||
api.registerCommand({
|
||||
name: "dreaming",
|
||||
@@ -214,7 +230,7 @@ export default definePluginEntry({
|
||||
api.registerCli(
|
||||
async ({ program }) => {
|
||||
const { registerMemoryCli } = await import("./cli.js");
|
||||
registerMemoryCli(program);
|
||||
registerMemoryCli(program, { acquireLocalService });
|
||||
},
|
||||
{
|
||||
descriptors: [
|
||||
|
||||
@@ -52,6 +52,10 @@ import {
|
||||
} from "./dreaming-repair.js";
|
||||
import { asRecord } from "./dreaming-shared.js";
|
||||
import { resolveShortTermPromotionDreamingConfig } from "./dreaming.js";
|
||||
import type {
|
||||
MemoryCoreAcquireLocalService,
|
||||
MemoryCoreLocalServiceHost,
|
||||
} from "./memory/embedding-local-service.js";
|
||||
import { formatMemoryVectorDegradedWriteReason } from "./memory/manager-vector-warning.js";
|
||||
import { previewGroundedRemMarkdown } from "./rem-evidence.js";
|
||||
import { previewRemHarness } from "./rem-harness.js";
|
||||
@@ -540,6 +544,7 @@ async function withMemoryManagerForAgent(params: {
|
||||
cfg: OpenClawConfig;
|
||||
agentId: string;
|
||||
purpose?: MemoryManagerPurpose;
|
||||
acquireLocalService?: MemoryCoreAcquireLocalService;
|
||||
run: (manager: MemoryManager) => Promise<void>;
|
||||
}): Promise<void> {
|
||||
const managerParams: Parameters<typeof getMemorySearchManager>[0] = {
|
||||
@@ -549,6 +554,9 @@ async function withMemoryManagerForAgent(params: {
|
||||
if (params.purpose) {
|
||||
managerParams.purpose = params.purpose;
|
||||
}
|
||||
if (params.acquireLocalService) {
|
||||
managerParams.acquireLocalService = params.acquireLocalService;
|
||||
}
|
||||
await withManager<MemoryManager>({
|
||||
getManager: () => getMemorySearchManager(managerParams),
|
||||
onMissing: (error) => defaultRuntime.log(error ?? "Memory search disabled."),
|
||||
@@ -740,7 +748,10 @@ async function scanMemorySources(params: {
|
||||
return { sources: scans, totalFiles, issues };
|
||||
}
|
||||
|
||||
export async function runMemoryStatus(opts: MemoryCommandOptions) {
|
||||
export async function runMemoryStatus(
|
||||
opts: MemoryCommandOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
setVerbose(Boolean(opts.verbose));
|
||||
const { config: cfg, diagnostics } = await loadMemoryCommandConfig("memory status");
|
||||
emitMemorySecretResolveDiagnostics(diagnostics, { json: Boolean(opts.json) });
|
||||
@@ -763,6 +774,7 @@ export async function runMemoryStatus(opts: MemoryCommandOptions) {
|
||||
cfg,
|
||||
agentId,
|
||||
purpose: managerPurpose,
|
||||
acquireLocalService: hostOptions?.acquireLocalService,
|
||||
run: async (manager) => {
|
||||
const deep = Boolean(opts.deep || opts.index);
|
||||
let embeddingProbe: MemoryEmbeddingProbeResult | undefined;
|
||||
@@ -1166,7 +1178,10 @@ export async function runMemoryStatus(opts: MemoryCommandOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function runMemoryIndex(opts: MemoryCommandOptions) {
|
||||
export async function runMemoryIndex(
|
||||
opts: MemoryCommandOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
setVerbose(Boolean(opts.verbose));
|
||||
const { config: cfg, diagnostics } = await loadMemoryCommandConfig("memory index");
|
||||
emitMemorySecretResolveDiagnostics(diagnostics);
|
||||
@@ -1176,6 +1191,7 @@ export async function runMemoryIndex(opts: MemoryCommandOptions) {
|
||||
cfg,
|
||||
agentId,
|
||||
purpose: "cli",
|
||||
acquireLocalService: hostOptions?.acquireLocalService,
|
||||
run: async (manager) => {
|
||||
try {
|
||||
const syncFn = manager.sync ? manager.sync.bind(manager) : undefined;
|
||||
@@ -1329,6 +1345,7 @@ export async function runMemoryIndex(opts: MemoryCommandOptions) {
|
||||
export async function runMemorySearch(
|
||||
queryArg: string | undefined,
|
||||
opts: MemorySearchCommandOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
const query = opts.query ?? queryArg;
|
||||
if (!query) {
|
||||
@@ -1352,6 +1369,7 @@ export async function runMemorySearch(
|
||||
cfg,
|
||||
agentId,
|
||||
purpose: "cli",
|
||||
acquireLocalService: hostOptions?.acquireLocalService,
|
||||
run: async (manager) => {
|
||||
const sessionKey = buildCliMemorySearchSessionKey(agentId);
|
||||
let results: Awaited<ReturnType<typeof manager.search>>;
|
||||
@@ -1416,7 +1434,10 @@ export async function runMemorySearch(
|
||||
});
|
||||
}
|
||||
|
||||
export async function runMemoryPromote(opts: MemoryPromoteCommandOptions) {
|
||||
export async function runMemoryPromote(
|
||||
opts: MemoryPromoteCommandOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
const { config: cfg, diagnostics } = await loadMemoryCommandConfig("memory promote");
|
||||
emitMemorySecretResolveDiagnostics(diagnostics, { json: Boolean(opts.json) });
|
||||
const agentId = resolveAgent(cfg, opts.agent);
|
||||
@@ -1425,6 +1446,7 @@ export async function runMemoryPromote(opts: MemoryPromoteCommandOptions) {
|
||||
cfg,
|
||||
agentId,
|
||||
purpose: "status",
|
||||
acquireLocalService: hostOptions?.acquireLocalService,
|
||||
run: async (manager) => {
|
||||
const status = manager.status();
|
||||
const workspaceDir = status.workspaceDir?.trim();
|
||||
@@ -1594,6 +1616,7 @@ export async function runMemoryPromote(opts: MemoryPromoteCommandOptions) {
|
||||
export async function runMemoryPromoteExplain(
|
||||
selectorArg: string | undefined,
|
||||
opts: MemoryPromoteExplainOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
const selector = selectorArg?.trim();
|
||||
if (!selector) {
|
||||
@@ -1610,6 +1633,7 @@ export async function runMemoryPromoteExplain(
|
||||
cfg,
|
||||
agentId,
|
||||
purpose: "status",
|
||||
acquireLocalService: hostOptions?.acquireLocalService,
|
||||
run: async (manager) => {
|
||||
const status = manager.status();
|
||||
const workspaceDir = status.workspaceDir?.trim();
|
||||
@@ -1709,7 +1733,10 @@ export async function runMemoryPromoteExplain(
|
||||
});
|
||||
}
|
||||
|
||||
export async function runMemoryRemHarness(opts: MemoryRemHarnessOptions) {
|
||||
export async function runMemoryRemHarness(
|
||||
opts: MemoryRemHarnessOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
const { config: cfg, diagnostics } = await loadMemoryCommandConfig("memory rem-harness");
|
||||
emitMemorySecretResolveDiagnostics(diagnostics, { json: Boolean(opts.json) });
|
||||
const agentId = resolveAgent(cfg, opts.agent);
|
||||
@@ -1718,6 +1745,7 @@ export async function runMemoryRemHarness(opts: MemoryRemHarnessOptions) {
|
||||
cfg,
|
||||
agentId,
|
||||
purpose: "status",
|
||||
acquireLocalService: hostOptions?.acquireLocalService,
|
||||
run: async (manager) => {
|
||||
const status = manager.status();
|
||||
const managerWorkspaceDir = status.workspaceDir?.trim();
|
||||
@@ -1889,7 +1917,10 @@ export async function runMemoryRemHarness(opts: MemoryRemHarnessOptions) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function runMemoryRemBackfill(opts: MemoryRemBackfillOptions) {
|
||||
export async function runMemoryRemBackfill(
|
||||
opts: MemoryRemBackfillOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
const { config: cfg, diagnostics } = await loadMemoryCommandConfig("memory rem-backfill");
|
||||
emitMemorySecretResolveDiagnostics(diagnostics, { json: Boolean(opts.json) });
|
||||
const agentId = resolveAgent(cfg, opts.agent);
|
||||
@@ -1898,6 +1929,7 @@ export async function runMemoryRemBackfill(opts: MemoryRemBackfillOptions) {
|
||||
cfg,
|
||||
agentId,
|
||||
purpose: "status",
|
||||
acquireLocalService: hostOptions?.acquireLocalService,
|
||||
run: async (manager) => {
|
||||
const status = manager.status();
|
||||
const workspaceDir = status.workspaceDir?.trim();
|
||||
|
||||
@@ -216,10 +216,13 @@ describe("memory cli", () => {
|
||||
expect(loggedOutput(spy)).not.toContain(expected);
|
||||
}
|
||||
|
||||
async function runMemoryCli(args: string[]) {
|
||||
async function runMemoryCli(
|
||||
args: string[],
|
||||
hostOptions?: Parameters<typeof registerMemoryCli>[1],
|
||||
) {
|
||||
const program = new Command();
|
||||
program.name("test");
|
||||
registerMemoryCli(program);
|
||||
registerMemoryCli(program, hostOptions);
|
||||
await program.parseAsync(["memory", ...args], { from: "user" });
|
||||
}
|
||||
|
||||
@@ -1444,6 +1447,21 @@ describe("memory cli", () => {
|
||||
expect(close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("passes the host local-service hook to CLI memory managers", async () => {
|
||||
const close = vi.fn(async () => {});
|
||||
mockManager({ search: vi.fn(async () => []), close });
|
||||
const acquireLocalService = vi.fn(async () => undefined);
|
||||
|
||||
await runMemoryCli(["search", "hello"], { acquireLocalService });
|
||||
|
||||
expect(getMemorySearchManager).toHaveBeenCalledWith({
|
||||
cfg: {},
|
||||
agentId: "main",
|
||||
purpose: "cli",
|
||||
acquireLocalService,
|
||||
});
|
||||
});
|
||||
|
||||
it("accepts --query for memory search", async () => {
|
||||
const close = vi.fn(async () => {});
|
||||
const search = vi.fn(async () => []);
|
||||
|
||||
@@ -18,6 +18,7 @@ import type {
|
||||
MemoryRemHarnessOptions,
|
||||
MemorySearchCommandOptions,
|
||||
} from "./cli.types.js";
|
||||
import type { MemoryCoreLocalServiceHost } from "./memory/embedding-local-service.js";
|
||||
import {
|
||||
DEFAULT_PROMOTION_MIN_RECALL_COUNT,
|
||||
DEFAULT_PROMOTION_MIN_SCORE,
|
||||
@@ -28,42 +29,62 @@ const loadMemoryCliRuntime = createLazyRuntimeModule(() => import("./cli.runtime
|
||||
|
||||
const DECIMAL_NUMBER_RE = /^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/;
|
||||
|
||||
export async function runMemoryStatus(opts: MemoryCommandOptions) {
|
||||
export async function runMemoryStatus(
|
||||
opts: MemoryCommandOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
const runtime = await loadMemoryCliRuntime();
|
||||
await runtime.runMemoryStatus(opts);
|
||||
await runtime.runMemoryStatus(opts, hostOptions);
|
||||
}
|
||||
|
||||
async function runMemoryIndex(opts: MemoryCommandOptions) {
|
||||
async function runMemoryIndex(
|
||||
opts: MemoryCommandOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
const runtime = await loadMemoryCliRuntime();
|
||||
await runtime.runMemoryIndex(opts);
|
||||
await runtime.runMemoryIndex(opts, hostOptions);
|
||||
}
|
||||
|
||||
async function runMemorySearch(queryArg: string | undefined, opts: MemorySearchCommandOptions) {
|
||||
async function runMemorySearch(
|
||||
queryArg: string | undefined,
|
||||
opts: MemorySearchCommandOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
const runtime = await loadMemoryCliRuntime();
|
||||
await runtime.runMemorySearch(queryArg, opts);
|
||||
await runtime.runMemorySearch(queryArg, opts, hostOptions);
|
||||
}
|
||||
|
||||
async function runMemoryPromote(opts: MemoryPromoteCommandOptions) {
|
||||
async function runMemoryPromote(
|
||||
opts: MemoryPromoteCommandOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
const runtime = await loadMemoryCliRuntime();
|
||||
await runtime.runMemoryPromote(opts);
|
||||
await runtime.runMemoryPromote(opts, hostOptions);
|
||||
}
|
||||
|
||||
async function runMemoryPromoteExplain(
|
||||
selectorArg: string | undefined,
|
||||
opts: MemoryPromoteExplainOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
const runtime = await loadMemoryCliRuntime();
|
||||
await runtime.runMemoryPromoteExplain(selectorArg, opts);
|
||||
await runtime.runMemoryPromoteExplain(selectorArg, opts, hostOptions);
|
||||
}
|
||||
|
||||
async function runMemoryRemHarness(opts: MemoryRemHarnessOptions) {
|
||||
async function runMemoryRemHarness(
|
||||
opts: MemoryRemHarnessOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
const runtime = await loadMemoryCliRuntime();
|
||||
await runtime.runMemoryRemHarness(opts);
|
||||
await runtime.runMemoryRemHarness(opts, hostOptions);
|
||||
}
|
||||
|
||||
async function runMemoryRemBackfill(opts: MemoryRemBackfillOptions) {
|
||||
async function runMemoryRemBackfill(
|
||||
opts: MemoryRemBackfillOptions,
|
||||
hostOptions?: MemoryCoreLocalServiceHost,
|
||||
) {
|
||||
const runtime = await loadMemoryCliRuntime();
|
||||
await runtime.runMemoryRemBackfill(opts);
|
||||
await runtime.runMemoryRemBackfill(opts, hostOptions);
|
||||
}
|
||||
|
||||
function invalidCliArgument(message: string): Error & { code: string; exitCode: number } {
|
||||
@@ -100,7 +121,7 @@ function parseMemoryCliNonNegativeIntegerOption(value: string, flag: string): nu
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function registerMemoryCli(program: Command) {
|
||||
export function registerMemoryCli(program: Command, hostOptions?: MemoryCoreLocalServiceHost) {
|
||||
const memory = program
|
||||
.command("memory")
|
||||
.description("Search, inspect, and reindex memory files")
|
||||
@@ -158,7 +179,7 @@ export function registerMemoryCli(program: Command) {
|
||||
.option("--fix", "Repair stale recall locks and normalize promotion metadata")
|
||||
.option("--verbose", "Verbose logging", false)
|
||||
.action(async (opts: MemoryCommandOptions & { force?: boolean }) => {
|
||||
await runMemoryStatus(opts);
|
||||
await runMemoryStatus(opts, hostOptions);
|
||||
});
|
||||
|
||||
memory
|
||||
@@ -168,7 +189,7 @@ export function registerMemoryCli(program: Command) {
|
||||
.option("--force", "Force full reindex", false)
|
||||
.option("--verbose", "Verbose logging", false)
|
||||
.action(async (opts: MemoryCommandOptions) => {
|
||||
await runMemoryIndex(opts);
|
||||
await runMemoryIndex(opts, hostOptions);
|
||||
});
|
||||
|
||||
memory
|
||||
@@ -185,7 +206,7 @@ export function registerMemoryCli(program: Command) {
|
||||
)
|
||||
.option("--json", "Print JSON")
|
||||
.action(async (queryArg: string | undefined, opts: MemorySearchCommandOptions) => {
|
||||
await runMemorySearch(queryArg, opts);
|
||||
await runMemorySearch(queryArg, opts, hostOptions);
|
||||
});
|
||||
|
||||
memory
|
||||
@@ -214,7 +235,7 @@ export function registerMemoryCli(program: Command) {
|
||||
.option("--include-promoted", "Include already promoted candidates", false)
|
||||
.option("--json", "Print JSON")
|
||||
.action(async (opts: MemoryPromoteCommandOptions) => {
|
||||
await runMemoryPromote(opts);
|
||||
await runMemoryPromote(opts, hostOptions);
|
||||
});
|
||||
|
||||
memory
|
||||
@@ -225,7 +246,7 @@ export function registerMemoryCli(program: Command) {
|
||||
.option("--include-promoted", "Include already promoted candidates", false)
|
||||
.option("--json", "Print JSON")
|
||||
.action(async (selectorArg: string | undefined, opts: MemoryPromoteExplainOptions) => {
|
||||
await runMemoryPromoteExplain(selectorArg, opts);
|
||||
await runMemoryPromoteExplain(selectorArg, opts, hostOptions);
|
||||
});
|
||||
|
||||
memory
|
||||
@@ -237,7 +258,7 @@ export function registerMemoryCli(program: Command) {
|
||||
.option("--include-promoted", "Include already promoted deep candidates", false)
|
||||
.option("--json", "Print JSON")
|
||||
.action(async (opts: MemoryRemHarnessOptions) => {
|
||||
await runMemoryRemHarness(opts);
|
||||
await runMemoryRemHarness(opts, hostOptions);
|
||||
});
|
||||
|
||||
memory
|
||||
@@ -258,7 +279,7 @@ export function registerMemoryCli(program: Command) {
|
||||
)
|
||||
.option("--json", "Print JSON")
|
||||
.action(async (opts: MemoryRemBackfillOptions) => {
|
||||
await runMemoryRemBackfill(opts);
|
||||
await runMemoryRemBackfill(opts, hostOptions);
|
||||
});
|
||||
|
||||
memory.action(() => {
|
||||
|
||||
@@ -7,3 +7,7 @@ export type MemoryCoreAcquireLocalService = (
|
||||
},
|
||||
signal?: AbortSignal | null,
|
||||
) => Promise<{ release: () => void } | undefined>;
|
||||
|
||||
export type MemoryCoreLocalServiceHost = {
|
||||
acquireLocalService?: MemoryCoreAcquireLocalService;
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "openclaw/plugin-sdk/memory-core-host-runtime-core";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { Type } from "typebox";
|
||||
import type { MemoryCoreAcquireLocalService } from "./memory/embedding-local-service.js";
|
||||
type MemorySearchManagerResult = Awaited<
|
||||
ReturnType<(typeof import("./memory/index.js"))["getMemorySearchManager"]>
|
||||
>;
|
||||
@@ -21,6 +22,7 @@ type MemoryToolOptions = {
|
||||
agentSessionKey?: string;
|
||||
sandboxed?: boolean;
|
||||
oneShotCliRun?: boolean;
|
||||
acquireLocalService?: MemoryCoreAcquireLocalService;
|
||||
};
|
||||
|
||||
export const loadMemoryToolRuntime = createLazyRuntimeModule(() => import("./tools.runtime.js"));
|
||||
@@ -59,6 +61,7 @@ export async function getMemoryManagerContextWithPurpose(params: {
|
||||
cfg: OpenClawConfig;
|
||||
agentId: string;
|
||||
purpose?: "default" | "status" | "cli";
|
||||
acquireLocalService?: MemoryCoreAcquireLocalService;
|
||||
}): Promise<
|
||||
| {
|
||||
manager: NonNullable<MemorySearchManagerResult["manager"]>;
|
||||
@@ -74,6 +77,7 @@ export async function getMemoryManagerContextWithPurpose(params: {
|
||||
cfg: params.cfg,
|
||||
agentId: params.agentId,
|
||||
purpose: params.purpose,
|
||||
...(params.acquireLocalService ? { acquireLocalService: params.acquireLocalService } : {}),
|
||||
});
|
||||
return manager
|
||||
? {
|
||||
|
||||
@@ -169,6 +169,25 @@ describe("memory_search unavailable payloads", () => {
|
||||
expect(details.results.map((entry) => entry.score)).toEqual([1, 1, 1, 2]);
|
||||
});
|
||||
|
||||
it("passes the host local-service hook to tool memory managers", async () => {
|
||||
const acquireLocalService = vi.fn(async () => undefined);
|
||||
const tool = createMemorySearchTool({
|
||||
config: asOpenClawConfig({
|
||||
agents: { list: [{ id: "main", default: true }] },
|
||||
}),
|
||||
acquireLocalService,
|
||||
});
|
||||
if (!tool) {
|
||||
throw new Error("tool missing");
|
||||
}
|
||||
|
||||
await tool.execute("local-service-hook", { query: "hello" });
|
||||
|
||||
expect(getMemorySearchManagerMockParams()).toEqual([
|
||||
expect.objectContaining({ acquireLocalService }),
|
||||
]);
|
||||
});
|
||||
|
||||
it("returns explicit unavailable metadata for quota failures", async () => {
|
||||
setMemorySearchImpl(async () => {
|
||||
throw new Error("openai embeddings failed: 429 insufficient_quota");
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
resolveMemoryDeepDreamingConfig,
|
||||
} from "openclaw/plugin-sdk/memory-core-host-status";
|
||||
import { asRecord } from "./dreaming-shared.js";
|
||||
import type { MemoryCoreAcquireLocalService } from "./memory/embedding-local-service.js";
|
||||
import { filterMemorySearchHitsBySessionVisibility } from "./session-search-visibility.js";
|
||||
import { recordShortTermRecalls } from "./short-term-promotion.js";
|
||||
import {
|
||||
@@ -453,6 +454,7 @@ export function createMemorySearchTool(options: {
|
||||
agentSessionKey?: string;
|
||||
sandboxed?: boolean;
|
||||
oneShotCliRun?: boolean;
|
||||
acquireLocalService?: MemoryCoreAcquireLocalService;
|
||||
}) {
|
||||
return createMemoryTool({
|
||||
options,
|
||||
@@ -525,6 +527,7 @@ export function createMemorySearchTool(options: {
|
||||
cfg,
|
||||
agentId,
|
||||
purpose: memoryManagerPurpose,
|
||||
acquireLocalService: options.acquireLocalService,
|
||||
}),
|
||||
),
|
||||
)
|
||||
@@ -614,6 +617,7 @@ export function createMemorySearchTool(options: {
|
||||
cfg,
|
||||
agentId,
|
||||
purpose: memoryManagerPurpose,
|
||||
acquireLocalService: options.acquireLocalService,
|
||||
}),
|
||||
);
|
||||
if ("error" in refreshed) {
|
||||
@@ -775,6 +779,7 @@ export function createMemoryGetTool(options: {
|
||||
agentId?: string;
|
||||
agentSessionKey?: string;
|
||||
sandboxed?: boolean;
|
||||
acquireLocalService?: MemoryCoreAcquireLocalService;
|
||||
}) {
|
||||
return createMemoryTool({
|
||||
options,
|
||||
@@ -839,6 +844,7 @@ export function createMemoryGetTool(options: {
|
||||
cfg,
|
||||
agentId,
|
||||
purpose: "status",
|
||||
acquireLocalService: options.acquireLocalService,
|
||||
});
|
||||
if ("error" in memory) {
|
||||
return jsonResult({ path: relPath, text: "", disabled: true, error: memory.error });
|
||||
|
||||
Reference in New Issue
Block a user