Files
openclaw/src/plugins/memory-runtime.ts
clawsweeper[bot] 1c1c75df72 fix(memory): close local embedding providers on timeout (#84048)
Summary:
- The branch adds a close lifecycle for local memory embedding providers, scoped memory search/index teardown for one agent, Active Memory timeout cleanup, focused tests, and a changelog entry.
- Reproducibility: yes. The linked issue gives a concrete OpenClaw 2026.5.18 Telegram Active Memory timeout pa ... current-main source inspection confirms there is no timeout cleanup for that local embedding provider path.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(memory): close local embedding providers on timeout

Validation:
- ClawSweeper review passed for head 8e2e369b5c.
- Required merge gates passed before the squash merge.

Prepared head SHA: 8e2e369b5c
Review: https://github.com/openclaw/openclaw/pull/84048#issuecomment-4485705481

Co-authored-by: brokemac79 <martin_cleary@yahoo.co.uk>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
2026-05-19 09:19:09 +00:00

85 lines
2.8 KiB
TypeScript

import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { resolveUserPath } from "../utils.js";
import { getLoadedRuntimePluginRegistry } from "./active-runtime-registry.js";
import { normalizePluginsConfig } from "./config-state.js";
import { getMemoryRuntime } from "./memory-state.js";
import { ensureStandaloneRuntimePluginRegistryLoaded } from "./runtime/standalone-runtime-registry-loader.js";
function resolveMemoryRuntimePluginIds(config: OpenClawConfig): string[] {
const plugins = normalizePluginsConfig(config.plugins);
const memorySlot = plugins.slots.memory;
if (!plugins.enabled || typeof memorySlot !== "string" || memorySlot.trim().length === 0) {
return [];
}
const pluginId = memorySlot.trim();
if (plugins.deny.includes(pluginId) || plugins.entries[pluginId]?.enabled === false) {
return [];
}
return [pluginId];
}
function resolveMemoryRuntimeWorkspaceDir(cfg: OpenClawConfig): string | undefined {
const agentId = resolveDefaultAgentId(cfg);
const dir = resolveAgentWorkspaceDir(cfg, agentId);
if (typeof dir !== "string" || !dir.trim()) {
return undefined;
}
return resolveUserPath(dir);
}
function ensureMemoryRuntime(cfg?: OpenClawConfig) {
const current = getMemoryRuntime();
if (current || !cfg) {
return current;
}
const onlyPluginIds = resolveMemoryRuntimePluginIds(cfg);
if (onlyPluginIds.length === 0) {
return getMemoryRuntime();
}
getLoadedRuntimePluginRegistry({ requiredPluginIds: onlyPluginIds });
if (getMemoryRuntime()) {
return getMemoryRuntime();
}
const workspaceDir = resolveMemoryRuntimeWorkspaceDir(cfg);
ensureStandaloneRuntimePluginRegistryLoaded({
requiredPluginIds: onlyPluginIds,
loadOptions: {
config: cfg,
onlyPluginIds,
workspaceDir,
},
});
return getMemoryRuntime();
}
export async function getActiveMemorySearchManager(params: {
cfg: OpenClawConfig;
agentId: string;
purpose?: "default" | "status" | "cli";
}) {
const runtime = ensureMemoryRuntime(params.cfg);
if (!runtime) {
return { manager: null, error: "memory plugin unavailable" };
}
return await runtime.getMemorySearchManager(params);
}
export function resolveActiveMemoryBackendConfig(params: { cfg: OpenClawConfig; agentId: string }) {
return ensureMemoryRuntime(params.cfg)?.resolveMemoryBackendConfig(params) ?? null;
}
export async function closeActiveMemorySearchManagers(cfg?: OpenClawConfig): Promise<void> {
void cfg;
const runtime = getMemoryRuntime();
await runtime?.closeAllMemorySearchManagers?.();
}
export async function closeActiveMemorySearchManager(params: {
cfg: OpenClawConfig;
agentId: string;
}): Promise<void> {
const runtime = getMemoryRuntime();
await runtime?.closeMemorySearchManager?.(params);
}