From 025081dbc576df169e58d17dd1a4189a09c28bf6 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 28 Apr 2026 06:20:15 +0100 Subject: [PATCH] refactor(memory-host): consolidate core adapter --- .../memory-host-sdk/src/engine-embeddings.ts | 4 +- .../memory-host-sdk/src/engine-foundation.ts | 40 +- packages/memory-host-sdk/src/engine-qmd.ts | 2 +- .../src/host/backend-config.test.ts | 6 +- .../src/host/backend-config.ts | 32 +- .../memory-host-sdk/src/host/config-utils.ts | 425 ++++++++++++++++++ .../src/host/embeddings-remote-client.ts | 2 +- packages/memory-host-sdk/src/host/internal.ts | 17 +- .../src/host/openclaw-runtime.ts | 128 ++++++ .../memory-host-sdk/src/host/read-file.ts | 6 +- .../memory-host-sdk/src/host/remote-http.ts | 8 +- .../memory-host-sdk/src/host/session-files.ts | 33 +- .../src/host/test-helpers/ssrf.ts | 15 - packages/memory-host-sdk/src/runtime-cli.ts | 18 +- packages/memory-host-sdk/src/runtime-core.ts | 40 +- 15 files changed, 658 insertions(+), 118 deletions(-) create mode 100644 packages/memory-host-sdk/src/host/config-utils.ts create mode 100644 packages/memory-host-sdk/src/host/openclaw-runtime.ts delete mode 100644 packages/memory-host-sdk/src/host/test-helpers/ssrf.ts diff --git a/packages/memory-host-sdk/src/engine-embeddings.ts b/packages/memory-host-sdk/src/engine-embeddings.ts index 0e003f3b725..33a7216a8f3 100644 --- a/packages/memory-host-sdk/src/engine-embeddings.ts +++ b/packages/memory-host-sdk/src/engine-embeddings.ts @@ -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"; diff --git a/packages/memory-host-sdk/src/engine-foundation.ts b/packages/memory-host-sdk/src/engine-foundation.ts index d05dd4c2625..d7a84798299 100644 --- a/packages/memory-host-sdk/src/engine-foundation.ts +++ b/packages/memory-host-sdk/src/engine-foundation.ts @@ -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"; diff --git a/packages/memory-host-sdk/src/engine-qmd.ts b/packages/memory-host-sdk/src/engine-qmd.ts index 3fe6db650d7..bdd495fcb53 100644 --- a/packages/memory-host-sdk/src/engine-qmd.ts +++ b/packages/memory-host-sdk/src/engine-qmd.ts @@ -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, diff --git a/packages/memory-host-sdk/src/host/backend-config.test.ts b/packages/memory-host-sdk/src/host/backend-config.test.ts index 887d0ee3217..3ffbb7f073b 100644 --- a/packages/memory-host-sdk/src/host/backend-config.test.ts +++ b/packages/memory-host-sdk/src/host/backend-config.test.ts @@ -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; @@ -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", () => { diff --git a/packages/memory-host-sdk/src/host/backend-config.ts b/packages/memory-host-sdk/src/host/backend-config.ts index 905e45cefc8..4a81efa08b0 100644 --- a/packages/memory-host-sdk/src/host/backend-config.ts +++ b/packages/memory-host-sdk/src/host/backend-config.ts @@ -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 = { diff --git a/packages/memory-host-sdk/src/host/config-utils.ts b/packages/memory-host-sdk/src/host/config-utils.ts new file mode 100644 index 00000000000..25d0824acef --- /dev/null +++ b/packages/memory-host-sdk/src/host/config-utils.ts @@ -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; + } + >; + }; +}; + +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 = { + 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; +} diff --git a/packages/memory-host-sdk/src/host/embeddings-remote-client.ts b/packages/memory-host-sdk/src/host/embeddings-remote-client.ts index 4968b8ddd07..551f3d27011 100644 --- a/packages/memory-host-sdk/src/host/embeddings-remote-client.ts +++ b/packages/memory-host-sdk/src/host/embeddings-remote-client.ts @@ -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"; diff --git a/packages/memory-host-sdk/src/host/internal.ts b/packages/memory-host-sdk/src/host/internal.ts index 1a97e100ffd..078d3b68254 100644 --- a/packages/memory-host-sdk/src/host/internal.ts +++ b/packages/memory-host-sdk/src/host/internal.ts @@ -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"; diff --git a/packages/memory-host-sdk/src/host/openclaw-runtime.ts b/packages/memory-host-sdk/src/host/openclaw-runtime.ts new file mode 100644 index 00000000000..489a299dc02 --- /dev/null +++ b/packages/memory-host-sdk/src/host/openclaw-runtime.ts @@ -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"; diff --git a/packages/memory-host-sdk/src/host/read-file.ts b/packages/memory-host-sdk/src/host/read-file.ts index 5686ccb6dda..4cb60a06538 100644 --- a/packages/memory-host-sdk/src/host/read-file.ts +++ b/packages/memory-host-sdk/src/host/read-file.ts @@ -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 { diff --git a/packages/memory-host-sdk/src/host/remote-http.ts b/packages/memory-host-sdk/src/host/remote-http.ts index c89ef2ffd05..21d1307540a 100644 --- a/packages/memory-host-sdk/src/host/remote-http.ts +++ b/packages/memory-host-sdk/src/host/remote-http.ts @@ -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"; diff --git a/packages/memory-host-sdk/src/host/session-files.ts b/packages/memory-host-sdk/src/host/session-files.ts index 02a483a4f67..e2eab1b7b0d 100644 --- a/packages/memory-host-sdk/src/host/session-files.ts +++ b/packages/memory-host-sdk/src/host/session-files.ts @@ -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 { - const { createSubsystemLogger } = await import("../../../../src/logging/subsystem.js"); createSubsystemLogger("memory").debug(`Failed reading session file ${absPath}: ${String(err)}`); } diff --git a/packages/memory-host-sdk/src/host/test-helpers/ssrf.ts b/packages/memory-host-sdk/src/host/test-helpers/ssrf.ts deleted file mode 100644 index 8b2e160f439..00000000000 --- a/packages/memory-host-sdk/src/host/test-helpers/ssrf.ts +++ /dev/null @@ -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 }), - }; - }); -} diff --git a/packages/memory-host-sdk/src/runtime-cli.ts b/packages/memory-host-sdk/src/runtime-cli.ts index 9a1b858cd0d..edfd0f4e1a9 100644 --- a/packages/memory-host-sdk/src/runtime-cli.ts +++ b/packages/memory-host-sdk/src/runtime-cli.ts @@ -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"; diff --git a/packages/memory-host-sdk/src/runtime-core.ts b/packages/memory-host-sdk/src/runtime-core.ts index 9b14431b031..29abed737da 100644 --- a/packages/memory-host-sdk/src/runtime-core.ts +++ b/packages/memory-host-sdk/src/runtime-core.ts @@ -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";