refactor(memory-host): consolidate core adapter

This commit is contained in:
Peter Steinberger
2026-04-28 06:20:15 +01:00
parent 82eb90b8a2
commit 025081dbc5
15 changed files with 658 additions and 118 deletions

View File

@@ -5,7 +5,7 @@ export {
listRegisteredMemoryEmbeddingProviders,
listMemoryEmbeddingProviders,
listRegisteredMemoryEmbeddingProviderAdapters,
} from "../../../src/plugins/memory-embedding-provider-runtime.js";
} from "./host/openclaw-runtime.js";
export type {
MemoryEmbeddingBatchChunk,
MemoryEmbeddingBatchOptions,
@@ -14,7 +14,7 @@ export type {
MemoryEmbeddingProviderCreateOptions,
MemoryEmbeddingProviderCreateResult,
MemoryEmbeddingProviderRuntime,
} from "../../../src/plugins/memory-embedding-providers.js";
} from "./host/openclaw-runtime.js";
export { createLocalEmbeddingProvider, DEFAULT_LOCAL_MODEL } from "./host/embeddings.js";
export { extractBatchErrorMessage, formatUnavailableBatchError } from "./host/batch-error-utils.js";
export { postJsonWithRetry } from "./host/batch-http.js";

View File

@@ -6,37 +6,37 @@ export {
resolveAgentWorkspaceDir,
resolveDefaultAgentId,
resolveSessionAgentId,
} from "../../../src/agents/agent-scope.js";
} from "./host/openclaw-runtime.js";
export {
resolveMemorySearchConfig,
resolveMemorySearchSyncConfig,
type ResolvedMemorySearchConfig,
type ResolvedMemorySearchSyncConfig,
} from "../../../src/agents/memory-search.js";
export { parseDurationMs } from "../../../src/cli/parse-duration.js";
export { loadConfig } from "../../../src/config/config.js";
export { resolveStateDir } from "../../../src/config/paths.js";
export { resolveSessionTranscriptsDirForAgent } from "../../../src/config/sessions/paths.js";
} from "./host/openclaw-runtime.js";
export { parseDurationMs } from "./host/openclaw-runtime.js";
export { loadConfig } from "./host/openclaw-runtime.js";
export { resolveStateDir } from "./host/openclaw-runtime.js";
export { resolveSessionTranscriptsDirForAgent } from "./host/openclaw-runtime.js";
export {
hasConfiguredSecretInput,
normalizeResolvedSecretInputString,
} from "../../../src/config/types.secrets.js";
export { writeFileWithinRoot } from "../../../src/infra/fs-safe.js";
export { createSubsystemLogger } from "../../../src/logging/subsystem.js";
export { detectMime } from "../../../src/media/mime.js";
export { resolveGlobalSingleton } from "../../../src/shared/global-singleton.js";
export { onSessionTranscriptUpdate } from "../../../src/sessions/transcript-events.js";
export { splitShellArgs } from "../../../src/utils/shell-argv.js";
export { runTasksWithConcurrency } from "../../../src/utils/run-with-concurrency.js";
} from "./host/openclaw-runtime.js";
export { writeFileWithinRoot } from "./host/openclaw-runtime.js";
export { createSubsystemLogger } from "./host/openclaw-runtime.js";
export { detectMime } from "./host/openclaw-runtime.js";
export { resolveGlobalSingleton } from "./host/openclaw-runtime.js";
export { onSessionTranscriptUpdate } from "./host/openclaw-runtime.js";
export { splitShellArgs } from "./host/openclaw-runtime.js";
export { runTasksWithConcurrency } from "./host/openclaw-runtime.js";
export {
shortenHomeInString,
shortenHomePath,
resolveUserPath,
truncateUtf16Safe,
} from "../../../src/utils.js";
export type { OpenClawConfig } from "../../../src/config/config.js";
export type { SessionSendPolicyConfig } from "../../../src/config/types.base.js";
export type { SecretInput } from "../../../src/config/types.secrets.js";
} from "./host/openclaw-runtime.js";
export type { OpenClawConfig } from "./host/openclaw-runtime.js";
export type { SessionSendPolicyConfig } from "./host/openclaw-runtime.js";
export type { SecretInput } from "./host/openclaw-runtime.js";
export type {
MemoryBackend,
MemoryCitationsMode,
@@ -44,5 +44,5 @@ export type {
MemoryQmdIndexPath,
MemoryQmdMcporterConfig,
MemoryQmdSearchMode,
} from "../../../src/config/types.memory.js";
export type { MemorySearchConfig } from "../../../src/config/types.tools.js";
} from "./host/openclaw-runtime.js";
export type { MemorySearchConfig } from "./host/openclaw-runtime.js";

View File

