openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
261 lines (260 loc) • 10.3 kB
JavaScript
import { i as isPathInside } from "./path-BlG8lhgR.js";
import { k as splitShellArgs } from "./shell-wrapper-resolution-CFL_Vekh.js";
import { a as uniqueStrings, i as normalizeStringEntries, t as normalizeLowercaseStringOrEmpty } from "./string-utils-BtCofrRx.js";
import "./fs-utils-BCGk0EZn.js";
import { a as resolveAgentWorkspaceDir, n as normalizeAgentId, r as parseDurationMs, s as resolveUserPath, t as CANONICAL_ROOT_MEMORY_FILENAME } from "./config-utils-YtuO3P-Q.js";
import fs from "node:fs";
import path from "node:path";
//#region packages/memory-host-sdk/src/host/backend-config.ts
function escapeQmdExactFilePattern(fileName) {
return fileName.replace(/[\\*?[\]{}()!+@]/g, "\\$&");
}
const DEFAULT_BACKEND = "builtin";
const DEFAULT_CITATIONS = "auto";
const DEFAULT_QMD_INTERVAL = "5m";
const DEFAULT_QMD_DEBOUNCE_MS = 15e3;
const DEFAULT_QMD_TIMEOUT_MS = 4e3;
const DEFAULT_QMD_SEARCH_MODE = "search";
const DEFAULT_QMD_STARTUP = "off";
const DEFAULT_QMD_STARTUP_DELAY_MS = 12e4;
const DEFAULT_QMD_EMBED_INTERVAL = "60m";
const DEFAULT_QMD_COMMAND_TIMEOUT_MS = 3e4;
const DEFAULT_QMD_UPDATE_TIMEOUT_MS = 12e4;
const DEFAULT_QMD_EMBED_TIMEOUT_MS = 12e4;
const DEFAULT_QMD_LIMITS = {
maxResults: 4,
maxSnippetChars: 450,
maxInjectedChars: 2200,
timeoutMs: DEFAULT_QMD_TIMEOUT_MS
};
const DEFAULT_QMD_MCPORTER = {
enabled: false,
serverName: "qmd",
startDaemon: true
};
const DEFAULT_QMD_SCOPE = {
default: "deny",
rules: [{
action: "allow",
match: { chatType: "direct" }
}]
};
function sanitizeName(input) {
return normalizeLowercaseStringOrEmpty(input).replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "collection";
}
function scopeCollectionBase(base, agentId) {
return `${base}-${sanitizeName(agentId)}`;
}
function canonicalizePathForContainment(rawPath) {
const resolved = path.resolve(rawPath);
let current = resolved;
const suffix = [];
while (true) try {
const canonical = path.normalize(fs.realpathSync.native(current));
return path.normalize(path.join(canonical, ...suffix));
} catch {
const parent = path.dirname(current);
if (parent === current) return path.normalize(resolved);
suffix.unshift(path.basename(current));
current = parent;
}
}
function isPathInsideRoot(candidatePath, rootPath) {
return isPathInside(canonicalizePathForContainment(rootPath), canonicalizePathForContainment(candidatePath));
}
function ensureUniqueName(base, existing) {
const name = sanitizeName(base);
if (!existing.has(name)) {
existing.add(name);
return name;
}
let suffix = 2;
while (existing.has(`${name}-${suffix}`)) suffix += 1;
const unique = `${name}-${suffix}`;
existing.add(unique);
return unique;
}
function resolvePath(raw, workspaceDir) {
const trimmed = raw.trim();
if (!trimmed) throw new Error("path required");
if (trimmed.startsWith("~") || path.isAbsolute(trimmed)) return path.normalize(resolveUserPath(trimmed));
return path.normalize(path.resolve(workspaceDir, trimmed));
}
function resolveIntervalMs(raw) {
const value = raw?.trim();
if (!value) return parseDurationMs(DEFAULT_QMD_INTERVAL, { defaultUnit: "m" });
try {
return parseDurationMs(value, { defaultUnit: "m" });
} catch {
return parseDurationMs(DEFAULT_QMD_INTERVAL, { defaultUnit: "m" });
}
}
function resolveEmbedIntervalMs(raw) {
const value = raw?.trim();
if (!value) return parseDurationMs(DEFAULT_QMD_EMBED_INTERVAL, { defaultUnit: "m" });
try {
return parseDurationMs(value, { defaultUnit: "m" });
} catch {
return parseDurationMs(DEFAULT_QMD_EMBED_INTERVAL, { defaultUnit: "m" });
}
}
function resolveDebounceMs(raw) {
if (typeof raw === "number" && Number.isFinite(raw) && raw >= 0) return Math.floor(raw);
return DEFAULT_QMD_DEBOUNCE_MS;
}
function resolveTimeoutMs(raw, fallback) {
return resolvePositiveIntegerConfig(raw, fallback);
}
function resolvePositiveIntegerConfig(raw, fallback) {
if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0) return fallback;
return Math.max(1, Math.floor(raw));
}
function resolveStartupMode(raw) {
const value = raw?.startup;
if (value === "idle" || value === "immediate" || value === "off") return value;
return DEFAULT_QMD_STARTUP;
}
function resolveStartupDelayMs(raw) {
if (typeof raw === "number" && Number.isFinite(raw) && raw >= 0) return Math.floor(raw);
return DEFAULT_QMD_STARTUP_DELAY_MS;
}
function resolveLimits(raw) {
return {
maxResults: resolvePositiveIntegerConfig(raw?.maxResults, DEFAULT_QMD_LIMITS.maxResults),
maxSnippetChars: resolvePositiveIntegerConfig(raw?.maxSnippetChars, DEFAULT_QMD_LIMITS.maxSnippetChars),
maxInjectedChars: resolvePositiveIntegerConfig(raw?.maxInjectedChars, DEFAULT_QMD_LIMITS.maxInjectedChars),
timeoutMs: resolvePositiveIntegerConfig(raw?.timeoutMs, DEFAULT_QMD_LIMITS.timeoutMs)
};
}
function resolveSearchMode(raw) {
if (raw === "search" || raw === "vsearch" || raw === "query") return raw;
return DEFAULT_QMD_SEARCH_MODE;
}
function resolveSearchTool(raw) {
const value = raw?.trim();
return value ? value : void 0;
}
function resolveSessionConfig(cfg, workspaceDir) {
const enabled = Boolean(cfg?.enabled);
const exportDirRaw = cfg?.exportDir?.trim();
return {
enabled,
exportDir: exportDirRaw ? resolvePath(exportDirRaw, workspaceDir) : void 0,
retentionDays: resolvePositiveIntegerConfig(cfg?.retentionDays)
};
}
function resolveCustomPaths(rawPaths, workspaceDir, existing, agentId) {
if (!rawPaths?.length) return [];
const collections = [];
const seenRoots = /* @__PURE__ */ new Set();
rawPaths.forEach((entry, index) => {
const trimmedPath = entry?.path?.trim();
if (!trimmedPath) return;
let resolved;
let collectionPath;
try {
resolved = resolvePath(trimmedPath, workspaceDir);
} catch {
return;
}
collectionPath = resolved;
let pattern = entry.pattern?.trim() || "**/*.md";
try {
if (fs.statSync(resolved).isFile()) {
collectionPath = path.dirname(resolved);
pattern = escapeQmdExactFilePattern(path.basename(resolved));
}
} catch {}
const dedupeKey = `${collectionPath}\u0000${pattern}`;
if (seenRoots.has(dedupeKey)) return;
seenRoots.add(dedupeKey);
const explicitName = entry.name?.trim();
const name = ensureUniqueName(explicitName && !isPathInsideRoot(collectionPath, workspaceDir) ? explicitName : scopeCollectionBase(explicitName || `custom-${index + 1}`, agentId), existing);
collections.push({
name,
path: collectionPath,
pattern,
kind: "custom"
});
});
return collections;
}
function resolveMcporterConfig(raw) {
const parsed = { ...DEFAULT_QMD_MCPORTER };
if (!raw) return parsed;
if (raw.enabled !== void 0) parsed.enabled = raw.enabled;
if (typeof raw.serverName === "string" && raw.serverName.trim()) parsed.serverName = raw.serverName.trim();
if (raw.startDaemon !== void 0) parsed.startDaemon = raw.startDaemon;
if (parsed.enabled && raw.startDaemon === void 0) parsed.startDaemon = true;
return parsed;
}
function resolveDefaultCollections(include, workspaceDir, existing, agentId) {
if (!include) return [];
return [{
path: workspaceDir,
pattern: CANONICAL_ROOT_MEMORY_FILENAME,
base: "memory-root"
}, {
path: path.join(workspaceDir, "memory"),
pattern: "**/*.md",
base: "memory-dir"
}].map((entry) => ({
name: ensureUniqueName(scopeCollectionBase(entry.base, agentId), existing),
path: entry.path,
pattern: entry.pattern,
kind: "memory"
}));
}
function resolveMemoryBackendConfig(params) {
const normalizedAgentId = normalizeAgentId(params.agentId);
const backend = params.cfg.memory?.backend ?? DEFAULT_BACKEND;
const citations = params.cfg.memory?.citations ?? DEFAULT_CITATIONS;
if (backend !== "qmd") return {
backend: "builtin",
citations
};
const workspaceDir = resolveAgentWorkspaceDir(params.cfg, normalizedAgentId);
const qmdCfg = params.cfg.memory?.qmd;
const includeDefaultMemory = qmdCfg?.includeDefaultMemory !== false;
const nameSet = /* @__PURE__ */ new Set();
const agentEntry = params.cfg.agents?.list?.find((entry) => normalizeAgentId(entry?.id) === normalizedAgentId);
const searchExtraPaths = uniqueStrings(normalizeStringEntries([...params.cfg.agents?.defaults?.memorySearch?.extraPaths ?? [], ...agentEntry?.memorySearch?.extraPaths ?? []].filter((value) => typeof value === "string"))).map((pathValue) => ({ path: pathValue }));
const mergedExtraCollections = [...params.cfg.agents?.defaults?.memorySearch?.qmd?.extraCollections ?? [], ...agentEntry?.memorySearch?.qmd?.extraCollections ?? []].filter((value) => value !== null && typeof value === "object" && typeof value.path === "string");
const allQmdPaths = [
...qmdCfg?.paths ?? [],
...searchExtraPaths,
...mergedExtraCollections
];
const collections = [...resolveDefaultCollections(includeDefaultMemory, workspaceDir, nameSet, normalizedAgentId), ...resolveCustomPaths(allQmdPaths, workspaceDir, nameSet, normalizedAgentId)];
const rawCommand = qmdCfg?.command?.trim() || "qmd";
return {
backend: "qmd",
citations,
qmd: {
command: splitShellArgs(rawCommand)?.[0] || rawCommand.split(/\s+/)[0] || "qmd",
mcporter: resolveMcporterConfig(qmdCfg?.mcporter),
searchMode: resolveSearchMode(qmdCfg?.searchMode),
rerank: qmdCfg?.rerank,
searchTool: resolveSearchTool(qmdCfg?.searchTool),
collections,
includeDefaultMemory,
sessions: resolveSessionConfig(qmdCfg?.sessions, workspaceDir),
update: {
intervalMs: resolveIntervalMs(qmdCfg?.update?.interval),
debounceMs: resolveDebounceMs(qmdCfg?.update?.debounceMs),
onBoot: qmdCfg?.update?.onBoot !== false,
startup: resolveStartupMode(qmdCfg?.update),
startupDelayMs: resolveStartupDelayMs(qmdCfg?.update?.startupDelayMs),
waitForBootSync: qmdCfg?.update?.waitForBootSync === true,
embedIntervalMs: resolveEmbedIntervalMs(qmdCfg?.update?.embedInterval),
commandTimeoutMs: resolveTimeoutMs(qmdCfg?.update?.commandTimeoutMs, DEFAULT_QMD_COMMAND_TIMEOUT_MS),
updateTimeoutMs: resolveTimeoutMs(qmdCfg?.update?.updateTimeoutMs, DEFAULT_QMD_UPDATE_TIMEOUT_MS),
embedTimeoutMs: resolveTimeoutMs(qmdCfg?.update?.embedTimeoutMs, DEFAULT_QMD_EMBED_TIMEOUT_MS)
},
limits: resolveLimits(qmdCfg?.limits),
scope: qmdCfg?.scope ?? DEFAULT_QMD_SCOPE
}
};
}
//#endregion
export { resolveMemoryBackendConfig as t };