feat(memory): delegate system prompt section to active memory plugin
Add registerMemoryPromptSection to the plugin API so the active memory plugin provides its own system prompt builder callback. The built-in memory-core plugin registers the existing Memory Recall section via this new API. When no memory plugin is active (or a plugin does not register a builder), the memory section is simply omitted from the system prompt. This lets third-party memory plugins inject their own guidance at the same position in the prompt.
This commit is contained in:
parent
4c60956d8e
commit
02222b93c5
@ -54,6 +54,7 @@ function fakeApi(overrides: Partial<OpenClawPluginApi> = {}): OpenClawPluginApi
|
||||
registerHttpRoute() {},
|
||||
registerCommand() {},
|
||||
registerContextEngine() {},
|
||||
registerMemoryPromptSection() {},
|
||||
on() {},
|
||||
resolvePath: (p) => p,
|
||||
...overrides,
|
||||
|
||||
@ -1,11 +1,35 @@
|
||||
import type { MemoryPromptSectionBuilder } from "openclaw/plugin-sdk/memory-core";
|
||||
import { definePluginEntry } from "openclaw/plugin-sdk/core";
|
||||
|
||||
const buildPromptSection: MemoryPromptSectionBuilder = ({ availableTools, citationsMode }) => {
|
||||
if (!availableTools.has("memory_search") && !availableTools.has("memory_get")) {
|
||||
return [];
|
||||
}
|
||||
const lines = [
|
||||
"## Memory Recall",
|
||||
"Before answering anything about prior work, decisions, dates, people, preferences, or todos: run memory_search on MEMORY.md + memory/*.md; then use memory_get to pull only the needed lines. If low confidence after search, say you checked.",
|
||||
];
|
||||
if (citationsMode === "off") {
|
||||
lines.push(
|
||||
"Citations are disabled: do not mention file paths or line numbers in replies unless the user explicitly asks.",
|
||||
);
|
||||
} else {
|
||||
lines.push(
|
||||
"Citations: include Source: <path#line> when it helps the user verify memory snippets.",
|
||||
);
|
||||
}
|
||||
lines.push("");
|
||||
return lines;
|
||||
};
|
||||
|
||||
export default definePluginEntry({
|
||||
id: "memory-core",
|
||||
name: "Memory (Core)",
|
||||
description: "File-backed memory search tools and CLI",
|
||||
kind: "memory",
|
||||
register(api) {
|
||||
api.registerMemoryPromptSection(buildPromptSection);
|
||||
|
||||
api.registerTool(
|
||||
(ctx) => {
|
||||
const memorySearchTool = api.runtime.tools.createMemorySearchTool({
|
||||
|
||||
@ -2,6 +2,7 @@ import { createHmac, createHash } from "node:crypto";
|
||||
import type { ReasoningLevel, ThinkLevel } from "../auto-reply/thinking.js";
|
||||
import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
|
||||
import type { MemoryCitationsMode } from "../config/types.memory.js";
|
||||
import { buildMemoryPromptSection } from "../memory/prompt-section.js";
|
||||
import { listDeliverableMessageChannels } from "../utils/message-channel.js";
|
||||
import type { ResolvedTimeFormat } from "./date-time.js";
|
||||
import type { EmbeddedContextFile } from "./pi-embedded-helpers.js";
|
||||
@ -43,24 +44,10 @@ function buildMemorySection(params: {
|
||||
if (params.isMinimal) {
|
||||
return [];
|
||||
}
|
||||
if (!params.availableTools.has("memory_search") && !params.availableTools.has("memory_get")) {
|
||||
return [];
|
||||
}
|
||||
const lines = [
|
||||
"## Memory Recall",
|
||||
"Before answering anything about prior work, decisions, dates, people, preferences, or todos: run memory_search on MEMORY.md + memory/*.md; then use memory_get to pull only the needed lines. If low confidence after search, say you checked.",
|
||||
];
|
||||
if (params.citationsMode === "off") {
|
||||
lines.push(
|
||||
"Citations are disabled: do not mention file paths or line numbers in replies unless the user explicitly asks.",
|
||||
);
|
||||
} else {
|
||||
lines.push(
|
||||
"Citations: include Source: <path#line> when it helps the user verify memory snippets.",
|
||||
);
|
||||
}
|
||||
lines.push("");
|
||||
return lines;
|
||||
return buildMemoryPromptSection({
|
||||
availableTools: params.availableTools,
|
||||
citationsMode: params.citationsMode,
|
||||
});
|
||||
}
|
||||
|
||||
function buildUserIdentitySection(ownerLine: string | undefined, isMinimal: boolean) {
|
||||
|
||||
52
src/memory/prompt-section.test.ts
Normal file
52
src/memory/prompt-section.test.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { describe, it, expect, beforeEach } from "vitest";
|
||||
import {
|
||||
registerMemoryPromptSection,
|
||||
buildMemoryPromptSection,
|
||||
_resetMemoryPromptSection,
|
||||
} from "./prompt-section.js";
|
||||
|
||||
describe("memory prompt section registry", () => {
|
||||
beforeEach(() => {
|
||||
_resetMemoryPromptSection();
|
||||
});
|
||||
|
||||
it("returns empty array when no builder is registered", () => {
|
||||
const result = buildMemoryPromptSection({
|
||||
availableTools: new Set(["memory_search", "memory_get"]),
|
||||
});
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("delegates to the registered builder", () => {
|
||||
registerMemoryPromptSection(({ availableTools }) => {
|
||||
if (!availableTools.has("memory_search")) return [];
|
||||
return ["## Custom Memory", "Use custom memory tools.", ""];
|
||||
});
|
||||
|
||||
const result = buildMemoryPromptSection({
|
||||
availableTools: new Set(["memory_search"]),
|
||||
});
|
||||
expect(result).toEqual(["## Custom Memory", "Use custom memory tools.", ""]);
|
||||
});
|
||||
|
||||
it("passes citationsMode to the builder", () => {
|
||||
registerMemoryPromptSection(({ citationsMode }) => {
|
||||
return [`citations: ${citationsMode ?? "default"}`];
|
||||
});
|
||||
|
||||
expect(
|
||||
buildMemoryPromptSection({
|
||||
availableTools: new Set(),
|
||||
citationsMode: "off",
|
||||
}),
|
||||
).toEqual(["citations: off"]);
|
||||
});
|
||||
|
||||
it("last registration wins", () => {
|
||||
registerMemoryPromptSection(() => ["first"]);
|
||||
registerMemoryPromptSection(() => ["second"]);
|
||||
|
||||
const result = buildMemoryPromptSection({ availableTools: new Set() });
|
||||
expect(result).toEqual(["second"]);
|
||||
});
|
||||
});
|
||||
29
src/memory/prompt-section.ts
Normal file
29
src/memory/prompt-section.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import type { MemoryCitationsMode } from "../config/types.memory.js";
|
||||
|
||||
/**
|
||||
* Callback that the active memory plugin provides to build
|
||||
* its section of the agent system prompt.
|
||||
*/
|
||||
export type MemoryPromptSectionBuilder = (params: {
|
||||
availableTools: Set<string>;
|
||||
citationsMode?: MemoryCitationsMode;
|
||||
}) => string[];
|
||||
|
||||
// Module-level singleton — only one memory plugin can be active (exclusive slot).
|
||||
let _builder: MemoryPromptSectionBuilder | undefined;
|
||||
|
||||
export function registerMemoryPromptSection(builder: MemoryPromptSectionBuilder): void {
|
||||
_builder = builder;
|
||||
}
|
||||
|
||||
export function buildMemoryPromptSection(params: {
|
||||
availableTools: Set<string>;
|
||||
citationsMode?: MemoryCitationsMode;
|
||||
}): string[] {
|
||||
return _builder?.(params) ?? [];
|
||||
}
|
||||
|
||||
/** Reset state (for tests). */
|
||||
export function _resetMemoryPromptSection(): void {
|
||||
_builder = undefined;
|
||||
}
|
||||
@ -2,4 +2,5 @@
|
||||
// Keep this list additive and scoped to symbols used under extensions/memory-core.
|
||||
|
||||
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
||||
export type { MemoryPromptSectionBuilder } from "../memory/prompt-section.js";
|
||||
export type { OpenClawPluginApi } from "../plugins/types.js";
|
||||
|
||||
@ -1341,6 +1341,10 @@ export type OpenClawPluginApi = {
|
||||
id: string,
|
||||
factory: import("../context-engine/registry.js").ContextEngineFactory,
|
||||
) => void;
|
||||
/** Register the system prompt section builder for this memory plugin (exclusive slot). */
|
||||
registerMemoryPromptSection: (
|
||||
builder: import("../memory/prompt-section.js").MemoryPromptSectionBuilder,
|
||||
) => void;
|
||||
resolvePath: (input: string) => string;
|
||||
/** Register a lifecycle hook handler */
|
||||
on: <K extends PluginHookName>(
|
||||
|
||||
@ -23,6 +23,7 @@ export function createTestPluginApi(api: TestPluginApiInput): OpenClawPluginApi
|
||||
onConversationBindingResolved() {},
|
||||
registerCommand() {},
|
||||
registerContextEngine() {},
|
||||
registerMemoryPromptSection() {},
|
||||
resolvePath(input: string) {
|
||||
return input;
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user