openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,109 lines (1,108 loc) • 49.1 kB
JavaScript
import { c as isRecord } from "../../record-coerce-DItp3I4t.js";
import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "../../string-coerce-CIXf7egm.js";
import { y as uniqueStrings } from "../../string-normalization-DsCfAx8q.js";
import { r as createLazyRuntimeModule } from "../../lazy-runtime-CgCh8H_K.js";
import { n as normalizeAgentId } from "../../agent-id-CeT3w4ap.js";
import { o as listAgentIds } from "../../agent-scope-config-DcbEhP0R.js";
import { t as formatErrorMessage } from "../../errors-Db3Ymjlb.js";
import { n as SILENT_REPLY_TOKEN } from "../../tokens-DbeAFqg7.js";
import { b as resolveSessionAgentIdsStrict } from "../../agent-scope-DbtJyKUL.js";
import { p as parseNonNegativeByteSize } from "../../zod-schema-DnirJMRA.js";
import { t as ErrorCodes } from "../../gateway-error-details-w0nAGBBp.js";
import { d as errorShape } from "../../error-codes-Bo8q2D1o.js";
import { C as resolveMemoryDreamingPluginConfig, D as resolveMemoryRemDreamingConfig, T as resolveMemoryDreamingWorkspaces, _ as MANAGED_MEMORY_DREAMING_CRON_TAG, g as MANAGED_MEMORY_DREAMING_CRON_NAME, v as MEMORY_DREAMING_SYSTEM_EVENT_TEXT, x as resolveMemoryDeepDreamingConfig } from "../../dreaming-DmJZL5-O.js";
import { u as peekSystemEventEntries } from "../../system-events-C03jsM0j.js";
import { t as resolveEffectiveCompactionReserveTokens } from "../../agent-compaction-constants-CzVH4jGZ.js";
import { t as jsonResult } from "../../tool-results-BCM3fdVS.js";
import { d as readPositiveIntegerParam, h as readToolStringParam } from "../../common-Bm6UTDDA.js";
import { t as resolveMemoryBackendConfig } from "../../backend-config-D3tXhXDP.js";
import { t as DEFAULT_AGENT_COMPACTION_RESERVE_TOKENS_FLOOR } from "../../agent-settings-nT27BX_u.js";
import { n as resolveCronStyleNow } from "../../current-time-Cwxy-JQb.js";
import "../../string-coerce-runtime-GQa0ehRA.js";
import "../../routing-adlWg0R3.js";
import "../../agent-scope-runtime-h57Nypqc.js";
import { t as definePluginEntry } from "../../plugin-entry-zfBGJaNO.js";
import { r as resolvePluginConfigObject } from "../../plugin-config-runtime-BaaMs-tR.js";
import "../../system-event-runtime-CZ2hxQ9w.js";
import "../../gateway-runtime-BkyVf3uU.js";
import "../../channel-actions-CrPVkBaw.js";
import "../../memory-core-host-status-DAqa4uMk.js";
import "../../memory-core-host-runtime-core-5HBGxdZe.js";
import "../../memory-core-host-runtime-files-CETo_g0W.js";
import { p as configureMemoryCoreDreamingState } from "../../dreaming-state-B0qd2W7q.js";
import { t as includesSystemEventToken } from "../../dreaming-shared-BQNg89r7.js";
import { t as resolveMemoryCoreNowMs } from "../../time-BhFVUM0b.js";
import { t as appendFailedDreamingEvent } from "../../dreaming-events-CzumSDAB.js";
import "../../background-context-tbmmWTrm.js";
import { i as resolveMemoryToolContext, n as MEMORY_SEARCH_TOOL_CONTRACT, r as buildMemoryPromptSection, t as MEMORY_GET_TOOL_CONTRACT } from "../../memory-tool-contract-C0rvquIq.js";
import { t as normalizeSessionBackfillSelection } from "../../session-backfill-selection-CjIn4YJO.js";
//#region extensions/memory-core/src/dreaming.ts
const RUNTIME_CRON_RECONCILE_INTERVAL_MS = 6e4;
const HEARTBEAT_ISOLATED_SESSION_SUFFIX = ":heartbeat";
const MANAGED_DREAMING_DECLARATION_KEY = "memory-core:memory-dreaming-promotion";
function formatRepairSummary(repair) {
const actions = [];
if (repair.rewroteStore) {
const removedOverflowEntries = repair.removedOverflowEntries ?? 0;
const details = [
repair.removedInvalidEntries > 0 ? `-${repair.removedInvalidEntries} invalid` : null,
(repair.removedDanglingEntries ?? 0) > 0 ? `-${repair.removedDanglingEntries} dangling` : null,
removedOverflowEntries > 0 ? `-${removedOverflowEntries} overflow` : null
].filter(Boolean).join(", ");
actions.push(`rewrote recall store${details ? ` (${details})` : ""}`);
}
if (repair.removedStaleLock) actions.push("removed stale promotion lock");
return actions.join(", ");
}
function resolveManagedCronDescription(config) {
const recencyHalfLifeDays = config.recencyHalfLifeDays;
return `${MANAGED_MEMORY_DREAMING_CRON_TAG} Promote weighted short-term recalls into MEMORY.md (limit=${config.limit}, minScore=${config.minScore.toFixed(3)}, minRecallCount=${config.minRecallCount}, minUniqueQueries=${config.minUniqueQueries}, recencyHalfLifeDays=${recencyHalfLifeDays}, maxAgeDays=${config.maxAgeDays ?? "none"}).`;
}
function buildManagedDreamingCronJob(config) {
return {
declarationKey: MANAGED_DREAMING_DECLARATION_KEY,
name: MANAGED_MEMORY_DREAMING_CRON_NAME,
description: resolveManagedCronDescription(config),
enabled: true,
schedule: {
kind: "cron",
expr: config.cron,
...config.timezone ? { tz: config.timezone } : {}
},
sessionTarget: "isolated",
wakeMode: "now",
payload: {
kind: "agentTurn",
message: MEMORY_DREAMING_SYSTEM_EVENT_TEXT,
lightContext: true
},
delivery: { mode: "none" }
};
}
function resolveManagedDreamingPayloadToken(payload) {
const payloadKind = normalizeLowercaseStringOrEmpty(normalizeOptionalString(payload?.kind));
if (payloadKind === "systemevent") return normalizeOptionalString(payload?.text);
if (payloadKind === "agentturn") return normalizeOptionalString(payload?.message);
}
function isManagedDreamingJob(job) {
if (normalizeOptionalString(job.declarationKey) === MANAGED_DREAMING_DECLARATION_KEY) return true;
if (normalizeOptionalString(job.name) !== "Memory Dreaming Promotion") return false;
if (normalizeOptionalString(job.description)?.includes("[managed-by=memory-core.short-term-promotion]")) return true;
return resolveManagedDreamingPayloadToken(job.payload) === MEMORY_DREAMING_SYSTEM_EVENT_TEXT;
}
function isLegacyPhaseDreamingJob(job) {
const description = normalizeOptionalString(job.description);
if (description?.includes("[managed-by=memory-core.dreaming.light]") || description?.includes("[managed-by=memory-core.dreaming.rem]")) return true;
const name = normalizeOptionalString(job.name);
const payloadText = normalizeOptionalString(job.payload?.text);
if (name === "Memory Light Dreaming" && payloadText === "__openclaw_memory_core_light_sleep__") return true;
return name === "Memory REM Dreaming" && payloadText === "__openclaw_memory_core_rem_sleep__";
}
async function removeStaleManagedDreamingRows(cron) {
return await cron.removeStaleJobFamily({
declarationKey: MANAGED_DREAMING_DECLARATION_KEY,
name: "Memory Dreaming Promotion",
ownerPluginTag: "[managed-by=memory-core.short-term-promotion]"
}) ?? 0;
}
async function migrateLegacyPhaseDreamingCronJobs(params) {
let migrated = 0;
for (const job of params.legacyJobs) try {
if ((await params.cron.remove(job.id)).removed === true) migrated += 1;
} catch (err) {
params.logger.warn(`memory-core: failed to migrate legacy phase dreaming cron job ${job.id}: ${formatErrorMessage(err)}`);
}
if (migrated > 0) {
if (params.mode === "enabled") params.logger.info(`memory-core: migrated ${migrated} legacy phase dreaming cron job(s) to the unified dreaming controller.`);
else params.logger.info(`memory-core: completed legacy phase dreaming cron migration while unified dreaming is disabled (${migrated} job(s) removed).`);
}
return migrated;
}
function buildManagedDreamingPatch(job, desired) {
const patch = {};
if (normalizeOptionalString(job.name) !== desired.name) patch.name = desired.name;
if (normalizeOptionalString(job.description) !== desired.description) patch.description = desired.description;
if (job.enabled !== true) patch.enabled = true;
const scheduleKind = normalizeLowercaseStringOrEmpty(normalizeOptionalString(job.schedule?.kind));
const scheduleExpr = normalizeOptionalString(job.schedule?.expr);
const scheduleTz = normalizeOptionalString(job.schedule?.tz);
if (scheduleKind !== "cron" || scheduleExpr !== desired.schedule.expr || scheduleTz !== desired.schedule.tz) patch.schedule = desired.schedule;
if (normalizeLowercaseStringOrEmpty(normalizeOptionalString(job.sessionTarget)) !== desired.sessionTarget) patch.sessionTarget = desired.sessionTarget;
if (normalizeLowercaseStringOrEmpty(normalizeOptionalString(job.wakeMode)) !== "now") patch.wakeMode = "now";
const payloadKind = normalizeLowercaseStringOrEmpty(normalizeOptionalString(job.payload?.kind));
const payloadToken = resolveManagedDreamingPayloadToken(job.payload);
const desiredPayloadToken = desired.payload.kind === "systemEvent" ? desired.payload.text : desired.payload.message;
if (payloadKind !== normalizeLowercaseStringOrEmpty(desired.payload.kind) || payloadToken !== desiredPayloadToken || desired.payload.kind === "agentTurn" && job.payload?.lightContext !== desired.payload.lightContext) patch.payload = desired.payload;
if (normalizeLowercaseStringOrEmpty(normalizeOptionalString(job.delivery?.mode)) !== "none") patch.delivery = desired.delivery;
return Object.keys(patch).length > 0 ? patch : null;
}
function sortManagedJobs(managed) {
return managed.toSorted((a, b) => {
const aCreated = typeof a.createdAtMs === "number" && Number.isFinite(a.createdAtMs) ? a.createdAtMs : Number.MAX_SAFE_INTEGER;
const bCreated = typeof b.createdAtMs === "number" && Number.isFinite(b.createdAtMs) ? b.createdAtMs : Number.MAX_SAFE_INTEGER;
if (aCreated !== bCreated) return aCreated - bCreated;
return a.id.localeCompare(b.id);
});
}
function isCronServiceLike(candidate) {
return isRecord(candidate) && typeof candidate.list === "function" && typeof candidate.add === "function" && typeof candidate.update === "function" && typeof candidate.remove === "function" && typeof candidate.removeStaleJobFamily === "function";
}
function resolveCronServiceFromGatewayContext(context) {
const cron = context.getCron?.();
return isCronServiceLike(cron) ? cron : null;
}
function resolveDreamingTriggerSessionKeys(sessionKey) {
const normalized = normalizeOptionalString(sessionKey);
if (!normalized) return [];
const keys = [normalized];
if (normalized.endsWith(HEARTBEAT_ISOLATED_SESSION_SUFFIX)) {
const baseSessionKey = normalized.slice(0, -10).trim();
if (baseSessionKey) keys.push(baseSessionKey);
}
return uniqueStrings(keys);
}
function hasPendingManagedDreamingCronEvent(sessionKey) {
return resolveDreamingTriggerSessionKeys(sessionKey).some((candidateSessionKey) => peekSystemEventEntries(candidateSessionKey).some((event) => event.contextKey?.startsWith("cron:") === true && normalizeOptionalString(event.text) === "__openclaw_memory_core_short_term_promotion_dream__"));
}
async function reconcileShortTermDreamingCronJob(params) {
const cron = params.cron;
if (!cron) return {
status: "unavailable",
removed: 0
};
const allJobs = await cron.list({ includeDisabled: true });
const managed = allJobs.filter(isManagedDreamingJob);
const legacyPhaseJobs = allJobs.filter(isLegacyPhaseDreamingJob);
if (!params.config.enabled) {
let removed = await migrateLegacyPhaseDreamingCronJobs({
cron,
legacyJobs: legacyPhaseJobs,
logger: params.logger,
mode: "disabled"
});
for (const job of managed) try {
if ((await cron.remove(job.id)).removed === true) removed += 1;
} catch (err) {
params.logger.warn(`memory-core: failed to remove managed dreaming cron job ${job.id}: ${formatErrorMessage(err)}`);
}
removed += await removeStaleManagedDreamingRows(cron);
if (removed > 0) params.logger.info(`memory-core: removed ${removed} managed dreaming cron job(s).`);
return {
status: "disabled",
removed
};
}
const desired = buildManagedDreamingCronJob(params.config);
const primary = managed.find((job) => job.declarationKey === MANAGED_DREAMING_DECLARATION_KEY);
if (!primary) {
await cron.add(desired);
let removed = await migrateLegacyPhaseDreamingCronJobs({
cron,
legacyJobs: legacyPhaseJobs,
logger: params.logger,
mode: "enabled"
});
for (const job of managed) {
if ((await cron.remove(job.id)).removed !== true) throw new Error(`failed to replace legacy managed dreaming cron job ${job.id}`);
removed += 1;
}
removed += await removeStaleManagedDreamingRows(cron);
params.logger.info(managed.length === 0 ? "memory-core: created managed dreaming cron job." : "memory-core: replaced legacy managed dreaming cron job identity.");
return {
status: "added",
removed
};
}
const duplicates = sortManagedJobs(managed.filter((job) => job.id !== primary.id));
let removed = await migrateLegacyPhaseDreamingCronJobs({
cron,
legacyJobs: legacyPhaseJobs,
logger: params.logger,
mode: "enabled"
});
for (const duplicate of duplicates) try {
if ((await cron.remove(duplicate.id)).removed === true) removed += 1;
} catch (err) {
params.logger.warn(`memory-core: failed to prune duplicate managed dreaming cron job ${duplicate.id}: ${formatErrorMessage(err)}`);
}
removed += await removeStaleManagedDreamingRows(cron);
const patch = buildManagedDreamingPatch(primary, desired);
if (!patch) {
if (removed > 0) params.logger.info("memory-core: pruned duplicate managed dreaming cron jobs.");
return {
status: "noop",
removed
};
}
await cron.update(primary.id, patch);
params.logger.info("memory-core: updated managed dreaming cron job.");
return {
status: "updated",
removed
};
}
async function runShortTermDreamingPromotionIfTriggered(params) {
if (params.trigger !== "heartbeat" && params.trigger !== "cron") return;
if (!includesSystemEventToken(params.cleanedBody, "__openclaw_memory_core_short_term_promotion_dream__")) return;
if (!params.config.enabled) return {
handled: true,
reason: "memory-core: short-term dreaming disabled"
};
const recencyHalfLifeDays = params.config.recencyHalfLifeDays;
const fallbackWorkspaceDir = normalizeOptionalString(params.workspaceDir);
const triggerAgentId = normalizeLowercaseStringOrEmpty(params.agentId);
const seenWorkspaces = /* @__PURE__ */ new Set();
const workspaces = [];
const addWorkspace = (workspaceDir, agentId, agentIds = [agentId]) => {
if (!workspaceDir || seenWorkspaces.has(workspaceDir)) return;
seenWorkspaces.add(workspaceDir);
workspaces.push({
...agentId ? { agentId } : {},
agentIds,
workspaceDir
});
};
const resolveWorkspaceOwnerAgentId = (agentIds) => {
if (triggerAgentId && agentIds.includes(triggerAgentId)) return triggerAgentId;
return agentIds.toSorted()[0] ?? triggerAgentId;
};
if (params.cfg) for (const entry of resolveMemoryDreamingWorkspaces(params.cfg, {
primaryWorkspaceDir: fallbackWorkspaceDir,
...triggerAgentId ? { primaryAgentId: triggerAgentId } : {}
})) addWorkspace(entry.workspaceDir, resolveWorkspaceOwnerAgentId(entry.agentIds), entry.agentIds);
if (workspaces.length === 0 && fallbackWorkspaceDir) addWorkspace(fallbackWorkspaceDir, triggerAgentId);
if (workspaces.length === 0) {
params.logger.warn("memory-core: dreaming promotion skipped because no memory workspace is available.");
return {
handled: true,
reason: "memory-core: short-term dreaming missing workspace"
};
}
if (params.config.limit === 0) {
params.logger.info("memory-core: dreaming promotion skipped because limit=0.");
return {
handled: true,
reason: "memory-core: short-term dreaming disabled by limit"
};
}
if (params.config.verboseLogging) params.logger.info(`memory-core: dreaming verbose enabled (cron=${params.config.cron}, limit=${params.config.limit}, minScore=${params.config.minScore.toFixed(3)}, minRecallCount=${params.config.minRecallCount}, minUniqueQueries=${params.config.minUniqueQueries}, recencyHalfLifeDays=${recencyHalfLifeDays}, maxAgeDays=${params.config.maxAgeDays ?? "none"}, workspaces=${workspaces.length}).`);
let totalCandidates = 0;
let totalApplied = 0;
let failedWorkspaces = 0;
let degradedNarratives = 0;
let pendingNarratives = 0;
const pluginConfig = params.cfg ? resolveMemoryDreamingPluginConfig(params.cfg) : void 0;
const detachNarratives = params.trigger === "cron";
const [{ writeDeepDreamingReport }, { appendFallbackNarrativeEntry, runDreamNarrative }, { runDreamingSweepPhases }, { applyShortTermPromotions, repairShortTermPromotionArtifacts, rankShortTermPromotionCandidates }] = await Promise.all([
import("../../dreaming-markdown-BiX-VMp-.js"),
import("../../dreaming-narrative-BoIW60I9.js"),
import("../../dreaming-phases-Dn8ULJQQ.js"),
import("../../short-term-promotion-SmgcLDac.js")
]);
for (const { agentId, agentIds, workspaceDir } of workspaces) {
const sweepNowMs = Date.now();
try {
const phaseResult = await runDreamingSweepPhases({
agentId,
workspaceDir,
pluginConfig,
cfg: params.cfg,
logger: params.logger,
subagent: params.subagent,
detachNarratives,
nowMs: sweepNowMs
});
degradedNarratives += phaseResult?.degradedPhases ?? 0;
pendingNarratives += phaseResult?.pendingNarratives ?? 0;
} catch (err) {
failedWorkspaces += 1;
params.logger.error(`memory-core: dreaming sweep failed for workspace ${workspaceDir}: ${formatErrorMessage(err)}`);
continue;
}
try {
const reportLines = [];
const repair = await repairShortTermPromotionArtifacts({ workspaceDir });
if (repair.changed) {
params.logger.info(`memory-core: normalized recall artifacts before dreaming (${formatRepairSummary(repair)}) [workspace=${workspaceDir}].`);
reportLines.push(`- Repaired recall artifacts: ${formatRepairSummary(repair)}.`);
}
const candidates = await rankShortTermPromotionCandidates({
workspaceDir,
limit: params.config.limit,
minScore: params.config.minScore,
minRecallCount: params.config.minRecallCount,
minUniqueQueries: params.config.minUniqueQueries,
recencyHalfLifeDays,
maxAgeDays: params.config.maxAgeDays,
nowMs: sweepNowMs
});
totalCandidates += candidates.length;
reportLines.push(`- Ranked ${candidates.length} candidate(s) for durable promotion.`);
if (params.config.verboseLogging) {
const candidateSummary = candidates.length > 0 ? candidates.map((candidate) => `${candidate.path}:${candidate.startLine}-${candidate.endLine} score=${candidate.score.toFixed(3)} signals=${candidate.signalCount} recalls=${candidate.recallCount} queries=${candidate.uniqueQueries} components={freq=${candidate.components.frequency.toFixed(3)},rel=${candidate.components.relevance.toFixed(3)},div=${candidate.components.diversity.toFixed(3)},rec=${candidate.components.recency.toFixed(3)},cons=${candidate.components.consolidation.toFixed(3)},concept=${candidate.components.conceptual.toFixed(3)}}`).join(" | ") : "none";
params.logger.info(`memory-core: dreaming candidate details [workspace=${workspaceDir}] ${candidateSummary}`);
}
const applied = await applyShortTermPromotions({
agentId,
workspaceAgentIds: agentIds,
workspaceDir,
candidates,
limit: params.config.limit,
minScore: params.config.minScore,
minRecallCount: params.config.minRecallCount,
minUniqueQueries: params.config.minUniqueQueries,
maxAgeDays: params.config.maxAgeDays,
maxPromotedSnippetTokens: params.config.maxPromotedSnippetTokens,
maxPriorEntryLossFraction: params.config.maxPriorEntryLossFraction,
consolidation: {
...params.subagent ? { subagent: params.subagent } : {},
...params.config.execution?.model ? { model: params.config.execution.model } : {},
logger: params.logger
},
timezone: params.config.timezone,
nowMs: sweepNowMs
});
totalApplied += applied.applied;
reportLines.push(`- Promoted ${applied.applied} candidate(s) into MEMORY.md.`);
if (params.config.verboseLogging) {
const appliedSummary = applied.appliedCandidates.length > 0 ? applied.appliedCandidates.map((candidate) => `${candidate.path}:${candidate.startLine}-${candidate.endLine} score=${candidate.score.toFixed(3)} signals=${candidate.signalCount} recalls=${candidate.recallCount}`).join(" | ") : "none";
params.logger.info(`memory-core: dreaming applied details [workspace=${workspaceDir}] ${appliedSummary}`);
}
await writeDeepDreamingReport({
workspaceDir,
bodyLines: reportLines,
nowMs: sweepNowMs,
timezone: params.config.timezone,
storage: params.config.storage ?? {
mode: "separate",
separateReports: false
}
});
if (candidates.length > 0 || applied.applied > 0) {
const data = {
phase: "deep",
snippets: candidates.map((c) => c.snippet).filter(Boolean),
promotions: applied.appliedCandidates.map((c) => c.snippet).filter(Boolean),
sourceEntryKeys: [...new Set([...candidates, ...applied.appliedCandidates].map((c) => c.key))]
};
if (!params.subagent) await appendFallbackNarrativeEntry({
workspaceDir,
data,
nowMs: sweepNowMs,
timezone: params.config.timezone,
logger: params.logger,
reason: "subagent runtime is unavailable"
});
else {
const narrativeOutcome = await runDreamNarrative({
agentId,
subagent: params.subagent,
workspaceDir,
data,
nowMs: sweepNowMs,
timezone: params.config.timezone,
model: params.config.execution?.model,
logger: params.logger,
detached: detachNarratives
});
if (narrativeOutcome.status === "degraded") degradedNarratives += 1;
else if (narrativeOutcome.status === "pending") pendingNarratives += 1;
}
}
} catch (err) {
failedWorkspaces += 1;
const error = formatErrorMessage(err);
params.logger.error(`memory-core: dreaming promotion failed for workspace ${workspaceDir}: ${error}`);
await appendFailedDreamingEvent({
workspaceDir,
phase: "deep",
error,
storageMode: params.config.storage?.mode ?? "separate",
nowMs: sweepNowMs,
logger: params.logger
});
}
}
const summary = `memory-core: dreaming promotion complete (workspaces=${workspaces.length}, candidates=${totalCandidates}, applied=${totalApplied}, failed=${failedWorkspaces}, degraded=${degradedNarratives}, narrativesPending=${pendingNarratives}).`;
if (failedWorkspaces === workspaces.length || degradedNarratives > 0) params.logger.warn(summary);
else params.logger.info(summary);
return {
handled: true,
reason: degradedNarratives > 0 ? "memory-core: short-term dreaming degraded" : "memory-core: short-term dreaming processed"
};
}
function registerShortTermPromotionDreaming(api) {
let resolveServiceCron = null;
let unavailableCronWarningEmitted = false;
let startupDreamingCleanupTimer = null;
const dreamingTasks = /* @__PURE__ */ new Set();
let runtimeCronReconcileTimer = null;
let gatewayLifecycleGeneration = 0;
let disposed = true;
let serviceStartedAtMs;
const resolveCurrentConfig = () => api.runtime.config?.current?.() ?? api.config;
const disposeDreaming = () => {
disposed = true;
gatewayLifecycleGeneration += 1;
if (startupDreamingCleanupTimer) {
clearTimeout(startupDreamingCleanupTimer);
startupDreamingCleanupTimer = null;
}
if (runtimeCronReconcileTimer) {
clearInterval(runtimeCronReconcileTimer);
runtimeCronReconcileTimer = null;
}
resolveServiceCron = null;
};
const reconcileManagedDreamingCron = async (params) => {
const startupCfg = params.reason === "startup" ? params.startupConfig ?? api.config : resolveCurrentConfig();
const pluginConfig = params.reason === "startup" ? resolveMemoryDreamingPluginConfig(startupCfg) ?? resolveMemoryDreamingPluginConfig(api.config) ?? api.pluginConfig : resolveMemoryDreamingPluginConfig(startupCfg);
const config = resolveMemoryDeepDreamingConfig({
pluginConfig,
cfg: startupCfg
});
const cron = resolveServiceCron?.() ?? null;
if (!cron && config.enabled && !unavailableCronWarningEmitted) {
if (params.reason === "startup") api.logger.debug?.("memory-core: cron service not yet available at service start; deferring to runtime reconciliation.");
else {
api.logger.warn("memory-core: managed dreaming cron could not be reconciled (cron service unavailable).");
unavailableCronWarningEmitted = true;
}
}
if (cron) unavailableCronWarningEmitted = false;
await reconcileShortTermDreamingCronJob({
cron,
config,
logger: api.logger
});
};
const startRuntimeCronReconcileTimer = () => {
if (disposed || runtimeCronReconcileTimer) return;
runtimeCronReconcileTimer = setInterval(() => {
trackDreamingTask(reconcileManagedDreamingCron({ reason: "runtime" })).catch((err) => {
api.logger.error(`memory-core: dreaming cron reconcile failed: ${formatErrorMessage(err)}`);
});
}, RUNTIME_CRON_RECONCILE_INTERVAL_MS);
runtimeCronReconcileTimer.unref?.();
};
const trackDreamingTask = (task) => {
dreamingTasks.add(task);
task.then(() => dreamingTasks.delete(task), () => dreamingTasks.delete(task));
return task;
};
const startDreamingSessionCleanup = async (config, generation, startupStartedAtMs) => {
const { DREAMING_ORPHAN_MIN_AGE_MS, scrubDreamingNarrativeArtifacts } = await import("../../dreaming-session-cleanup-B4aOKzOK.js");
if (disposed || generation !== gatewayLifecycleGeneration) return;
const scrubConfiguredAgents = async (currentConfig, nowMs) => {
const agentIds = uniqueStrings(resolveMemoryDreamingWorkspaces(currentConfig).flatMap(({ agentIds: workspaceAgentIds }) => workspaceAgentIds));
for (const agentId of agentIds) {
if (disposed || generation !== gatewayLifecycleGeneration) return;
try {
await scrubDreamingNarrativeArtifacts({
agentId,
config: currentConfig,
logger: api.logger,
...nowMs === void 0 ? {} : { nowMs }
});
} catch (error) {
api.logger.warn(`memory-core: dreaming startup cleanup failed for agent ${agentId}: ${formatErrorMessage(error)}`);
}
}
};
await scrubConfiguredAgents(config, startupStartedAtMs);
if (disposed || generation !== gatewayLifecycleGeneration) return;
const cleanupTimer = setTimeout(() => {
if (disposed || generation !== gatewayLifecycleGeneration || startupDreamingCleanupTimer !== cleanupTimer) return;
startupDreamingCleanupTimer = null;
trackDreamingTask(scrubConfiguredAgents(resolveCurrentConfig(), startupStartedAtMs + DREAMING_ORPHAN_MIN_AGE_MS - 1).catch((error) => {
api.logger.warn(`memory-core: deferred dreaming startup cleanup failed: ${formatErrorMessage(error)}`);
}));
}, DREAMING_ORPHAN_MIN_AGE_MS);
startupDreamingCleanupTimer = cleanupTimer;
startupDreamingCleanupTimer.unref?.();
};
api.registerService({
id: "memory-core-dreaming",
async start(ctx) {
if (!ctx.getCron) return;
serviceStartedAtMs = Date.now();
disposed = false;
resolveServiceCron = () => resolveCronServiceFromGatewayContext(ctx);
try {
await trackDreamingTask(reconcileManagedDreamingCron({
reason: "startup",
startupConfig: ctx.config
}));
} catch (err) {
api.logger.error(`memory-core: dreaming startup reconciliation failed: ${formatErrorMessage(err)}`);
} finally {
startRuntimeCronReconcileTimer();
}
},
async stop() {
disposeDreaming();
await Promise.allSettled(dreamingTasks);
}
});
api.on("gateway_start", async (_event, ctx) => {
if (disposed || serviceStartedAtMs === void 0) return;
if (startupDreamingCleanupTimer) {
clearTimeout(startupDreamingCleanupTimer);
startupDreamingCleanupTimer = null;
}
const generation = ++gatewayLifecycleGeneration;
await trackDreamingTask(startDreamingSessionCleanup(ctx.config ?? api.config, generation, serviceStartedAtMs)).catch((error) => {
api.logger.warn(`memory-core: dreaming startup cleanup failed: ${formatErrorMessage(error)}`);
});
});
api.on("before_agent_reply", async (event, ctx) => {
try {
if (ctx.trigger !== "heartbeat" && ctx.trigger !== "cron") return;
const currentConfig = resolveCurrentConfig();
const hasManagedDreamingToken = includesSystemEventToken(event.cleanedBody, MEMORY_DREAMING_SYSTEM_EVENT_TEXT);
const isManagedHeartbeatTrigger = ctx.trigger === "heartbeat" && hasPendingManagedDreamingCronEvent(ctx.sessionKey);
const isManagedCronTrigger = ctx.trigger === "cron";
if (!(hasManagedDreamingToken && (isManagedHeartbeatTrigger || isManagedCronTrigger))) return;
const config = resolveMemoryDeepDreamingConfig({
pluginConfig: resolveMemoryDreamingPluginConfig(currentConfig),
cfg: currentConfig
});
return await runShortTermDreamingPromotionIfTriggered({
cleanedBody: event.cleanedBody,
trigger: ctx.trigger,
agentId: ctx.agentId,
workspaceDir: ctx.workspaceDir,
cfg: currentConfig,
config,
logger: api.logger,
subagent: config.enabled ? api.runtime?.subagent : void 0
});
} catch (err) {
api.logger.error(`memory-core: dreaming trigger failed: ${formatErrorMessage(err)}`);
return;
}
}, { eligibleTriggers: ["heartbeat", "cron"] });
}
//#endregion
//#region extensions/memory-core/src/flush-plan.ts
const DEFAULT_MEMORY_FLUSH_SOFT_TOKENS = 4e3;
const DEFAULT_MEMORY_FLUSH_FORCE_TRANSCRIPT_BYTES = 2097152;
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, 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;
let softThresholdTokens = normalizeNonNegativeInt(defaults?.softThresholdTokens) ?? DEFAULT_MEMORY_FLUSH_SOFT_TOKENS;
const forceFlushTranscriptBytes = parseNonNegativeByteSize(defaults?.forceFlushTranscriptBytes) ?? DEFAULT_MEMORY_FLUSH_FORCE_TRANSCRIPT_BYTES;
let reserveTokensFloor = DEFAULT_AGENT_COMPACTION_RESERVE_TOKENS_FLOOR;
const contextWindowTokens = normalizeNonNegativeInt(params.contextWindowTokens);
if (contextWindowTokens !== null && contextWindowTokens > 0) {
reserveTokensFloor = resolveEffectiveCompactionReserveTokens({
contextTokenBudget: contextWindowTokens,
reserveTokens: reserveTokensFloor
});
softThresholdTokens = Math.min(softThresholdTokens, Math.floor((contextWindowTokens - reserveTokensFloor) / 2));
}
const { timeLine, userTimezone } = resolveCronStyleNow(cfg ?? {}, nowMs);
const dateStamp = formatDateStampInTimezone(nowMs, userTimezone);
const relativePath = `memory/${dateStamp}.md`;
const promptBase = ensureNoReplyHint(ensureMemoryFlushSafetyHints(DEFAULT_MEMORY_FLUSH_PROMPT));
const systemPrompt = ensureNoReplyHint(ensureMemoryFlushSafetyHints(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/session-backfill-gateway.ts
const SESSION_BACKFILL_GATEWAY_METHODS = {
preview: "memory.sessionBackfill.preview",
apply: "memory.sessionBackfill.apply",
rollback: "memory.sessionBackfill.rollback"
};
var InvalidSessionBackfillRequestError = class extends Error {};
const loadSessionBackfillGatewayRuntime = createLazyRuntimeModule(() => import("../../session-backfill-gateway.runtime--kPVwzdy.js"));
function paramsRecord(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("params must be an object.");
return value;
}
function assertOnlyKeys(params, allowed) {
const unexpected = Object.keys(params).filter((key) => !allowed.has(key));
if (unexpected.length > 0) throw new Error(`unexpected parameter: ${unexpected[0]}`);
}
function readOptionalSessionBoundary(params, key) {
const raw = params[key];
if (raw !== void 0 && typeof raw !== "string") throw new Error(`${key} must be a string.`);
return readToolStringParam(params, key);
}
function readGatewayParams(value) {
const params = paramsRecord(value);
assertOnlyKeys(params, /* @__PURE__ */ new Set([
"agentId",
"from",
"to",
"limitDays"
]));
return {
agentId: normalizeAgentId(readToolStringParam(params, "agentId", { required: true })),
...normalizeSessionBackfillSelection({
from: readOptionalSessionBoundary(params, "from"),
to: readOptionalSessionBoundary(params, "to"),
limitDays: readPositiveIntegerParam(params, "limitDays")
}, {
from: "from",
to: "to",
limitDays: "limitDays"
})
};
}
function readRollbackParams(value) {
const params = paramsRecord(value);
assertOnlyKeys(params, /* @__PURE__ */ new Set(["agentId"]));
return { agentId: normalizeAgentId(readToolStringParam(params, "agentId", { required: true })) };
}
function resolveExecutionContext(api, agentId) {
const config = api.runtime.config.current();
if (!listAgentIds(config).includes(agentId)) throw new InvalidSessionBackfillRequestError(`Unknown agent id "${agentId}".`);
const workspaceDir = api.runtime.agent.resolveAgentWorkspaceDir(config, agentId);
const pluginConfig = resolvePluginConfigObject(config, "memory-core");
const remConfig = resolveMemoryRemDreamingConfig({
cfg: config,
pluginConfig
});
return {
workspaceDir,
...pluginConfig ? { pluginConfig } : {},
...remConfig.timezone !== void 0 ? { timezone: remConfig.timezone } : {}
};
}
function gatewayResult(result, options) {
return {
days: result.days.length,
candidates: result.candidateCount,
perDay: result.days.map((day) => ({
day: day.day,
candidateCount: day.candidateCount,
sample: day.topCandidates.slice(0, 3)
})),
staged: result.stagedEntries,
...!options.includeCursor ? { truncated: options.continuation.hasMore } : {},
...options.includeCursor ? { cursor: {
advanced: options.continuation.advanced,
exhausted: result.candidateCount === 0 && !options.continuation.hasMore,
hasMore: options.continuation.hasMore
} } : {}
};
}
function respondInvalid(respond, error) {
const message = error instanceof Error ? error.message : String(error);
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, message));
}
function respondUnavailable(respond, error) {
const message = error instanceof Error ? error.message : String(error);
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, message));
}
function registerSessionBackfillGatewayMethods(api) {
const registerBackfill = (method, apply) => {
api.registerGatewayMethod(method, async ({ params, respond }) => {
let request;
try {
request = readGatewayParams(params);
} catch (error) {
respondInvalid(respond, error);
return;
}
try {
const context = resolveExecutionContext(api, request.agentId);
const { executeSessionBackfillBatch } = await loadSessionBackfillGatewayRuntime();
const execution = await executeSessionBackfillBatch({
...request,
...context,
...apply ? { apply: true } : {}
});
respond(true, gatewayResult(execution.result, {
includeCursor: apply,
continuation: execution.continuation
}));
} catch (error) {
if (error instanceof InvalidSessionBackfillRequestError) respondInvalid(respond, error);
else respondUnavailable(respond, error);
}
}, { scope: apply ? "operator.admin" : "operator.read" });
};
registerBackfill(SESSION_BACKFILL_GATEWAY_METHODS.preview, false);
registerBackfill(SESSION_BACKFILL_GATEWAY_METHODS.apply, true);
api.registerGatewayMethod(SESSION_BACKFILL_GATEWAY_METHODS.rollback, async ({ params, respond }) => {
let request;
try {
request = readRollbackParams(params);
} catch (error) {
respondInvalid(respond, error);
return;
}
try {
const context = resolveExecutionContext(api, request.agentId);
const { executeSessionBackfill } = await loadSessionBackfillGatewayRuntime();
const result = await executeSessionBackfill({
...request,
...context,
rollback: true
});
respond(true, {
removedDiaryEntries: result.rollback?.removedDiaryEntries ?? 0,
removedStagedEntries: result.rollback?.removedStagedEntries ?? 0
});
} catch (error) {
if (error instanceof InvalidSessionBackfillRequestError) respondInvalid(respond, error);
else respondUnavailable(respond, error);
}
}, { scope: "operator.admin" });
}
//#endregion
//#region extensions/memory-core/index.ts
const loadMemoryToolsModule = createLazyRuntimeModule(() => import("../../tools-BAdImrVR.js"));
const loadStandingIntentsModule = createLazyRuntimeModule(() => import("../../standing-intents-CePIXLZ5.js"));
const loadStandingIntentToolModule = createLazyRuntimeModule(() => import("../../standing-intents-tool-BdUqNMA8.js"));
const loadRuntimeProviderModule = createLazyRuntimeModule(() => import("../../runtime-provider-_c6MVJqH.js"));
function createLazyMemoryTool(params) {
const initialContext = resolveMemoryToolContext(params.options);
if (!initialContext) return null;
let toolPromise;
const loadTool = async () => {
toolPromise ??= loadMemoryToolsModule().then((module) => params.load(module, params.options));
return await toolPromise;
};
return {
label: params.contract.label,
name: params.contract.name,
description: params.contract.describe(initialContext.sources),
parameters: params.contract.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,
contract: MEMORY_SEARCH_TOOL_CONTRACT,
load: (module, loadOptions) => module.createMemorySearchTool(loadOptions)
});
}
function createLazyMemoryGetTool(options) {
return createLazyMemoryTool({
options,
contract: MEMORY_GET_TOOL_CONTRACT,
load: (module, loadOptions) => module.createMemoryGetTool(loadOptions)
});
}
function createLazyStandingIntentTool(ctx, reportUnavailable) {
if (ctx.senderIsOwner !== true) return null;
const cfg = ctx.getRuntimeConfig?.() ?? ctx.runtimeConfig ?? ctx.config;
const provider = ctx.messageChannel?.trim();
const senderId = ctx.requesterSenderId?.trim();
if (!cfg) {
reportUnavailable("runtime config is unavailable for this turn");
return null;
}
const { sessionAgentId: agentId } = resolveSessionAgentIdsStrict({
sessionKey: ctx.sessionKey,
config: cfg,
agentId: ctx.agentId
});
let toolPromise;
const loadTool = async () => {
toolPromise ??= loadStandingIntentToolModule().then((module) => module.createStandingIntentTool({
agentId,
...ctx.sessionId ? { sourceSessionId: ctx.sessionId } : {},
...ctx.nativeChannelId ? { conversationId: ctx.nativeChannelId } : {},
...provider ? { provider } : {},
...ctx.agentAccountId ? { accountId: ctx.agentAccountId } : {},
...senderId ? { senderId } : {}
}));
return await toolPromise;
};
return {
label: "Standing Intent",
name: "intent",
description: "Create, list, or explicitly cancel event-conditioned standing intents. A created intent is armed; the system injects the reminder automatically when it triggers. Do not deliver it early or cancel it unless the user asks. Use scheduled tasks for time-based reminders.",
parameters: {
type: "object",
properties: {
action: {
type: "string",
enum: [
"create",
"list",
"cancel"
]
},
id: { type: "string" },
description: { type: "string" },
triggerKeywords: {
type: "array",
items: { type: "string" }
},
scope: {
type: "string",
enum: [
"conversation",
"channel",
"anywhere"
],
default: "channel"
},
senderScope: {
type: "string",
enum: ["sender", "anyone"],
default: "sender"
},
expiresAt: { type: "string" },
maxFires: {
type: "integer",
minimum: 1
},
cooldownSeconds: {
type: "integer",
minimum: 0
},
status: {
type: "string",
enum: [
"pending",
"armed",
"fired",
"done",
"cancelled",
"expired"
]
}
},
required: ["action"],
additionalProperties: false
},
execute: async (toolCallId, params, signal, onUpdate) => {
return await (await loadTool()).execute(toolCallId, params, signal, onUpdate);
}
};
}
function resolveMemoryToolOptions(ctx, host) {
const getConfig = ctx.getRuntimeConfig ? () => ctx.getRuntimeConfig?.() : () => ctx.runtimeConfig ?? ctx.config;
return {
config: getConfig(),
getConfig,
agentId: ctx.agentId,
agentSessionKey: ctx.sessionKey,
sandboxed: ctx.sandboxed,
oneShotCliRun: ctx.oneShotCliRun,
conversationRecall: ctx.conversationRecall,
activeProjectKeys: ctx.activeProjectKeys,
...host.acquireLocalService ? { acquireLocalService: host.acquireLocalService } : {}
};
}
function createLazyMemoryRuntime(host) {
return {
async getMemorySearchManager(params) {
const { createMemoryRuntime } = await loadRuntimeProviderModule();
return await createMemoryRuntime(host).getMemorySearchManager(params);
},
async authorizeSearchHits(params) {
const { createMemoryRuntime } = await loadRuntimeProviderModule();
const runtime = createMemoryRuntime(host);
if (!runtime.authorizeSearchHits) throw new Error("memory-core runtime search authorization is unavailable");
return await runtime.authorizeSearchHits(params);
},
async classifyWorkspaceMemoryPaths(params) {
const [{ classifyWorkspaceMemoryPaths }, dreamingState] = await Promise.all([import("../../workspace-path-classifier-DSn-GNB3.js"), import("../../dreaming-state-C8InVCcK.js")]);
if (host.openKeyedStore) dreamingState.configureMemoryCoreDreamingState(host.openKeyedStore);
return await classifyWorkspaceMemoryPaths(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: "OpenClaw Memory",
description: "File-backed memory search tools and CLI",
kind: "memory",
register(api) {
const acquireLocalService = (...args) => api.runtime.llm.acquireLocalService(...args);
const openKeyedStore = (options) => api.runtime.state.openKeyedStore(options);
const host = {
acquireLocalService,
openKeyedStore
};
configureMemoryCoreDreamingState(openKeyedStore);
const memoryRuntime = createLazyMemoryRuntime(host);
registerShortTermPromotionDreaming(api);
registerSessionBackfillGatewayMethods(api);
api.registerMemoryCapability({
deterministicRecallToolName: "memory_search",
supportsPrivateTranscriptRecall: true,
promptBuilder: (params) => {
if (!params.availableTools.has("memory_search") && !params.availableTools.has("memory_get")) return [];
const liveConfig = api.runtime.config?.current ? api.runtime.config.current() : api.config;
const context = resolveMemoryToolContext({
config: liveConfig,
agentId: params.agentId,
agentSessionKey: params.agentSessionKey
});
return context ? buildMemoryPromptSection({
...params,
sources: context.sources
}) : [];
},
flushPlanResolver: buildMemoryFlushPlan,
runtime: memoryRuntime,
publicArtifacts: { async listArtifacts(params) {
const { listMemoryCorePublicArtifacts } = await import("../../public-artifacts-V6oH0fRo.js");
return await listMemoryCorePublicArtifacts(params);
} }
});
api.registerTool((ctx) => createLazyMemorySearchTool(resolveMemoryToolOptions(ctx, host)), { names: ["memory_search"] });
api.registerTool((ctx) => createLazyMemoryGetTool(resolveMemoryToolOptions(ctx, host)), { names: ["memory_get"] });
api.registerTool((ctx) => createLazyStandingIntentTool(ctx, (reason) => {
api.logger.warn(`memory-core: intent tool unavailable: ${reason}`);
}), { names: ["intent"] });
api.on("before_prompt_build", async (event, ctx) => {
if (ctx.trigger !== "user") return;
try {
const module = await loadStandingIntentsModule();
if (!module.isEligibleStandingIntentTurn(ctx)) return;
const config = api.runtime.config?.current?.() ?? api.config;
const { sessionAgentId: agentId } = resolveSessionAgentIdsStrict({
sessionKey: ctx.sessionKey,
config,
agentId: ctx.agentId
});
const intents = module.matchStandingIntents({
agentId,
prompt: event.prompt,
...ctx.channelId ?? ctx.chatId ? { channel: ctx.channelId ?? ctx.chatId } : {},
...ctx.channel ?? ctx.messageProvider ? { provider: ctx.channel ?? ctx.messageProvider } : {},
...ctx.accountId ? { accountId: ctx.accountId } : {},
...ctx.senderId ? { senderId: ctx.senderId } : {}
});
const prependContext = module.buildStandingIntentContext(intents);
return prependContext ? { prependContext } : void 0;
} catch (error) {
api.logger.warn?.(`memory-core: standing intent matching failed: ${error instanceof Error ? error.message : String(error)}`);
return;
}
});
api.on("before_agent_reply", async (_event, ctx) => {
if (ctx.trigger !== "heartbeat" && ctx.trigger !== "cron") return;
try {
const module = await loadStandingIntentsModule();
const config = api.runtime.config?.current?.() ?? api.config;
const { sessionAgentId: agentId } = resolveSessionAgentIdsStrict({
sessionKey: ctx.sessionKey,
config,
agentId: ctx.agentId
});
module.sweepStandingIntents({ agentId });
} catch (error) {
api.logger.warn?.(`memory-core: standing intent maintenance failed: ${error instanceof Error ? error.message : String(error)}`);
}
}, { eligibleTriggers: ["heartbeat", "cron"] });
api.registerCommand({
name: "dreaming",
description: "Enable or disable memory dreaming.",
acceptsArgs: true,
exposeSenderIsOwner: true,
handler: async (ctx) => {
const { handleDreamingCommand } = await import("../../dreaming-command-CcymTJyt.js");
return await handleDreamingCommand(api, ctx);
}
});
api.registerCli(async ({ program }) => {
const { registerMemoryCli } = await import("./cli.js");
registerMemoryCli(program, host);
}, { descriptors: [{
name: "memory",
description: "Search, inspect, and reindex memory files",
hasSubcommands: true
}] });
}
});
//#endregion
export { memory_core_default as default };