@@ -12,7 +12,7 @@ export {
type SessionFileEntry,
type SessionTranscriptClassification,
} from "./host/session-files.js";
export { parseUsageCountedSessionIdFromFileName } from "../../../src/config/sessions/artifacts.js";
export { parseUsageCountedSessionIdFromFileName } from "./host/openclaw-runtime.js";
export { parseQmdQueryJson, type QmdQueryResult } from "./host/qmd-query-parser.js";
export {
deriveQmdScopeChannel,

View File

@@ -4,9 +4,8 @@ import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
import { resolveAgentWorkspaceDir } from "../../../../src/agents/agent-scope-config.js";
import type { OpenClawConfig } from "../../../../src/config/config.js";
import { resolveMemoryBackendConfig } from "./backend-config.js";
import type { OpenClawConfig } from "./config-utils.js";
type ResolvedMemoryBackendConfig = ReturnType<typeof resolveMemoryBackendConfig>;
@@ -170,8 +169,7 @@ describe("resolveMemoryBackendConfig", () => {
const resolved = resolveMemoryBackendConfig({ cfg, agentId: "main" });
const custom = resolved.qmd?.collections.find((c) => c.name.startsWith("custom-notes"));
expect(custom).toBeDefined();
const workspaceRoot = resolveAgentWorkspaceDir(cfg, "main");
expect(custom?.path).toBe(path.resolve(workspaceRoot, "notes"));
expect(custom?.path).toBe(path.resolve("/workspace/root", "notes"));
});
it("scopes qmd collection names per agent", () => {

View File

@@ -1,21 +1,21 @@
import fs from "node:fs";
import path from "node:path";
import { resolveAgentWorkspaceDir } from "../../../../src/agents/agent-scope-config.js";
import { parseDurationMs } from "../../../../src/cli/parse-duration.js";
import type { OpenClawConfig } from "../../../../src/config/config.js";
import type { SessionSendPolicyConfig } from "../../../../src/config/types.base.js";
import type {
MemoryBackend,
MemoryCitationsMode,
MemoryQmdConfig,
MemoryQmdIndexPath,
MemoryQmdMcporterConfig,
MemoryQmdSearchMode,
} from "../../../../src/config/types.memory.js";
import { CANONICAL_ROOT_MEMORY_FILENAME } from "../../../../src/memory/root-memory-files.js";
import { normalizeAgentId } from "../../../../src/routing/session-key.js";
import { resolveUserPath } from "../../../../src/utils.js";
import { splitShellArgs } from "../../../../src/utils/shell-argv.js";
import {
CANONICAL_ROOT_MEMORY_FILENAME,
type MemoryBackend,
type MemoryCitationsMode,
type MemoryQmdConfig,
type MemoryQmdIndexPath,
type MemoryQmdMcporterConfig,
type MemoryQmdSearchMode,
type OpenClawConfig,
parseDurationMs,
resolveAgentWorkspaceDir,
normalizeAgentId,
resolveUserPath,
type SessionSendPolicyConfig,
splitShellArgs,
} from "./config-utils.js";
import { normalizeLowercaseStringOrEmpty } from "./string-utils.js";
export type ResolvedMemoryBackendConfig = {

View File

@@ -0,0 +1,425 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "./string-utils.js";
export type ChatType = "direct" | "group" | "channel";
export type MemoryBackend = "builtin" | "qmd";
export type MemoryCitationsMode = "auto" | "on" | "off";
export type MemoryQmdSearchMode = "query" | "search" | "vsearch";
export type SessionSendPolicyAction = "allow" | "deny";
export type SessionSendPolicyMatch = {
channel?: string;
chatType?: ChatType;
keyPrefix?: string;
rawKeyPrefix?: string;
};
export type SessionSendPolicyRule = {
action: SessionSendPolicyAction;
match?: SessionSendPolicyMatch;
};
export type SessionSendPolicyConfig = {
default?: SessionSendPolicyAction;
rules?: SessionSendPolicyRule[];
};
export type MemoryQmdIndexPath = {
path: string;
name?: string;
pattern?: string;
};
export type MemoryQmdMcporterConfig = {
enabled?: boolean;
serverName?: string;
startDaemon?: boolean;
};
export type MemoryQmdSessionConfig = {
enabled?: boolean;
exportDir?: string;
retentionDays?: number;
};
export type MemoryQmdUpdateConfig = {
interval?: string;
debounceMs?: number;
onBoot?: boolean;
waitForBootSync?: boolean;
embedInterval?: string;
commandTimeoutMs?: number;
updateTimeoutMs?: number;
embedTimeoutMs?: number;
};
export type MemoryQmdLimitsConfig = {
maxResults?: number;
maxSnippetChars?: number;
maxInjectedChars?: number;
timeoutMs?: number;
};
export type MemoryQmdConfig = {
command?: string;
mcporter?: MemoryQmdMcporterConfig;
searchMode?: MemoryQmdSearchMode;
searchTool?: string;
includeDefaultMemory?: boolean;
paths?: MemoryQmdIndexPath[];
sessions?: MemoryQmdSessionConfig;
update?: MemoryQmdUpdateConfig;
limits?: MemoryQmdLimitsConfig;
scope?: SessionSendPolicyConfig;
};
export type MemoryConfig = {
backend?: MemoryBackend;
citations?: MemoryCitationsMode;
qmd?: MemoryQmdConfig;
};
export type MemorySearchConfig = {
enabled?: boolean;
extraPaths?: string[];
qmd?: {
extraCollections?: MemoryQmdIndexPath[];
};
};
export type AgentContextLimitsConfig = {
memoryGetMaxChars?: number;
memoryGetDefaultLines?: number;
};
export type SecretInput =
| string
| {
source: string;
provider: string;
id: string;
};
type AgentConfig = {
id?: string;
default?: boolean;
workspace?: string;
memorySearch?: MemorySearchConfig;
contextLimits?: AgentContextLimitsConfig;
};
export type OpenClawConfig = {
agents?: {
defaults?: {
workspace?: string;
memorySearch?: MemorySearchConfig;
contextLimits?: AgentContextLimitsConfig;
};
list?: AgentConfig[];
};
memory?: MemoryConfig;
models?: {
providers?: Record<
string,
{
api?: string;
baseUrl?: string;
headers?: Record<string, SecretInput>;
}
>;
};
};
export const CANONICAL_ROOT_MEMORY_FILENAME = "MEMORY.md";
const DEFAULT_AGENT_ID = "main";
const VALID_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
const INVALID_CHARS_RE = /[^a-z0-9_-]+/g;
const LEADING_DASH_RE = /^-+/;
const TRAILING_DASH_RE = /-+$/;
const LEGACY_STATE_DIRNAMES = [".clawdbot"] as const;
const NEW_STATE_DIRNAME = ".openclaw";
const DURATION_MULTIPLIERS: Record<string, number> = {
ms: 1,
s: 1000,
m: 60_000,
h: 3_600_000,
d: 86_400_000,
};
export function normalizeAgentId(value: string | undefined | null): string {
const trimmed = (value ?? "").trim();
if (!trimmed) {
return DEFAULT_AGENT_ID;
}
const normalized = normalizeLowercaseStringOrEmpty(trimmed);
if (VALID_ID_RE.test(trimmed)) {
return normalized;
}
return (
normalized
.replace(INVALID_CHARS_RE, "-")
.replace(LEADING_DASH_RE, "")
.replace(TRAILING_DASH_RE, "")
.slice(0, 64) || DEFAULT_AGENT_ID
);
}
function normalizeHomeValue(value: string | undefined): string | undefined {
const trimmed = normalizeOptionalString(value);
if (!trimmed || trimmed === "undefined" || trimmed === "null") {
return undefined;
}
return trimmed;
}
function resolveRawOsHomeDir(env: NodeJS.ProcessEnv, homedir: () => string): string | undefined {
return (
normalizeHomeValue(env.HOME) ??
normalizeHomeValue(env.USERPROFILE) ??
normalizeHomeValue(homedir())
);
}
function resolveRequiredHomeDir(
env: NodeJS.ProcessEnv = process.env,
homedir: () => string = os.homedir,
): string {
const explicitHome = normalizeHomeValue(env.OPENCLAW_HOME);
const rawHome = explicitHome
? explicitHome.replace(/^~(?=$|[\\/])/, resolveRawOsHomeDir(env, homedir) ?? "")
: resolveRawOsHomeDir(env, homedir);
return rawHome ? path.resolve(rawHome) : path.resolve(process.cwd());
}
export function resolveUserPath(
input: string,
env: NodeJS.ProcessEnv = process.env,
homedir: () => string = os.homedir,
): string {
const trimmed = input.trim();
if (!trimmed) {
return trimmed;
}
if (trimmed.startsWith("~")) {
return path.resolve(trimmed.replace(/^~(?=$|[\\/])/, resolveRequiredHomeDir(env, homedir)));
}
return path.resolve(trimmed);
}
function legacyStateDirs(homedir: () => string): string[] {
return LEGACY_STATE_DIRNAMES.map((dir) => path.join(homedir(), dir));
}
export function resolveStateDir(
env: NodeJS.ProcessEnv = process.env,
homedir: () => string = os.homedir,
): string {
const override = env.OPENCLAW_STATE_DIR?.trim();
if (override) {
return resolveUserPath(override, env, homedir);
}
const effectiveHome = () => resolveRequiredHomeDir(env, homedir);
const nextDir = path.join(effectiveHome(), NEW_STATE_DIRNAME);
if (env.OPENCLAW_TEST_FAST === "1" || fs.existsSync(nextDir)) {
return nextDir;
}
const existingLegacy = legacyStateDirs(effectiveHome).find((dir) => {
try {
return fs.existsSync(dir);
} catch {
return false;
}
});
return existingLegacy ?? nextDir;
}
function resolveDefaultAgentWorkspaceDir(env: NodeJS.ProcessEnv = process.env): string {
const home = resolveRequiredHomeDir(env, os.homedir);
const profile = env.OPENCLAW_PROFILE?.trim();
if (profile && normalizeLowercaseStringOrEmpty(profile) !== "default") {
return path.join(home, ".openclaw", `workspace-${profile}`);
}
return path.join(home, ".openclaw", "workspace");
}
function listAgentEntries(cfg: OpenClawConfig): AgentConfig[] {
return Array.isArray(cfg.agents?.list)
? cfg.agents.list.filter((entry): entry is AgentConfig => Boolean(entry))
: [];
}
function resolveDefaultAgentId(cfg: OpenClawConfig): string {
const agents = listAgentEntries(cfg);
if (agents.length === 0) {
return DEFAULT_AGENT_ID;
}
const chosen = (agents.find((agent) => agent.default) ?? agents[0])?.id;
return normalizeAgentId(chosen || DEFAULT_AGENT_ID);
}
function resolveAgentConfig(cfg: OpenClawConfig, agentId: string): AgentConfig | undefined {
const id = normalizeAgentId(agentId);
return listAgentEntries(cfg).find((entry) => normalizeAgentId(entry.id) === id);
}
function stripNullBytes(value: string): string {
return value.replaceAll("\0", "");
}
export function resolveAgentWorkspaceDir(
cfg: OpenClawConfig,
agentId: string,
env: NodeJS.ProcessEnv = process.env,
): string {
const id = normalizeAgentId(agentId);
const configured = resolveAgentConfig(cfg, id)?.workspace?.trim();
if (configured) {
return stripNullBytes(resolveUserPath(configured, env));
}
const fallback = cfg.agents?.defaults?.workspace?.trim();
if (id === resolveDefaultAgentId(cfg)) {
return stripNullBytes(
fallback ? resolveUserPath(fallback, env) : resolveDefaultAgentWorkspaceDir(env),
);
}
if (fallback) {
return stripNullBytes(path.join(resolveUserPath(fallback, env), id));
}
return stripNullBytes(path.join(resolveStateDir(env), `workspace-${id}`));
}
export function resolveAgentContextLimits(
cfg: OpenClawConfig | undefined,
agentId?: string | null,
): AgentContextLimitsConfig | undefined {
const defaults = cfg?.agents?.defaults?.contextLimits;
if (!cfg || !agentId) {
return defaults;
}
return resolveAgentConfig(cfg, agentId)?.contextLimits ?? defaults;
}
export function resolveMemorySearchConfig(
cfg: OpenClawConfig,
agentId: string,
): { enabled: boolean; extraPaths: string[] } | null {
const defaults = cfg.agents?.defaults?.memorySearch;
const overrides = resolveAgentConfig(cfg, agentId)?.memorySearch;
const enabled = overrides?.enabled ?? defaults?.enabled ?? true;
if (!enabled) {
return null;
}
const rawPaths = [...(defaults?.extraPaths ?? []), ...(overrides?.extraPaths ?? [])]
.map((value) => value.trim())
.filter(Boolean);
return {
enabled,
extraPaths: Array.from(new Set(rawPaths)),
};
}
export function parseDurationMs(
raw: string,
opts?: { defaultUnit?: "ms" | "s" | "m" | "h" | "d" },
): number {
const trimmed = normalizeLowercaseStringOrEmpty(normalizeOptionalString(raw) ?? "");
if (!trimmed) {
throw new Error("invalid duration (empty)");
}
const single = /^(\d+(?:\.\d+)?)(ms|s|m|h|d)?$/.exec(trimmed);
if (single) {
const value = Number(single[1]);
if (!Number.isFinite(value) || value < 0) {
throw new Error(`invalid duration: ${raw}`);
}
const unit = single[2] ?? opts?.defaultUnit ?? "ms";
return Math.round(value * (DURATION_MULTIPLIERS[unit] ?? 1));
}
let totalMs = 0;
let consumed = 0;
const tokenRe = /(\d+(?:\.\d+)?)(ms|s|m|h|d)/g;
for (const match of trimmed.matchAll(tokenRe)) {
const [full, valueRaw, unitRaw] = match;
const index = match.index ?? -1;
if (!full || !valueRaw || !unitRaw || index !== consumed) {
throw new Error(`invalid duration: ${raw}`);
}
const value = Number(valueRaw);
const multiplier = DURATION_MULTIPLIERS[unitRaw];
if (!Number.isFinite(value) || value < 0 || !multiplier) {
throw new Error(`invalid duration: ${raw}`);
}
totalMs += value * multiplier;
consumed += full.length;
}
if (consumed !== trimmed.length || consumed === 0) {
throw new Error(`invalid duration: ${raw}`);
}
return Math.round(totalMs);
}
const DOUBLE_QUOTE_ESCAPES = new Set(["\\", '"', "$", "`", "\n", "\r"]);
export function splitShellArgs(raw: string): string[] | null {
const tokens: string[] = [];
let buf = "";
let inSingle = false;
let inDouble = false;
let escaped = false;
const pushToken = () => {
if (buf.length > 0) {
tokens.push(buf);
buf = "";
}
};
for (let i = 0; i < raw.length; i += 1) {
const ch = raw[i];
if (escaped) {
buf += ch;
escaped = false;
continue;
}
if (!inSingle && !inDouble && ch === "\\") {
escaped = true;
continue;
}
if (inSingle) {
if (ch === "'") {
inSingle = false;
} else {
buf += ch;
}
continue;
}
if (inDouble) {
const next = raw[i + 1];
if (ch === "\\" && next && DOUBLE_QUOTE_ESCAPES.has(next)) {
buf += next;
i += 1;
} else if (ch === '"') {
inDouble = false;
} else {
buf += ch;
}
continue;
}
if (ch === "'") {
inSingle = true;
} else if (ch === '"') {
inDouble = true;
} else if (ch === "#" && buf.length === 0) {
break;
} else if (/\s/.test(ch)) {
pushToken();
} else {
buf += ch;
}
}
if (escaped || inSingle || inDouble) {
return null;
}
pushToken();
return tokens;
}

View File

@@ -1,5 +1,5 @@
import { requireApiKey, resolveApiKeyForProvider } from "../../../../src/agents/model-auth.js";
import type { EmbeddingProviderOptions } from "./embeddings.types.js";
import { requireApiKey, resolveApiKeyForProvider } from "./openclaw-runtime.js";
import { buildRemoteBaseUrlPolicy } from "./remote-http.js";
import { resolveMemorySecretInputString } from "./secret-input.js";
import type { SsrFPolicy } from "./ssrf-policy.js";

View File

@@ -2,14 +2,7 @@ import crypto from "node:crypto";
import fsSync from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";
import { detectMime } from "../../../../src/media/mime.js";
import {
CANONICAL_ROOT_MEMORY_FILENAME,
resolveCanonicalRootMemoryFile,
shouldSkipRootMemoryAuxiliaryPath,
} from "../../../../src/memory/root-memory-files.js";
import { CHARS_PER_TOKEN_ESTIMATE, estimateStringChars } from "../../../../src/utils/cjk-chars.js";
import { runTasksWithConcurrency } from "../../../../src/utils/run-with-concurrency.js";
import { CANONICAL_ROOT_MEMORY_FILENAME } from "./config-utils.js";
import { estimateStructuredEmbeddingInputBytes } from "./embedding-input-limits.js";
import { buildTextEmbeddingInput, type EmbeddingInput } from "./embedding-inputs.js";
import { isFileMissingError } from "./fs-utils.js";
@@ -19,6 +12,14 @@ import {
type MemoryMultimodalModality,
type MemoryMultimodalSettings,
} from "./multimodal.js";
import {
CHARS_PER_TOKEN_ESTIMATE,
detectMime,
estimateStringChars,
resolveCanonicalRootMemoryFile,
runTasksWithConcurrency,
shouldSkipRootMemoryAuxiliaryPath,
} from "./openclaw-runtime.js";
export { hashText } from "./hash.js";
import { hashText } from "./hash.js";

View File

@@ -0,0 +1,128 @@
export { resolveCronStyleNow } from "../../../../src/agents/current-time.js";
export {
resolveAgentContextLimits,
resolveAgentDir,
resolveAgentWorkspaceDir,
resolveDefaultAgentId,
resolveSessionAgentId,
} from "../../../../src/agents/agent-scope.js";
export { requireApiKey, resolveApiKeyForProvider } from "../../../../src/agents/model-auth.js";
export { stripInternalRuntimeContext } from "../../../../src/agents/internal-runtime-context.js";
export { DEFAULT_PI_COMPACTION_RESERVE_TOKENS_FLOOR } from "../../../../src/agents/pi-settings.js";
export {
asToolParamsRecord,
jsonResult,
readNumberParam,
readStringParam,
} from "../../../../src/agents/tools/common.js";
export type { AnyAgentTool } from "../../../../src/agents/tools/common.js";
export {
resolveMemorySearchConfig,
resolveMemorySearchSyncConfig,
type ResolvedMemorySearchConfig,
type ResolvedMemorySearchSyncConfig,
} from "../../../../src/agents/memory-search.js";
export { isHeartbeatUserMessage } from "../../../../src/auto-reply/heartbeat-filter.js";
export { HEARTBEAT_PROMPT } from "../../../../src/auto-reply/heartbeat.js";
export { stripInboundMetadata } from "../../../../src/auto-reply/reply/strip-inbound-meta.js";
export {
HEARTBEAT_TOKEN,
SILENT_REPLY_TOKEN,
isSilentReplyPayloadText,
} from "../../../../src/auto-reply/tokens.js";
export { formatErrorMessage, withManager } from "../../../../src/cli/cli-utils.js";
export { resolveCommandSecretRefsViaGateway } from "../../../../src/cli/command-secret-gateway.js";
export { formatHelpExamples } from "../../../../src/cli/help-format.js";
export { parseDurationMs } from "../../../../src/cli/parse-duration.js";
export { withProgress, withProgressTotals } from "../../../../src/cli/progress.js";
export { parseNonNegativeByteSize } from "../../../../src/config/byte-size.js";
export {
getRuntimeConfig,
/** @deprecated Use getRuntimeConfig(), or pass the already loaded config through the call path. */
loadConfig,
} from "../../../../src/config/config.js";
export type { OpenClawConfig } from "../../../../src/config/config.js";
export { resolveStateDir } from "../../../../src/config/paths.js";
export {
isCompactionCheckpointTranscriptFileName,
isSessionArchiveArtifactName,
isUsageCountedSessionTranscriptFileName,
parseUsageCountedSessionIdFromFileName,
} from "../../../../src/config/sessions/artifacts.js";
export { resolveSessionTranscriptsDirForAgent } from "../../../../src/config/sessions/paths.js";
export type { SessionSendPolicyConfig } from "../../../../src/config/types.base.js";
export type {
MemoryBackend,
MemoryCitationsMode,
MemoryQmdConfig,
MemoryQmdIndexPath,
MemoryQmdMcporterConfig,
MemoryQmdSearchMode,
} from "../../../../src/config/types.memory.js";
export {
hasConfiguredSecretInput,
normalizeResolvedSecretInputString,
} from "../../../../src/config/types.secrets.js";
export type { SecretInput } from "../../../../src/config/types.secrets.js";
export type { MemorySearchConfig } from "../../../../src/config/types.tools.js";
export { isVerbose, setVerbose } from "../../../../src/globals.js";
export { isExecCompletionEvent } from "../../../../src/infra/heartbeat-events-filter.js";
export { writeFileWithinRoot } from "../../../../src/infra/fs-safe.js";
export { fetchWithSsrFGuard } from "../../../../src/infra/net/fetch-guard.js";
export { shouldUseEnvHttpProxyForUrl } from "../../../../src/infra/net/proxy-env.js";
export { ssrfPolicyFromHttpBaseUrlAllowedHostname } from "../../../../src/infra/net/ssrf.js";
export { redactSensitiveText } from "../../../../src/logging/redact.js";
export { createSubsystemLogger } from "../../../../src/logging/subsystem.js";
export { detectMime } from "../../../../src/media/mime.js";
export {
resolveCanonicalRootMemoryFile,
shouldSkipRootMemoryAuxiliaryPath,
} from "../../../../src/memory/root-memory-files.js";
export {
getMemoryEmbeddingProvider,
listMemoryEmbeddingProviders,
listRegisteredMemoryEmbeddingProviderAdapters,
listRegisteredMemoryEmbeddingProviders,
} from "../../../../src/plugins/memory-embedding-provider-runtime.js";
export type {
MemoryEmbeddingBatchChunk,
MemoryEmbeddingBatchOptions,
MemoryEmbeddingProvider,
MemoryEmbeddingProviderAdapter,
MemoryEmbeddingProviderCreateOptions,
MemoryEmbeddingProviderCreateResult,
MemoryEmbeddingProviderRuntime,
} from "../../../../src/plugins/memory-embedding-providers.js";
export { emptyPluginConfigSchema } from "../../../../src/plugins/config-schema.js";
export {
buildMemoryPromptSection as buildActiveMemoryPromptSection,
getMemoryCapabilityRegistration,
listActiveMemoryPublicArtifacts,
} from "../../../../src/plugins/memory-state.js";
export type {
MemoryFlushPlan,
MemoryFlushPlanResolver,
MemoryPluginCapability,
MemoryPluginPublicArtifact,
MemoryPluginPublicArtifactsProvider,
MemoryPluginRuntime,
MemoryPromptSectionBuilder,
} from "../../../../src/plugins/memory-state.js";
export type { OpenClawPluginApi } from "../../../../src/plugins/types.js";
export { defaultRuntime } from "../../../../src/runtime.js";
export { parseAgentSessionKey } from "../../../../src/routing/session-key.js";
export { hasInterSessionUserProvenance } from "../../../../src/sessions/input-provenance.js";
export { isCronRunSessionKey } from "../../../../src/sessions/session-key-utils.js";
export { onSessionTranscriptUpdate } from "../../../../src/sessions/transcript-events.js";
export { formatDocsLink } from "../../../../src/terminal/links.js";
export { colorize, isRich, theme } from "../../../../src/terminal/theme.js";
export { CHARS_PER_TOKEN_ESTIMATE, estimateStringChars } from "../../../../src/utils/cjk-chars.js";
export { runTasksWithConcurrency } from "../../../../src/utils/run-with-concurrency.js";
export { splitShellArgs } from "../../../../src/utils/shell-argv.js";
export {
resolveUserPath,
shortenHomeInString,
shortenHomePath,
truncateUtf16Safe,
} from "../../../../src/utils.js";
export { resolveGlobalSingleton } from "../../../../src/shared/global-singleton.js";

View File

@@ -3,9 +3,9 @@ import path from "node:path";
import {
resolveAgentContextLimits,
resolveAgentWorkspaceDir,
} from "../../../../src/agents/agent-scope.js";
import { resolveMemorySearchConfig } from "../../../../src/agents/memory-search.js";
import type { OpenClawConfig } from "../../../../src/config/config.js";
resolveMemorySearchConfig,
type OpenClawConfig,
} from "./config-utils.js";
import { isFileMissingError, statRegularFile } from "./fs-utils.js";
import { isMemoryPath, normalizeExtraMemoryPaths } from "./internal.js";
import {

View File

@@ -1,6 +1,8 @@
import { fetchWithSsrFGuard } from "../../../../src/infra/net/fetch-guard.js";
import { shouldUseEnvHttpProxyForUrl } from "../../../../src/infra/net/proxy-env.js";
import { ssrfPolicyFromHttpBaseUrlAllowedHostname } from "../../../../src/infra/net/ssrf.js";
import {
fetchWithSsrFGuard,
shouldUseEnvHttpProxyForUrl,
ssrfPolicyFromHttpBaseUrlAllowedHostname,
} from "./openclaw-runtime.js";
import type { SsrFPolicy } from "./ssrf-policy.js";
export const MEMORY_REMOTE_TRUSTED_ENV_PROXY_MODE = "trusted_env_proxy";

View File

@@ -1,22 +1,24 @@
import fsSync from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";
import { stripInternalRuntimeContext } from "../../../../src/agents/internal-runtime-context.js";
import { isHeartbeatUserMessage } from "../../../../src/auto-reply/heartbeat-filter.js";
import { HEARTBEAT_PROMPT } from "../../../../src/auto-reply/heartbeat.js";
import { stripInboundMetadata } from "../../../../src/auto-reply/reply/strip-inbound-meta.js";
import { HEARTBEAT_TOKEN, isSilentReplyPayloadText } from "../../../../src/auto-reply/tokens.js";
import {
isCompactionCheckpointTranscriptFileName,
isSessionArchiveArtifactName,
isUsageCountedSessionTranscriptFileName,
} from "../../../../src/config/sessions/artifacts.js";
import { resolveSessionTranscriptsDirForAgent } from "../../../../src/config/sessions/paths.js";
import { isExecCompletionEvent } from "../../../../src/infra/heartbeat-events-filter.js";
import { redactSensitiveText } from "../../../../src/logging/redact.js";
import { hasInterSessionUserProvenance } from "../../../../src/sessions/input-provenance.js";
import { isCronRunSessionKey } from "../../../../src/sessions/session-key-utils.js";
import { hashText } from "./hash.js";
import {
createSubsystemLogger,
HEARTBEAT_PROMPT,
HEARTBEAT_TOKEN,
hasInterSessionUserProvenance,
isCompactionCheckpointTranscriptFileName,
isCronRunSessionKey,
isExecCompletionEvent,
isHeartbeatUserMessage,
isSessionArchiveArtifactName,
isSilentReplyPayloadText,
isUsageCountedSessionTranscriptFileName,
redactSensitiveText,
resolveSessionTranscriptsDirForAgent,
stripInboundMetadata,
stripInternalRuntimeContext,
} from "./openclaw-runtime.js";
const DREAMING_NARRATIVE_RUN_PREFIX = "dreaming-narrative-";
// Keep the historical one-line-per-message export shape for normal turns, but
@@ -254,7 +256,6 @@ export function sessionPathForFile(absPath: string): string {
}
async function logSessionFileReadFailure(absPath: string, err: unknown): Promise<void> {
const { createSubsystemLogger } = await import("../../../../src/logging/subsystem.js");
createSubsystemLogger("memory").debug(`Failed reading session file ${absPath}: ${String(err)}`);
}

View File

@@ -1,15 +0,0 @@
import { vi } from "vitest";
import * as ssrf from "../../../../../src/infra/net/ssrf.js";
import { normalizeLowercaseStringOrEmpty } from "../string-utils.js";
export function mockPublicPinnedHostname() {
return vi.spyOn(ssrf, "resolvePinnedHostnameWithPolicy").mockImplementation(async (hostname) => {
const normalized = normalizeLowercaseStringOrEmpty(hostname).replace(/\.$/, "");
const addresses = ["93.184.216.34"];
return {
hostname: normalized,
addresses,
lookup: ssrf.createPinnedLookup({ hostname: normalized, addresses }),
};
});
}

View File

@@ -1,11 +1,11 @@
// Focused runtime contract for memory CLI/UI helpers.
export { formatErrorMessage, withManager } from "../../../src/cli/cli-utils.js";
export { formatHelpExamples } from "../../../src/cli/help-format.js";
export { resolveCommandSecretRefsViaGateway } from "../../../src/cli/command-secret-gateway.js";
export { withProgress, withProgressTotals } from "../../../src/cli/progress.js";
export { defaultRuntime } from "../../../src/runtime.js";
export { formatDocsLink } from "../../../src/terminal/links.js";
export { colorize, isRich, theme } from "../../../src/terminal/theme.js";
export { isVerbose, setVerbose } from "../../../src/globals.js";
export { shortenHomeInString, shortenHomePath } from "../../../src/utils.js";
export { formatErrorMessage, withManager } from "./host/openclaw-runtime.js";
export { formatHelpExamples } from "./host/openclaw-runtime.js";
export { resolveCommandSecretRefsViaGateway } from "./host/openclaw-runtime.js";
export { withProgress, withProgressTotals } from "./host/openclaw-runtime.js";
export { defaultRuntime } from "./host/openclaw-runtime.js";
export { formatDocsLink } from "./host/openclaw-runtime.js";
export { colorize, isRich, theme } from "./host/openclaw-runtime.js";
export { isVerbose, setVerbose } from "./host/openclaw-runtime.js";
export { shortenHomeInString, shortenHomePath } from "./host/openclaw-runtime.js";

View File

@@ -1,34 +1,34 @@
// Focused runtime contract for memory plugin config/state/helpers.
export type { AnyAgentTool } from "../../../src/agents/tools/common.js";
export { resolveCronStyleNow } from "../../../src/agents/current-time.js";
export { DEFAULT_PI_COMPACTION_RESERVE_TOKENS_FLOOR } from "../../../src/agents/pi-settings.js";
export { resolveDefaultAgentId, resolveSessionAgentId } from "../../../src/agents/agent-scope.js";
export { resolveMemorySearchConfig } from "../../../src/agents/memory-search.js";
export type { AnyAgentTool } from "./host/openclaw-runtime.js";
export { resolveCronStyleNow } from "./host/openclaw-runtime.js";
export { DEFAULT_PI_COMPACTION_RESERVE_TOKENS_FLOOR } from "./host/openclaw-runtime.js";
export { resolveDefaultAgentId, resolveSessionAgentId } from "./host/openclaw-runtime.js";
export { resolveMemorySearchConfig } from "./host/openclaw-runtime.js";
export {
asToolParamsRecord,
jsonResult,
readNumberParam,
readStringParam,
} from "../../../src/agents/tools/common.js";
export { SILENT_REPLY_TOKEN } from "../../../src/auto-reply/tokens.js";
export { parseNonNegativeByteSize } from "../../../src/config/byte-size.js";
} from "./host/openclaw-runtime.js";
export { SILENT_REPLY_TOKEN } from "./host/openclaw-runtime.js";
export { parseNonNegativeByteSize } from "./host/openclaw-runtime.js";
export {
getRuntimeConfig,
/** @deprecated Use getRuntimeConfig(), or pass the already loaded config through the call path. */
loadConfig,
} from "../../../src/config/config.js";
export { resolveStateDir } from "../../../src/config/paths.js";
export { resolveSessionTranscriptsDirForAgent } from "../../../src/config/sessions/paths.js";
export { emptyPluginConfigSchema } from "../../../src/plugins/config-schema.js";
} from "./host/openclaw-runtime.js";
export { resolveStateDir } from "./host/openclaw-runtime.js";
export { resolveSessionTranscriptsDirForAgent } from "./host/openclaw-runtime.js";
export { emptyPluginConfigSchema } from "./host/openclaw-runtime.js";
export {
buildMemoryPromptSection as buildActiveMemoryPromptSection,
listActiveMemoryPublicArtifacts,
buildActiveMemoryPromptSection,
getMemoryCapabilityRegistration,
} from "../../../src/plugins/memory-state.js";
export { parseAgentSessionKey } from "../../../src/routing/session-key.js";
export type { OpenClawConfig } from "../../../src/config/config.js";
export type { MemoryCitationsMode } from "../../../src/config/types.memory.js";
listActiveMemoryPublicArtifacts,
} from "./host/openclaw-runtime.js";
export { parseAgentSessionKey } from "./host/openclaw-runtime.js";
export type { OpenClawConfig } from "./host/openclaw-runtime.js";
export type { MemoryCitationsMode } from "./host/openclaw-runtime.js";
export type {
MemoryFlushPlan,
MemoryFlushPlanResolver,
@@ -37,5 +37,5 @@ export type {
MemoryPluginPublicArtifactsProvider,
MemoryPluginRuntime,
MemoryPromptSectionBuilder,
} from "../../../src/plugins/memory-state.js";
export type { OpenClawPluginApi } from "../../../src/plugins/types.js";
} from "./host/openclaw-runtime.js";
export type { OpenClawPluginApi } from "./host/openclaw-runtime.js";