The plugin registers tools (tdai_memory_search, tdai_conversation_search) and hooks with OpenClaw via index.ts, which parses the full pluginConfig on each host startup and translates OpenClaw events into TdaiCore calls; core memory logic is confined to src/core/tdai-core.ts while OpenClaw-specific bindings live in OpenClawHostAdapter. The plugin caches user prompts and recall state by session during before_prompt_build, sweeping stale entries after 10 minutes or at 10k capacity, enabling post-agent metrics and preventing unbounded memory growth under high concurrency.
The plugin's entry point is ./index.ts, declared in the openclaw.extensions field of package.json — the file OpenClaw loads to register the plugin.[1] Activation is automatic on host startup via "onStartup": true in openclaw.plugin.json.[2] The plugin contracts two tools with the OpenClaw host: tdai_memory_search and tdai_conversation_search.[2] The openclaw.bundle.stageRuntimeDependencies flag is set to true, instructing the OpenClaw bundler to stage runtime dependencies alongside the plugin.[1]
To route context-offload requests to the plugin, the plugins.slots.contextEngine field must be set to "memory-tencentdb" in the OpenClaw configuration.[3] An OpenClaw plugin slot (plugins.slots.<name>) is a configuration mechanism that routes a named class of internal requests — such as context-offload — to a specified plugin instead of the default handler.
index.ts is a thin shell that registers tools and hooks with OpenClaw and translates OpenClaw events into TdaiCore calls; all core memory logic lives in src/core/tdai-core.ts.[4] OpenClaw calls register() multiple times (plugin scan → gateway start → per-channel bootstrap → config reload); each call receives the full pluginConfig from openclaw.json, and index.ts parses it fresh every time.[4] resetReporter() is called on every register() invocation so that reporter singleton state is flushed and config changes take effect on hot-reload.[4] index.ts supports a cli-metadata registration mode in which only CLI commands are registered and all runtime initialization is skipped; OpenClaw uses this mode to discover CLI subcommands without starting the full plugin.[4] CLI commands are exposed under the memory-tdai subcommand group (seed, query, stats), registered via registerMemoryTdaiCli.[4]
The hook-policy auto-patch (ensurePluginHookPolicy) is applied only when the OpenClaw host version is ≥ 2026.4.24; on older hosts, or when api.runtime.version is undefined, the patch is skipped to avoid silently mutating the user's openclaw.json.[4] If the embedding configuration is incomplete, index.ts logs a prominent error via api.logger.error with the prefix [EMBEDDING CONFIG ERROR] so the user can identify the misconfiguration at startup.[4] pluginStartTimestamp is set to Date.now() when the plugin registers in full mode and is passed to performAutoCapture as a fallback cursor to prevent the first agent_end from dumping the entire session history into L0 when no checkpoint exists yet.[4] sharedMemoryCleaner is a process-level singleton (LocalMemoryCleaner | undefined) used to prevent concurrent cleanup races when the plugin is started more than once in the same process.[4]
pendingOriginalPrompts caches the clean user prompt (before prependContext injection), cache creation time, and session message count at before_prompt_build time; the message count serves as a fallback slice offset when the timestamp cursor is unreliable.[4] pendingRecallCache caches L1 memories, L3 persona, recall strategy, and duration from before_prompt_build for retrieval at agent_end, enabling the agent_turn metric event; it is keyed by sessionKey.[4] pendingRecallEndTimestamps stores the recall-completion timestamp per session and is used in agent_end to estimate LLM reasoning time as agent_end_start − recall_end_ts; stale entries are swept alongside the prompt cache.[4] The prompt cache has a TTL of 10 minutes (PROMPT_CACHE_TTL_MS = 10 * 60 * 1000) and a hard size cap of 10,000 entries (PROMPT_CACHE_MAX_SIZE = 10_000) to prevent unbounded growth in high-concurrency scenarios.[4] sweepStaleCaches() sweeps both pendingOriginalPrompts and pendingRecallCache using the same TTL and hard-cap logic, evicting the oldest entries first when either map exceeds PROMPT_CACHE_MAX_SIZE.[4]
OpenClawHostAdapter in src/adapters/openclaw/host-adapter.ts translates the OpenClaw plugin API into the host-agnostic HostAdapter interface consumed by TdaiCore, confining all OpenClaw-specific dependencies to the adapter layer — see TdaiCore & host abstraction for the full host-adapter contract.[5] OpenClawHostAdapter carries readonly hostType = "openclaw" for runtime discrimination between adapter implementations.[5] OpenClawHostAdapter is constructed with { api, pluginDataDir, openclawConfig } and immediately creates an OpenClawLLMRunnerFactory from api.runtime.agent and api.logger.[5] OpenClawHostAdapter.getRuntimeContext() returns a RuntimeContext with userId: "default_user", empty sessionId/sessionKey, platform: "openclaw", workspaceDir: process.cwd(), and dataDir set to the resolved plugin data directory.[5] OpenClawHostAdapter.buildRuntimeContextForSession(sessionKey, sessionId?) merges per-hook session identifiers into the base RuntimeContext; if sessionId is omitted it defaults to an empty string.[5] OpenClawHostAdapter exposes getPluginApi(), getOpenClawConfig(), and getPluginDataDir() as escape hatches for legacy callers still accessing OpenClaw internals directly during migration.[5]
Sources