feat(memory-wiki): add shared memory search bridge

This commit is contained in:
Vincent Koc
2026-04-05 21:36:54 +01:00
parent ca94f02959
commit 00372508b5
14 changed files with 441 additions and 59 deletions

View File

@@ -296,6 +296,7 @@ Current bundled provider examples:
| `plugin-sdk/memory-host-events` | Memory host event journal alias | Vendor-neutral alias for memory host event journal helpers |
| `plugin-sdk/memory-host-files` | Memory host file/runtime alias | Vendor-neutral alias for memory host file/runtime helpers |
| `plugin-sdk/memory-host-markdown` | Managed markdown helpers | Shared managed-markdown helpers for memory-adjacent plugins |
| `plugin-sdk/memory-host-search` | Active memory search facade | Lazy active-memory search-manager runtime facade |
| `plugin-sdk/memory-host-status` | Memory host status alias | Vendor-neutral alias for memory host status helpers |
| `plugin-sdk/memory-lancedb` | Bundled memory-lancedb helpers | Memory-lancedb helper surface |
| `plugin-sdk/testing` | Test utilities | Test helpers and mocks |

View File

@@ -262,6 +262,7 @@ explicitly promotes one as public.
| `plugin-sdk/memory-host-events` | Vendor-neutral alias for memory host event journal helpers |
| `plugin-sdk/memory-host-files` | Vendor-neutral alias for memory host file/runtime helpers |
| `plugin-sdk/memory-host-markdown` | Shared managed-markdown helpers for memory-adjacent plugins |
| `plugin-sdk/memory-host-search` | Active memory runtime facade for search-manager access |
| `plugin-sdk/memory-host-status` | Vendor-neutral alias for memory host status helpers |
| `plugin-sdk/memory-lancedb` | Bundled memory-lancedb helper surface |
</Accordion>

View File

