/** * OpenClaw plugin API types + DKG integration types. * * Upstream memory types in the bottom half of this file are vendored from * the OpenClaw source tree — `src/plugins/memory-state.ts` and * `packages/memory-host-sdk/src/host/types.ts` at commit * `dae060390b1d17aa949c4a1a0c12fbc3b1eedb79`. `@openclaw/plugin-sdk` does * not publicly export memory types, so they are mirrored here instead of * imported. Resync on OpenClaw minor-version bumps. */ export interface OpenClawPluginApi { config: Record; pluginConfig?: Record; registrationMode?: 'full' | 'setup-only' | 'setup-runtime' | 'cli-metadata'; registerTool(tool: OpenClawTool): void; registerHook(event: string, handler: (...args: any[]) => Promise, opts?: { name: string; }): void; /** * Register a typed-hook handler. The OpenClaw upstream * (`openclaw/src/plugins/registry.ts:registerTypedHook` → * `registry.typedHooks` → `hooks.ts:runModifyingHook`) sequentially * awaits handlers and reads their return value: modifying hooks like * `before_prompt_build` honor `{ appendSystemContext, ... }`, and * fire-and-forget hooks like `agent_end` ignore the return. * * The signature accepts `unknown | Promise` to match this * contract — the previous `=> void` was an under-specification that * masked the dependency on return-value propagation (R16.1). */ on(event: string, handler: (...args: any[]) => unknown | Promise): void; logger: { info?(...args: any[]): void; warn?(...args: any[]): void; debug?(...args: any[]): void; }; /** Register a bidirectional channel plugin. */ registerChannel?(opts: { plugin: OpenClawChannelAdapter; dock?: unknown; }): void; /** Register an HTTP route on the gateway. */ registerHttpRoute?(route: { method: string; path: string; auth?: string; handler: (req: any, res: any) => Promise | void; }): void; /** Register a long-running background service. */ registerService?(service: { name: string; start: () => Promise; stop: () => Promise; }): void; /** Route an inbound channel message through the session system. */ routeInboundMessage?(message: ChannelInboundMessage): Promise; /** * Register the memory-slot capability. Optional on the type so adapters * can feature-detect older gateway versions at runtime. */ registerMemoryCapability?(capability: MemoryPluginCapability): void; /** Workspace directory path (set by gateway). */ workspaceDir?: string; } export interface OpenClawTool { name: string; description: string; parameters: JSONSchemaObject; execute(toolCallId: string, params: Record): Promise; } export interface JSONSchemaObject { type: 'object'; /** * Property `type` matches the JSON Schema Draft 7 shape: either a * single primitive type name or an array of alternative type names * (e.g. `['number', 'string']` for a field that accepts both a * numeric literal and a stringified-numeric value). Codex Bug B35 * required the array form on the compat `dkg_memory_search` tool * so legacy callers passing stringified numerics can pass * schema-validation on hosts that actually enforce the schema. */ properties: Record; required?: string[]; } export interface OpenClawToolResult { content: Array<{ type: 'text'; text: string; }>; details?: unknown; } export interface OpenClawGatewayLifecycleContext { accountId?: string; account?: Record; cfg?: any; runtime?: any; abortSignal?: AbortSignal; getStatus?: () => Record; setStatus?: (status: Record) => void; } export interface OpenClawGatewayLifecycleAdapter { startAccount(ctx: OpenClawGatewayLifecycleContext): Promise; stopAccount(ctx: OpenClawGatewayLifecycleContext): Promise; } /** Inbound message from an external channel into OpenClaw. */ export interface ChannelInboundMessage { /** Channel name (e.g. "dkg-ui"). */ channelName: string; /** Sender identifier within the channel. */ senderId: string; /** Whether the sender is the agent owner (bypasses permission checks). */ senderIsOwner?: boolean; /** Message text. */ text: string; /** Correlation ID for request-reply tracking. */ correlationId?: string; /** Optional transcript session key when the caller already knows the resolved route. */ sessionKey?: string; /** OpenClaw context-style alias for `sessionKey`. */ SessionKey?: string; } /** Outbound reply from OpenClaw to an external channel. */ export interface ChannelOutboundReply { /** Correlation ID matching the inbound message. */ correlationId?: string; /** Reply text. */ text: string; /** Session-internal turn ID. */ turnId?: string; /** OpenClaw transcript session key resolved for this channel dispatch. */ sessionKey?: string; /** OpenClaw context-style alias for `sessionKey` returned by legacy routes. */ SessionKey?: string; /** Tool calls made during this turn. */ toolCalls?: Array<{ name: string; args: Record; result: unknown; }>; } /** * Channel adapter interface. * * Mirrors OpenClaw's ChannelPlugin contract — channels are bidirectional * message transports. The adapter registers one of these per channel. */ export interface OpenClawChannelAdapter { /** Channel identifier (must be unique across registered channels). */ id: string; /** Display name for the channel. */ name: string; /** Channel metadata. */ meta?: { displayName?: string; [key: string]: unknown; }; /** Channel capabilities. */ capabilities?: Record; /** Channel account/config adapter expected by current OpenClaw runtimes. */ config?: { listAccountIds(cfg: any): string[]; resolveAccount(cfg: any, accountId?: string): Record; defaultAccountId?(cfg: any): string; isEnabled?(account: Record, cfg: any): boolean; isConfigured?(account: Record, cfg: any): boolean | Promise; describeAccount?(account: Record, cfg: any): Record; disabledReason?(account: Record, cfg: any): string; unconfiguredReason?(account: Record, cfg: any): string; }; /** Called when the gateway starts. Set up transport here. */ start?(): Promise; /** Called when the gateway stops. Tear down transport. */ stop?(): Promise; /** Per-account lifecycle hooks consumed by current OpenClaw gateway monitors. */ gateway?: OpenClawGatewayLifecycleAdapter; /** * Called by the gateway when the agent produces a reply for this channel. * The adapter should deliver it via its transport. */ onOutbound?(reply: ChannelOutboundReply): Promise; } /** Discriminator for where a `MemorySearchResult` originated. */ export type MemorySource = 'memory' | 'sessions'; /** * Narrower adapter-local discriminant identifying which of the six * retrieval layers produced a hit: * - `agent-context-wm`: agent-context graph, working-memory view * - `agent-context-swm`: agent-context graph, shared-working-memory view * - `agent-context-vm`: agent-context graph, verifiable-memory view * - `project-wm`: user-selected project graph, working-memory view * - `project-swm`: user-selected project graph, shared-working-memory view * - `project-vm`: user-selected project graph, verifiable-memory view * * Note the rename from the earlier `chat-turns-wm` layer: the slot-backed * fan-out no longer pins on the `chat-turns` assertion, so the layer * name describes the context graph + view pair rather than a specific * assertion. Chat-turn content still lands in `agent-context-wm` because * `ChatMemoryManager.storeChatExchange` writes into the `chat-turns` * assertion under the agent's WM namespace — it's included in the WM * view's graph-prefix scan along with any other literals the agent has * written into the agent-context graph. * * Kept as an optional, non-upstream field on `MemorySearchResult` so the * closed upstream `source` union (`memory | sessions`) stays intact while * adapter internals (trust-weighted ranking, dedup across layers, test * assertions) can distinguish VM from SWM from WM without re-parsing `path`. */ export type MemoryLayer = 'agent-context-wm' | 'agent-context-swm' | 'agent-context-vm' | 'project-wm' | 'project-swm' | 'project-vm'; /** * Upstream search result shape. `path` is an opaque identifier (file path or * graph-backed synthetic URI). `startLine`/`endLine` are `1` for graph-backed * backends that have no natural line concept. `snippet` is the matched text, * typically truncated. `source` distinguishes per-project memory hits from * session-history hits. `layer` is an adapter-local, non-upstream discriminant * that pins the specific retrieval layer (chat-turns WM / project WM / SWM / * VM) — populated by `DkgMemorySearchManager` for trust-weighted ranking and * dedup without widening the closed upstream `source` union. `citation` is a * free-form string per upstream `packages/memory-host-sdk/src/host/types.ts:3-10` * — do not promote to a structured object without checking upstream first. */ export interface MemorySearchResult { path: string; startLine: number; endLine: number; score: number; snippet: string; source: MemorySource; layer?: MemoryLayer; citation?: string; } /** Search opts the memory slot passes through. */ export interface MemorySearchOptions { maxResults?: number; minScore?: number; sessionKey?: string; /** * T74 — Observability tag. Identifies the caller in the * `[dkg-memory] search fired (caller=…)` log line so operators can * distinguish W3 auto-recall hook firings from explicit `memory_search` * tool calls (and other future callers). Free-form short string; * defaults to `'unknown'` when not supplied. */ caller?: string; } /** * Upstream `readFile` request shape. Non-nullable return per upstream contract * (`readFile` cannot signal "not found" structurally — callers must inspect * `text`). V10 memory is graph-native, so the DKG provider returns an empty * shell unconditionally. */ export interface MemoryReadFileRequest { relPath: string; from?: number; lines?: number; } export interface MemoryReadFileResult { text: string; path: string; } /** * Synchronous status block returned by `MemorySearchManager.status()`. * `backend` is a closed union (`"builtin" | "qmd"`) in the upstream contract; * the DKG provider reports `"builtin"` as a pragmatic lie because no * `"custom"` option exists upstream. Logged as an upstream gap. */ export interface MemoryProviderStatus { backend: 'builtin' | 'qmd'; provider: string; model?: string; vector?: { enabled: boolean; available: boolean; }; fts?: { enabled: boolean; available: boolean; }; cache?: { enabled: boolean; }; batch?: { enabled: boolean; }; sources?: readonly MemorySource[]; workspaceDir?: string; custom?: Record; [key: string]: unknown; } /** * Upstream probe result shape. The only documented fields are `ok` and an * optional `error` string. No structured `notAvailable` field — logged as an * upstream gap. */ export interface MemoryEmbeddingProbeResult { ok: boolean; error?: string; } /** * Upstream `MemorySearchManager` contract — what `registerMemoryCapability` * expects from a plugin's memory runtime. The DKG provider implements this * against real V10 primitives (assertion-scoped WM SPARQL queries) rather * than the file-backed pattern `memory-core` / `memory-lancedb` use. * * NOTE on the probe return-type asymmetry: upstream declares * `probeEmbeddingAvailability(): Promise` but * `probeVectorAvailability(): Promise`. Do not "normalize" the * shapes — a bare boolean `false` from `probeVectorAvailability` is * mandatory because upstream evaluates the result with `if (available) …` * and a `{ok:false,error:…}` object is truthy, which would silently claim * a vector backend is available when it is not. FAIL #2 from * openclaw-runtime's audit catches exactly this. */ export interface MemorySearchManager { search(query: string, options?: MemorySearchOptions): Promise; readFile(request: MemoryReadFileRequest): Promise; status(): MemoryProviderStatus; probeEmbeddingAvailability(): Promise; probeVectorAvailability(): Promise; sync?(): Promise; close?(): Promise; } /** Factory call parameters — upstream passes cfg + agent + session. */ export interface MemoryRuntimeRequest { cfg?: Record; agentId?: string; sessionKey?: string; purpose?: string; } /** * Factory result — a search manager plus optional non-fatal error. * * `manager` is nullable: when the plugin cannot build a manager for a * given request (daemon down, session resolution failure, transient * dependency unavailable), upstream accepts `{ manager: null, error: * "..." }` and routes the caller through a fallback instead of crashing. * FAIL #3 from openclaw-runtime's audit: the type was previously * non-nullable, which would have forced a construction throw in those * paths rather than the documented graceful-decline contract. * * Upstream internally wraps `MemorySearchManager` as * `RegisteredMemorySearchManager` — openclaw-runtime hasn't traced the * wrapper fields yet, but the runtime contract accepts a bare * `MemorySearchManager` because upstream's loader wraps it. Resync this * type if a future audit finds the bare shape no longer satisfies the * registration call. */ export interface MemoryRuntimeResult { manager: MemorySearchManager | null; error?: string; } /** * Upstream `MemoryPluginRuntime` shape. `getMemorySearchManager` is the * per-(cfg, agentId, sessionKey) factory; a slot-filling plugin is expected * to return a manager instance or an error-wrapped null manager. */ export interface MemoryPluginRuntime { getMemorySearchManager(request: MemoryRuntimeRequest): Promise; resolveMemoryBackendConfig?(request: { cfg?: Record; agentId?: string; }): Record; closeAllMemorySearchManagers?(): Promise; } /** * Prompt-section builder — emits the memory region of the system prompt. * Called synchronously during prompt assembly. */ export type MemoryPromptSectionBuilder = (params: { availableTools: Set; citationsMode?: string; }) => string[]; /** * Full capability object handed to `api.registerMemoryCapability`. All four * fields optional — the minimum is `{ runtime }`, which is what the DKG * adapter registers. */ export interface MemoryPluginCapability { runtime?: MemoryPluginRuntime; promptBuilder?: MemoryPromptSectionBuilder; flushPlanResolver?: (params: { cfg?: Record; nowMs?: number; }) => unknown; publicArtifacts?: { listArtifacts: (...args: any[]) => Promise; }; } export interface DkgOpenClawConfig { /** DKG daemon HTTP URL (default: "http://127.0.0.1:9200"). */ daemonUrl?: string; /** * Explicit DKG home directory for the daemon the adapter targets via * `daemonUrl`. Used only for loading the node-level `/auth.token` * when the daemon runs with a custom home outside the auto-detected * `~/.dkg` / `~/.dkg-dev` homes. * * This is not used for agent identity probing; the adapter resolves the * default agent through `/api/agent/identity` with the node-level Bearer * token. */ dkgHome?: string; /** * Explicit adapter state directory for file-backed runtime state such as * ChatTurnWriter watermarks. Setup writes a workspace-scoped default when * it can discover the workspace. Runtime honors explicit user config after * gateway/env state sources and before the current `api.workspaceDir`; * setup-owned defaults are only a fallback for older gateways. */ stateDir?: string; /** * Setup-owned provenance marker for `stateDir`. Setup writes this as * `"setup-default"` only when it writes its workspace-scoped default; * absent means a matching `stateDir` must be treated as user-owned. */ stateDirSource?: 'setup-default'; /** * Setup-owned metadata for the workspace that installed this adapter entry. * Runtime combines it with `stateDirSource` to distinguish setup-written * stateDir defaults from explicit user stateDir overrides. */ installedWorkspace?: string; /** DKG memory integration config. */ memory?: { enabled?: boolean; /** * @deprecated v0/Phase-0 file-watcher config. Retained for backward * compatibility with existing workspace configs; ignored by v1. */ memoryDir?: string; /** * @deprecated v0/Phase-0 file-watcher config. Retained for backward * compatibility with existing workspace configs; ignored by v1. */ watchDebounceMs?: number; }; /** DKG UI channel bridge config. */ channel?: { enabled?: boolean; /** * Port for the channel bridge HTTP server. * Only used if `api.registerHttpRoute` is not available. * Default: 9201. */ port?: number; }; } //# sourceMappingURL=types.d.ts.map