import { DkgDaemonClient } from './dkg-client.js'; import type { DkgOpenClawConfig, OpenClawPluginApi } from './types.js'; export declare const SHARE_NOT_PUBLISH_READY_WARNING: string; export declare const SHARE_SUBSET_NOT_PUBLISH_READY_WARNING: string; export declare const SHARE_INCOMPLETE_PROMOTE_WARNING: string; export declare const ATOMIC_SHARE_SIGNING_RECOVERY: string; /** * #1116: pick the not-publish-ready share warning from the share outcome. Returns * `undefined` when the share IS publish-ready (no warning). Precedence: * 1. sealed:true + publishReady:false → incomplete atomic delivery. * 2. sealed:false + legacy subset response → legacy read-only warning. * 3. sealed:false + atomic response → retry the complete share from WM. * Duplicated byte-identical on MCP (TS) + Hermes (Python). */ export declare function classifyShareWarning(outcome: { sealed?: boolean; publishReady?: boolean; isSubset: boolean; }): string | undefined; export declare class DkgNodePlugin { private config; private dkgHome; private client; private daemonClientGeneration; private publisher; private channelPlugin; private channelPluginConfigFingerprint; private channelPluginStopInFlight; private channelPluginStartQueued; private pendingChannelStartApi; private pendingChannelStartRegistrationMode; private pendingChannelStartFingerprint; private memoryPlugin; private hookSurface; private hookSurfaceApi; /** * T31 — Every HookSurface this plugin has built across its lifetime, * keyed only by insertion (Set, not WeakSet — explicit `stop()` is * the lifecycle anchor). Multi-phase init can hand the plugin a new * `api` registry on every inbound turn (`Re-registering plugin * surfaces … into new registry` in operator logs); the gateway then * dispatches `before_prompt_build` against whichever registry it * decides to use, which is not necessarily the latest one we hold. * Destroying the old surface on `apiChanged` (the previous behavior) * orphaned all handlers bound to the prior api — `fireCount=0` even * after multiple chats. Now we keep every surface live so whichever * api the gateway emits against has a bound handler; `stop()` * destroys them all together. */ private allHookSurfaces; /** * T34 — Install timestamp per surface. Used by `evictStaleHookSurfaces` * to bound the surface set's growth in long-lived processes that get * many `apiChanged` re-registrations: surfaces that have lived past a * grace window without firing are presumed orphaned by the gateway and * are destroyed + dropped. The latest surface (`this.hookSurface`) is * always preserved regardless of age. WeakMap so a destroyed surface * that escapes our explicit `delete` can still be GC'd via natural * reachability rules. */ private hookSurfaceInstalledAt; private typedHookFireSeq; private typedHookFireGeneration; /** * Grace window before a never-fired surface becomes evictable. Long * enough that a slow gateway dispatch path doesn't trigger spurious * eviction (typed `before_prompt_build` typically fires within seconds * of register; 5 minutes is a 60×+ safety margin), short enough that * a process re-registered hundreds of times still bounds the set's * memory footprint within an hour. */ private static readonly HOOK_SURFACE_STALE_THRESHOLD_MS; private promptSectionInstalled; private autoRecallInFlight; private chatTurnWriter; private chatTurnWriterStateDir; private chatTurnWriterStateDirSource; private chatTurnWriterStateLayout; private chatTurnWriterMigrationTarget; private warnedLegacyGameConfig; private localAgentIntegrationRetryTimer; /** * Retry attempt counter for `scheduleLocalAgentIntegrationRetry`. Used to * compute the exponential-backoff delay (`base * 2^attempt`, capped). * Reset to 0 on a successful `syncLocalAgentIntegrationState` call so * subsequent transient failures start from the base delay again. */ private localAgentIntegrationRetryAttempt; /** * Last reason string logged by the retry loop, used to dedup identical * warnings. One `warn` per distinct transition; repeats with the same * reason are logged at `debug` level instead (typically silent at * default log level). On success we emit one `info` line so operators * see the recovery. */ private lastLocalAgentIntegrationWarnReason; /** * Most recent error message captured by `loadStoredOpenClawIntegration`. * Written at the catch site, read by the retry dedup logic in * `syncLocalAgentIntegrationState`. Null when there is no pending * failure or after a successful load. */ private lastLocalAgentIntegrationLoadError; /** * Serial promise chain for local-agent integration HTTP mutations. * T364 round 13 — pre-fix `clearLocalAgentChannelIntegration` and * `syncLocalAgentIntegrationState` both guarded `channel.enabled` / * `daemonClientGeneration` only BEFORE their HTTP write, so a config * flip while either request was awaiting the daemon could leave the * daemon record in the wrong state: an older * `connectLocalAgentIntegration({ enabled: true })` resolving AFTER * a newer disable write would re-enable the integration even though * the channel was already off. Serializing every integration write * onto a single chain ensures only one HTTP mutation is in flight * at a time, and each operation re-checks generation + channel state * inside the serialized step (so the latest config wins regardless * of write ordering on the wire). */ private localAgentIntegrationWriteChain; private nodePeerId; /** * In-flight handle for the node peer ID probe, used to debounce * concurrent `ensureNodePeerId` calls so multiple resolver fires do not * stampede `/api/status`. Null when no probe is running. Codex Bug B9. */ private peerIdProbeInFlight; /** * Node agent address returned by `/api/agent/identity`. The daemon resolves * the adapter's node-level Bearer token to its default agent address, which * is the WM namespace used by default-agent assertion writes. */ private nodeAgentAddress; /** * Debounces concurrent `ensureNodeAgentAddress` calls so a burst of * resolver fires collapses to one daemon identity probe. Mirrors the * `peerIdProbeInFlight` pattern. Null when no probe is running. */ private agentAddressProbeInFlight; /** * Timer for the one-shot deferred retry after a failed initial probe * at register time. Belt-and-suspenders with `ensureNodePeerId`: the * lazy re-probe is the primary recovery path, but the deferred retry * covers the case where a deployment sits idle between register and * the first `dkg_memory_import` / slot search call. Codex Bug B9. */ private peerIdDeferredRetryTimer; /** Cached API handle used by `ensureNodePeerId` for logging. Set on register. */ private memoryResolverApi; /** * Resolver wired to the live channel-plugin session-state map + a cached * list of subscribed context graphs for the write-path clarification * response. The `getSession` lookup returns the UI-selected project CG * that `DkgChannelPlugin.dispatchViaPluginSdk` stashed on the resolved * `sessionKey` at the start of the current dispatch, or `undefined` for * non-UI turns / expired entries. `DkgMemorySearchManager.search` uses * the CG to fire a second `/api/query` against the project's `'memory'` * WM assertion; `dkg_memory_import` uses it as the fallback target CG * when the agent does not supply one explicitly. * * `getDefaultAgentAddress` fires a best-effort `ensureNodePeerId()` * when the cached peerId is still undefined. This keeps the B2 * retryable-clarification loop from soft-bricking permanently when the * register-time probe hit a cold daemon: the next turn's resolver call * self-heals the state. Codex Bug B9. */ private readonly memorySessionResolver; private availableContextGraphCache; /** * Wall-clock timestamp (ms epoch) of the last successful context-graph * cache populate. `0` means never populated. Compared against * `AVAILABLE_CONTEXT_GRAPH_CACHE_TTL_MS` in * `memorySessionResolver.listAvailableContextGraphs` to decide when to * fire a lazy refresh. Codex Bug B23. */ private availableContextGraphCacheAt; /** * In-flight handle for a `refreshMemoryResolverState` call. Concurrent * callers share this promise and await it instead of getting a stale * cache back. Codex Bug B49: the previous boolean guard returned * immediately on concurrent calls, so `refreshAvailableContextGraphs` * callers who expected a synchronous refresh could observe the * in-flight background refresh as "nothing to do" and see the stale * cache. Tracking the promise lets multiple callers share one refresh * while all observing the populated result. */ private refreshStateInFlight; constructor(config?: DkgOpenClawConfig); updateConfig(config?: DkgOpenClawConfig, options?: { partial?: boolean; }): void; private resolveDaemonClientOptions; private refreshDaemonClientForConfigUpdate; private resetDaemonScopedCachesForClientChange; /** * T364 round 13 — chain `work` onto the serial integration write * pipeline. Each operation runs after the previous one settles * (success or failure) and re-checks generation + channel state * inside its own body, so an older enable that was queued before a * disable cannot resolve after the disable's HTTP write and re-flip * the daemon record. */ private serializeLocalAgentIntegrationWrite; /** Whether the base runtime (daemon client, lifecycle hooks) has been initialized. */ private initialized; /** * Counter for registration-mode probe diagnostics. Incremented on each * register() call when DKG_PROBE_REGISTRATION_MODE=1 for sequencing logs. */ private probeRegisterCallCount; private probeApiInstalls; private probeInternalEventsInstalled; private probeCurrent; /** * Track hook fires per (event, mechanism) for the registration-mode probe. * Maps "event:via" to fire count. */ private probeHookFireCounts; /** * Register the DKG plugin with an OpenClaw plugin API instance. * On the first call: full init (lifecycle hooks, daemon handshake, integration modules). * On subsequent calls (gateway multi-phase init): re-registers tools into the new registry. */ register(api: OpenClawPluginApi): void; /** * Idempotent constructor for `ChatTurnWriter`. Resolves the per-workspace * `stateDir` (R16.2) and creates the writer if it doesn't exist yet. * Called from BOTH: * - First-time path inside the `runtimeEnabled` branch. * - Re-entry path before `installHooksIfNeeded`, to cover the * `setup-only → full` upgrade where the first call skipped * construction (R17.2 + qa-engineer follow-up). * * T18 — Re-resolves stateDir on every call. If the writer was * previously constructed with the home-dir fallback because no * better path was available (typically during early * `setup-runtime` when `runtime.state.resolveStateDir()` / * `api.workspaceDir` haven't been wired yet), and a better path * is now available, rebuild the writer at the new location and * best-effort migrate the watermark file. Without this, the * fallback path is permanent and a later workspace-scoped resolve * never takes effect. */ private ensureChatTurnWriter; /** * Install the 5 W4a/W4b hooks via HookSurface, supporting multi-phase * init. Rebuild the surface when: * (a) ANY prior install recorded a failure (`installedVia === 'none'`), * whether typed (api.on was undefined at first-call) or internal * (globalThis hook map not created yet); OR * (b) the gateway passed a new `api` instance on re-entry * (`openclaw-entry.mjs` reuses the singleton across new * registries, so typed hooks bound to the previous api object * would otherwise never fire against the new one). * Retrying on internal-hook failures too is load-bearing: if the first * register() call runs before the gateway sets up the internal-hook map, * cross-channel persistence (W4b) would otherwise stay dead forever * even after the map appears on a later re-entry. */ private installHooksIfNeeded; /** * T11 — Idempotent prompt-section install. Called from both the * first-time `register()` path AND the same-api retry branch so a * `setup-runtime → full` upgrade (where the first call had * `isFullMode === false` and skipped the install) installs the * "Prefer memory_search" guidance once the api flips to full mode. * The `promptSectionInstalled` flag prevents a double-install on * subsequent same-api re-registers. */ private tryInstallPromptSection; /** * Internal-hook handler factories. Both the initial install and the * same-api retry path use these so the mode-independent re-assert * wrapper is consistent across paths. A late retry that recovered turn * persistence WITHOUT the wrapper would silently lose slot-ownership * defense-in-depth on every internal-hook fire. */ private makeMessageReceivedHandler; private makeMessageSentHandler; /** * True only when a retained surface still owns the adapter wrapper for * this internal event in the current global hook map. */ private internalHookEventIsLive; private recordTypedHookFire; private observedTypedHookSinceInstall; private observedTypedOptions; private observedTypedHandler; /** * T34 — Bounded-retention eviction for `allHookSurfaces`. Multi-phase * init can hand the plugin a fresh `api` on every inbound turn, so * over hours a long-lived process accumulates surface objects that the * gateway will never dispatch against again. Eviction policy: * * * NEVER evict the latest surface (`this.hookSurface`) — even if * idle for now, it's the most recent target the gateway might * have switched to and we don't want to disable freshly-installed * handlers. * * For older surfaces: evict if `installedAt` is more than * `HOOK_SURFACE_STALE_THRESHOLD_MS` ago AND aggregate `fireCount` * across all events on that surface is 0. A surface that has * fired even once might still be a live dispatch target (the * gateway can keep dispatching against an api long after we got * a new one, depending on internal routing); preserving it costs * a few closures and is the safer default. * * The grace window is wide enough that legitimate slow first-fires * (e.g., a setup-runtime → full transition where typed hooks only * dispatch after the first non-trivial inbound turn) won't trigger * spurious eviction. Surfaces that NEVER fire over multiple minutes * are presumed orphaned by the gateway and reclaimed. */ private evictStaleHookSurfaces; /** * Register DKG integration modules: channel and memory. * Each module is optional — enabled via config flags. */ private startChannelPlugin; private stopChannelPluginForReconfigure; private queueChannelPluginStartAfterStop; private registerIntegrationModules; private registerLocalAgentIntegration; private clearLocalAgentChannelIntegration; private clearLocalAgentIntegrationRetry; private scheduleLocalAgentIntegrationRetry; private warnOnLegacyGameConfig; private syncLocalAgentIntegrationState; private loadStoredOpenClawIntegration; private wasOpenClawExplicitlyUserDisconnected; private buildOpenClawTransport; private resolveGatewayBaseUrl; private hasGatewayConfig; private formatGatewayBaseUrl; private normalizePort; private normalizeGatewayHost; stop(): Promise; getClient(): DkgDaemonClient; /** * Populate the memory resolver's node-peer-ID + subscribed context-graph * cache from the daemon. Non-blocking; failures warn and leave caches * empty so the resolver falls back to single-graph reads and an empty * needs_clarification list on writes. * * When the peer-ID probe leaves `nodePeerId` undefined (daemon startup * race, `/api/status` 5xx, network flap), schedules a deferred one-shot * retry so a gateway that sits idle until the first `dkg_memory_import` * call still recovers. The primary recovery path is the on-demand * `ensureNodePeerId` fired by the resolver. Codex Bug B9. */ private refreshMemoryResolverState; /** * Single-shot `/api/status` call that updates `nodePeerId` on success * and logs on failure. Pulled out of `refreshMemoryResolverState` so it * can be reused by `ensureNodePeerId` without dragging the CG cache * refresh along. Does NOT debounce — callers are responsible for * preventing concurrent calls (see `ensureNodePeerId`'s in-flight * promise guard). */ private probeNodePeerIdOnce; /** * On-demand best-effort re-probe of the node peer ID, fired by the * memory resolver when a caller asks for the default agent address and * the cached peerId is still undefined. Debounced via * `peerIdProbeInFlight`: concurrent callers share the same promise so * a burst of resolver fires collapses to one `/api/status` call. * * Returns immediately without firing if: * - `nodePeerId` is already populated (no-op), * - the memory resolver API was never cached (register() hasn't run or * memory module was disabled — nothing to probe against), * - a probe is already in flight. * * Codex Bug B9 — fixes the "register-time one-shot probe fails → * permanent soft-brick" case where every subsequent turn got B2's * retryable clarification with no actual retry path. */ private ensureNodePeerId; /** * Single-shot HTTP probe of the daemon's default agent identity. * * Uses the DkgDaemonClient constructor-loaded node-level Bearer token. * The daemon maps that token to its default agent address and returns the * canonical WM namespace identifier used for default-agent writes. * * Does NOT debounce; caller (`ensureNodeAgentAddress`) handles concurrent * call dedup via the in-flight promise guard. */ private probeNodeAgentAddressOnce; /** * Resolve the WM identifier for default-agent self-reads. * Mirrors the daemon writer-side priority: default agent address when the * identity endpoint has resolved it, otherwise the node peerId fallback. */ private resolveDefaultAgentAddress; private ensureNodeAgentAddress; /** * Schedules a one-shot deferred retry of the peer-ID probe. Cheap * belt-and-suspenders for the case where a gateway registers against a * daemon that is still booting and then sits idle for seconds before * the first resolver call would fire `ensureNodePeerId` lazily. No-op * if a retry is already scheduled or if `nodePeerId` has already been * populated by a concurrent lazy probe. Codex Bug B9. */ private schedulePeerIdDeferredRetry; private json; private error; private daemonError; /** * Register a context graph on-chain, tolerating the idempotent * "already registered" case (returns `undefined` so callers don't claim a * fresh registration). Any OTHER failure rethrows — the caller surfaces it as * a tool error and must NOT proceed. Used by handleAssertionPublish (CONTRACT * §G) for the register-then-publish path of the canonical per-KA publish tool. */ private registerContextGraphIfNeeded; /** * Exposes the tool-handler surface to the out-of-class `build*Tools` helpers * without leaking the handlers into the public `DkgNodePlugin` type. The * `handle*` methods stay `private`; this returns bound references that the * builders delegate to via the {@link DkgToolHost} structural contract. */ private toolHost; private tools; private handleStatus; /** * W3 — auto-recall handler for the `before_prompt_build` typed hook. * * Fires every turn. Takes the last user message from the run, calls * `DkgMemorySearchManager.searchNarrow` (WM-only, top 5, 250ms budget * via `Promise.race`), and returns an `appendSystemContext` block that * OpenClaw merges into the system prompt. * * Returns `undefined` (not `{}` or empty string) on any of: * - no memoryPlugin registered * - no user message in event.messages * - query shorter than 2 chars * - timeout exceeded * - zero hits returned * * Per plan v2.1 A2: empty-string returns break prompt caching. Every * early-return path must return `undefined`. */ private handleBeforePromptBuild; /** * Agent-callable recall button. Runs the full 6-layer SPARQL fan-out * (agent-context WM/SWM/VM + project CG WM/SWM/VM when resolved) via * `DkgMemorySearchManager`, returns trust-weighted ranked hits. */ private handleMemorySearch; private handleListContextGraphs; private handleQuery; private handleQueryCatalogList; private handleQueryCatalogRun; private handleQueryCatalogSave; private handleFindAgents; private handleSendMessage; private handleReadMessages; private handleInvokeSkill; private handleContextGraphCreate; private handleContextGraphInvite; private handleParticipantAdd; private handleParticipantRemove; private handleParticipantList; private handleJoinRequestList; private handleJoinRequestApprove; private handleJoinRequestReject; private handleSubscribe; private handleWalletBalances; /** * Env-gated diagnostic probe for registration-mode behavior. * Fires only when DKG_PROBE_REGISTRATION_MODE=1. Logs: * - Each register() call with mode, call count, and API surface availability * - Each hook dispatch with event name, registration mechanism, and mode */ private runRegistrationModeProbe; private handleAssertionCreate; private handleAssertionWrite; private handleAssertionFinalize; private handleAssertionPromote; private handleAssertionPublish; private handleAssertionPullFrom; private handleAssertionDiscard; private handleAssertionImportFile; private handleAssertionQuery; private handleImportArtifactResolve; private handleImportArtifactReadMarkdown; private handleSemanticEnrichmentWrite; private handleAssertionHistory; private handleSubGraphCreate; private handleSubGraphList; } //# sourceMappingURL=DkgNodePlugin.d.ts.map