@@ -249,17 +249,18 @@ export async function runWikiSearch(params: {
await syncMemoryWikiImportedSources({ config: params.config, appConfig: params.appConfig });
const results = await searchMemoryWiki({
config: params.config,
appConfig: params.appConfig,
query: params.query,
maxResults: params.maxResults,
});
const summary = params.json
? JSON.stringify(results, null, 2)
: results.length === 0
? "No wiki results."
? "No wiki or memory results."
: results
.map(
(result, index) =>
`${index + 1}. ${result.title} (${result.kind})\nPath: ${result.path}\nSnippet: ${result.snippet}`,
`${index + 1}. ${result.title} (${result.corpus}/${result.kind})\nPath: ${result.path}${typeof result.startLine === "number" && typeof result.endLine === "number" ? `\nLines: ${result.startLine}-${result.endLine}` : ""}\nSnippet: ${result.snippet}`,
)
.join("\n\n");
writeOutput(summary, params.stdout);
@@ -278,6 +279,7 @@ export async function runWikiGet(params: {
await syncMemoryWikiImportedSources({ config: params.config, appConfig: params.appConfig });
const result = await getMemoryWikiPage({
config: params.config,
appConfig: params.appConfig,
lookup: params.lookup,
fromLine: params.fromLine,
lineCount: params.lineCount,
@@ -540,7 +542,7 @@ export function registerWikiCli(
wiki
.command("search")
.description("Search wiki pages")
.description("Search wiki pages and, when configured, the active memory corpus")
.argument("<query>", "Search query")
.option("--max-results <n>", "Maximum results", (value: string) => Number(value))
.option("--json", "Print JSON")
@@ -556,7 +558,7 @@ export function registerWikiCli(
wiki
.command("get")
.description("Read a wiki page by id or relative path")
.description("Read a wiki page by id or relative path, with optional active-memory fallback")
.argument("<lookup>", "Relative path or page id")
.option("--from <n>", "Start line", (value: string) => Number(value))
.option("--lines <n>", "Number of lines", (value: string) => Number(value))

View File

@@ -207,6 +207,7 @@ export function registerMemoryWikiGatewayMethods(params: {
true,
await searchMemoryWiki({
config,
appConfig,
query,
maxResults,
}),
@@ -249,6 +250,7 @@ export function registerMemoryWikiGatewayMethods(params: {
true,
await getMemoryWikiPage({
config,
appConfig,
lookup,
fromLine,
lineCount,

View File

@@ -1,20 +1,69 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../api.js";
import { resolveMemoryWikiConfig } from "./config.js";
import { renderWikiMarkdown } from "./markdown.js";
import { getMemoryWikiPage, searchMemoryWiki } from "./query.js";
import { initializeMemoryWikiVault } from "./vault.js";
const { getActiveMemorySearchManagerMock } = vi.hoisted(() => ({
getActiveMemorySearchManagerMock: vi.fn(),
}));
vi.mock("openclaw/plugin-sdk/memory-host-search", () => ({
getActiveMemorySearchManager: getActiveMemorySearchManagerMock,
}));
const tempDirs: string[] = [];
afterEach(async () => {
await Promise.all(tempDirs.splice(0).map((dir) => fs.rm(dir, { recursive: true, force: true })));
});
beforeEach(() => {
getActiveMemorySearchManagerMock.mockReset();
getActiveMemorySearchManagerMock.mockResolvedValue({ manager: null, error: "unavailable" });
});
function createAppConfig(): OpenClawConfig {
return {
agents: {
list: [{ id: "main", default: true }],
},
} as OpenClawConfig;
}
function createMemoryManager(overrides?: {
searchResults?: Array<{
path: string;
startLine: number;
endLine: number;
score: number;
snippet: string;
source: "memory" | "sessions";
citation?: string;
}>;
readResult?: { text: string; path: string };
}) {
return {
search: vi.fn().mockResolvedValue(overrides?.searchResults ?? []),
readFile: vi.fn().mockImplementation(async () => {
if (!overrides?.readResult) {
throw new Error("missing");
}
return overrides.readResult;
}),
status: vi.fn().mockReturnValue({ backend: "builtin", provider: "builtin" }),
probeEmbeddingAvailability: vi.fn().mockResolvedValue({ ok: true }),
probeVectorAvailability: vi.fn().mockResolvedValue(false),
close: vi.fn().mockResolvedValue(undefined),
};
}
describe("searchMemoryWiki", () => {
it("finds pages by title and body", async () => {
it("finds wiki pages by title and body", async () => {
const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-wiki-query-"));
tempDirs.push(rootDir);
const config = resolveMemoryWikiConfig(
@@ -34,12 +83,105 @@ describe("searchMemoryWiki", () => {
const results = await searchMemoryWiki({ config, query: "alpha" });
expect(results).toHaveLength(1);
expect(results[0]?.corpus).toBe("wiki");
expect(results[0]?.path).toBe("sources/alpha.md");
expect(getActiveMemorySearchManagerMock).not.toHaveBeenCalled();
});
it("includes active memory results when shared search and all corpora are enabled", async () => {
const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-wiki-query-"));
tempDirs.push(rootDir);
const config = resolveMemoryWikiConfig(
{
vault: { path: rootDir },
search: { backend: "shared", corpus: "all" },
},
{ homedir: "/Users/tester" },
);
await initializeMemoryWikiVault(config);
await fs.writeFile(
path.join(rootDir, "sources", "alpha.md"),
renderWikiMarkdown({
frontmatter: { pageType: "source", id: "source.alpha", title: "Alpha Source" },
body: "# Alpha Source\n\nalpha body text\n",
}),
"utf8",
);
const manager = createMemoryManager({
searchResults: [
{
path: "MEMORY.md",
startLine: 4,
endLine: 8,
score: 42,
snippet: "alpha durable memory",
source: "memory",
citation: "MEMORY.md#L4-L8",
},
],
});
getActiveMemorySearchManagerMock.mockResolvedValue({ manager });
const results = await searchMemoryWiki({
config,
appConfig: createAppConfig(),
query: "alpha",
maxResults: 5,
});
expect(results).toHaveLength(2);
expect(results.some((result) => result.corpus === "wiki")).toBe(true);
expect(results.some((result) => result.corpus === "memory")).toBe(true);
expect(manager.search).toHaveBeenCalledWith("alpha", { maxResults: 5 });
});
it("keeps memory search disabled when the backend is local", async () => {
const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-wiki-query-"));
tempDirs.push(rootDir);
const config = resolveMemoryWikiConfig(
{
vault: { path: rootDir },
search: { backend: "local", corpus: "all" },
},
{ homedir: "/Users/tester" },
);
await initializeMemoryWikiVault(config);
await fs.writeFile(
path.join(rootDir, "sources", "alpha.md"),
renderWikiMarkdown({
frontmatter: { pageType: "source", id: "source.alpha", title: "Alpha Source" },
body: "# Alpha Source\n\nalpha only wiki\n",
}),
"utf8",
);
const manager = createMemoryManager({
searchResults: [
{
path: "MEMORY.md",
startLine: 1,
endLine: 2,
score: 50,
snippet: "alpha memory",
source: "memory",
},
],
});
getActiveMemorySearchManagerMock.mockResolvedValue({ manager });
const results = await searchMemoryWiki({
config,
appConfig: createAppConfig(),
query: "alpha",
});
expect(results).toHaveLength(1);
expect(results[0]?.corpus).toBe("wiki");
expect(manager.search).not.toHaveBeenCalled();
});
});
describe("getMemoryWikiPage", () => {
it("reads pages by relative path and slices line ranges", async () => {
it("reads wiki pages by relative path and slices line ranges", async () => {
const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-wiki-query-"));
tempDirs.push(rootDir);
const config = resolveMemoryWikiConfig(
@@ -63,9 +205,53 @@ describe("getMemoryWikiPage", () => {
lineCount: 2,
});
expect(result?.corpus).toBe("wiki");
expect(result?.path).toBe("sources/alpha.md");
expect(result?.content).toContain("line one");
expect(result?.content).toContain("line two");
expect(result?.content).not.toContain("line three");
});
it("falls back to active memory reads when memory corpus is selected", async () => {
const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-wiki-query-"));
tempDirs.push(rootDir);
const config = resolveMemoryWikiConfig(
{
vault: { path: rootDir },
search: { backend: "shared", corpus: "memory" },
},
{ homedir: "/Users/tester" },
);
await initializeMemoryWikiVault(config);
const manager = createMemoryManager({
readResult: {
path: "MEMORY.md",
text: "durable alpha memory\nline two",
},
});
getActiveMemorySearchManagerMock.mockResolvedValue({ manager });
const result = await getMemoryWikiPage({
config,
appConfig: createAppConfig(),
lookup: "MEMORY.md",
fromLine: 2,
lineCount: 2,
});
expect(result).toEqual({
corpus: "memory",
path: "MEMORY.md",
title: "MEMORY",
kind: "memory",
content: "durable alpha memory\nline two",
fromLine: 2,
lineCount: 2,
});
expect(manager.readFile).toHaveBeenCalledWith({
relPath: "MEMORY.md",
from: 2,
lines: 2,
});
});
});

View File

@@ -1,5 +1,9 @@
import fs from "node:fs/promises";
import path from "node:path";
import { resolveDefaultAgentId } from "openclaw/plugin-sdk/config-runtime";
import type { MemorySearchResult } from "openclaw/plugin-sdk/memory-host-files";
import { getActiveMemorySearchManager } from "openclaw/plugin-sdk/memory-host-search";
import type { OpenClawConfig } from "../api.js";
import type { ResolvedMemoryWikiConfig } from "./config.js";
import { parseWikiMarkdown, toWikiPageSummary, type WikiPageSummary } from "./markdown.js";
import { initializeMemoryWikiVault } from "./vault.js";
@@ -7,18 +11,24 @@ import { initializeMemoryWikiVault } from "./vault.js";
const QUERY_DIRS = ["entities", "concepts", "sources", "syntheses", "reports"] as const;
export type WikiSearchResult = {
corpus: "wiki" | "memory";
path: string;
title: string;
kind: WikiPageSummary["kind"];
kind: WikiPageSummary["kind"] | "memory";
score: number;
snippet: string;
id?: string;
startLine?: number;
endLine?: number;
citation?: string;
memorySource?: MemorySearchResult["source"];
};
export type WikiGetResult = {
corpus: "wiki" | "memory";
path: string;
title: string;
kind: WikiPageSummary["kind"];
kind: WikiPageSummary["kind"] | "memory";
content: string;
fromLine: number;
lineCount: number;
@@ -113,6 +123,74 @@ function normalizeLookupKey(value: string): string {
return normalized.endsWith(".md") ? normalized : normalized.replace(/\/+$/, "");
}
function buildLookupCandidates(lookup: string): string[] {
const normalized = normalizeLookupKey(lookup);
const withExtension = normalized.endsWith(".md") ? normalized : `${normalized}.md`;
return [...new Set([normalized, withExtension])];
}
function shouldSearchWiki(config: ResolvedMemoryWikiConfig): boolean {
return config.search.corpus === "wiki" || config.search.corpus === "all";
}
function shouldSearchSharedMemory(
config: ResolvedMemoryWikiConfig,
appConfig?: OpenClawConfig,
): boolean {
return (
config.search.backend === "shared" &&
appConfig !== undefined &&
(config.search.corpus === "memory" || config.search.corpus === "all")
);
}
async function resolveActiveMemoryManager(appConfig?: OpenClawConfig) {
if (!appConfig) {
return null;
}
try {
const { manager } = await getActiveMemorySearchManager({
cfg: appConfig,
agentId: resolveDefaultAgentId(appConfig),
});
return manager;
} catch {
return null;
}
}
function buildMemorySearchTitle(resultPath: string): string {
const basename = path.basename(resultPath, path.extname(resultPath));
return basename.length > 0 ? basename : resultPath;
}
function toWikiSearchResult(page: QueryableWikiPage, query: string): WikiSearchResult {
return {
corpus: "wiki",
path: page.relativePath,
title: page.title,
kind: page.kind,
score: scorePage(page, query),
snippet: buildSnippet(page.raw, query),
...(page.id ? { id: page.id } : {}),
};
}
function toMemoryWikiSearchResult(result: MemorySearchResult): WikiSearchResult {
return {
corpus: "memory",
path: result.path,
title: buildMemorySearchTitle(result.path),
kind: "memory",
score: result.score,
snippet: result.snippet,
startLine: result.startLine,
endLine: result.endLine,
memorySource: result.source,
...(result.citation ? { citation: result.citation } : {}),
};
}
export function resolveQueryableWikiPageByLookup(
pages: QueryableWikiPage[],
lookup: string,
@@ -131,22 +209,29 @@ export function resolveQueryableWikiPageByLookup(
export async function searchMemoryWiki(params: {
config: ResolvedMemoryWikiConfig;
appConfig?: OpenClawConfig;
query: string;
maxResults?: number;
}): Promise<WikiSearchResult[]> {
await initializeMemoryWikiVault(params.config);
const pages = await readQueryableWikiPages(params.config.vault.path);
const maxResults = Math.max(1, params.maxResults ?? 10);
return pages
.map((page) => ({
path: page.relativePath,
title: page.title,
kind: page.kind,
score: scorePage(page, params.query),
snippet: buildSnippet(page.raw, params.query),
...(page.id ? { id: page.id } : {}),
}))
.filter((page) => page.score > 0)
const wikiResults = shouldSearchWiki(params.config)
? (await readQueryableWikiPages(params.config.vault.path))
.map((page) => toWikiSearchResult(page, params.query))
.filter((page) => page.score > 0)
: [];
const sharedMemoryManager = shouldSearchSharedMemory(params.config, params.appConfig)
? await resolveActiveMemoryManager(params.appConfig)
: null;
const memoryResults = sharedMemoryManager
? (await sharedMemoryManager.search(params.query, { maxResults })).map((result) =>
toMemoryWikiSearchResult(result),
)
: [];
return [...wikiResults, ...memoryResults]
.toSorted((left, right) => {
if (left.score !== right.score) {
return right.score - left.score;
@@ -158,30 +243,65 @@ export async function searchMemoryWiki(params: {
export async function getMemoryWikiPage(params: {
config: ResolvedMemoryWikiConfig;
appConfig?: OpenClawConfig;
lookup: string;
fromLine?: number;
lineCount?: number;
}): Promise<WikiGetResult | null> {
await initializeMemoryWikiVault(params.config);
const pages = await readQueryableWikiPages(params.config.vault.path);
const page = resolveQueryableWikiPageByLookup(pages, params.lookup);
if (!page) {
const fromLine = Math.max(1, params.fromLine ?? 1);
const lineCount = Math.max(1, params.lineCount ?? 200);
if (shouldSearchWiki(params.config)) {
const pages = await readQueryableWikiPages(params.config.vault.path);
const page = resolveQueryableWikiPageByLookup(pages, params.lookup);
if (page) {
const parsed = parseWikiMarkdown(page.raw);
const lines = parsed.body.split(/\r?\n/);
const slice = lines.slice(fromLine - 1, fromLine - 1 + lineCount).join("\n");
return {
corpus: "wiki",
path: page.relativePath,
title: page.title,
kind: page.kind,
content: slice,
fromLine,
lineCount,
...(page.id ? { id: page.id } : {}),
};
}
}
if (!shouldSearchSharedMemory(params.config, params.appConfig)) {
return null;
}
const parsed = parseWikiMarkdown(page.raw);
const lines = parsed.body.split(/\r?\n/);
const fromLine = Math.max(1, params.fromLine ?? 1);
const lineCount = Math.max(1, params.lineCount ?? 200);
const slice = lines.slice(fromLine - 1, fromLine - 1 + lineCount).join("\n");
const manager = await resolveActiveMemoryManager(params.appConfig);
if (!manager) {
return null;
}
return {
path: page.relativePath,
title: page.title,
kind: page.kind,
content: slice,
fromLine,
lineCount,
...(page.id ? { id: page.id } : {}),
};
for (const relPath of buildLookupCandidates(params.lookup)) {
try {
const result = await manager.readFile({
relPath,
from: fromLine,
lines: lineCount,
});
return {
corpus: "memory",
path: result.path,
title: buildMemorySearchTitle(result.path),
kind: "memory",
content: result.text,
fromLine,
lineCount,
};
} catch {
continue;
}
}
return null;
}

View File

@@ -74,23 +74,25 @@ export function createWikiSearchTool(
return {
name: "wiki_search",
label: "Wiki Search",
description: "Search wiki pages by title, path, id, or body text.",
description:
"Search wiki pages and, when shared search is enabled, the active memory corpus by title, path, id, or body text.",
parameters: WikiSearchSchema,
execute: async (_toolCallId, rawParams) => {
const params = rawParams as { query: string; maxResults?: number };
await syncImportedSourcesIfNeeded(config, appConfig);
const results = await searchMemoryWiki({
config,
appConfig,
query: params.query,
maxResults: params.maxResults,
});
const text =
results.length === 0
? "No wiki results."
? "No wiki or memory results."
: results
.map(
(result, index) =>
`${index + 1}. ${result.title} (${result.kind})\nPath: ${result.path}\nSnippet: ${result.snippet}`,
`${index + 1}. ${result.title} (${result.corpus}/${result.kind})\nPath: ${result.path}${typeof result.startLine === "number" && typeof result.endLine === "number" ? `\nLines: ${result.startLine}-${result.endLine}` : ""}\nSnippet: ${result.snippet}`,
)
.join("\n\n");
return {
@@ -176,13 +178,15 @@ export function createWikiGetTool(
return {
name: "wiki_get",
label: "Wiki Get",
description: "Read a wiki page by id or relative path.",
description:
"Read a wiki page by id or relative path, or fall back to the active memory corpus when shared search is enabled.",
parameters: WikiGetSchema,
execute: async (_toolCallId, rawParams) => {
const params = rawParams as { lookup: string; fromLine?: number; lineCount?: number };
await syncImportedSourcesIfNeeded(config, appConfig);
const result = await getMemoryWikiPage({
config,
appConfig,
lookup: params.lookup,
fromLine: params.fromLine,
lineCount: params.lineCount,

View File

@@ -715,6 +715,10 @@
"types": "./dist/plugin-sdk/memory-host-markdown.d.ts",
"default": "./dist/plugin-sdk/memory-host-markdown.js"
},
"./plugin-sdk/memory-host-search": {
"types": "./dist/plugin-sdk/memory-host-search.d.ts",
"default": "./dist/plugin-sdk/memory-host-search.js"
},
"./plugin-sdk/memory-host-status": {
"types": "./dist/plugin-sdk/memory-host-status.d.ts",
"default": "./dist/plugin-sdk/memory-host-status.js"

View File

@@ -168,6 +168,7 @@
"memory-host-events",
"memory-host-files",
"memory-host-markdown",
"memory-host-search",
"memory-host-status",
"memory-lancedb",
"msteams",

View File

@@ -3,4 +3,4 @@
export { listMemoryFiles, normalizeExtraMemoryPaths } from "./host/internal.js";
export { readAgentMemoryFile } from "./host/read-file.js";
export { resolveMemoryBackendConfig } from "./host/backend-config.js";
export type { MemorySearchResult } from "./host/types.js";
export type { MemorySearchManager, MemorySearchResult } from "./host/types.js";

View File

@@ -0,0 +1,5 @@
export {
closeActiveMemorySearchManagers,
getActiveMemorySearchManager,
resolveActiveMemoryBackendConfig,
} from "../plugins/memory-runtime.js";

View File

@@ -0,0 +1,42 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import {
closeActiveMemorySearchManagers,
getActiveMemorySearchManager,
} from "./memory-host-search.js";
const { closeActiveMemorySearchManagersMock, getActiveMemorySearchManagerMock } = vi.hoisted(
() => ({
closeActiveMemorySearchManagersMock: vi.fn(),
getActiveMemorySearchManagerMock: vi.fn(),
}),
);
vi.mock("./memory-host-search.runtime.js", () => ({
closeActiveMemorySearchManagers: closeActiveMemorySearchManagersMock,
getActiveMemorySearchManager: getActiveMemorySearchManagerMock,
}));
describe("memory-host-search facade", () => {
beforeEach(() => {
closeActiveMemorySearchManagersMock.mockReset();
getActiveMemorySearchManagerMock.mockReset();
});
it("delegates active manager lookup to the lazy runtime module", async () => {
const cfg = { agents: { list: [{ id: "main", default: true }] } } as OpenClawConfig;
const expected = { manager: null, error: "unavailable" };
getActiveMemorySearchManagerMock.mockResolvedValue(expected);
await expect(getActiveMemorySearchManager({ cfg, agentId: "main" })).resolves.toEqual(expected);
expect(getActiveMemorySearchManagerMock).toHaveBeenCalledWith({ cfg, agentId: "main" });
});
it("delegates runtime cleanup to the lazy runtime module", async () => {
const cfg = { agents: { list: [{ id: "main", default: true }] } } as OpenClawConfig;
await closeActiveMemorySearchManagers(cfg);
expect(closeActiveMemorySearchManagersMock).toHaveBeenCalledWith(cfg);
});
});

View File

@@ -0,0 +1,29 @@
import type { OpenClawConfig } from "../config/config.js";
import type { RegisteredMemorySearchManager } from "../plugins/memory-state.js";
type ActiveMemorySearchPurpose = "default" | "status";
export type ActiveMemorySearchManagerResult = {
manager: RegisteredMemorySearchManager | null;
error?: string;
};
type MemoryHostSearchRuntimeModule = typeof import("./memory-host-search.runtime.js");
async function loadMemoryHostSearchRuntime(): Promise<MemoryHostSearchRuntimeModule> {
return await import("./memory-host-search.runtime.js");
}
export async function getActiveMemorySearchManager(params: {
cfg: OpenClawConfig;
agentId: string;
purpose?: ActiveMemorySearchPurpose;
}): Promise<ActiveMemorySearchManagerResult> {
const runtime = await loadMemoryHostSearchRuntime();
return await runtime.getActiveMemorySearchManager(params);
}
export async function closeActiveMemorySearchManagers(cfg?: OpenClawConfig): Promise<void> {
const runtime = await loadMemoryHostSearchRuntime();
await runtime.closeActiveMemorySearchManagers(cfg);
}

View File

@@ -1,10 +1,6 @@
import type { OpenClawConfig } from "../config/config.js";
import type { MemoryCitationsMode } from "../config/types.memory.js";
import type {
MemoryEmbeddingProbeResult,
MemoryProviderStatus,
MemorySyncProgressUpdate,
} from "../memory-host-sdk/engine-storage.js";
import type { MemorySearchManager } from "../memory-host-sdk/runtime-files.js";
export type MemoryPromptSectionBuilder = (params: {
availableTools: Set<string>;
@@ -25,18 +21,7 @@ export type MemoryFlushPlanResolver = (params: {
nowMs?: number;
}) => MemoryFlushPlan | null;
export type RegisteredMemorySearchManager = {
status(): MemoryProviderStatus;
probeEmbeddingAvailability(): Promise<MemoryEmbeddingProbeResult>;
probeVectorAvailability(): Promise<boolean>;
sync?(params?: {
reason?: string;
force?: boolean;
sessionFiles?: string[];
progress?: (update: MemorySyncProgressUpdate) => void;
}): Promise<void>;
close?(): Promise<void>;
};
export type RegisteredMemorySearchManager = MemorySearchManager;
export type MemoryRuntimeQmdConfig = {
command?: string;