openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
295 lines (294 loc) • 12.6 kB
JavaScript
import { y as resolveSessionAgentIds } from "../../agent-scope-MrLta7Pq.js";
import { n as parseNonNegativeByteSize } from "../../zod-schema-DG_h_EOU.js";
import { n as SILENT_REPLY_TOKEN } from "../../tokens-U6o7_k27.js";
import "../../agent-settings-Btt05WNB.js";
import { l as jsonResult } from "../../common-C4yy9V-D.js";
import { n as resolveCronStyleNow } from "../../current-time-D-ClLmMh.js";
import { t as resolveMemorySearchConfig } from "../../memory-search-CTXTHmj4.js";
import { t as definePluginEntry } from "../../plugin-entry-C7DUzV0e.js";
import "../../memory-core-host-runtime-core-DdKSmfO1.js";
import { t as resolveMemoryBackendConfig } from "../../backend-config-Bk4OTHnB.js";
import "../../memory-core-host-runtime-files-BEOYVaYk.js";
import { u as configureMemoryCoreDreamingState } from "../../dreaming-state-D7W5VtYL.js";
import { t as registerShortTermPromotionDreaming } from "../../dreaming-BTtew_Nb.js";
import { t as resolveMemoryCoreNowMs } from "../../time-DhPCijtC.js";
import { r as registerBuiltInMemoryEmbeddingProviders } from "../../provider-adapters-B6qmIXxN.js";
const MEMORY_FLUSH_TARGET_HINT = "Store durable memories only in memory/YYYY-MM-DD.md (create memory/ if needed).";
const MEMORY_FLUSH_APPEND_ONLY_HINT = "If memory/YYYY-MM-DD.md already exists, APPEND new content only and do not overwrite existing entries.";
const MEMORY_FLUSH_READ_ONLY_HINT = "Treat workspace bootstrap/reference files such as MEMORY.md, DREAMS.md, SOUL.md, TOOLS.md, and AGENTS.md as read-only during this flush; never overwrite, replace, or edit them.";
const MEMORY_FLUSH_REQUIRED_HINTS = [
MEMORY_FLUSH_TARGET_HINT,
MEMORY_FLUSH_APPEND_ONLY_HINT,
MEMORY_FLUSH_READ_ONLY_HINT
];
const DEFAULT_MEMORY_FLUSH_PROMPT = [
"Pre-compaction memory flush.",
MEMORY_FLUSH_TARGET_HINT,
MEMORY_FLUSH_READ_ONLY_HINT,
MEMORY_FLUSH_APPEND_ONLY_HINT,
"Do NOT create timestamped variant files (e.g., YYYY-MM-DD-HHMM.md); always use the canonical YYYY-MM-DD.md filename.",
`If nothing to store, reply with ${SILENT_REPLY_TOKEN}.`
].join(" ");
const DEFAULT_MEMORY_FLUSH_SYSTEM_PROMPT = [
"Pre-compaction memory flush turn.",
"The session is near auto-compaction; capture durable memories to disk.",
MEMORY_FLUSH_TARGET_HINT,
MEMORY_FLUSH_READ_ONLY_HINT,
MEMORY_FLUSH_APPEND_ONLY_HINT,
`You may reply, but usually ${SILENT_REPLY_TOKEN} is correct.`
].join(" ");
function formatDateStampInTimezone(nowMs, timezone) {
const parts = new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
year: "numeric",
month: "2-digit",
day: "2-digit"
}).formatToParts(new Date(nowMs));
const year = parts.find((part) => part.type === "year")?.value;
const month = parts.find((part) => part.type === "month")?.value;
const day = parts.find((part) => part.type === "day")?.value;
if (year && month && day) return `${year}-${month}-${day}`;
return new Date(resolveMemoryCoreNowMs(nowMs)).toISOString().slice(0, 10);
}
function normalizeNonNegativeInt(value) {
if (typeof value !== "number" || !Number.isFinite(value)) return null;
const int = Math.floor(value);
return int >= 0 ? int : null;
}
function ensureNoReplyHint(text) {
if (text.includes("NO_REPLY")) return text;
return `${text}\n\nIf no user-visible reply is needed, start with ${SILENT_REPLY_TOKEN}.`;
}
function ensureMemoryFlushSafetyHints(text) {
let next = text.trim();
for (const hint of MEMORY_FLUSH_REQUIRED_HINTS) if (!next.includes(hint)) next = next ? `${next}\n\n${hint}` : hint;
return next;
}
function appendCurrentTimeLine(text, timeLine) {
const trimmed = text.trimEnd();
if (!trimmed) return timeLine;
if (trimmed.includes("Current time:")) return trimmed;
return `${trimmed}\n${timeLine}`;
}
function buildMemoryFlushPlan(params = {}) {
const resolved = params;
const nowMs = resolveMemoryCoreNowMs(resolved.nowMs);
const cfg = resolved.cfg;
const defaults = cfg?.agents?.defaults?.compaction?.memoryFlush;
if (defaults?.enabled === false) return null;
const softThresholdTokens = normalizeNonNegativeInt(defaults?.softThresholdTokens) ?? 4e3;
const forceFlushTranscriptBytes = parseNonNegativeByteSize(defaults?.forceFlushTranscriptBytes) ?? 2097152;
const reserveTokensFloor = normalizeNonNegativeInt(cfg?.agents?.defaults?.compaction?.reserveTokensFloor) ?? 2e4;
const { timeLine, userTimezone } = resolveCronStyleNow(cfg ?? {}, nowMs);
const dateStamp = formatDateStampInTimezone(nowMs, userTimezone);
const relativePath = `memory/${dateStamp}.md`;
const promptBase = ensureNoReplyHint(ensureMemoryFlushSafetyHints(defaults?.prompt?.trim() || DEFAULT_MEMORY_FLUSH_PROMPT));
const systemPrompt = ensureNoReplyHint(ensureMemoryFlushSafetyHints(defaults?.systemPrompt?.trim() || DEFAULT_MEMORY_FLUSH_SYSTEM_PROMPT));
return {
softThresholdTokens,
forceFlushTranscriptBytes,
reserveTokensFloor,
model: defaults?.model?.trim() || void 0,
prompt: appendCurrentTimeLine(promptBase.replaceAll("YYYY-MM-DD", dateStamp), timeLine),
systemPrompt: systemPrompt.replaceAll("YYYY-MM-DD", dateStamp),
relativePath
};
}
//#endregion
//#region extensions/memory-core/src/prompt-section.ts
const buildPromptSection = ({ availableTools, citationsMode }) => {
const hasMemorySearch = availableTools.has("memory_search");
const hasMemoryGet = availableTools.has("memory_get");
if (!hasMemorySearch && !hasMemoryGet) return [];
let toolGuidance;
if (hasMemorySearch && hasMemoryGet) toolGuidance = "Before answering anything about prior work, decisions, dates, people, preferences, or todos: run memory_search on MEMORY.md + memory/*.md + indexed session transcripts; then use memory_get to pull only the needed lines. If low confidence after search, say you checked.";
else if (hasMemorySearch) toolGuidance = "Before answering anything about prior work, decisions, dates, people, preferences, or todos: run memory_search on MEMORY.md + memory/*.md + indexed session transcripts and answer from the matching results. If low confidence after search, say you checked.";
else toolGuidance = "Before answering anything about prior work, decisions, dates, people, preferences, or todos that already point to a specific memory file or note: run memory_get to pull only the needed lines. If low confidence after reading them, say you checked.";
const lines = ["## Memory Recall", toolGuidance];
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;
};
//#endregion
//#region extensions/memory-core/index.ts
let memoryToolsModulePromise;
let runtimeProviderModulePromise;
function loadMemoryToolsModule() {
memoryToolsModulePromise ??= import("../../tools-C_JRyDeX.js");
return memoryToolsModulePromise;
}
function loadRuntimeProviderModule() {
runtimeProviderModulePromise ??= import("../../runtime-provider-o9yLX_e5.js");
return runtimeProviderModulePromise;
}
function getToolConfig(options) {
return options.getConfig?.() ?? options.config;
}
function hasMemoryToolContext(options) {
const cfg = getToolConfig(options);
if (!cfg) return false;
const { sessionAgentId: agentId } = resolveSessionAgentIds({
sessionKey: options.agentSessionKey,
config: cfg,
agentId: options.agentId
});
return Boolean(resolveMemorySearchConfig(cfg, agentId));
}
const MemorySearchSchema = {
type: "object",
properties: {
query: { type: "string" },
maxResults: {
type: "integer",
minimum: 1
},
minScore: { type: "number" },
corpus: {
type: "string",
enum: [
"memory",
"wiki",
"all",
"sessions"
]
}
},
required: ["query"],
additionalProperties: false
};
const MemoryGetSchema = {
type: "object",
properties: {
path: { type: "string" },
from: {
type: "integer",
minimum: 1
},
lines: {
type: "integer",
minimum: 1
},
corpus: {
type: "string",
enum: [
"memory",
"wiki",
"all"
]
}
},
required: ["path"],
additionalProperties: false
};
function createLazyMemoryTool(params) {
if (!hasMemoryToolContext(params.options)) return null;
let toolPromise;
const loadTool = async () => {
toolPromise ??= loadMemoryToolsModule().then((module) => params.load(module, params.options));
return await toolPromise;
};
return {
label: params.label,
name: params.name,
description: params.description,
parameters: params.parameters,
execute: async (toolCallId, toolParams, signal, onUpdate) => {
const tool = await loadTool();
if (!tool) return jsonResult({
disabled: true,
unavailable: true,
error: "memory search unavailable"
});
return await tool.execute(toolCallId, toolParams, signal, onUpdate);
}
};
}
function createLazyMemorySearchTool(options) {
return createLazyMemoryTool({
options,
label: "Memory Search",
name: "memory_search",
description: "Mandatory recall step: semantically search MEMORY.md + memory/*.md (and optional session transcripts) before answering questions about prior work, decisions, dates, people, preferences, or todos. Optional `corpus=wiki` or `corpus=all` also searches registered compiled-wiki supplements. `corpus=memory` restricts hits to indexed memory files (excludes session transcript chunks from ranking). `corpus=sessions` restricts hits to indexed session transcripts (same visibility rules as session history tools). If response has disabled=true, memory retrieval is unavailable and should be surfaced to the user.",
parameters: MemorySearchSchema,
load: (module, loadOptions) => module.createMemorySearchTool(loadOptions)
});
}
function createLazyMemoryGetTool(options) {
return createLazyMemoryTool({
options,
label: "Memory Get",
name: "memory_get",
description: "Safe exact excerpt read from MEMORY.md or memory/*.md. Defaults to a bounded excerpt when lines are omitted, includes truncation/continuation info when more content exists, and `corpus=wiki` reads from registered compiled-wiki supplements.",
parameters: MemoryGetSchema,
load: (module, loadOptions) => module.createMemoryGetTool(loadOptions)
});
}
function resolveMemoryToolOptions(ctx) {
const getConfig = () => ctx.getRuntimeConfig?.() ?? ctx.runtimeConfig ?? ctx.config;
return {
config: getConfig(),
getConfig,
agentId: ctx.agentId,
agentSessionKey: ctx.sessionKey,
sandboxed: ctx.sandboxed
};
}
const memoryRuntime = {
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);
}
};
var memory_core_default = definePluginEntry({
id: "memory-core",
name: "Memory (Core)",
description: "File-backed memory search tools and CLI",
kind: "memory",
register(api) {
configureMemoryCoreDreamingState((options) => api.runtime.state.openKeyedStore(options));
registerBuiltInMemoryEmbeddingProviders(api);
registerShortTermPromotionDreaming(api);
api.registerMemoryCapability({
promptBuilder: buildPromptSection,
flushPlanResolver: buildMemoryFlushPlan,
runtime: memoryRuntime,
publicArtifacts: { async listArtifacts(params) {
const { listMemoryCorePublicArtifacts } = await import("../../public-artifacts-BTact06i.js");
return await listMemoryCorePublicArtifacts(params);
} }
});
api.registerTool((ctx) => createLazyMemorySearchTool(resolveMemoryToolOptions(ctx)), { names: ["memory_search"] });
api.registerTool((ctx) => createLazyMemoryGetTool(resolveMemoryToolOptions(ctx)), { names: ["memory_get"] });
api.registerCommand({
name: "dreaming",
description: "Enable or disable memory dreaming.",
acceptsArgs: true,
handler: async (ctx) => {
const { handleDreamingCommand } = await import("../../dreaming-command-B4Q_7_lY.js");
return await handleDreamingCommand(api, ctx);
}
});
api.registerCli(async ({ program }) => {
const { registerMemoryCli } = await import("./cli.js");
registerMemoryCli(program);
}, { descriptors: [{
name: "memory",
description: "Search, inspect, and reindex memory files",
hasSubcommands: true
}] });
}
});
//#endregion
export { memory_core_default as default };