import { TSchema } from '@sinclair/typebox'; interface RemnicPiConfig { remnicDaemonUrl: string; authToken?: string; namespace?: string; recallMode: "auto" | "minimal" | "full" | "graph_mode" | "no_recall"; recallTopK: number; recallBudgetChars: number; recallEnabled: boolean; observeEnabled: boolean; observeSkipExtraction: boolean; compactionEnabled: boolean; mcpToolsEnabled: boolean; statusEnabled: boolean; requestTimeoutMs: number; startupRequestTimeoutMs: number; /** * Per-turn request budget for observe/recall. MUST stay below the host's * in-handler kill budget (Pi/omp kills handlers at 30 s). Defaults to 20 s, * capped at 25 s so a misconfiguration can never produce a structurally * unsatisfiable timeout (issue #1626). */ turnRequestTimeoutMs: number; /** * Soft cap on a single observe POST body in bytes. The client chunks observe * batches to stay under this; individual oversized messages are truncated * with a marker. Defaults to 100 KiB, safely under the daemon's default * 128 KiB `maxBodyBytes` (issue #1600). */ observeMaxBytes: number; /** * Maximum retry attempts for observe/recall on transient connection-level * failures (socket close, ECONNRESET, EPIPE). Observe is dedupe-safe so * retrying is harmless (issue #1602). */ observeMaxRetries: number; /** * Cooldown base for the daemon-reachability circuit breaker. When observe/ * recall fails on a timeout or connection error, subsequent turns skip fast * for an exponentially growing window starting at this value (issue #1626). */ daemonCooldownMs: number; } interface LoadConfigOptions { configPath?: string; env?: NodeJS.ProcessEnv; } interface RecallResponse { context?: string; results?: Array<{ id?: string; content?: string; score?: number; category?: string; }>; count?: number; } interface ObserveMessagePart { ordinal?: number; kind: "text" | "tool_call" | "tool_result" | "patch" | "file_read" | "file_write" | "step_start" | "step_finish" | "snapshot" | "retry"; payload: Record; toolName?: string | null; filePath?: string | null; createdAt?: string | null; } interface ObserveMessage { role: "user" | "assistant"; content: string; sourceFormat?: "pi"; rawContent?: unknown; parts?: ObserveMessagePart[]; } interface McpTool { name: string; description?: string; inputSchema?: Record; } interface RequestOptions { timeoutMs?: number; /** Transient-retry budget for connection-level failures (socket close, ECONNRESET). */ maxRetries?: number; } interface ObserveOptions extends RequestOptions { /** Soft cap on a single observe POST body in bytes; oversize batches are chunked. */ maxBytes?: number; } /** * Result of a startup namespace-writability preflight (issue #1888 part 3). * `not_writable` is a DEFINITIVE misconfiguration answer from the daemon (the * configured namespace resolves as non-writable for this principal), which the * client surfaces loudly. `indeterminate` means the daemon could not be reached * or answered unexpectedly (timeout, network, auth, 5xx) — the client must NOT * cry wolf about the namespace on those, since the answer is unknown. */ type NamespacePreflightResult = { readonly status: "writable"; readonly namespace: string; } | { readonly status: "not_writable"; readonly reason: "not_writable" | "unsupported"; readonly namespace: string; } | { readonly status: "indeterminate"; readonly detail: string; }; declare class RemnicClient { private readonly config; private requestId; private unreachableUntil; private consecutiveFailures; constructor(config: RemnicPiConfig); /** True when the daemon is not in a known-unreachable cooldown. */ isReachable(): boolean; /** Clear the circuit breaker — call after any successful daemon interaction. */ markReachable(): void; /** * Enter (or extend) an unreachable cooldown. The cooldown grows exponentially * with consecutive failures (base, 2×base, 4×base, …) capped at 60 s, so a * flapping daemon is retried gently while a hard-down host backs off hard. */ markUnreachable(baseCooldownMs: number): void; health(options?: RequestOptions): Promise>; /** * Startup namespace-writability preflight (issue #1888 part 3). Asks the * daemon — read-only, no write, no side effect — whether the configured * namespace resolves as writable for this client's (token-resolved) * principal. A `not_writable` answer is definitive and surfaced loudly; any * transport failure returns `indeterminate` so a flaky daemon never triggers * a false namespace-misconfig alarm. */ preflightNamespace(sessionKey: string | undefined, options?: RequestOptions): Promise; recall(query: string, sessionKey: string, cwd: string, options?: RequestOptions): Promise; recallExplain(sessionKey: string, options?: RequestOptions): Promise>; observe(sessionKey: string, cwd: string, messages: ObserveMessage[], options?: ObserveOptions): Promise>; storeMemory(content: string, sessionKey: string, options?: RequestOptions): Promise>; lcmSearch(query: string, sessionKey: string, limit?: number): Promise>; lcmCompactionFlush(sessionKey: string): Promise>; lcmCompactionRecord(sessionKey: string, tokensBefore: number, tokensAfter: number): Promise>; contextCheckpoint(sessionKey: string, context: string): Promise>; mcpListTools(options?: RequestOptions): Promise; mcpTool(name: string, args: Record): Promise>; /** * Single HTTP attempt with the configured timeout. No retry — retry of * transient connection failures lives in {@link requestWithRetry}. */ private request; /** * Wrap {@link request} with a small bounded retry loop for transient * connection-level failures (socket close mid-request, ECONNRESET, EPIPE). * Timeouts (our own AbortController) and HTTP responses (4xx/5xx) are NOT * retried here — timeouts already burned the full budget, and HTTP errors * carry semantic meaning the caller must handle. Observe/recall are * dedupe-safe so retrying a transiently-failed POST is harmless (#1602). */ private requestWithRetry; private mcpRequest; } declare function textFromMessage(message: unknown): string; type PiApi = { on(event: string, handler: (event: any, ctx: any) => unknown | Promise): void; registerCommand(name: string, options: { description?: string; handler: (args: string, ctx: any) => Promise; }): void; registerTool(tool: Record): void; appendEntry(customType: string, data?: T): void; }; interface RemnicPiExtensionOptions extends LoadConfigOptions { config?: RemnicPiConfig; } declare function createRemnicPiExtension(options?: RemnicPiExtensionOptions): (pi: PiApi) => Promise; declare function remnicPiExtension(pi: PiApi): Promise; declare function toPiToolParametersSchema(inputSchema: unknown): TSchema; declare function stripSessionOwnedSchemaFields(inputSchema: unknown): Record; declare function stripSessionOwnedRuntimeFields(value: unknown): unknown; declare function observeMessages(ctx: any, client: RemnicClient, rawMessages: unknown[], observedHashes: Set, liveObservedReplayKeys?: Map): Promise; declare function buildCompactionSummary(preparation: any): string; declare function isDaemonUnreachableError(err: unknown): boolean; export { type RemnicPiExtensionOptions, buildCompactionSummary, createRemnicPiExtension, remnicPiExtension as default, isDaemonUnreachableError, observeMessages, stripSessionOwnedRuntimeFields, stripSessionOwnedSchemaFields, textFromMessage, toPiToolParametersSchema };