TdaiCore is a host-neutral facade in src/core/tdai-core.ts that centralizes memory operations (recall, capture, search, pipeline) for both OpenClaw and Hermes/Gateway; it depends only on abstract HostAdapter and LLMRunner interfaces, never concrete host logic. HostAdapter abstracts three concerns — runtime context (user/session), LLM invocation, and logging — allowing TdaiCore to work across OpenClaw, Hermes, and Gateway without hardcoded host coupling.
TdaiCore in src/core/tdai-core.ts is the host-neutral facade for all TDAI memory capabilities — recall, capture, search, and pipeline management — that both OpenClaw and Hermes/Gateway call. It depends only on abstract HostAdapter and LLMRunner interfaces, never on a specific host.[1] src/core/types.ts is the host-neutral boundary layer that defines the abstract interfaces (HostAdapter, LLMRunner, LLMRunnerFactory, RuntimeContext, Logger) TdaiCore depends on; each host environment (OpenClaw, Hermes, Gateway) must provide its own implementations of HostAdapter and LLMRunnerFactory.[2]
HostAdapter answers three questions for TdaiCore: who is the current user/session (getRuntimeContext()), how to call an LLM (getLLMRunnerFactory()), and where to log (getLogger()). Its hostType is one of "openclaw", "hermes", or "standalone".[2] Two concrete HostAdapter implementations exist: OpenClawHostAdapter (wraps OpenClawPluginApi) and StandaloneHostAdapter (wraps Gateway HTTP request context for Hermes/Gateway hosts).[2] RuntimeContext.platform accepts the known literals "openclaw", "hermes", "cli", or "gateway", or any arbitrary string for future or custom hosts.[2] RuntimeContext.agentContext distinguishes between four execution modes: "primary", "subagent", "cron", and "flush".[2]
LLMRunner.run() returns an empty string (not null/undefined) when the LLM produces no output, and throws on timeout, network error, or unrecoverable LLM failure.[2] Two concrete LLMRunner implementations exist: OpenClawLLMRunner (wraps CleanContextRunner/runEmbeddedPiAgent for the OpenClaw host) and StandaloneLLMRunner (direct OpenAI-compatible HTTP calls for Gateway/Hermes hosts).[2] LLMRunParams.timeoutMs defaults to 120_000 ms (2 minutes) when omitted.[2] LLMRunnerCreateOptions.enableTools defaults to false (text-only output); set it to true for LLM tasks that require file tool access, such as L2 scene or L3 persona generation.[2] LLMRunnerCreateOptions.modelRef takes a "provider/model" string (e.g. "openai/gpt-4o") and takes precedence over the host's default model when specified.[2] When LLMRunParams.workspaceDir is omitted in a tool-enabled run (enableTools: true), a clean empty workspace is used instead of inheriting the session workspace.[2]
The Logger interface in src/core/types.ts requires info, warn, and error methods; debug is optional. Named variants such as StoreLogger and PluginLogger are type aliases of Logger, kept only for backward compatibility.[2]
TdaiCore.initialize() must be called once before any other methods. It initializes data directories, starts async store initialization, and — if extraction is enabled — creates and wires the pipeline manager.[1] Pipeline runners are wired after the store is ready, but initialize() also wires them in degraded mode (JSONL fallback, no embedding) if store initialization fails, so the pipeline remains functional despite storage errors.[1] TdaiCore.handleBeforeRecall(userText, sessionKey) performs memory retrieval before an LLM turn, corresponding to OpenClaw's before_prompt_build hook or Hermes's prefetch() call.[1] TdaiCore.handleTurnCommitted(turn) handles conversation capture and pipeline trigger after a turn completes, corresponding to OpenClaw's agent_end hook or Hermes's sync_turn() call.[1] TdaiCore.searchMemories(params) searches L1 structured memories and maps to the tdai_memory_search tool; the limit parameter defaults to 5.[1] TdaiCoreOptions accepts an optional sessionFilter (defaults to a SessionFilter with an empty exclusion list) and an optional instanceId for metric reporting.[1]
CompletedTurn.originalUserMessageCount in src/core/types.ts records the number of messages in the session at before_prompt_build time; the L0 recorder uses this to locate the exact user message that was modified by prependContext injection.[2]
TdaiCore.destroy() drains all in-flight background tasks (tracked in bgTasks) with a 5-second hard timeout before closing vectorStore and embeddingService, preventing late updateL0Embedding calls from landing on an already-closed database connection.[1] After closing vector store and embedding service, TdaiCore.destroy() calls resetStores(this.dataDir), ensuring per-directory store caches are cleared on shutdown.[1] Each background task registered in bgTasks removes itself in its own finally handler, keeping the set bounded to the number of currently-running background tasks.[1]
Canonical usage of TdaiCore with the OpenClaw adapter:
// OpenClaw path (in-process)
const adapter = new OpenClawHostAdapter({ api, pluginDataDir, config });
const core = new TdaiCore({ hostAdapter: adapter, config: parsedCfg });
await core.initialize();
const recall = await core.handleBeforeRecall("user query", "session-1");
Sources