@remnic/plugin-pi 9.7.14 → 9.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -86,6 +86,25 @@ interface ObserveOptions extends RequestOptions {
86
86
  /** Soft cap on a single observe POST body in bytes; oversize batches are chunked. */
87
87
  maxBytes?: number;
88
88
  }
89
+ /**
90
+ * Result of a startup namespace-writability preflight (issue #1888 part 3).
91
+ * `not_writable` is a DEFINITIVE misconfiguration answer from the daemon (the
92
+ * configured namespace resolves as non-writable for this principal), which the
93
+ * client surfaces loudly. `indeterminate` means the daemon could not be reached
94
+ * or answered unexpectedly (timeout, network, auth, 5xx) — the client must NOT
95
+ * cry wolf about the namespace on those, since the answer is unknown.
96
+ */
97
+ type NamespacePreflightResult = {
98
+ readonly status: "writable";
99
+ readonly namespace: string;
100
+ } | {
101
+ readonly status: "not_writable";
102
+ readonly reason: "not_writable" | "unsupported";
103
+ readonly namespace: string;
104
+ } | {
105
+ readonly status: "indeterminate";
106
+ readonly detail: string;
107
+ };
89
108
  declare class RemnicClient {
90
109
  private readonly config;
91
110
  private requestId;
@@ -103,6 +122,15 @@ declare class RemnicClient {
103
122
  */
104
123
  markUnreachable(baseCooldownMs: number): void;
105
124
  health(options?: RequestOptions): Promise<Record<string, unknown>>;
125
+ /**
126
+ * Startup namespace-writability preflight (issue #1888 part 3). Asks the
127
+ * daemon — read-only, no write, no side effect — whether the configured
128
+ * namespace resolves as writable for this client's (token-resolved)
129
+ * principal. A `not_writable` answer is definitive and surfaced loudly; any
130
+ * transport failure returns `indeterminate` so a flaky daemon never triggers
131
+ * a false namespace-misconfig alarm.
132
+ */
133
+ preflightNamespace(sessionKey: string | undefined, options?: RequestOptions): Promise<NamespacePreflightResult>;
106
134
  recall(query: string, sessionKey: string, cwd: string, options?: RequestOptions): Promise<RecallResponse>;
107
135
  recallExplain(sessionKey: string, options?: RequestOptions): Promise<Record<string, unknown>>;
108
136
  observe(sessionKey: string, cwd: string, messages: ObserveMessage[], options?: ObserveOptions): Promise<Record<string, unknown>>;
package/dist/index.js CHANGED
@@ -239,6 +239,39 @@ var RemnicClient = class {
239
239
  async health(options = {}) {
240
240
  return this.request("GET", "/engram/v1/health", void 0, options);
241
241
  }
242
+ /**
243
+ * Startup namespace-writability preflight (issue #1888 part 3). Asks the
244
+ * daemon — read-only, no write, no side effect — whether the configured
245
+ * namespace resolves as writable for this client's (token-resolved)
246
+ * principal. A `not_writable` answer is definitive and surfaced loudly; any
247
+ * transport failure returns `indeterminate` so a flaky daemon never triggers
248
+ * a false namespace-misconfig alarm.
249
+ */
250
+ async preflightNamespace(sessionKey, options = {}) {
251
+ const params = new URLSearchParams();
252
+ if (this.config.namespace) params.set("namespace", this.config.namespace);
253
+ if (sessionKey) params.set("session", sessionKey);
254
+ params.set("op", this.config.observeEnabled ? "observe" : "memory_store");
255
+ const qs = params.toString();
256
+ const path2 = `/engram/v1/namespace/writable${qs ? `?${qs}` : ""}`;
257
+ try {
258
+ const payload = await this.request(
259
+ "GET",
260
+ path2,
261
+ void 0,
262
+ options
263
+ );
264
+ if (payload?.ok === true && typeof payload.namespace === "string" && payload.namespace.length > 0) {
265
+ return { status: "writable", namespace: payload.namespace };
266
+ }
267
+ if (payload?.ok === false && (payload.reason === "not_writable" || payload.reason === "unsupported") && typeof payload.namespace === "string" && payload.namespace.length > 0) {
268
+ return { status: "not_writable", reason: payload.reason, namespace: payload.namespace };
269
+ }
270
+ return { status: "indeterminate", detail: "unexpected preflight response shape" };
271
+ } catch (err) {
272
+ return { status: "indeterminate", detail: err instanceof Error ? err.message : String(err) };
273
+ }
274
+ }
242
275
  async recall(query, sessionKey, cwd, options = {}) {
243
276
  const merged = {
244
277
  ...options,
@@ -751,8 +784,14 @@ function createRemnicPiExtension(options = {}) {
751
784
  if (!session) return;
752
785
  const { state } = getSessionState(session.sessionKey, sessionStates);
753
786
  restoreObservedState(session, state.observedHashes);
754
- if (!config.statusEnabled) return;
755
- await setStatus(session, client, config);
787
+ const reachable = await probeDaemonHealth(client, config);
788
+ if (config.statusEnabled) {
789
+ session.setStatus(
790
+ "remnic",
791
+ reachable ? `Remnic ${config.namespace ? `(${config.namespace})` : "ready"}` : "Remnic offline"
792
+ );
793
+ }
794
+ await runNamespacePreflight(pi, session, client, config);
756
795
  });
757
796
  pi.on("context", async (event, ctx) => {
758
797
  const session = snapshotPiContext(ctx);
@@ -1180,14 +1219,43 @@ function persistObservedState(pi, observedHashes) {
1180
1219
  recordedAt: (/* @__PURE__ */ new Date()).toISOString()
1181
1220
  });
1182
1221
  }
1183
- async function setStatus(session, client, config) {
1222
+ async function probeDaemonHealth(client, config) {
1184
1223
  try {
1185
1224
  await client.health({ timeoutMs: config.startupRequestTimeoutMs });
1186
1225
  client.markReachable();
1187
- session.setStatus("remnic", `Remnic ${config.namespace ? `(${config.namespace})` : "ready"}`);
1226
+ return true;
1188
1227
  } catch (err) {
1189
1228
  if (isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);
1190
- session.setStatus("remnic", "Remnic offline");
1229
+ return false;
1230
+ }
1231
+ }
1232
+ async function runNamespacePreflight(pi, session, client, config) {
1233
+ if (!config.authToken || !client.isReachable()) return;
1234
+ const result = await client.preflightNamespace(session.sessionKey, {
1235
+ timeoutMs: config.startupRequestTimeoutMs
1236
+ });
1237
+ if (result.status === "not_writable") {
1238
+ const fix = result.reason === "unsupported" ? "The daemon has namespaces disabled, so only its default namespace is writable \u2014 set the client's namespace to the daemon's defaultNamespace (or omit it)." : "Fix the client's namespace config: it must match a namespacePolicies entry, or be the daemon's defaultNamespace/sharedNamespace.";
1239
+ const message = `Remnic: configured namespace "${result.namespace}" is NOT writable for this client's principal (${result.reason}). Every memory write will be rejected and dead-lettered (recoverable via \`remnic quarantine list\`), NOT stored. ${fix}`;
1240
+ session.notify(message, "error");
1241
+ pi.appendEntry(STATE_CUSTOM_TYPE, {
1242
+ level: "error",
1243
+ code: "NAMESPACE_NOT_WRITABLE",
1244
+ namespace: result.namespace,
1245
+ reason: result.reason,
1246
+ message,
1247
+ persistent: true,
1248
+ recordedAt: (/* @__PURE__ */ new Date()).toISOString()
1249
+ });
1250
+ return;
1251
+ }
1252
+ if (result.status === "writable") {
1253
+ pi.appendEntry(STATE_CUSTOM_TYPE, {
1254
+ level: "info",
1255
+ code: "NAMESPACE_OK",
1256
+ namespace: result.namespace,
1257
+ recordedAt: (/* @__PURE__ */ new Date()).toISOString()
1258
+ });
1191
1259
  }
1192
1260
  }
1193
1261
  function snapshotPiContext(ctx, options = {}) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/client.ts","../src/messages.ts"],"sourcesContent":["import { Type, type TSchema } from \"@sinclair/typebox\";\n\nimport { loadConfig, type LoadConfigOptions, type RemnicPiConfig } from \"./config.js\";\nimport { RemnicClient, isTransientNetworkError, type McpTool, type ObserveMessage } from \"./client.js\";\nimport {\n hashObservedMessage,\n latestUserRecallTarget,\n observedMessageDedupeKey,\n sessionKeyFromContext,\n summarizeMessages,\n textFromMessage,\n toObserveMessage,\n} from \"./messages.js\";\n\ntype PiApi = {\n on(event: string, handler: (event: any, ctx: any) => unknown | Promise<unknown>): void;\n registerCommand(name: string, options: { description?: string; handler: (args: string, ctx: any) => Promise<void> }): void;\n registerTool(tool: Record<string, unknown>): void;\n appendEntry<T = unknown>(customType: string, data?: T): void;\n};\n\nexport interface RemnicPiExtensionOptions extends LoadConfigOptions {\n config?: RemnicPiConfig;\n}\n\nconst STATE_CUSTOM_TYPE = \"remnic_state\";\nconst MAX_OBSERVED_HASHES = 2000;\nconst MAX_SESSION_STATES = 50;\nconst MAX_CONTEXT_CHARS = 12000;\nconst TRUNCATION_NOTICE = \"\\n\\n[Remnic context truncated]\";\nconst SESSION_OWNED_FIELDS = new Set([\"sessionKey\", \"namespace\", \"cwd\"]);\n\ntype PiSessionState = {\n observedHashes: Set<string>;\n liveObservedReplayKeys: Map<string, number>;\n lastInjectedRecallKey: string;\n};\n\ntype NotifyLevel = \"info\" | \"success\" | \"warning\" | \"error\";\ntype NotifyFn = (message: string, level: NotifyLevel) => void;\n\ntype PiContextSnapshot = {\n sessionKey: string;\n cwd: string;\n entries: any[];\n branch: any[];\n notify: NotifyFn;\n setStatus: (key: string, value: string) => void;\n compact?: () => unknown;\n};\ntype PiContextSnapshotOptions = {\n includeSessionHistory?: boolean;\n};\n\nexport function createRemnicPiExtension(options: RemnicPiExtensionOptions = {}) {\n const config = options.config ?? loadConfig(options);\n const client = new RemnicClient(config);\n const sessionStates = new Map<string, PiSessionState>();\n\n return async function remnicPiExtension(pi: PiApi): Promise<void> {\n pi.on(\"session_start\", async (_event, ctx) => {\n const session = snapshotPiContext(ctx, { includeSessionHistory: true });\n if (!session) return;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n restoreObservedState(session, state.observedHashes);\n if (!config.statusEnabled) return;\n await setStatus(session, client, config);\n });\n\n pi.on(\"context\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.recallEnabled || !config.authToken) return;\n // Circuit breaker: skip recall fast while the daemon is known-down so a\n // dead host doesn't block every turn on a doomed request (#1626).\n if (!client.isReachable()) return;\n const recallTarget = latestUserRecallTarget(Array.isArray(event.messages) ? event.messages : []);\n if (!recallTarget) return;\n const { query } = recallTarget;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n if (recallTarget.dedupeKey === state.lastInjectedRecallKey) return;\n\n try {\n const recalled = await client.recall(query, session.sessionKey, session.cwd, {\n timeoutMs: config.turnRequestTimeoutMs,\n });\n client.markReachable();\n const context = trimContext(recalled.context ?? \"\", config.recallBudgetChars);\n if (!context) return;\n state.lastInjectedRecallKey = recallTarget.dedupeKey;\n return {\n messages: [\n ...event.messages,\n {\n role: \"user\",\n content: [{ type: \"text\", text: `Remnic recalled context for this turn:\\n\\n${context}` }],\n remnicInjected: true,\n timestamp: Date.now(),\n },\n ],\n };\n } catch (err) {\n // Only trip the breaker when the daemon is genuinely unreachable\n // (timeout or connection-level failure) — not on a transient HTTP\n // error, so a one-off failure still retries on the next turn (#1626).\n if (isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);\n session.notify(`Remnic recall unavailable: ${errorMessage(err)}`, \"warning\");\n }\n });\n\n pi.on(\"message_end\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.observeEnabled || !isUserMessage(event.message)) return;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n await observeMessagesForSession(session, client, [event.message], state.observedHashes, state.liveObservedReplayKeys, config);\n });\n\n pi.on(\"turn_end\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.observeEnabled) return;\n const messages = [event.message, ...(Array.isArray(event.toolResults) ? event.toolResults : [])];\n const { state } = getSessionState(session.sessionKey, sessionStates);\n await observeMessagesForSession(session, client, messages, state.observedHashes, state.liveObservedReplayKeys, config);\n });\n\n pi.on(\"session_shutdown\", async (_event, ctx) => {\n const session = snapshotPiContext(ctx, { includeSessionHistory: true });\n if (!session) return;\n const { sessionKey, state } = getSessionState(session.sessionKey, sessionStates);\n if (config.observeEnabled) {\n const branchMessages = branchMessagesWithEntryIdentity(session.branch);\n const unobservedBranchMessages = skipLiveObservedReplayMessages(session.sessionKey, branchMessages, state.liveObservedReplayKeys);\n if (unobservedBranchMessages.length > 0) {\n await observeMessagesForSession(session, client, unobservedBranchMessages, state.observedHashes, undefined, config, true);\n }\n }\n persistObservedState(pi, state.observedHashes);\n sessionStates.delete(sessionKey);\n });\n\n pi.on(\"session_before_compact\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.compactionEnabled || !config.authToken) return;\n const preparation = event.preparation ?? {};\n try {\n await client.lcmCompactionFlush(session.sessionKey);\n } catch (err) {\n session.notify(`Remnic LCM flush failed: ${errorMessage(err)}`, \"warning\");\n return;\n }\n\n const tokensBefore = finiteTokenCount(preparation.tokensBefore);\n const tokensAfter = finiteTokenCount(preparation.tokensAfter);\n if (tokensBefore !== null && tokensAfter !== null) {\n try {\n await client.lcmCompactionRecord(session.sessionKey, tokensBefore, tokensAfter);\n } catch (err) {\n session.notify(`Remnic LCM compaction token record failed: ${errorMessage(err)}`, \"warning\");\n }\n }\n\n const summary = buildCompactionSummary(preparation);\n if (!summary.trim()) return;\n try {\n await client.contextCheckpoint(session.sessionKey, summary);\n } catch (err) {\n session.notify(`Remnic context checkpoint failed: ${errorMessage(err)}`, \"warning\");\n }\n const details = fileDetailsFromPreparation(preparation);\n return {\n compaction: {\n summary,\n firstKeptEntryId: preparation.firstKeptEntryId,\n tokensBefore: preparation.tokensBefore,\n details: {\n ...details,\n remnic: { version: 1, source: \"pi\" },\n },\n },\n };\n });\n\n registerCommands(pi, client, config);\n if (config.mcpToolsEnabled && config.authToken) {\n await registerMcpTools(pi, client, config);\n }\n };\n}\n\nexport default async function remnicPiExtension(pi: PiApi): Promise<void> {\n await createRemnicPiExtension()(pi);\n}\n\nfunction registerCommands(pi: PiApi, client: RemnicClient, config: RemnicPiConfig): void {\n pi.registerCommand(\"remnic-status\", {\n description: \"Check Remnic daemon status\",\n handler: commandHandler(async (_args, _ctx, session) => {\n const health = await client.health();\n // The daemon responded (any HTTP result), so clear any stale cooldown a\n // prior timeout left on the shared client (cursor review).\n client.markReachable();\n session.notify(`Remnic ${health.ok ? \"healthy\" : \"unhealthy\"} at ${config.remnicDaemonUrl}`, health.ok ? \"success\" : \"warning\");\n }),\n });\n\n pi.registerCommand(\"remnic-recall\", {\n description: \"Recall Remnic context for a query\",\n handler: commandHandler(async (args, _ctx, session) => {\n const query = args.trim();\n if (!query) {\n session.notify(\"Usage: /remnic-recall <query>\", \"warning\");\n return;\n }\n // Pass the general request budget so requestWithRetry shares ONE deadline\n // across retries (total <= requestTimeoutMs) instead of looping through\n // observeMaxRetries full timeouts and blocking the interactive command\n // for several minutes on a flaky connection (cursor review).\n const result = await client.recall(query, session.sessionKey, session.cwd, {\n timeoutMs: config.requestTimeoutMs,\n });\n // The daemon responded, so clear any stale cooldown a prior timeout left\n // on the shared client (cursor review).\n client.markReachable();\n session.notify(trimContext(result.context ?? \"(no Remnic context)\", MAX_CONTEXT_CHARS), \"info\");\n }),\n });\n\n pi.registerCommand(\"remnic-remember\", {\n description: \"Store a Remnic memory\",\n handler: commandHandler(async (args, _ctx, session) => {\n const content = args.trim();\n if (!content) {\n session.notify(\"Usage: /remnic-remember <memory>\", \"warning\");\n return;\n }\n await client.storeMemory(content, session.sessionKey);\n session.notify(\"Stored Remnic memory\", \"success\");\n }),\n });\n\n pi.registerCommand(\"remnic-lcm-search\", {\n description: \"Search Remnic LCM archived Pi context\",\n handler: commandHandler(async (args, _ctx, session) => {\n const query = args.trim();\n if (!query) {\n session.notify(\"Usage: /remnic-lcm-search <query>\", \"warning\");\n return;\n }\n const result = await client.lcmSearch(query, session.sessionKey);\n session.notify(JSON.stringify(result, null, 2), \"info\");\n }),\n });\n\n pi.registerCommand(\"remnic-why\", {\n description: \"Explain the last Remnic recall\",\n handler: commandHandler(async (_args, _ctx, session) => {\n const result = await client.recallExplain(session.sessionKey);\n session.notify(JSON.stringify(result, null, 2), \"info\");\n }),\n });\n\n pi.registerCommand(\"remnic-compact\", {\n description: \"Trigger Pi compaction with Remnic LCM coordination\",\n handler: commandHandler(async (_args, _ctx, session) => {\n session.compact?.();\n session.notify(\"Compaction requested\", \"info\");\n }),\n });\n}\n\nfunction commandHandler(\n handler: (args: string, ctx: any, session: PiContextSnapshot) => Promise<void>,\n): (args: string, ctx: any) => Promise<void> {\n return async (args, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n try {\n await handler(args, ctx, session);\n } catch (err) {\n session.notify(`Remnic command failed: ${errorMessage(err)}`, \"warning\");\n }\n };\n}\n\nasync function registerMcpTools(pi: PiApi, client: RemnicClient, config: RemnicPiConfig): Promise<void> {\n let tools: McpTool[] = [];\n try {\n tools = await client.mcpListTools({ timeoutMs: config.startupRequestTimeoutMs });\n } catch {\n return;\n }\n for (const tool of tools) {\n if (!tool.name.startsWith(\"remnic.\")) continue;\n const piToolName = tool.name.replace(/^remnic\\./, \"remnic_\").replace(/[^a-zA-Z0-9_]/g, \"_\");\n pi.registerTool({\n name: piToolName,\n label: tool.name,\n description: tool.description ?? `Call ${tool.name}`,\n parameters: toPiToolParametersSchema(tool.inputSchema),\n async execute(_toolCallId: string, params: Record<string, unknown>, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: any) {\n const session = snapshotPiContext(ctx);\n if (!session) {\n return {\n content: [{ type: \"text\", text: \"Remnic tool skipped because the Pi context is no longer active.\" }],\n details: { skipped: true, reason: \"stale_context\" },\n };\n }\n const safeParams = stripSessionOwnedRuntimeFields(params ?? {}) as Record<string, unknown>;\n const result = await client.mcpTool(tool.name, {\n ...safeParams,\n sessionKey: session.sessionKey,\n namespace: config.namespace,\n cwd: session.cwd,\n });\n return {\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\n details: result,\n };\n },\n });\n }\n}\n\nexport function toPiToolParametersSchema(inputSchema: unknown): TSchema {\n return Type.Unsafe(stripSessionOwnedSchemaFields(inputSchema));\n}\n\nexport function stripSessionOwnedSchemaFields(inputSchema: unknown): Record<string, unknown> {\n if (!isRecord(inputSchema)) {\n return { type: \"object\", properties: {}, additionalProperties: true };\n }\n return stripSessionOwnedSchemaNode(inputSchema) as Record<string, unknown>;\n}\n\nfunction stripSessionOwnedSchemaNode(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripSessionOwnedSchemaNode(entry));\n }\n if (!isRecord(value)) {\n return value;\n }\n const schema: Record<string, unknown> = { ...value };\n if (isRecord(value.properties)) {\n const properties: Record<string, unknown> = {};\n for (const [key, property] of Object.entries(value.properties)) {\n if (SESSION_OWNED_FIELDS.has(key)) continue;\n properties[key] = stripSessionOwnedSchemaNode(property);\n }\n schema.properties = properties;\n }\n if (Array.isArray(value.required)) {\n schema.required = value.required.filter(\n (field) => typeof field !== \"string\" || !SESSION_OWNED_FIELDS.has(field),\n );\n }\n for (const key of [\"items\", \"additionalProperties\", \"not\"] as const) {\n if (isRecord(value[key])) {\n schema[key] = stripSessionOwnedSchemaNode(value[key]);\n }\n }\n for (const key of [\"oneOf\", \"anyOf\", \"allOf\"] as const) {\n if (Array.isArray(value[key])) {\n schema[key] = value[key].map((entry) => stripSessionOwnedSchemaNode(entry));\n }\n }\n return schema;\n}\n\nexport function stripSessionOwnedRuntimeFields(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripSessionOwnedRuntimeFields(entry));\n }\n if (!isRecord(value)) {\n return value;\n }\n const sanitized: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n if (SESSION_OWNED_FIELDS.has(key)) continue;\n sanitized[key] = stripSessionOwnedRuntimeFields(child);\n }\n return sanitized;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isUserMessage(message: unknown): boolean {\n return isRecord(message) && message.role === \"user\";\n}\n\nfunction getSessionState(sessionKey: string, states: Map<string, PiSessionState>): { sessionKey: string; state: PiSessionState } {\n let state = states.get(sessionKey);\n if (!state) {\n state = {\n observedHashes: new Set<string>(),\n liveObservedReplayKeys: new Map<string, number>(),\n lastInjectedRecallKey: \"\",\n };\n states.set(sessionKey, state);\n pruneSessionStates(states);\n }\n return { sessionKey, state };\n}\n\nfunction pruneSessionStates(states: Map<string, PiSessionState>): void {\n while (states.size > MAX_SESSION_STATES) {\n const oldest = states.keys().next().value;\n if (typeof oldest !== \"string\") return;\n states.delete(oldest);\n }\n}\n\nexport async function observeMessages(\n ctx: any,\n client: RemnicClient,\n rawMessages: unknown[],\n observedHashes: Set<string>,\n liveObservedReplayKeys?: Map<string, number>,\n): Promise<void> {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n await observeMessagesForSession(session, client, rawMessages, observedHashes, liveObservedReplayKeys);\n}\n\nasync function observeMessagesForSession(\n session: PiContextSnapshot,\n client: RemnicClient,\n rawMessages: unknown[],\n observedHashes: Set<string>,\n liveObservedReplayKeys?: Map<string, number>,\n config?: RemnicPiConfig,\n forceAttempt = false,\n): Promise<void> {\n const messages: ObserveMessage[] = [];\n const pendingHashes = new Set<string>();\n for (const raw of rawMessages) {\n const message = toObserveMessage(raw);\n if (!message) continue;\n const hash = observedMessageDedupeKey(message, session.sessionKey);\n if (hash && (observedHashes.has(hash) || pendingHashes.has(hash))) continue;\n if (hash) pendingHashes.add(hash);\n messages.push(message);\n }\n if (messages.length === 0) return;\n // Circuit breaker: when config is wired (the live Pi handlers always pass\n // it), skip observe fast while the daemon is known-down so a dead host\n // doesn't burn the full per-turn budget on every turn (#1626). Shutdown is\n // the exception — it is the last chance to observe the branch before the\n // session tears down, so force the attempt even mid-cooldown; a failure\n // still trips the breaker normally (codex review).\n if (config && !forceAttempt && !client.isReachable()) return;\n // Live turn hooks are bounded by the per-turn budget to protect the host's\n // ~30s handler window (#1626). Shutdown is teardown with no such constraint,\n // so the forced replay uses the general request budget — otherwise a large\n // unobserved branch would time out exactly when forceAttempt tried to save it\n // (cursor review).\n const observeOptions = config\n ? { timeoutMs: forceAttempt ? config.requestTimeoutMs : config.turnRequestTimeoutMs }\n : undefined;\n try {\n await client.observe(session.sessionKey, session.cwd, messages, observeOptions);\n if (config) client.markReachable();\n for (const hash of pendingHashes) rememberObservedHash(observedHashes, hash);\n if (liveObservedReplayKeys) {\n for (const message of messages) {\n rememberLiveObservedReplayKey(liveObservedReplayKeys, liveReplayKey(message, session.sessionKey));\n }\n }\n } catch (err) {\n if (config && isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);\n session.notify(`Remnic observe failed: ${errorMessage(err)}`, \"warning\");\n }\n}\n\nexport function buildCompactionSummary(preparation: any): string {\n const previousSummary = typeof preparation.previousSummary === \"string\"\n ? preparation.previousSummary.trim()\n : \"\";\n const messages = [\n ...(Array.isArray(preparation.messagesToSummarize) ? preparation.messagesToSummarize : []),\n ...(Array.isArray(preparation.turnPrefixMessages) ? preparation.turnPrefixMessages : []),\n ];\n const transcript = summarizeMessages(messages, 24000);\n const details = fileDetailsFromPreparation(preparation);\n\n if (\n !previousSummary &&\n !transcript &&\n details.readFiles.length === 0 &&\n details.modifiedFiles.length === 0\n ) {\n return \"\";\n }\n\n const sections: string[] = [\n \"## Remnic Pi Context Checkpoint\",\n \"\",\n \"This checkpoint was created by Remnic during Pi context compaction.\",\n ];\n if (previousSummary) sections.push(\"\", \"## Previous Summary\", previousSummary);\n if (transcript) sections.push(\"\", \"## Conversation Excerpt\", transcript);\n if (details.readFiles.length > 0) sections.push(\"\", \"<read-files>\", ...details.readFiles, \"</read-files>\");\n if (details.modifiedFiles.length > 0) sections.push(\"\", \"<modified-files>\", ...details.modifiedFiles, \"</modified-files>\");\n return sections.join(\"\\n\");\n}\n\nfunction fileDetailsFromPreparation(preparation: any): { readFiles: string[]; modifiedFiles: string[] } {\n const fileOps = preparation?.fileOps;\n const read = fileOps?.read instanceof Set ? Array.from(fileOps.read).filter(isString) : [];\n const edited = fileOps?.edited instanceof Set ? Array.from(fileOps.edited).filter(isString) : [];\n const written = fileOps?.written instanceof Set ? Array.from(fileOps.written).filter(isString) : [];\n const modified = new Set([...edited, ...written]);\n return {\n readFiles: read.filter((file) => !modified.has(file)).sort(),\n modifiedFiles: Array.from(modified).sort(),\n };\n}\n\nfunction restoreObservedState(session: PiContextSnapshot, observedHashes: Set<string>): void {\n for (const entry of session.entries) {\n if (entry?.type !== \"custom\" || entry.customType !== STATE_CUSTOM_TYPE) continue;\n const hashes = entry.data?.observedHashes;\n if (Array.isArray(hashes)) {\n for (const hash of hashes) {\n if (typeof hash === \"string\") rememberObservedHash(observedHashes, hash);\n }\n }\n }\n}\n\nfunction rememberObservedHash(observedHashes: Set<string>, hash: string): void {\n if (observedHashes.has(hash)) return;\n while (observedHashes.size >= MAX_OBSERVED_HASHES) {\n const oldest = observedHashes.keys().next().value;\n if (typeof oldest !== \"string\") break;\n observedHashes.delete(oldest);\n }\n observedHashes.add(hash);\n}\n\nfunction rememberLiveObservedReplayKey(liveObservedReplayKeys: Map<string, number>, key: string): void {\n liveObservedReplayKeys.set(key, (liveObservedReplayKeys.get(key) ?? 0) + 1);\n}\n\nfunction consumeLiveObservedReplayKey(liveObservedReplayKeys: Map<string, number>, key: string): boolean {\n const count = liveObservedReplayKeys.get(key) ?? 0;\n if (count <= 0) return false;\n if (count === 1) liveObservedReplayKeys.delete(key);\n else liveObservedReplayKeys.set(key, count - 1);\n return true;\n}\n\nfunction skipLiveObservedReplayMessages(\n sessionKey: string,\n rawMessages: unknown[],\n liveObservedReplayKeys: Map<string, number>,\n): unknown[] {\n if (liveObservedReplayKeys.size === 0) return rawMessages;\n const unobserved: unknown[] = [];\n for (const raw of rawMessages) {\n const message = toObserveMessage(raw);\n if (message && consumeLiveObservedReplayKey(liveObservedReplayKeys, liveReplayKey(message, sessionKey))) {\n continue;\n }\n unobserved.push(raw);\n }\n return unobserved;\n}\n\nfunction liveReplayKey(message: ObserveMessage, sessionKey: string): string {\n return hashObservedMessage(message, sessionKey, \"live-replay\");\n}\n\nfunction persistObservedState(pi: PiApi, observedHashes: Set<string>): void {\n const observed = Array.from(observedHashes).slice(-MAX_OBSERVED_HASHES);\n pi.appendEntry(STATE_CUSTOM_TYPE, {\n observedHashes: observed,\n recordedAt: new Date().toISOString(),\n });\n}\n\nasync function setStatus(session: PiContextSnapshot, client: RemnicClient, config: RemnicPiConfig): Promise<void> {\n try {\n await client.health({ timeoutMs: config.startupRequestTimeoutMs });\n // A successful health probe means the daemon is reachable, so clear any\n // stale cooldown a prior recall/observe timeout left on the shared client —\n // otherwise the next live hook fast-skips even though the daemon is up\n // (codex review).\n client.markReachable();\n session.setStatus(\"remnic\", `Remnic ${config.namespace ? `(${config.namespace})` : \"ready\"}`);\n } catch (err) {\n // Startup just proved the daemon is unreachable, so trip the fast-skip\n // breaker too — otherwise the first live context/observe hook still spends\n // the full turn budget on a doomed request before the breaker would trip\n // on its own (codex review).\n if (isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);\n session.setStatus(\"remnic\", \"Remnic offline\");\n }\n}\n\nfunction snapshotPiContext(ctx: any, options: PiContextSnapshotOptions = {}): PiContextSnapshot | null {\n const sessionKey = safeSessionKeyFromContext(ctx);\n if (!sessionKey) return null;\n const cwd = safeStringRead(() => ctx?.cwd, \"\");\n const hasUI = safeRead(() => ctx?.hasUI, undefined) === false;\n const ui = hasUI ? undefined : safeRead(() => ctx?.ui, undefined);\n const compact = safeRead(() => ctx?.compact, undefined);\n const includeSessionHistory = options.includeSessionHistory === true;\n return {\n sessionKey,\n cwd,\n entries: includeSessionHistory ? safeEntries(ctx) : [],\n branch: includeSessionHistory ? safeBranch(ctx) : [],\n notify: makeNotifier(ui, hasUI),\n setStatus: makeStatusSetter(ui, hasUI),\n compact: typeof compact === \"function\" ? () => compact.call(ctx) : undefined,\n };\n}\n\nfunction safeSessionKeyFromContext(ctx: any): string | null {\n try {\n return sessionKeyFromContext(ctx);\n } catch {\n return null;\n }\n}\n\nfunction makeNotifier(ui: unknown, hasUI: boolean): NotifyFn {\n if (hasUI || !isRecord(ui) || typeof ui.notify !== \"function\") {\n return () => undefined;\n }\n const notifyFn = ui.notify;\n return (message, level) => {\n try {\n notifyFn.call(ui, message, level);\n } catch {\n // Pi invalidates session-bound UI objects during reload/replacement. A\n // notification failure must not tear down Remnic's hooks.\n }\n };\n}\n\nfunction makeStatusSetter(ui: unknown, hasUI: boolean): PiContextSnapshot[\"setStatus\"] {\n if (hasUI || !isRecord(ui) || typeof ui.setStatus !== \"function\") {\n return () => undefined;\n }\n const setStatusFn = ui.setStatus;\n return (key, value) => {\n try {\n setStatusFn.call(ui, key, value);\n } catch {\n // See makeNotifier: stale UI should not make extension startup fail.\n }\n };\n}\n\nfunction safeRead<T>(read: () => T, fallback: T): T {\n try {\n return read();\n } catch {\n return fallback;\n }\n}\n\nfunction safeStringRead(read: () => unknown, fallback: string): string {\n const value = safeRead(read, fallback);\n return typeof value === \"string\" ? value : fallback;\n}\n\nfunction safeEntries(ctx: any): any[] {\n try {\n const entries = ctx.sessionManager?.getEntries?.();\n return Array.isArray(entries) ? entries : [];\n } catch {\n return [];\n }\n}\n\nfunction safeBranch(ctx: any): any[] {\n try {\n const branch = ctx.sessionManager?.getBranch?.();\n return Array.isArray(branch) ? branch : [];\n } catch {\n return [];\n }\n}\n\nfunction branchMessagesWithEntryIdentity(branch: any[]): unknown[] {\n const messages: unknown[] = [];\n for (const entry of branch) {\n const message = messageWithEntryIdentity(entry);\n if (message) messages.push(message);\n }\n return messages;\n}\n\nfunction messageWithEntryIdentity(entry: any): unknown | null {\n const message = entry?.message;\n if (!message || typeof message !== \"object\" || Array.isArray(message)) return message ?? null;\n\n const source = isRecord(entry) ? entry : {};\n const enriched: Record<string, unknown> = { ...(message as Record<string, unknown>) };\n assignMissingIdentity(enriched, \"entryId\", source.id ?? source.entryId ?? source.entry_id);\n assignMissingIdentity(enriched, \"timestamp\", source.timestamp);\n assignMissingIdentity(enriched, \"createdAt\", source.createdAt ?? source.created_at);\n return enriched;\n}\n\nfunction assignMissingIdentity(target: Record<string, unknown>, field: string, value: unknown): void {\n if (target[field] !== undefined) return;\n if (typeof value === \"string\" && value.length > 0) {\n target[field] = value;\n return;\n }\n if (typeof value === \"number\" && Number.isFinite(value)) {\n target[field] = value;\n }\n}\n\nfunction trimContext(value: string, budget: number): string {\n if (value.length <= budget) return value;\n if (budget <= TRUNCATION_NOTICE.length) return TRUNCATION_NOTICE.slice(0, budget);\n return `${value.slice(0, budget - TRUNCATION_NOTICE.length)}${TRUNCATION_NOTICE}`;\n}\n\n\nexport function isDaemonUnreachableError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (/Remnic request timed out/.test(err.message)) return true;\n // Retry-budget exhaustion means transient failures ate the whole per-turn\n // deadline inside requestWithRetry — the daemon is effectively unreachable\n // for this turn, so trip the breaker and cool down instead of burning another\n // full budget on the next hook (codex review). This error only arises from\n // transient connection failures, never from a semantic HTTP response.\n if (/Remnic request exceeded the \\d+ms budget before retry/.test(err.message)) return true;\n // Multi-chunk observe throws its own budget-exceeded message when the shared\n // per-turn deadline is exhausted across chunks; that is also an effectively-\n // unreachable condition for the turn, so trip the breaker and fast-skip\n // subsequent turns instead of piling on more doomed chunked observes (cursor).\n if (/Remnic observe exceeded the per-turn budget of \\d+ms/.test(err.message)) return true;\n return isTransientNetworkError(err);\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction finiteTokenCount(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 ? value : null;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\nexport { textFromMessage };\n","import { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\n// Leaf submodule (not the `@remnic/core` barrel) so the omp pre-bundle's\n// `bun build` does not pull the rest of core — including the LanceDB native\n// asset — into the extension bundle. See PR #1641.\nimport { expandTildePath } from \"@remnic/core/utils/path\";\n\nimport { REMNIC_PI_EXTENSION_DIR_NAME, resolvePiAgentHome } from \"./paths.js\";\n\nexport interface RemnicPiConfig {\n remnicDaemonUrl: string;\n authToken?: string;\n namespace?: string;\n recallMode: \"auto\" | \"minimal\" | \"full\" | \"graph_mode\" | \"no_recall\";\n recallTopK: number;\n recallBudgetChars: number;\n recallEnabled: boolean;\n observeEnabled: boolean;\n observeSkipExtraction: boolean;\n compactionEnabled: boolean;\n mcpToolsEnabled: boolean;\n statusEnabled: boolean;\n requestTimeoutMs: number;\n startupRequestTimeoutMs: number;\n /**\n * Per-turn request budget for observe/recall. MUST stay below the host's\n * in-handler kill budget (Pi/omp kills handlers at 30 s). Defaults to 20 s,\n * capped at 25 s so a misconfiguration can never produce a structurally\n * unsatisfiable timeout (issue #1626).\n */\n turnRequestTimeoutMs: number;\n /**\n * Soft cap on a single observe POST body in bytes. The client chunks observe\n * batches to stay under this; individual oversized messages are truncated\n * with a marker. Defaults to 100 KiB, safely under the daemon's default\n * 128 KiB `maxBodyBytes` (issue #1600).\n */\n observeMaxBytes: number;\n /**\n * Maximum retry attempts for observe/recall on transient connection-level\n * failures (socket close, ECONNRESET, EPIPE). Observe is dedupe-safe so\n * retrying is harmless (issue #1602).\n */\n observeMaxRetries: number;\n /**\n * Cooldown base for the daemon-reachability circuit breaker. When observe/\n * recall fails on a timeout or connection error, subsequent turns skip fast\n * for an exponentially growing window starting at this value (issue #1626).\n */\n daemonCooldownMs: number;\n}\n\nexport interface LoadConfigOptions {\n configPath?: string;\n env?: NodeJS.ProcessEnv;\n}\n\nconst DEFAULT_CONFIG: RemnicPiConfig = {\n remnicDaemonUrl: \"http://127.0.0.1:4318\",\n recallMode: \"auto\",\n recallTopK: 8,\n recallBudgetChars: 12000,\n recallEnabled: true,\n observeEnabled: true,\n observeSkipExtraction: false,\n compactionEnabled: true,\n mcpToolsEnabled: true,\n statusEnabled: true,\n requestTimeoutMs: 60000,\n startupRequestTimeoutMs: 1000,\n // Default 20 s is comfortably under the Pi/omp 30 s handler budget (#1626).\n turnRequestTimeoutMs: 20000,\n // Default 100 KiB leaves headroom under the daemon's 128 KiB default (#1600).\n observeMaxBytes: 102400,\n observeMaxRetries: 2,\n // Base cooldown for the circuit breaker; doubles on consecutive failures (#1626).\n daemonCooldownMs: 5000,\n};\n\nfunction defaultConfigPath(env: NodeJS.ProcessEnv): string {\n return path.join(resolvePiAgentHome(env), \"extensions\", REMNIC_PI_EXTENSION_DIR_NAME, \"remnic.config.json\");\n}\n\nfunction coerceBoolean(value: unknown, fallback: boolean, fieldName: string): boolean {\n if (value === undefined || value === null) return fallback;\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true;\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false;\n }\n throw new Error(`Invalid boolean value for Remnic Pi config field ${fieldName}`);\n}\n\nfunction coercePositiveInt(value: unknown, fallback: number, max: number, fieldName: string): number {\n if (value === undefined || value === null || value === \"\") return fallback;\n let parsed: number;\n if (typeof value === \"number\") {\n parsed = value;\n } else if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.length === 0) return fallback;\n if (!/^[+-]?\\d+$/.test(trimmed)) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 1 to ${max}`);\n }\n parsed = Number(trimmed);\n } else {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 1 to ${max}`);\n }\n if (!Number.isInteger(parsed) || parsed <= 0 || parsed > max) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 1 to ${max}`);\n }\n return parsed;\n}\n\n/**\n * Like {@link coercePositiveInt} but allows 0, for knobs where 0 is a\n * meaningful \"disabled\" value (e.g. observeMaxRetries). Still rejects\n * negatives, non-integers, and values above the cap.\n */\nfunction coerceNonNegativeInt(value: unknown, fallback: number, max: number, fieldName: string): number {\n if (value === undefined || value === null || value === \"\") return fallback;\n let parsed: number;\n if (typeof value === \"number\") {\n parsed = value;\n } else if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.length === 0) return fallback;\n if (!/^[+-]?\\d+$/.test(trimmed)) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 0 to ${max}`);\n }\n parsed = Number(trimmed);\n } else {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 0 to ${max}`);\n }\n if (!Number.isInteger(parsed) || parsed < 0 || parsed > max) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 0 to ${max}`);\n }\n return parsed;\n}\n\nfunction coerceOptionalNonEmptyString(value: unknown, fieldName: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === \"string\" && value.trim().length > 0) return value.trim();\n throw new Error(`Invalid string value for Remnic Pi config field ${fieldName}`);\n}\n\nfunction coerceOptionalString(value: unknown, fieldName: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n throw new Error(`Invalid string value for Remnic Pi config field ${fieldName}`);\n}\n\nfunction coerceOptionalHttpUrl(value: unknown, fieldName: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value !== \"string\") {\n throw new Error(`Invalid URL value for Remnic Pi config field ${fieldName}: expected an http or https URL`);\n }\n const trimmed = value.trim();\n if (trimmed.length === 0) return undefined;\n try {\n const parsed = new URL(trimmed);\n if (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") return trimTrailingSlashes(trimmed);\n } catch {\n // Fall through to the shared error below.\n }\n throw new Error(`Invalid URL value for Remnic Pi config field ${fieldName}: expected an http or https URL`);\n}\n\nfunction coerceRecallMode(value: unknown): RemnicPiConfig[\"recallMode\"] {\n if (value === undefined || value === null || value === \"\") return DEFAULT_CONFIG.recallMode;\n if (\n value === \"minimal\" ||\n value === \"full\" ||\n value === \"graph_mode\" ||\n value === \"no_recall\" ||\n value === \"auto\"\n ) {\n return value;\n }\n throw new Error(`Invalid recallMode value for Remnic Pi config: ${JSON.stringify(value)}`);\n}\n\nfunction readConfigFile(configPath: string): Record<string, unknown> {\n if (!existsSync(configPath)) return {};\n try {\n const raw = readFileSync(configPath, \"utf-8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n throw new Error(\"expected a JSON object\");\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to load Remnic Pi config at ${configPath}: ${reason}`);\n }\n}\n\nfunction trimTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;\n return value.slice(0, end);\n}\n\nexport function resolveConfigPath(options: LoadConfigOptions = {}): string {\n const env = options.env ?? process.env;\n // REMNIC_PI_CONFIG keeps precedence for upstream Pi; REMNIC_OMP_CONFIG lets an\n // omp (oh-my-pi) direct load (`omp -e npm:@remnic/plugin-pi`) point the shared\n // runtime module at its own config without an explicit configPath. Connector\n // installs always pass an explicit configPath, so this only affects direct loads.\n return expandTildePath(\n options.configPath || env.REMNIC_PI_CONFIG || env.REMNIC_OMP_CONFIG || defaultConfigPath(env),\n );\n}\n\nexport function loadConfig(options: LoadConfigOptions = {}): RemnicPiConfig {\n const env = options.env ?? process.env;\n const fileConfig = readConfigFile(resolveConfigPath(options));\n const daemonUrl =\n coerceOptionalHttpUrl(fileConfig.remnicDaemonUrl, \"remnicDaemonUrl\") ??\n coerceOptionalHttpUrl(env.REMNIC_DAEMON_URL, \"REMNIC_DAEMON_URL\") ??\n DEFAULT_CONFIG.remnicDaemonUrl;\n const authToken =\n coerceOptionalString(fileConfig.authToken, \"authToken\") ??\n coerceOptionalString(env.REMNIC_PI_AUTH_TOKEN, \"REMNIC_PI_AUTH_TOKEN\");\n const namespace = coerceOptionalNonEmptyString(fileConfig.namespace, \"namespace\");\n\n const requestTimeoutMs = coercePositiveInt(\n fileConfig.requestTimeoutMs,\n DEFAULT_CONFIG.requestTimeoutMs,\n 60_000,\n \"requestTimeoutMs\",\n );\n // When turnRequestTimeoutMs is not explicitly set, derive it from the\n // configured requestTimeoutMs (capped at the default turn budget) so an\n // existing install that lowered requestTimeoutMs below 20s keeps its tighter\n // per-turn budget instead of being silently raised back to 20s (codex review).\n const turnFallback = Math.min(requestTimeoutMs, DEFAULT_CONFIG.turnRequestTimeoutMs);\n const turnRequestTimeoutMs = coercePositiveInt(\n fileConfig.turnRequestTimeoutMs,\n turnFallback,\n 25_000,\n \"turnRequestTimeoutMs\",\n );\n\n return {\n remnicDaemonUrl: daemonUrl,\n authToken,\n namespace,\n recallMode: coerceRecallMode(fileConfig.recallMode),\n recallTopK: coercePositiveInt(fileConfig.recallTopK, DEFAULT_CONFIG.recallTopK, 50, \"recallTopK\"),\n recallBudgetChars: coercePositiveInt(fileConfig.recallBudgetChars, DEFAULT_CONFIG.recallBudgetChars, 64000, \"recallBudgetChars\"),\n recallEnabled: coerceBoolean(fileConfig.recallEnabled, DEFAULT_CONFIG.recallEnabled, \"recallEnabled\"),\n observeEnabled: coerceBoolean(fileConfig.observeEnabled, DEFAULT_CONFIG.observeEnabled, \"observeEnabled\"),\n observeSkipExtraction: coerceBoolean(fileConfig.observeSkipExtraction, DEFAULT_CONFIG.observeSkipExtraction, \"observeSkipExtraction\"),\n compactionEnabled: coerceBoolean(fileConfig.compactionEnabled, DEFAULT_CONFIG.compactionEnabled, \"compactionEnabled\"),\n mcpToolsEnabled: coerceBoolean(fileConfig.mcpToolsEnabled, DEFAULT_CONFIG.mcpToolsEnabled, \"mcpToolsEnabled\"),\n statusEnabled: coerceBoolean(fileConfig.statusEnabled, DEFAULT_CONFIG.statusEnabled, \"statusEnabled\"),\n requestTimeoutMs,\n startupRequestTimeoutMs: coercePositiveInt(\n fileConfig.startupRequestTimeoutMs,\n DEFAULT_CONFIG.startupRequestTimeoutMs,\n 60_000,\n \"startupRequestTimeoutMs\",\n ),\n turnRequestTimeoutMs,\n observeMaxBytes: coercePositiveInt(\n fileConfig.observeMaxBytes,\n DEFAULT_CONFIG.observeMaxBytes,\n 8_388_608,\n \"observeMaxBytes\",\n ),\n observeMaxRetries: coerceNonNegativeInt(fileConfig.observeMaxRetries, DEFAULT_CONFIG.observeMaxRetries, 5, \"observeMaxRetries\"),\n daemonCooldownMs: coercePositiveInt(fileConfig.daemonCooldownMs, DEFAULT_CONFIG.daemonCooldownMs, 60_000, \"daemonCooldownMs\"),\n };\n}\n","import type { RemnicPiConfig } from \"./config.js\";\n\nexport interface RecallResponse {\n context?: string;\n results?: Array<{ id?: string; content?: string; score?: number; category?: string }>;\n count?: number;\n}\n\nexport interface ObserveMessagePart {\n ordinal?: number;\n kind: \"text\" | \"tool_call\" | \"tool_result\" | \"patch\" | \"file_read\" | \"file_write\" | \"step_start\" | \"step_finish\" | \"snapshot\" | \"retry\";\n payload: Record<string, unknown>;\n toolName?: string | null;\n filePath?: string | null;\n createdAt?: string | null;\n}\n\nexport interface ObserveMessage {\n role: \"user\" | \"assistant\";\n content: string;\n sourceFormat?: \"pi\";\n rawContent?: unknown;\n parts?: ObserveMessagePart[];\n}\n\nexport interface McpTool {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n}\n\nexport interface RequestOptions {\n timeoutMs?: number;\n /** Transient-retry budget for connection-level failures (socket close, ECONNRESET). */\n maxRetries?: number;\n}\n\nexport interface ObserveOptions extends RequestOptions {\n /** Soft cap on a single observe POST body in bytes; oversize batches are chunked. */\n maxBytes?: number;\n}\n\nexport class RemnicHttpError extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(message);\n }\n}\n\ninterface ObserveBody {\n sessionKey: string;\n cwd: string;\n namespace?: string;\n skipExtraction: boolean;\n messages: ObserveMessage[];\n}\n\nconst encoder = new TextEncoder();\nconst RETRY_BASE_DELAY_MS = 200;\nconst MAX_COOLDOWN_MS = 60_000;\nconst TRUNCATION_MARKER = \"\\n\\n[Remnic observe truncated: payload exceeded client size cap]\";\n\nexport class RemnicClient {\n private requestId = 0;\n // Circuit-breaker state: when the daemon is known-unreachable, observe/recall\n // callers skip fast instead of blocking every turn on a doomed request (#1626).\n private unreachableUntil = 0;\n private consecutiveFailures = 0;\n\n constructor(private readonly config: RemnicPiConfig) {}\n\n /** True when the daemon is not in a known-unreachable cooldown. */\n isReachable(): boolean {\n return Date.now() >= this.unreachableUntil;\n }\n\n /** Clear the circuit breaker — call after any successful daemon interaction. */\n markReachable(): void {\n this.consecutiveFailures = 0;\n this.unreachableUntil = 0;\n }\n\n /**\n * Enter (or extend) an unreachable cooldown. The cooldown grows exponentially\n * with consecutive failures (base, 2×base, 4×base, …) capped at 60 s, so a\n * flapping daemon is retried gently while a hard-down host backs off hard.\n */\n markUnreachable(baseCooldownMs: number): void {\n this.consecutiveFailures += 1;\n const factor = 2 ** Math.min(this.consecutiveFailures - 1, 4);\n const cooldown = Math.min(baseCooldownMs * factor, MAX_COOLDOWN_MS);\n this.unreachableUntil = Date.now() + cooldown;\n }\n\n async health(options: RequestOptions = {}): Promise<Record<string, unknown>> {\n return this.request(\"GET\", \"/engram/v1/health\", undefined, options);\n }\n\n async recall(\n query: string,\n sessionKey: string,\n cwd: string,\n options: RequestOptions = {},\n ): Promise<RecallResponse> {\n // Recall is a read-only query; retry transient connection failures with the\n // same budget as observe (#1602). Default to the per-turn budget when the\n // caller omits timeoutMs so the retries share one deadline (like observe)\n // instead of each attempt reusing the full general request timeout (cursor\n // review). Callers that need more (e.g. the manual /remnic-recall command)\n // pass an explicit timeoutMs.\n const merged: RequestOptions = {\n ...options,\n timeoutMs: options.timeoutMs ?? this.config.turnRequestTimeoutMs,\n maxRetries: options.maxRetries ?? this.config.observeMaxRetries,\n };\n return this.requestWithRetry(\n \"POST\",\n \"/engram/v1/recall\",\n {\n query,\n sessionKey,\n cwd,\n namespace: this.config.namespace,\n topK: this.config.recallTopK,\n mode: this.config.recallMode,\n },\n merged,\n );\n }\n\n async recallExplain(sessionKey: string, options: RequestOptions = {}): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\n \"POST\",\n \"/engram/v1/recall/explain\",\n {\n sessionKey,\n namespace: this.config.namespace,\n },\n options,\n );\n }\n\n async observe(\n sessionKey: string,\n cwd: string,\n messages: ObserveMessage[],\n options: ObserveOptions = {},\n ): Promise<Record<string, unknown>> {\n const maxBytes = options.maxBytes ?? this.config.observeMaxBytes;\n // Observe runs on the live turn hooks, so bound it by the per-turn budget\n // (#1626) regardless of how many chunks the payload splits into. A missing\n // override previously let single-chunk observe fall back to the 60s general\n // budget while multi-chunk used the 20s turn budget (cursor review). An\n // explicit override is honored so callers outside the turn (shutdown replay,\n // tests) can extend it.\n const turnBudgetMs = options.timeoutMs ?? this.config.turnRequestTimeoutMs;\n const retryOptions: RequestOptions = {\n timeoutMs: turnBudgetMs,\n maxRetries: options.maxRetries ?? this.config.observeMaxRetries,\n };\n const chunks = chunkObservePayload(this.config, sessionKey, cwd, messages, maxBytes);\n if (chunks.length === 1) {\n return this.requestWithRetry(\"POST\", \"/engram/v1/observe\", chunks[0], retryOptions);\n }\n // Multiple chunks: send sequentially within the SAME per-turn deadline so\n // the TOTAL observe time stays under turnBudgetMs (not per-chunk), which\n // keeps it inside the host's ~30s handler budget (#1626). Each chunk is\n // retried independently on transient connection failures; observe is\n // dedupe-safe, so a partial failure just re-sends on the next turn.\n const deadline = Date.now() + turnBudgetMs;\n const results: Record<string, unknown>[] = [];\n for (let i = 0; i < chunks.length; i++) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n throw new Error(\n `Remnic observe exceeded the per-turn budget of ${turnBudgetMs}ms across ${chunks.length} chunks (completed ${i})`,\n );\n }\n const chunkOptions: RequestOptions = { ...retryOptions, timeoutMs: remaining };\n const result = await this.requestWithRetry<Record<string, unknown>>(\n \"POST\",\n \"/engram/v1/observe\",\n chunks[i],\n chunkOptions,\n );\n if (result && typeof result === \"object\") {\n results.push(result);\n }\n }\n return mergeObserveResults(results);\n }\n\n async storeMemory(content: string, sessionKey: string, options: RequestOptions = {}): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\"POST\", \"/engram/v1/memories\", {\n content,\n category: \"fact\",\n sourceReason: \"Captured from Pi via Remnic extension\",\n sessionKey,\n namespace: this.config.namespace,\n }, options);\n }\n\n async lcmSearch(query: string, sessionKey: string, limit = 10): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\"POST\", \"/engram/v1/lcm/search\", {\n query,\n sessionKey,\n namespace: this.config.namespace,\n limit,\n });\n }\n\n async lcmCompactionFlush(sessionKey: string): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\"POST\", \"/engram/v1/lcm/compaction/flush\", {\n sessionKey,\n namespace: this.config.namespace,\n });\n }\n\n async lcmCompactionRecord(sessionKey: string, tokensBefore: number, tokensAfter: number): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\"POST\", \"/engram/v1/lcm/compaction/record\", {\n sessionKey,\n namespace: this.config.namespace,\n tokensBefore,\n tokensAfter,\n });\n }\n\n async contextCheckpoint(sessionKey: string, context: string): Promise<Record<string, unknown>> {\n return this.mcpTool(\"remnic.context_checkpoint\", {\n sessionKey,\n context,\n namespace: this.config.namespace,\n });\n }\n\n async mcpListTools(options: RequestOptions = {}): Promise<McpTool[]> {\n const result = await this.mcpRequest(\"tools/list\", {}, options);\n const tools = result.tools;\n return Array.isArray(tools) ? tools.filter(isMcpTool) : [];\n }\n\n async mcpTool(name: string, args: Record<string, unknown>): Promise<Record<string, unknown>> {\n return this.mcpRequest(\"tools/call\", {\n name,\n arguments: args,\n });\n }\n\n /**\n * Single HTTP attempt with the configured timeout. No retry — retry of\n * transient connection failures lives in {@link requestWithRetry}.\n */\n private async request<T = Record<string, unknown>>(\n method: string,\n pathname: string,\n body?: unknown,\n options: RequestOptions = {},\n ): Promise<T> {\n const controller = new AbortController();\n // A per-request override is honored only when it is a finite positive number;\n // 0, negative, NaN, or non-finite values would make setTimeout abort\n // immediately (or behave erratically), so fall back to the general budget.\n // In practice the override is always sourced from the validated\n // `startupRequestTimeoutMs` / `turnRequestTimeoutMs` config, but this keeps\n // the client robust to any future caller (Copilot review).\n const override = options.timeoutMs;\n const timeoutMs =\n typeof override === \"number\" && Number.isFinite(override) && override > 0\n ? override\n : this.config.requestTimeoutMs;\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(`${this.config.remnicDaemonUrl}${pathname}`, {\n method,\n headers: {\n ...(body === undefined ? {} : { \"Content-Type\": \"application/json\" }),\n ...(this.config.authToken ? { Authorization: `Bearer ${this.config.authToken}` } : {}),\n \"X-Engram-Client-Id\": \"pi\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller.signal,\n });\n const text = await response.text();\n let payload: unknown = {};\n let parseError: unknown;\n if (text) {\n try {\n payload = JSON.parse(text);\n } catch (err) {\n parseError = err;\n }\n }\n if (!response.ok) {\n const message = responseErrorMessage(response, text, payload, parseError);\n if (response.status === 413) {\n // Surface the body size so operators can tune the cap (#1600).\n const bodyBytes = body === undefined ? 0 : jsonBytes(body);\n throw new RemnicHttpError(response.status, `${message} (observed body ${bodyBytes} bytes; cap via observeMaxBytes)`);\n }\n throw new RemnicHttpError(response.status, message);\n }\n if (parseError) {\n const reason = parseError instanceof Error ? parseError.message : String(parseError);\n throw new Error(`Invalid JSON response from Remnic daemon (${response.status} ${response.statusText || \"OK\"}): ${reason}`);\n }\n return payload as T;\n } catch (err) {\n if (isAbortError(err)) {\n throw new Error(`Remnic request timed out after ${timeoutMs}ms`);\n }\n throw err;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n /**\n * Wrap {@link request} with a small bounded retry loop for transient\n * connection-level failures (socket close mid-request, ECONNRESET, EPIPE).\n * Timeouts (our own AbortController) and HTTP responses (4xx/5xx) are NOT\n * retried here — timeouts already burned the full budget, and HTTP errors\n * carry semantic meaning the caller must handle. Observe/recall are\n * dedupe-safe so retrying a transiently-failed POST is harmless (#1602).\n */\n private async requestWithRetry<T = Record<string, unknown>>(\n method: string,\n pathname: string,\n body: unknown,\n options: RequestOptions = {},\n ): Promise<T> {\n const maxRetries = options.maxRetries ?? 0;\n // Share ONE deadline across all attempts (including backoff sleeps) when the\n // caller passes a per-turn/per-operation budget, so a late transient failure\n // cannot burn a full timeout on every retry and overshoot the host's ~30s\n // handler window (#1602/#1626 — cursor + codex reviews). The first attempt\n // keeps the original timeoutMs verbatim (preserving error messages/timing);\n // only retries are tightened to the remaining budget.\n const budgetMs = options.timeoutMs;\n const hasDeadline = typeof budgetMs === \"number\" && Number.isFinite(budgetMs) && budgetMs > 0;\n const deadline = hasDeadline ? Date.now() + budgetMs : Number.POSITIVE_INFINITY;\n let attempt = 0;\n let attemptOptions = options;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n try {\n return await this.request<T>(method, pathname, body, attemptOptions);\n } catch (err) {\n if (attempt >= maxRetries || !isTransientNetworkError(err)) throw err;\n const delayMs = RETRY_BASE_DELAY_MS * 2 ** attempt;\n if (hasDeadline) {\n // The backoff sleep counts against the shared deadline; bail BEFORE\n // sleeping if the sleep alone would overshoot the remaining budget,\n // so a sub-backoff timeoutMs never blocks for the full backoff only\n // to then throw (cursor review).\n const remainingBeforeSleep = deadline - Date.now();\n if (remainingBeforeSleep <= delayMs) {\n throw new Error(\n `Remnic request exceeded the ${budgetMs}ms budget before retry ${attempt + 1} (${method} ${pathname})`,\n );\n }\n }\n await sleep(delayMs);\n attempt += 1;\n if (hasDeadline) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n throw new Error(\n `Remnic request exceeded the ${budgetMs}ms budget before retry ${attempt} (${method} ${pathname})`,\n );\n }\n attemptOptions = { ...options, timeoutMs: remaining };\n }\n }\n }\n }\n\n private async mcpRequest(\n method: string,\n params: Record<string, unknown>,\n options: RequestOptions = {},\n ): Promise<Record<string, unknown>> {\n this.requestId += 1;\n const payload = await this.request<Record<string, unknown>>(\"POST\", \"/mcp\", {\n jsonrpc: \"2.0\",\n id: this.requestId,\n method,\n params,\n }, options);\n if (payload.error) {\n throw new Error(JSON.stringify(payload.error));\n }\n return (payload.result && typeof payload.result === \"object\" ? payload.result : payload) as Record<string, unknown>;\n }\n}\n\nfunction isMcpTool(value: unknown): value is McpTool {\n return !!value && typeof value === \"object\" && \"name\" in value && typeof value.name === \"string\";\n}\n\nfunction isAbortError(err: unknown): boolean {\n return err instanceof Error && (err.name === \"AbortError\" || err.message === \"This operation was aborted\");\n}\n\n/**\n * Classify connection-level failures that are safe to retry: the request never\n * reached the daemon (or died mid-flight), so a retry is idempotent. Excludes\n * our own AbortController timeouts and HTTP responses (those carry meaning).\n */\nexport function isTransientNetworkError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (isAbortError(err)) return false;\n if (err instanceof RemnicHttpError) return false;\n const lower = (err.message ?? \"\").toLowerCase();\n // Bun fetch: \"The socket connection was closed unexpectedly.\"\n if (lower.includes(\"socket connection was closed\")) return true;\n if (lower.includes(\"socket closed\")) return true;\n // Node undici / OS codes surfaced in the message.\n if (lower.includes(\"econnreset\")) return true;\n if (lower.includes(\"epipe\")) return true;\n if (lower.includes(\"und_err_socket\")) return true;\n // Node wraps the real cause in err.cause (TypeError: fetch failed).\n if (lower.includes(\"fetch failed\")) return true;\n // Inspect the cause chain without an unchecked cast. Error.cause is\n // `unknown` in the ES2022 lib; narrow it before reading `.code`.\n const cause = err.cause;\n if (cause && typeof cause === \"object\" && \"code\" in cause) {\n const code = cause.code;\n if (typeof code === \"string\" && (code === \"ECONNRESET\" || code === \"EPIPE\" || code === \"UND_ERR_SOCKET\")) {\n return true;\n }\n }\n return false;\n}\n\nfunction sleep(ms: number): Promise<void> {\n // Plain Promise constructor: avoids Promise.withResolvers (ES2024 / Node 22+),\n // so retry backoff works on Node 20 and other runtimes that load plugin-pi.\n return new Promise<void>(resolve => setTimeout(resolve, ms));\n}\n\nfunction jsonBytes(value: unknown): number {\n return encoder.encode(JSON.stringify(value)).length;\n}\n\nfunction buildObserveEnvelope(config: RemnicPiConfig, sessionKey: string, cwd: string, messages: ObserveMessage[]): ObserveBody {\n return {\n sessionKey,\n cwd,\n namespace: config.namespace,\n skipExtraction: config.observeSkipExtraction,\n messages,\n };\n}\n\n/**\n * Split an observe batch into POST bodies whose serialized JSON stays under\n * `maxBytes`. Single messages that alone exceed the per-message budget are\n * truncated with a marker rather than dropped, so large tool outputs still\n * leave a trace in memory (#1600).\n */\nexport function chunkObservePayload(\n config: RemnicPiConfig,\n sessionKey: string,\n cwd: string,\n messages: ObserveMessage[],\n maxBytes: number,\n): ObserveBody[] {\n const envelopeOverhead = jsonBytes(buildObserveEnvelope(config, sessionKey, cwd, []));\n const messageBudget = maxBytes - envelopeOverhead;\n if (messageBudget <= 0) {\n // The envelope overhead alone meets/exceeds the cap, so no valid body can\n // fit — return a single chunk and let the daemon reject it visibly (this is\n // a degenerate/misconfigured cap, not the common case). A small-but-positive\n // budget (<=1024) is NOT degenerate: truncate/pack normally so oversized\n // messages are shrunk to fit instead of bypassing the #1600 safeguards\n // (cursor review).\n return [buildObserveEnvelope(config, sessionKey, cwd, messages)];\n }\n const chunks: ObserveMessage[][] = [];\n let current: ObserveMessage[] = [];\n let currentSize = 0;\n const flush = (): void => {\n if (current.length > 0) {\n chunks.push(current);\n current = [];\n currentSize = 0;\n }\n };\n for (const message of messages) {\n const size = jsonBytes(message);\n if (size > messageBudget) {\n flush();\n chunks.push([truncateObserveMessage(message, messageBudget)]);\n continue;\n }\n // Account for the JSON array comma separator before this message when it is\n // not the first in the chunk, so the serialized body never overshoots the\n // cap (review: cursor).\n if (current.length > 0 && currentSize + 1 + size > messageBudget) {\n flush();\n }\n if (current.length > 0) currentSize += 1;\n current.push(message);\n currentSize += size;\n }\n flush();\n if (chunks.length === 0) {\n return [buildObserveEnvelope(config, sessionKey, cwd, [])];\n }\n return chunks.map((msgs) => buildObserveEnvelope(config, sessionKey, cwd, msgs));\n}\n\nconst decoder = new TextDecoder();\n\nfunction truncateObserveMessage(message: ObserveMessage, budgetBytes: number): ObserveMessage {\n // A truncated observe keeps ONLY role + a content marker, dropping rawContent\n // and parts. Live Pi turns carry the full original message in rawContent and\n // parsed parts; those fields dominate the serialized size and would keep the\n // chunk over the cap (defeating #1600), so they are removed — the daemon\n // extracts from content. JSON-escape-aware: measures ACTUAL jsonBytes of each\n // candidate so escaping (\\n -> \\\\\\\\n) can't overshoot.\n const slim: ObserveMessage = { role: message.role, content: \"\" };\n const markerOnly = jsonBytes({ ...slim, content: TRUNCATION_MARKER });\n if (markerOnly > budgetBytes) {\n // Pathological: even the marker alone doesn't fit. Keep it anyway so the\n // turn isn't silently dropped.\n return { role: message.role, content: TRUNCATION_MARKER };\n }\n const fullContent = message.content + TRUNCATION_MARKER;\n if (jsonBytes({ ...slim, content: fullContent }) <= budgetBytes) {\n return { role: message.role, content: fullContent };\n }\n // Binary-search the largest content slice whose slim message fits. Slicing by\n // encoded bytes keeps multi-byte sequences intact where possible; the decoder\n // replaces any dangling tail with the replacement char.\n const encoded = encoder.encode(message.content);\n let lo = 0;\n let hi = encoded.length;\n while (lo < hi) {\n const mid = hi - Math.floor((hi - lo) / 2);\n const candidate = decoder.decode(encoded.subarray(0, mid)) + TRUNCATION_MARKER;\n if (jsonBytes({ ...slim, content: candidate }) <= budgetBytes) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n const truncated = lo > 0 ? decoder.decode(encoded.subarray(0, lo)) : \"\";\n return { role: message.role, content: truncated + TRUNCATION_MARKER };\n}\n\nfunction mergeObserveResults(results: Record<string, unknown>[]): Record<string, unknown> {\n if (results.length === 0) return {};\n if (results.length === 1) return results[0];\n const merged: Record<string, unknown> = {};\n let countSum = 0;\n let hasCount = false;\n for (const result of results) {\n for (const key of Object.keys(result)) {\n const value = result[key];\n if (key === \"count\" && typeof value === \"number\" && Number.isFinite(value)) {\n countSum += value;\n hasCount = true;\n } else {\n merged[key] = value;\n }\n }\n }\n if (hasCount) merged.count = countSum;\n return merged;\n}\n\nfunction responseErrorMessage(response: Response, text: string, payload: unknown, parseError: unknown): string {\n if (!parseError && payload && typeof payload === \"object\") {\n if (\"error\" in payload && typeof payload.error === \"string\" && payload.error.trim().length > 0) {\n return payload.error;\n }\n if (\"message\" in payload && typeof payload.message === \"string\" && payload.message.trim().length > 0) {\n return payload.message;\n }\n }\n\n const snippet = text.trim().replace(/\\s+/g, \" \").slice(0, 200);\n if (snippet.length > 0) {\n return response.statusText ? `${response.statusText}: ${snippet}` : snippet;\n }\n return response.statusText || `HTTP ${response.status}`;\n}\n","import { createHash } from \"node:crypto\";\n\n// Leaf submodule (not the `@remnic/core` barrel) so the omp pre-bundle's\n// `bun build` does not pull the rest of core into the extension bundle.\n// `message-parts` is a pure parser with no storage/native deps. See PR #1641.\nimport { parsePiMessageParts, type LcmMessagePartInput } from \"@remnic/core/message-parts\";\n\nimport type { ObserveMessage, ObserveMessagePart } from \"./client.js\";\n\ntype PiMessage = Record<string, unknown>;\n\nexport function sessionKeyFromContext(ctx: { sessionManager?: { getSessionId?: () => string } }): string {\n const id = ctx.sessionManager?.getSessionId?.();\n return id && id.trim().length > 0 ? `pi:${id}` : \"pi:default\";\n}\n\nexport function textFromMessage(message: unknown): string {\n if (!message || typeof message !== \"object\") return \"\";\n const obj = message as PiMessage;\n const role = typeof obj.role === \"string\" ? obj.role : \"message\";\n if (role === \"bashExecution\") {\n const command = typeof obj.command === \"string\" ? obj.command : \"\";\n const output = typeof obj.output === \"string\" ? obj.output : \"\";\n return [`Ran ${command}`, output].filter(Boolean).join(\"\\n\");\n }\n return textFromContent(obj.content).trim();\n}\n\nexport function latestUserQuery(messages: unknown[]): string {\n for (let index = messages.length - 1; index >= 0; index--) {\n const message = messages[index] as PiMessage;\n if (isExcludedFromContext(message) || isRemnicInjected(message)) continue;\n if (message?.role === \"user\") {\n const text = textFromMessage(message);\n if (text.length > 0) return text;\n }\n }\n return \"\";\n}\n\nexport function latestUserRecallTarget(\n messages: unknown[],\n): { query: string; dedupeKey: string } | null {\n for (let index = messages.length - 1; index >= 0; index--) {\n const message = messages[index] as PiMessage;\n if (isExcludedFromContext(message) || isRemnicInjected(message)) continue;\n if (message?.role !== \"user\") continue;\n const query = textFromMessage(message);\n if (query.length === 0) continue;\n const identity = stableObservedMessageIdentity(message);\n return {\n query,\n dedupeKey: identity ? `message:${identity}:${query}` : `query:${query}`,\n };\n }\n return null;\n}\n\nexport function toObserveMessage(message: unknown): ObserveMessage | null {\n if (!message || typeof message !== \"object\") return null;\n const obj = message as PiMessage;\n if (isExcludedFromContext(obj) || isRemnicInjected(obj)) return null;\n const role = obj.role === \"user\" || obj.role === \"bashExecution\" ? \"user\" : \"assistant\";\n const content = textFromMessage(obj);\n if (content.length === 0) return null;\n return {\n role,\n content,\n sourceFormat: \"pi\",\n rawContent: obj,\n parts: partsFromMessage(obj, content),\n };\n}\n\nexport function hashObservedMessage(message: ObserveMessage, sessionKey = \"\", identity = \"content\"): string {\n return createHash(\"sha256\")\n .update(sessionKey)\n .update(\"\\0\")\n .update(message.role)\n .update(\"\\0\")\n .update(identity)\n .update(\"\\0\")\n .update(message.content)\n .digest(\"hex\");\n}\n\nexport function observedMessageDedupeKey(\n message: ObserveMessage,\n sessionKey = \"\",\n): string | null {\n const identity = stableObservedMessageIdentity(message.rawContent);\n return identity ? hashObservedMessage(message, sessionKey, identity) : null;\n}\n\nexport function summarizeMessages(messages: unknown[], maxChars: number): string {\n const chunks: string[] = [];\n let used = 0;\n for (const message of messages) {\n if (isExcludedFromContext(message) || isRemnicInjected(message)) continue;\n const text = textFromMessage(message);\n if (!text) continue;\n const role = typeof (message as PiMessage)?.role === \"string\" ? (message as PiMessage).role : \"message\";\n const line = `[${role}] ${text}`;\n const separatorLength = chunks.length > 0 ? 2 : 0;\n const remaining = maxChars - used - separatorLength;\n if (remaining <= 0) break;\n const clipped = line.length > remaining ? line.slice(0, remaining) : line;\n if (clipped.length > 0) chunks.push(clipped);\n used += separatorLength + clipped.length;\n if (used >= maxChars) break;\n }\n return chunks.join(\"\\n\\n\");\n}\n\nexport function isExcludedFromContext(message: unknown): boolean {\n return !!message && typeof message === \"object\" && (message as PiMessage).excludeFromContext === true;\n}\n\nexport function isRemnicInjected(message: unknown): boolean {\n return !!message && typeof message === \"object\" && (message as PiMessage).remnicInjected === true;\n}\n\nfunction textFromContent(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n const chunks: string[] = [];\n for (const block of content) {\n if (!block || typeof block !== \"object\") continue;\n const obj = block as PiMessage;\n if (obj.type === \"text\" && typeof obj.text === \"string\") chunks.push(obj.text);\n if (obj.type === \"toolCall\" && typeof obj.name === \"string\") {\n chunks.push(`Tool ${obj.name} called with ${JSON.stringify(obj.arguments ?? {})}`);\n }\n }\n return chunks.join(\"\\n\");\n}\n\nfunction partsFromMessage(message: PiMessage, renderedContent: string): ObserveMessagePart[] {\n return parsePiMessageParts(message, {\n renderedContent,\n allowRenderedFallback: true,\n }).map(toObserveMessagePart);\n}\n\nfunction toObserveMessagePart(part: LcmMessagePartInput): ObserveMessagePart {\n return {\n ordinal: part.ordinal ?? undefined,\n kind: part.kind,\n payload: part.payload,\n toolName: part.toolName ?? part.tool_name ?? undefined,\n filePath: part.filePath ?? part.file_path ?? undefined,\n createdAt: part.createdAt ?? part.created_at ?? undefined,\n };\n}\n\nfunction stableObservedMessageIdentity(rawContent: unknown): string | null {\n if (rawContent && typeof rawContent === \"object\") {\n const obj = rawContent as PiMessage;\n const fields = [\n \"id\",\n \"entryId\",\n \"entry_id\",\n \"messageId\",\n \"message_id\",\n \"turnId\",\n \"turn_id\",\n \"timestamp\",\n \"createdAt\",\n \"created_at\",\n ];\n for (const field of fields) {\n const value = obj[field];\n if (typeof value === \"string\" && value.length > 0) return `${field}:${value}`;\n if (typeof value === \"number\" && Number.isFinite(value)) return `${field}:${value}`;\n }\n }\n return null;\n}\n"],"mappings":"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","names":["remnicPiExtension"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/client.ts","../src/messages.ts"],"sourcesContent":["import { Type, type TSchema } from \"@sinclair/typebox\";\n\nimport { loadConfig, type LoadConfigOptions, type RemnicPiConfig } from \"./config.js\";\nimport { RemnicClient, isTransientNetworkError, type McpTool, type ObserveMessage } from \"./client.js\";\nimport {\n hashObservedMessage,\n latestUserRecallTarget,\n observedMessageDedupeKey,\n sessionKeyFromContext,\n summarizeMessages,\n textFromMessage,\n toObserveMessage,\n} from \"./messages.js\";\n\ntype PiApi = {\n on(event: string, handler: (event: any, ctx: any) => unknown | Promise<unknown>): void;\n registerCommand(name: string, options: { description?: string; handler: (args: string, ctx: any) => Promise<void> }): void;\n registerTool(tool: Record<string, unknown>): void;\n appendEntry<T = unknown>(customType: string, data?: T): void;\n};\n\nexport interface RemnicPiExtensionOptions extends LoadConfigOptions {\n config?: RemnicPiConfig;\n}\n\nconst STATE_CUSTOM_TYPE = \"remnic_state\";\nconst MAX_OBSERVED_HASHES = 2000;\nconst MAX_SESSION_STATES = 50;\nconst MAX_CONTEXT_CHARS = 12000;\nconst TRUNCATION_NOTICE = \"\\n\\n[Remnic context truncated]\";\nconst SESSION_OWNED_FIELDS = new Set([\"sessionKey\", \"namespace\", \"cwd\"]);\n\ntype PiSessionState = {\n observedHashes: Set<string>;\n liveObservedReplayKeys: Map<string, number>;\n lastInjectedRecallKey: string;\n};\n\ntype NotifyLevel = \"info\" | \"success\" | \"warning\" | \"error\";\ntype NotifyFn = (message: string, level: NotifyLevel) => void;\n\ntype PiContextSnapshot = {\n sessionKey: string;\n cwd: string;\n entries: any[];\n branch: any[];\n notify: NotifyFn;\n setStatus: (key: string, value: string) => void;\n compact?: () => unknown;\n};\ntype PiContextSnapshotOptions = {\n includeSessionHistory?: boolean;\n};\n\nexport function createRemnicPiExtension(options: RemnicPiExtensionOptions = {}) {\n const config = options.config ?? loadConfig(options);\n const client = new RemnicClient(config);\n const sessionStates = new Map<string, PiSessionState>();\n\n return async function remnicPiExtension(pi: PiApi): Promise<void> {\n pi.on(\"session_start\", async (_event, ctx) => {\n const session = snapshotPiContext(ctx, { includeSessionHistory: true });\n if (!session) return;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n restoreObservedState(session, state.observedHashes);\n // Probe health + update the circuit breaker UNCONDITIONALLY so an offline\n // daemon is marked unreachable even when the status UI is off; otherwise\n // the namespace preflight and every later hook each burn a full request\n // budget on a doomed call. The status LABEL stays gated on statusEnabled.\n const reachable = await probeDaemonHealth(client, config);\n if (config.statusEnabled) {\n session.setStatus(\n \"remnic\",\n reachable ? `Remnic ${config.namespace ? `(${config.namespace})` : \"ready\"}` : \"Remnic offline\",\n );\n }\n await runNamespacePreflight(pi, session, client, config);\n });\n\n pi.on(\"context\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.recallEnabled || !config.authToken) return;\n // Circuit breaker: skip recall fast while the daemon is known-down so a\n // dead host doesn't block every turn on a doomed request (#1626).\n if (!client.isReachable()) return;\n const recallTarget = latestUserRecallTarget(Array.isArray(event.messages) ? event.messages : []);\n if (!recallTarget) return;\n const { query } = recallTarget;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n if (recallTarget.dedupeKey === state.lastInjectedRecallKey) return;\n\n try {\n const recalled = await client.recall(query, session.sessionKey, session.cwd, {\n timeoutMs: config.turnRequestTimeoutMs,\n });\n client.markReachable();\n const context = trimContext(recalled.context ?? \"\", config.recallBudgetChars);\n if (!context) return;\n state.lastInjectedRecallKey = recallTarget.dedupeKey;\n return {\n messages: [\n ...event.messages,\n {\n role: \"user\",\n content: [{ type: \"text\", text: `Remnic recalled context for this turn:\\n\\n${context}` }],\n remnicInjected: true,\n timestamp: Date.now(),\n },\n ],\n };\n } catch (err) {\n // Only trip the breaker when the daemon is genuinely unreachable\n // (timeout or connection-level failure) — not on a transient HTTP\n // error, so a one-off failure still retries on the next turn (#1626).\n if (isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);\n session.notify(`Remnic recall unavailable: ${errorMessage(err)}`, \"warning\");\n }\n });\n\n pi.on(\"message_end\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.observeEnabled || !isUserMessage(event.message)) return;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n await observeMessagesForSession(session, client, [event.message], state.observedHashes, state.liveObservedReplayKeys, config);\n });\n\n pi.on(\"turn_end\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.observeEnabled) return;\n const messages = [event.message, ...(Array.isArray(event.toolResults) ? event.toolResults : [])];\n const { state } = getSessionState(session.sessionKey, sessionStates);\n await observeMessagesForSession(session, client, messages, state.observedHashes, state.liveObservedReplayKeys, config);\n });\n\n pi.on(\"session_shutdown\", async (_event, ctx) => {\n const session = snapshotPiContext(ctx, { includeSessionHistory: true });\n if (!session) return;\n const { sessionKey, state } = getSessionState(session.sessionKey, sessionStates);\n if (config.observeEnabled) {\n const branchMessages = branchMessagesWithEntryIdentity(session.branch);\n const unobservedBranchMessages = skipLiveObservedReplayMessages(session.sessionKey, branchMessages, state.liveObservedReplayKeys);\n if (unobservedBranchMessages.length > 0) {\n await observeMessagesForSession(session, client, unobservedBranchMessages, state.observedHashes, undefined, config, true);\n }\n }\n persistObservedState(pi, state.observedHashes);\n sessionStates.delete(sessionKey);\n });\n\n pi.on(\"session_before_compact\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.compactionEnabled || !config.authToken) return;\n const preparation = event.preparation ?? {};\n try {\n await client.lcmCompactionFlush(session.sessionKey);\n } catch (err) {\n session.notify(`Remnic LCM flush failed: ${errorMessage(err)}`, \"warning\");\n return;\n }\n\n const tokensBefore = finiteTokenCount(preparation.tokensBefore);\n const tokensAfter = finiteTokenCount(preparation.tokensAfter);\n if (tokensBefore !== null && tokensAfter !== null) {\n try {\n await client.lcmCompactionRecord(session.sessionKey, tokensBefore, tokensAfter);\n } catch (err) {\n session.notify(`Remnic LCM compaction token record failed: ${errorMessage(err)}`, \"warning\");\n }\n }\n\n const summary = buildCompactionSummary(preparation);\n if (!summary.trim()) return;\n try {\n await client.contextCheckpoint(session.sessionKey, summary);\n } catch (err) {\n session.notify(`Remnic context checkpoint failed: ${errorMessage(err)}`, \"warning\");\n }\n const details = fileDetailsFromPreparation(preparation);\n return {\n compaction: {\n summary,\n firstKeptEntryId: preparation.firstKeptEntryId,\n tokensBefore: preparation.tokensBefore,\n details: {\n ...details,\n remnic: { version: 1, source: \"pi\" },\n },\n },\n };\n });\n\n registerCommands(pi, client, config);\n if (config.mcpToolsEnabled && config.authToken) {\n await registerMcpTools(pi, client, config);\n }\n };\n}\n\nexport default async function remnicPiExtension(pi: PiApi): Promise<void> {\n await createRemnicPiExtension()(pi);\n}\n\nfunction registerCommands(pi: PiApi, client: RemnicClient, config: RemnicPiConfig): void {\n pi.registerCommand(\"remnic-status\", {\n description: \"Check Remnic daemon status\",\n handler: commandHandler(async (_args, _ctx, session) => {\n const health = await client.health();\n // The daemon responded (any HTTP result), so clear any stale cooldown a\n // prior timeout left on the shared client (cursor review).\n client.markReachable();\n session.notify(`Remnic ${health.ok ? \"healthy\" : \"unhealthy\"} at ${config.remnicDaemonUrl}`, health.ok ? \"success\" : \"warning\");\n }),\n });\n\n pi.registerCommand(\"remnic-recall\", {\n description: \"Recall Remnic context for a query\",\n handler: commandHandler(async (args, _ctx, session) => {\n const query = args.trim();\n if (!query) {\n session.notify(\"Usage: /remnic-recall <query>\", \"warning\");\n return;\n }\n // Pass the general request budget so requestWithRetry shares ONE deadline\n // across retries (total <= requestTimeoutMs) instead of looping through\n // observeMaxRetries full timeouts and blocking the interactive command\n // for several minutes on a flaky connection (cursor review).\n const result = await client.recall(query, session.sessionKey, session.cwd, {\n timeoutMs: config.requestTimeoutMs,\n });\n // The daemon responded, so clear any stale cooldown a prior timeout left\n // on the shared client (cursor review).\n client.markReachable();\n session.notify(trimContext(result.context ?? \"(no Remnic context)\", MAX_CONTEXT_CHARS), \"info\");\n }),\n });\n\n pi.registerCommand(\"remnic-remember\", {\n description: \"Store a Remnic memory\",\n handler: commandHandler(async (args, _ctx, session) => {\n const content = args.trim();\n if (!content) {\n session.notify(\"Usage: /remnic-remember <memory>\", \"warning\");\n return;\n }\n await client.storeMemory(content, session.sessionKey);\n session.notify(\"Stored Remnic memory\", \"success\");\n }),\n });\n\n pi.registerCommand(\"remnic-lcm-search\", {\n description: \"Search Remnic LCM archived Pi context\",\n handler: commandHandler(async (args, _ctx, session) => {\n const query = args.trim();\n if (!query) {\n session.notify(\"Usage: /remnic-lcm-search <query>\", \"warning\");\n return;\n }\n const result = await client.lcmSearch(query, session.sessionKey);\n session.notify(JSON.stringify(result, null, 2), \"info\");\n }),\n });\n\n pi.registerCommand(\"remnic-why\", {\n description: \"Explain the last Remnic recall\",\n handler: commandHandler(async (_args, _ctx, session) => {\n const result = await client.recallExplain(session.sessionKey);\n session.notify(JSON.stringify(result, null, 2), \"info\");\n }),\n });\n\n pi.registerCommand(\"remnic-compact\", {\n description: \"Trigger Pi compaction with Remnic LCM coordination\",\n handler: commandHandler(async (_args, _ctx, session) => {\n session.compact?.();\n session.notify(\"Compaction requested\", \"info\");\n }),\n });\n}\n\nfunction commandHandler(\n handler: (args: string, ctx: any, session: PiContextSnapshot) => Promise<void>,\n): (args: string, ctx: any) => Promise<void> {\n return async (args, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n try {\n await handler(args, ctx, session);\n } catch (err) {\n session.notify(`Remnic command failed: ${errorMessage(err)}`, \"warning\");\n }\n };\n}\n\nasync function registerMcpTools(pi: PiApi, client: RemnicClient, config: RemnicPiConfig): Promise<void> {\n let tools: McpTool[] = [];\n try {\n tools = await client.mcpListTools({ timeoutMs: config.startupRequestTimeoutMs });\n } catch {\n return;\n }\n for (const tool of tools) {\n if (!tool.name.startsWith(\"remnic.\")) continue;\n const piToolName = tool.name.replace(/^remnic\\./, \"remnic_\").replace(/[^a-zA-Z0-9_]/g, \"_\");\n pi.registerTool({\n name: piToolName,\n label: tool.name,\n description: tool.description ?? `Call ${tool.name}`,\n parameters: toPiToolParametersSchema(tool.inputSchema),\n async execute(_toolCallId: string, params: Record<string, unknown>, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: any) {\n const session = snapshotPiContext(ctx);\n if (!session) {\n return {\n content: [{ type: \"text\", text: \"Remnic tool skipped because the Pi context is no longer active.\" }],\n details: { skipped: true, reason: \"stale_context\" },\n };\n }\n const safeParams = stripSessionOwnedRuntimeFields(params ?? {}) as Record<string, unknown>;\n const result = await client.mcpTool(tool.name, {\n ...safeParams,\n sessionKey: session.sessionKey,\n namespace: config.namespace,\n cwd: session.cwd,\n });\n return {\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\n details: result,\n };\n },\n });\n }\n}\n\nexport function toPiToolParametersSchema(inputSchema: unknown): TSchema {\n return Type.Unsafe(stripSessionOwnedSchemaFields(inputSchema));\n}\n\nexport function stripSessionOwnedSchemaFields(inputSchema: unknown): Record<string, unknown> {\n if (!isRecord(inputSchema)) {\n return { type: \"object\", properties: {}, additionalProperties: true };\n }\n return stripSessionOwnedSchemaNode(inputSchema) as Record<string, unknown>;\n}\n\nfunction stripSessionOwnedSchemaNode(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripSessionOwnedSchemaNode(entry));\n }\n if (!isRecord(value)) {\n return value;\n }\n const schema: Record<string, unknown> = { ...value };\n if (isRecord(value.properties)) {\n const properties: Record<string, unknown> = {};\n for (const [key, property] of Object.entries(value.properties)) {\n if (SESSION_OWNED_FIELDS.has(key)) continue;\n properties[key] = stripSessionOwnedSchemaNode(property);\n }\n schema.properties = properties;\n }\n if (Array.isArray(value.required)) {\n schema.required = value.required.filter(\n (field) => typeof field !== \"string\" || !SESSION_OWNED_FIELDS.has(field),\n );\n }\n for (const key of [\"items\", \"additionalProperties\", \"not\"] as const) {\n if (isRecord(value[key])) {\n schema[key] = stripSessionOwnedSchemaNode(value[key]);\n }\n }\n for (const key of [\"oneOf\", \"anyOf\", \"allOf\"] as const) {\n if (Array.isArray(value[key])) {\n schema[key] = value[key].map((entry) => stripSessionOwnedSchemaNode(entry));\n }\n }\n return schema;\n}\n\nexport function stripSessionOwnedRuntimeFields(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripSessionOwnedRuntimeFields(entry));\n }\n if (!isRecord(value)) {\n return value;\n }\n const sanitized: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n if (SESSION_OWNED_FIELDS.has(key)) continue;\n sanitized[key] = stripSessionOwnedRuntimeFields(child);\n }\n return sanitized;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isUserMessage(message: unknown): boolean {\n return isRecord(message) && message.role === \"user\";\n}\n\nfunction getSessionState(sessionKey: string, states: Map<string, PiSessionState>): { sessionKey: string; state: PiSessionState } {\n let state = states.get(sessionKey);\n if (!state) {\n state = {\n observedHashes: new Set<string>(),\n liveObservedReplayKeys: new Map<string, number>(),\n lastInjectedRecallKey: \"\",\n };\n states.set(sessionKey, state);\n pruneSessionStates(states);\n }\n return { sessionKey, state };\n}\n\nfunction pruneSessionStates(states: Map<string, PiSessionState>): void {\n while (states.size > MAX_SESSION_STATES) {\n const oldest = states.keys().next().value;\n if (typeof oldest !== \"string\") return;\n states.delete(oldest);\n }\n}\n\nexport async function observeMessages(\n ctx: any,\n client: RemnicClient,\n rawMessages: unknown[],\n observedHashes: Set<string>,\n liveObservedReplayKeys?: Map<string, number>,\n): Promise<void> {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n await observeMessagesForSession(session, client, rawMessages, observedHashes, liveObservedReplayKeys);\n}\n\nasync function observeMessagesForSession(\n session: PiContextSnapshot,\n client: RemnicClient,\n rawMessages: unknown[],\n observedHashes: Set<string>,\n liveObservedReplayKeys?: Map<string, number>,\n config?: RemnicPiConfig,\n forceAttempt = false,\n): Promise<void> {\n const messages: ObserveMessage[] = [];\n const pendingHashes = new Set<string>();\n for (const raw of rawMessages) {\n const message = toObserveMessage(raw);\n if (!message) continue;\n const hash = observedMessageDedupeKey(message, session.sessionKey);\n if (hash && (observedHashes.has(hash) || pendingHashes.has(hash))) continue;\n if (hash) pendingHashes.add(hash);\n messages.push(message);\n }\n if (messages.length === 0) return;\n // Circuit breaker: when config is wired (the live Pi handlers always pass\n // it), skip observe fast while the daemon is known-down so a dead host\n // doesn't burn the full per-turn budget on every turn (#1626). Shutdown is\n // the exception — it is the last chance to observe the branch before the\n // session tears down, so force the attempt even mid-cooldown; a failure\n // still trips the breaker normally (codex review).\n if (config && !forceAttempt && !client.isReachable()) return;\n // Live turn hooks are bounded by the per-turn budget to protect the host's\n // ~30s handler window (#1626). Shutdown is teardown with no such constraint,\n // so the forced replay uses the general request budget — otherwise a large\n // unobserved branch would time out exactly when forceAttempt tried to save it\n // (cursor review).\n const observeOptions = config\n ? { timeoutMs: forceAttempt ? config.requestTimeoutMs : config.turnRequestTimeoutMs }\n : undefined;\n try {\n await client.observe(session.sessionKey, session.cwd, messages, observeOptions);\n if (config) client.markReachable();\n for (const hash of pendingHashes) rememberObservedHash(observedHashes, hash);\n if (liveObservedReplayKeys) {\n for (const message of messages) {\n rememberLiveObservedReplayKey(liveObservedReplayKeys, liveReplayKey(message, session.sessionKey));\n }\n }\n } catch (err) {\n if (config && isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);\n session.notify(`Remnic observe failed: ${errorMessage(err)}`, \"warning\");\n }\n}\n\nexport function buildCompactionSummary(preparation: any): string {\n const previousSummary = typeof preparation.previousSummary === \"string\"\n ? preparation.previousSummary.trim()\n : \"\";\n const messages = [\n ...(Array.isArray(preparation.messagesToSummarize) ? preparation.messagesToSummarize : []),\n ...(Array.isArray(preparation.turnPrefixMessages) ? preparation.turnPrefixMessages : []),\n ];\n const transcript = summarizeMessages(messages, 24000);\n const details = fileDetailsFromPreparation(preparation);\n\n if (\n !previousSummary &&\n !transcript &&\n details.readFiles.length === 0 &&\n details.modifiedFiles.length === 0\n ) {\n return \"\";\n }\n\n const sections: string[] = [\n \"## Remnic Pi Context Checkpoint\",\n \"\",\n \"This checkpoint was created by Remnic during Pi context compaction.\",\n ];\n if (previousSummary) sections.push(\"\", \"## Previous Summary\", previousSummary);\n if (transcript) sections.push(\"\", \"## Conversation Excerpt\", transcript);\n if (details.readFiles.length > 0) sections.push(\"\", \"<read-files>\", ...details.readFiles, \"</read-files>\");\n if (details.modifiedFiles.length > 0) sections.push(\"\", \"<modified-files>\", ...details.modifiedFiles, \"</modified-files>\");\n return sections.join(\"\\n\");\n}\n\nfunction fileDetailsFromPreparation(preparation: any): { readFiles: string[]; modifiedFiles: string[] } {\n const fileOps = preparation?.fileOps;\n const read = fileOps?.read instanceof Set ? Array.from(fileOps.read).filter(isString) : [];\n const edited = fileOps?.edited instanceof Set ? Array.from(fileOps.edited).filter(isString) : [];\n const written = fileOps?.written instanceof Set ? Array.from(fileOps.written).filter(isString) : [];\n const modified = new Set([...edited, ...written]);\n return {\n readFiles: read.filter((file) => !modified.has(file)).sort(),\n modifiedFiles: Array.from(modified).sort(),\n };\n}\n\nfunction restoreObservedState(session: PiContextSnapshot, observedHashes: Set<string>): void {\n for (const entry of session.entries) {\n if (entry?.type !== \"custom\" || entry.customType !== STATE_CUSTOM_TYPE) continue;\n const hashes = entry.data?.observedHashes;\n if (Array.isArray(hashes)) {\n for (const hash of hashes) {\n if (typeof hash === \"string\") rememberObservedHash(observedHashes, hash);\n }\n }\n }\n}\n\nfunction rememberObservedHash(observedHashes: Set<string>, hash: string): void {\n if (observedHashes.has(hash)) return;\n while (observedHashes.size >= MAX_OBSERVED_HASHES) {\n const oldest = observedHashes.keys().next().value;\n if (typeof oldest !== \"string\") break;\n observedHashes.delete(oldest);\n }\n observedHashes.add(hash);\n}\n\nfunction rememberLiveObservedReplayKey(liveObservedReplayKeys: Map<string, number>, key: string): void {\n liveObservedReplayKeys.set(key, (liveObservedReplayKeys.get(key) ?? 0) + 1);\n}\n\nfunction consumeLiveObservedReplayKey(liveObservedReplayKeys: Map<string, number>, key: string): boolean {\n const count = liveObservedReplayKeys.get(key) ?? 0;\n if (count <= 0) return false;\n if (count === 1) liveObservedReplayKeys.delete(key);\n else liveObservedReplayKeys.set(key, count - 1);\n return true;\n}\n\nfunction skipLiveObservedReplayMessages(\n sessionKey: string,\n rawMessages: unknown[],\n liveObservedReplayKeys: Map<string, number>,\n): unknown[] {\n if (liveObservedReplayKeys.size === 0) return rawMessages;\n const unobserved: unknown[] = [];\n for (const raw of rawMessages) {\n const message = toObserveMessage(raw);\n if (message && consumeLiveObservedReplayKey(liveObservedReplayKeys, liveReplayKey(message, sessionKey))) {\n continue;\n }\n unobserved.push(raw);\n }\n return unobserved;\n}\n\nfunction liveReplayKey(message: ObserveMessage, sessionKey: string): string {\n return hashObservedMessage(message, sessionKey, \"live-replay\");\n}\n\nfunction persistObservedState(pi: PiApi, observedHashes: Set<string>): void {\n const observed = Array.from(observedHashes).slice(-MAX_OBSERVED_HASHES);\n pi.appendEntry(STATE_CUSTOM_TYPE, {\n observedHashes: observed,\n recordedAt: new Date().toISOString(),\n });\n}\n\n/**\n * Probe the daemon and update the shared circuit breaker. Returns whether the\n * daemon is reachable so the caller can render a status label. This runs at\n * session_start regardless of `statusEnabled`: the breaker update is a\n * data-path concern (a down daemon must be marked unreachable so the namespace\n * preflight and every later hook fast-skip instead of each burning a full\n * request budget), independent of whether the status UI is shown.\n */\nasync function probeDaemonHealth(client: RemnicClient, config: RemnicPiConfig): Promise<boolean> {\n try {\n await client.health({ timeoutMs: config.startupRequestTimeoutMs });\n // A successful probe means the daemon is reachable, so clear any stale\n // cooldown a prior recall/observe timeout left on the shared client.\n client.markReachable();\n return true;\n } catch (err) {\n // Startup just proved the daemon is unreachable, so trip the fast-skip\n // breaker — otherwise the first live hook spends the full turn budget on a\n // doomed request before the breaker would trip on its own.\n if (isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);\n return false;\n }\n}\n\n/**\n * Startup namespace-writability preflight (issue #1888 part 3). Runs at each\n * session_start. When the configured namespace is NOT writable for this\n * client's principal, every memory write is rejected and — since the\n * dead-letter quarantine landed — parked, never stored. A silent per-call\n * rejection is invisible; this surfaces it LOUDLY and persistently (an error\n * `remnic_state` entry + error notification, re-emitted every session while\n * broken).\n *\n * `appendEntry` has no delete, so the CURRENT state is always recorded: a\n * `NAMESPACE_OK` entry on a writable result makes the latest `remnic_state`\n * entry authoritative even across an extension/host restart (no in-memory\n * transition tracking that a restart would lose). Only errors notify — the OK\n * entry is a silent heartbeat, never a success toast every healthy session. A\n * daemon that cannot be reached (indeterminate) records nothing, leaving the\n * last known state intact — we neither cry wolf nor falsely clear a real error.\n */\nasync function runNamespacePreflight(\n pi: PiApi,\n session: PiContextSnapshot,\n client: RemnicClient,\n config: RemnicPiConfig,\n): Promise<void> {\n // No token → the client cannot write anyway; known-unreachable → the answer\n // would be indeterminate. Either way, do not touch the recorded state.\n if (!config.authToken || !client.isReachable()) return;\n const result = await client.preflightNamespace(session.sessionKey, {\n timeoutMs: config.startupRequestTimeoutMs,\n });\n if (result.status === \"not_writable\") {\n // The remediation differs by cause: `unsupported` means the daemon has\n // namespaces disabled, so ONLY its default namespace is writable — pointing\n // the operator at namespacePolicies would be misleading.\n const fix =\n result.reason === \"unsupported\"\n ? \"The daemon has namespaces disabled, so only its default namespace is writable — set the client's namespace to the daemon's defaultNamespace (or omit it).\"\n : \"Fix the client's namespace config: it must match a namespacePolicies entry, or be the daemon's defaultNamespace/sharedNamespace.\";\n const message =\n `Remnic: configured namespace \"${result.namespace}\" is NOT writable for this client's principal ` +\n `(${result.reason}). Every memory write will be rejected and dead-lettered (recoverable via ` +\n `\\`remnic quarantine list\\`), NOT stored. ${fix}`;\n session.notify(message, \"error\");\n pi.appendEntry(STATE_CUSTOM_TYPE, {\n level: \"error\",\n code: \"NAMESPACE_NOT_WRITABLE\",\n namespace: result.namespace,\n reason: result.reason,\n message,\n persistent: true,\n recordedAt: new Date().toISOString(),\n });\n return;\n }\n if (result.status === \"writable\") {\n pi.appendEntry(STATE_CUSTOM_TYPE, {\n level: \"info\",\n code: \"NAMESPACE_OK\",\n namespace: result.namespace,\n recordedAt: new Date().toISOString(),\n });\n }\n // indeterminate → record nothing; keep the last known state.\n}\n\nfunction snapshotPiContext(ctx: any, options: PiContextSnapshotOptions = {}): PiContextSnapshot | null {\n const sessionKey = safeSessionKeyFromContext(ctx);\n if (!sessionKey) return null;\n const cwd = safeStringRead(() => ctx?.cwd, \"\");\n const hasUI = safeRead(() => ctx?.hasUI, undefined) === false;\n const ui = hasUI ? undefined : safeRead(() => ctx?.ui, undefined);\n const compact = safeRead(() => ctx?.compact, undefined);\n const includeSessionHistory = options.includeSessionHistory === true;\n return {\n sessionKey,\n cwd,\n entries: includeSessionHistory ? safeEntries(ctx) : [],\n branch: includeSessionHistory ? safeBranch(ctx) : [],\n notify: makeNotifier(ui, hasUI),\n setStatus: makeStatusSetter(ui, hasUI),\n compact: typeof compact === \"function\" ? () => compact.call(ctx) : undefined,\n };\n}\n\nfunction safeSessionKeyFromContext(ctx: any): string | null {\n try {\n return sessionKeyFromContext(ctx);\n } catch {\n return null;\n }\n}\n\nfunction makeNotifier(ui: unknown, hasUI: boolean): NotifyFn {\n if (hasUI || !isRecord(ui) || typeof ui.notify !== \"function\") {\n return () => undefined;\n }\n const notifyFn = ui.notify;\n return (message, level) => {\n try {\n notifyFn.call(ui, message, level);\n } catch {\n // Pi invalidates session-bound UI objects during reload/replacement. A\n // notification failure must not tear down Remnic's hooks.\n }\n };\n}\n\nfunction makeStatusSetter(ui: unknown, hasUI: boolean): PiContextSnapshot[\"setStatus\"] {\n if (hasUI || !isRecord(ui) || typeof ui.setStatus !== \"function\") {\n return () => undefined;\n }\n const setStatusFn = ui.setStatus;\n return (key, value) => {\n try {\n setStatusFn.call(ui, key, value);\n } catch {\n // See makeNotifier: stale UI should not make extension startup fail.\n }\n };\n}\n\nfunction safeRead<T>(read: () => T, fallback: T): T {\n try {\n return read();\n } catch {\n return fallback;\n }\n}\n\nfunction safeStringRead(read: () => unknown, fallback: string): string {\n const value = safeRead(read, fallback);\n return typeof value === \"string\" ? value : fallback;\n}\n\nfunction safeEntries(ctx: any): any[] {\n try {\n const entries = ctx.sessionManager?.getEntries?.();\n return Array.isArray(entries) ? entries : [];\n } catch {\n return [];\n }\n}\n\nfunction safeBranch(ctx: any): any[] {\n try {\n const branch = ctx.sessionManager?.getBranch?.();\n return Array.isArray(branch) ? branch : [];\n } catch {\n return [];\n }\n}\n\nfunction branchMessagesWithEntryIdentity(branch: any[]): unknown[] {\n const messages: unknown[] = [];\n for (const entry of branch) {\n const message = messageWithEntryIdentity(entry);\n if (message) messages.push(message);\n }\n return messages;\n}\n\nfunction messageWithEntryIdentity(entry: any): unknown | null {\n const message = entry?.message;\n if (!message || typeof message !== \"object\" || Array.isArray(message)) return message ?? null;\n\n const source = isRecord(entry) ? entry : {};\n const enriched: Record<string, unknown> = { ...(message as Record<string, unknown>) };\n assignMissingIdentity(enriched, \"entryId\", source.id ?? source.entryId ?? source.entry_id);\n assignMissingIdentity(enriched, \"timestamp\", source.timestamp);\n assignMissingIdentity(enriched, \"createdAt\", source.createdAt ?? source.created_at);\n return enriched;\n}\n\nfunction assignMissingIdentity(target: Record<string, unknown>, field: string, value: unknown): void {\n if (target[field] !== undefined) return;\n if (typeof value === \"string\" && value.length > 0) {\n target[field] = value;\n return;\n }\n if (typeof value === \"number\" && Number.isFinite(value)) {\n target[field] = value;\n }\n}\n\nfunction trimContext(value: string, budget: number): string {\n if (value.length <= budget) return value;\n if (budget <= TRUNCATION_NOTICE.length) return TRUNCATION_NOTICE.slice(0, budget);\n return `${value.slice(0, budget - TRUNCATION_NOTICE.length)}${TRUNCATION_NOTICE}`;\n}\n\n\nexport function isDaemonUnreachableError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (/Remnic request timed out/.test(err.message)) return true;\n // Retry-budget exhaustion means transient failures ate the whole per-turn\n // deadline inside requestWithRetry — the daemon is effectively unreachable\n // for this turn, so trip the breaker and cool down instead of burning another\n // full budget on the next hook (codex review). This error only arises from\n // transient connection failures, never from a semantic HTTP response.\n if (/Remnic request exceeded the \\d+ms budget before retry/.test(err.message)) return true;\n // Multi-chunk observe throws its own budget-exceeded message when the shared\n // per-turn deadline is exhausted across chunks; that is also an effectively-\n // unreachable condition for the turn, so trip the breaker and fast-skip\n // subsequent turns instead of piling on more doomed chunked observes (cursor).\n if (/Remnic observe exceeded the per-turn budget of \\d+ms/.test(err.message)) return true;\n return isTransientNetworkError(err);\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction finiteTokenCount(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 ? value : null;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\nexport { textFromMessage };\n","import { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\n// Leaf submodule (not the `@remnic/core` barrel) so the omp pre-bundle's\n// `bun build` does not pull the rest of core — including the LanceDB native\n// asset — into the extension bundle. See PR #1641.\nimport { expandTildePath } from \"@remnic/core/utils/path\";\n\nimport { REMNIC_PI_EXTENSION_DIR_NAME, resolvePiAgentHome } from \"./paths.js\";\n\nexport interface RemnicPiConfig {\n remnicDaemonUrl: string;\n authToken?: string;\n namespace?: string;\n recallMode: \"auto\" | \"minimal\" | \"full\" | \"graph_mode\" | \"no_recall\";\n recallTopK: number;\n recallBudgetChars: number;\n recallEnabled: boolean;\n observeEnabled: boolean;\n observeSkipExtraction: boolean;\n compactionEnabled: boolean;\n mcpToolsEnabled: boolean;\n statusEnabled: boolean;\n requestTimeoutMs: number;\n startupRequestTimeoutMs: number;\n /**\n * Per-turn request budget for observe/recall. MUST stay below the host's\n * in-handler kill budget (Pi/omp kills handlers at 30 s). Defaults to 20 s,\n * capped at 25 s so a misconfiguration can never produce a structurally\n * unsatisfiable timeout (issue #1626).\n */\n turnRequestTimeoutMs: number;\n /**\n * Soft cap on a single observe POST body in bytes. The client chunks observe\n * batches to stay under this; individual oversized messages are truncated\n * with a marker. Defaults to 100 KiB, safely under the daemon's default\n * 128 KiB `maxBodyBytes` (issue #1600).\n */\n observeMaxBytes: number;\n /**\n * Maximum retry attempts for observe/recall on transient connection-level\n * failures (socket close, ECONNRESET, EPIPE). Observe is dedupe-safe so\n * retrying is harmless (issue #1602).\n */\n observeMaxRetries: number;\n /**\n * Cooldown base for the daemon-reachability circuit breaker. When observe/\n * recall fails on a timeout or connection error, subsequent turns skip fast\n * for an exponentially growing window starting at this value (issue #1626).\n */\n daemonCooldownMs: number;\n}\n\nexport interface LoadConfigOptions {\n configPath?: string;\n env?: NodeJS.ProcessEnv;\n}\n\nconst DEFAULT_CONFIG: RemnicPiConfig = {\n remnicDaemonUrl: \"http://127.0.0.1:4318\",\n recallMode: \"auto\",\n recallTopK: 8,\n recallBudgetChars: 12000,\n recallEnabled: true,\n observeEnabled: true,\n observeSkipExtraction: false,\n compactionEnabled: true,\n mcpToolsEnabled: true,\n statusEnabled: true,\n requestTimeoutMs: 60000,\n startupRequestTimeoutMs: 1000,\n // Default 20 s is comfortably under the Pi/omp 30 s handler budget (#1626).\n turnRequestTimeoutMs: 20000,\n // Default 100 KiB leaves headroom under the daemon's 128 KiB default (#1600).\n observeMaxBytes: 102400,\n observeMaxRetries: 2,\n // Base cooldown for the circuit breaker; doubles on consecutive failures (#1626).\n daemonCooldownMs: 5000,\n};\n\nfunction defaultConfigPath(env: NodeJS.ProcessEnv): string {\n return path.join(resolvePiAgentHome(env), \"extensions\", REMNIC_PI_EXTENSION_DIR_NAME, \"remnic.config.json\");\n}\n\nfunction coerceBoolean(value: unknown, fallback: boolean, fieldName: string): boolean {\n if (value === undefined || value === null) return fallback;\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true;\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false;\n }\n throw new Error(`Invalid boolean value for Remnic Pi config field ${fieldName}`);\n}\n\nfunction coercePositiveInt(value: unknown, fallback: number, max: number, fieldName: string): number {\n if (value === undefined || value === null || value === \"\") return fallback;\n let parsed: number;\n if (typeof value === \"number\") {\n parsed = value;\n } else if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.length === 0) return fallback;\n if (!/^[+-]?\\d+$/.test(trimmed)) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 1 to ${max}`);\n }\n parsed = Number(trimmed);\n } else {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 1 to ${max}`);\n }\n if (!Number.isInteger(parsed) || parsed <= 0 || parsed > max) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 1 to ${max}`);\n }\n return parsed;\n}\n\n/**\n * Like {@link coercePositiveInt} but allows 0, for knobs where 0 is a\n * meaningful \"disabled\" value (e.g. observeMaxRetries). Still rejects\n * negatives, non-integers, and values above the cap.\n */\nfunction coerceNonNegativeInt(value: unknown, fallback: number, max: number, fieldName: string): number {\n if (value === undefined || value === null || value === \"\") return fallback;\n let parsed: number;\n if (typeof value === \"number\") {\n parsed = value;\n } else if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.length === 0) return fallback;\n if (!/^[+-]?\\d+$/.test(trimmed)) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 0 to ${max}`);\n }\n parsed = Number(trimmed);\n } else {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 0 to ${max}`);\n }\n if (!Number.isInteger(parsed) || parsed < 0 || parsed > max) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 0 to ${max}`);\n }\n return parsed;\n}\n\nfunction coerceOptionalNonEmptyString(value: unknown, fieldName: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === \"string\" && value.trim().length > 0) return value.trim();\n throw new Error(`Invalid string value for Remnic Pi config field ${fieldName}`);\n}\n\nfunction coerceOptionalString(value: unknown, fieldName: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n throw new Error(`Invalid string value for Remnic Pi config field ${fieldName}`);\n}\n\nfunction coerceOptionalHttpUrl(value: unknown, fieldName: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value !== \"string\") {\n throw new Error(`Invalid URL value for Remnic Pi config field ${fieldName}: expected an http or https URL`);\n }\n const trimmed = value.trim();\n if (trimmed.length === 0) return undefined;\n try {\n const parsed = new URL(trimmed);\n if (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") return trimTrailingSlashes(trimmed);\n } catch {\n // Fall through to the shared error below.\n }\n throw new Error(`Invalid URL value for Remnic Pi config field ${fieldName}: expected an http or https URL`);\n}\n\nfunction coerceRecallMode(value: unknown): RemnicPiConfig[\"recallMode\"] {\n if (value === undefined || value === null || value === \"\") return DEFAULT_CONFIG.recallMode;\n if (\n value === \"minimal\" ||\n value === \"full\" ||\n value === \"graph_mode\" ||\n value === \"no_recall\" ||\n value === \"auto\"\n ) {\n return value;\n }\n throw new Error(`Invalid recallMode value for Remnic Pi config: ${JSON.stringify(value)}`);\n}\n\nfunction readConfigFile(configPath: string): Record<string, unknown> {\n if (!existsSync(configPath)) return {};\n try {\n const raw = readFileSync(configPath, \"utf-8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n throw new Error(\"expected a JSON object\");\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to load Remnic Pi config at ${configPath}: ${reason}`);\n }\n}\n\nfunction trimTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;\n return value.slice(0, end);\n}\n\nexport function resolveConfigPath(options: LoadConfigOptions = {}): string {\n const env = options.env ?? process.env;\n // REMNIC_PI_CONFIG keeps precedence for upstream Pi; REMNIC_OMP_CONFIG lets an\n // omp (oh-my-pi) direct load (`omp -e npm:@remnic/plugin-pi`) point the shared\n // runtime module at its own config without an explicit configPath. Connector\n // installs always pass an explicit configPath, so this only affects direct loads.\n return expandTildePath(\n options.configPath || env.REMNIC_PI_CONFIG || env.REMNIC_OMP_CONFIG || defaultConfigPath(env),\n );\n}\n\nexport function loadConfig(options: LoadConfigOptions = {}): RemnicPiConfig {\n const env = options.env ?? process.env;\n const fileConfig = readConfigFile(resolveConfigPath(options));\n const daemonUrl =\n coerceOptionalHttpUrl(fileConfig.remnicDaemonUrl, \"remnicDaemonUrl\") ??\n coerceOptionalHttpUrl(env.REMNIC_DAEMON_URL, \"REMNIC_DAEMON_URL\") ??\n DEFAULT_CONFIG.remnicDaemonUrl;\n const authToken =\n coerceOptionalString(fileConfig.authToken, \"authToken\") ??\n coerceOptionalString(env.REMNIC_PI_AUTH_TOKEN, \"REMNIC_PI_AUTH_TOKEN\");\n const namespace = coerceOptionalNonEmptyString(fileConfig.namespace, \"namespace\");\n\n const requestTimeoutMs = coercePositiveInt(\n fileConfig.requestTimeoutMs,\n DEFAULT_CONFIG.requestTimeoutMs,\n 60_000,\n \"requestTimeoutMs\",\n );\n // When turnRequestTimeoutMs is not explicitly set, derive it from the\n // configured requestTimeoutMs (capped at the default turn budget) so an\n // existing install that lowered requestTimeoutMs below 20s keeps its tighter\n // per-turn budget instead of being silently raised back to 20s (codex review).\n const turnFallback = Math.min(requestTimeoutMs, DEFAULT_CONFIG.turnRequestTimeoutMs);\n const turnRequestTimeoutMs = coercePositiveInt(\n fileConfig.turnRequestTimeoutMs,\n turnFallback,\n 25_000,\n \"turnRequestTimeoutMs\",\n );\n\n return {\n remnicDaemonUrl: daemonUrl,\n authToken,\n namespace,\n recallMode: coerceRecallMode(fileConfig.recallMode),\n recallTopK: coercePositiveInt(fileConfig.recallTopK, DEFAULT_CONFIG.recallTopK, 50, \"recallTopK\"),\n recallBudgetChars: coercePositiveInt(fileConfig.recallBudgetChars, DEFAULT_CONFIG.recallBudgetChars, 64000, \"recallBudgetChars\"),\n recallEnabled: coerceBoolean(fileConfig.recallEnabled, DEFAULT_CONFIG.recallEnabled, \"recallEnabled\"),\n observeEnabled: coerceBoolean(fileConfig.observeEnabled, DEFAULT_CONFIG.observeEnabled, \"observeEnabled\"),\n observeSkipExtraction: coerceBoolean(fileConfig.observeSkipExtraction, DEFAULT_CONFIG.observeSkipExtraction, \"observeSkipExtraction\"),\n compactionEnabled: coerceBoolean(fileConfig.compactionEnabled, DEFAULT_CONFIG.compactionEnabled, \"compactionEnabled\"),\n mcpToolsEnabled: coerceBoolean(fileConfig.mcpToolsEnabled, DEFAULT_CONFIG.mcpToolsEnabled, \"mcpToolsEnabled\"),\n statusEnabled: coerceBoolean(fileConfig.statusEnabled, DEFAULT_CONFIG.statusEnabled, \"statusEnabled\"),\n requestTimeoutMs,\n startupRequestTimeoutMs: coercePositiveInt(\n fileConfig.startupRequestTimeoutMs,\n DEFAULT_CONFIG.startupRequestTimeoutMs,\n 60_000,\n \"startupRequestTimeoutMs\",\n ),\n turnRequestTimeoutMs,\n observeMaxBytes: coercePositiveInt(\n fileConfig.observeMaxBytes,\n DEFAULT_CONFIG.observeMaxBytes,\n 8_388_608,\n \"observeMaxBytes\",\n ),\n observeMaxRetries: coerceNonNegativeInt(fileConfig.observeMaxRetries, DEFAULT_CONFIG.observeMaxRetries, 5, \"observeMaxRetries\"),\n daemonCooldownMs: coercePositiveInt(fileConfig.daemonCooldownMs, DEFAULT_CONFIG.daemonCooldownMs, 60_000, \"daemonCooldownMs\"),\n };\n}\n","import type { RemnicPiConfig } from \"./config.js\";\n\nexport interface RecallResponse {\n context?: string;\n results?: Array<{ id?: string; content?: string; score?: number; category?: string }>;\n count?: number;\n}\n\nexport interface ObserveMessagePart {\n ordinal?: number;\n kind: \"text\" | \"tool_call\" | \"tool_result\" | \"patch\" | \"file_read\" | \"file_write\" | \"step_start\" | \"step_finish\" | \"snapshot\" | \"retry\";\n payload: Record<string, unknown>;\n toolName?: string | null;\n filePath?: string | null;\n createdAt?: string | null;\n}\n\nexport interface ObserveMessage {\n role: \"user\" | \"assistant\";\n content: string;\n sourceFormat?: \"pi\";\n rawContent?: unknown;\n parts?: ObserveMessagePart[];\n}\n\nexport interface McpTool {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n}\n\nexport interface RequestOptions {\n timeoutMs?: number;\n /** Transient-retry budget for connection-level failures (socket close, ECONNRESET). */\n maxRetries?: number;\n}\n\nexport interface ObserveOptions extends RequestOptions {\n /** Soft cap on a single observe POST body in bytes; oversize batches are chunked. */\n maxBytes?: number;\n}\n\n/**\n * Result of a startup namespace-writability preflight (issue #1888 part 3).\n * `not_writable` is a DEFINITIVE misconfiguration answer from the daemon (the\n * configured namespace resolves as non-writable for this principal), which the\n * client surfaces loudly. `indeterminate` means the daemon could not be reached\n * or answered unexpectedly (timeout, network, auth, 5xx) — the client must NOT\n * cry wolf about the namespace on those, since the answer is unknown.\n */\nexport type NamespacePreflightResult =\n | { readonly status: \"writable\"; readonly namespace: string }\n | { readonly status: \"not_writable\"; readonly reason: \"not_writable\" | \"unsupported\"; readonly namespace: string }\n | { readonly status: \"indeterminate\"; readonly detail: string };\n\nexport class RemnicHttpError extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(message);\n }\n}\n\ninterface ObserveBody {\n sessionKey: string;\n cwd: string;\n namespace?: string;\n skipExtraction: boolean;\n messages: ObserveMessage[];\n}\n\nconst encoder = new TextEncoder();\nconst RETRY_BASE_DELAY_MS = 200;\nconst MAX_COOLDOWN_MS = 60_000;\nconst TRUNCATION_MARKER = \"\\n\\n[Remnic observe truncated: payload exceeded client size cap]\";\n\nexport class RemnicClient {\n private requestId = 0;\n // Circuit-breaker state: when the daemon is known-unreachable, observe/recall\n // callers skip fast instead of blocking every turn on a doomed request (#1626).\n private unreachableUntil = 0;\n private consecutiveFailures = 0;\n\n constructor(private readonly config: RemnicPiConfig) {}\n\n /** True when the daemon is not in a known-unreachable cooldown. */\n isReachable(): boolean {\n return Date.now() >= this.unreachableUntil;\n }\n\n /** Clear the circuit breaker — call after any successful daemon interaction. */\n markReachable(): void {\n this.consecutiveFailures = 0;\n this.unreachableUntil = 0;\n }\n\n /**\n * Enter (or extend) an unreachable cooldown. The cooldown grows exponentially\n * with consecutive failures (base, 2×base, 4×base, …) capped at 60 s, so a\n * flapping daemon is retried gently while a hard-down host backs off hard.\n */\n markUnreachable(baseCooldownMs: number): void {\n this.consecutiveFailures += 1;\n const factor = 2 ** Math.min(this.consecutiveFailures - 1, 4);\n const cooldown = Math.min(baseCooldownMs * factor, MAX_COOLDOWN_MS);\n this.unreachableUntil = Date.now() + cooldown;\n }\n\n async health(options: RequestOptions = {}): Promise<Record<string, unknown>> {\n return this.request(\"GET\", \"/engram/v1/health\", undefined, options);\n }\n\n /**\n * Startup namespace-writability preflight (issue #1888 part 3). Asks the\n * daemon — read-only, no write, no side effect — whether the configured\n * namespace resolves as writable for this client's (token-resolved)\n * principal. A `not_writable` answer is definitive and surfaced loudly; any\n * transport failure returns `indeterminate` so a flaky daemon never triggers\n * a false namespace-misconfig alarm.\n */\n async preflightNamespace(\n sessionKey: string | undefined,\n options: RequestOptions = {},\n ): Promise<NamespacePreflightResult> {\n const params = new URLSearchParams();\n if (this.config.namespace) params.set(\"namespace\", this.config.namespace);\n if (sessionKey) params.set(\"session\", sessionKey);\n // Check the op this client's ENABLED write path uses: automatic turn\n // capture (observe) when observation is on, else the explicit store op.\n params.set(\"op\", this.config.observeEnabled ? \"observe\" : \"memory_store\");\n const qs = params.toString();\n const path = `/engram/v1/namespace/writable${qs ? `?${qs}` : \"\"}`;\n try {\n const payload = await this.request<{ ok?: unknown; reason?: unknown; namespace?: unknown }>(\n \"GET\",\n path,\n undefined,\n options,\n );\n // Both branches require the full contract before they are trusted: an\n // `ok:true` without a concrete namespace, or an `ok:false` without a known\n // reason + concrete namespace, is malformed → indeterminate, never a false\n // writable/not-writable verdict.\n if (\n payload?.ok === true &&\n typeof payload.namespace === \"string\" &&\n payload.namespace.length > 0\n ) {\n return { status: \"writable\", namespace: payload.namespace };\n }\n if (\n payload?.ok === false &&\n (payload.reason === \"not_writable\" || payload.reason === \"unsupported\") &&\n typeof payload.namespace === \"string\" &&\n payload.namespace.length > 0\n ) {\n return { status: \"not_writable\", reason: payload.reason, namespace: payload.namespace };\n }\n return { status: \"indeterminate\", detail: \"unexpected preflight response shape\" };\n } catch (err) {\n return { status: \"indeterminate\", detail: err instanceof Error ? err.message : String(err) };\n }\n }\n\n async recall(\n query: string,\n sessionKey: string,\n cwd: string,\n options: RequestOptions = {},\n ): Promise<RecallResponse> {\n // Recall is a read-only query; retry transient connection failures with the\n // same budget as observe (#1602). Default to the per-turn budget when the\n // caller omits timeoutMs so the retries share one deadline (like observe)\n // instead of each attempt reusing the full general request timeout (cursor\n // review). Callers that need more (e.g. the manual /remnic-recall command)\n // pass an explicit timeoutMs.\n const merged: RequestOptions = {\n ...options,\n timeoutMs: options.timeoutMs ?? this.config.turnRequestTimeoutMs,\n maxRetries: options.maxRetries ?? this.config.observeMaxRetries,\n };\n return this.requestWithRetry(\n \"POST\",\n \"/engram/v1/recall\",\n {\n query,\n sessionKey,\n cwd,\n namespace: this.config.namespace,\n topK: this.config.recallTopK,\n mode: this.config.recallMode,\n },\n merged,\n );\n }\n\n async recallExplain(sessionKey: string, options: RequestOptions = {}): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\n \"POST\",\n \"/engram/v1/recall/explain\",\n {\n sessionKey,\n namespace: this.config.namespace,\n },\n options,\n );\n }\n\n async observe(\n sessionKey: string,\n cwd: string,\n messages: ObserveMessage[],\n options: ObserveOptions = {},\n ): Promise<Record<string, unknown>> {\n const maxBytes = options.maxBytes ?? this.config.observeMaxBytes;\n // Observe runs on the live turn hooks, so bound it by the per-turn budget\n // (#1626) regardless of how many chunks the payload splits into. A missing\n // override previously let single-chunk observe fall back to the 60s general\n // budget while multi-chunk used the 20s turn budget (cursor review). An\n // explicit override is honored so callers outside the turn (shutdown replay,\n // tests) can extend it.\n const turnBudgetMs = options.timeoutMs ?? this.config.turnRequestTimeoutMs;\n const retryOptions: RequestOptions = {\n timeoutMs: turnBudgetMs,\n maxRetries: options.maxRetries ?? this.config.observeMaxRetries,\n };\n const chunks = chunkObservePayload(this.config, sessionKey, cwd, messages, maxBytes);\n if (chunks.length === 1) {\n return this.requestWithRetry(\"POST\", \"/engram/v1/observe\", chunks[0], retryOptions);\n }\n // Multiple chunks: send sequentially within the SAME per-turn deadline so\n // the TOTAL observe time stays under turnBudgetMs (not per-chunk), which\n // keeps it inside the host's ~30s handler budget (#1626). Each chunk is\n // retried independently on transient connection failures; observe is\n // dedupe-safe, so a partial failure just re-sends on the next turn.\n const deadline = Date.now() + turnBudgetMs;\n const results: Record<string, unknown>[] = [];\n for (let i = 0; i < chunks.length; i++) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n throw new Error(\n `Remnic observe exceeded the per-turn budget of ${turnBudgetMs}ms across ${chunks.length} chunks (completed ${i})`,\n );\n }\n const chunkOptions: RequestOptions = { ...retryOptions, timeoutMs: remaining };\n const result = await this.requestWithRetry<Record<string, unknown>>(\n \"POST\",\n \"/engram/v1/observe\",\n chunks[i],\n chunkOptions,\n );\n if (result && typeof result === \"object\") {\n results.push(result);\n }\n }\n return mergeObserveResults(results);\n }\n\n async storeMemory(content: string, sessionKey: string, options: RequestOptions = {}): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\"POST\", \"/engram/v1/memories\", {\n content,\n category: \"fact\",\n sourceReason: \"Captured from Pi via Remnic extension\",\n sessionKey,\n namespace: this.config.namespace,\n }, options);\n }\n\n async lcmSearch(query: string, sessionKey: string, limit = 10): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\"POST\", \"/engram/v1/lcm/search\", {\n query,\n sessionKey,\n namespace: this.config.namespace,\n limit,\n });\n }\n\n async lcmCompactionFlush(sessionKey: string): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\"POST\", \"/engram/v1/lcm/compaction/flush\", {\n sessionKey,\n namespace: this.config.namespace,\n });\n }\n\n async lcmCompactionRecord(sessionKey: string, tokensBefore: number, tokensAfter: number): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\"POST\", \"/engram/v1/lcm/compaction/record\", {\n sessionKey,\n namespace: this.config.namespace,\n tokensBefore,\n tokensAfter,\n });\n }\n\n async contextCheckpoint(sessionKey: string, context: string): Promise<Record<string, unknown>> {\n return this.mcpTool(\"remnic.context_checkpoint\", {\n sessionKey,\n context,\n namespace: this.config.namespace,\n });\n }\n\n async mcpListTools(options: RequestOptions = {}): Promise<McpTool[]> {\n const result = await this.mcpRequest(\"tools/list\", {}, options);\n const tools = result.tools;\n return Array.isArray(tools) ? tools.filter(isMcpTool) : [];\n }\n\n async mcpTool(name: string, args: Record<string, unknown>): Promise<Record<string, unknown>> {\n return this.mcpRequest(\"tools/call\", {\n name,\n arguments: args,\n });\n }\n\n /**\n * Single HTTP attempt with the configured timeout. No retry — retry of\n * transient connection failures lives in {@link requestWithRetry}.\n */\n private async request<T = Record<string, unknown>>(\n method: string,\n pathname: string,\n body?: unknown,\n options: RequestOptions = {},\n ): Promise<T> {\n const controller = new AbortController();\n // A per-request override is honored only when it is a finite positive number;\n // 0, negative, NaN, or non-finite values would make setTimeout abort\n // immediately (or behave erratically), so fall back to the general budget.\n // In practice the override is always sourced from the validated\n // `startupRequestTimeoutMs` / `turnRequestTimeoutMs` config, but this keeps\n // the client robust to any future caller (Copilot review).\n const override = options.timeoutMs;\n const timeoutMs =\n typeof override === \"number\" && Number.isFinite(override) && override > 0\n ? override\n : this.config.requestTimeoutMs;\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(`${this.config.remnicDaemonUrl}${pathname}`, {\n method,\n headers: {\n ...(body === undefined ? {} : { \"Content-Type\": \"application/json\" }),\n ...(this.config.authToken ? { Authorization: `Bearer ${this.config.authToken}` } : {}),\n \"X-Engram-Client-Id\": \"pi\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller.signal,\n });\n const text = await response.text();\n let payload: unknown = {};\n let parseError: unknown;\n if (text) {\n try {\n payload = JSON.parse(text);\n } catch (err) {\n parseError = err;\n }\n }\n if (!response.ok) {\n const message = responseErrorMessage(response, text, payload, parseError);\n if (response.status === 413) {\n // Surface the body size so operators can tune the cap (#1600).\n const bodyBytes = body === undefined ? 0 : jsonBytes(body);\n throw new RemnicHttpError(response.status, `${message} (observed body ${bodyBytes} bytes; cap via observeMaxBytes)`);\n }\n throw new RemnicHttpError(response.status, message);\n }\n if (parseError) {\n const reason = parseError instanceof Error ? parseError.message : String(parseError);\n throw new Error(`Invalid JSON response from Remnic daemon (${response.status} ${response.statusText || \"OK\"}): ${reason}`);\n }\n return payload as T;\n } catch (err) {\n if (isAbortError(err)) {\n throw new Error(`Remnic request timed out after ${timeoutMs}ms`);\n }\n throw err;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n /**\n * Wrap {@link request} with a small bounded retry loop for transient\n * connection-level failures (socket close mid-request, ECONNRESET, EPIPE).\n * Timeouts (our own AbortController) and HTTP responses (4xx/5xx) are NOT\n * retried here — timeouts already burned the full budget, and HTTP errors\n * carry semantic meaning the caller must handle. Observe/recall are\n * dedupe-safe so retrying a transiently-failed POST is harmless (#1602).\n */\n private async requestWithRetry<T = Record<string, unknown>>(\n method: string,\n pathname: string,\n body: unknown,\n options: RequestOptions = {},\n ): Promise<T> {\n const maxRetries = options.maxRetries ?? 0;\n // Share ONE deadline across all attempts (including backoff sleeps) when the\n // caller passes a per-turn/per-operation budget, so a late transient failure\n // cannot burn a full timeout on every retry and overshoot the host's ~30s\n // handler window (#1602/#1626 — cursor + codex reviews). The first attempt\n // keeps the original timeoutMs verbatim (preserving error messages/timing);\n // only retries are tightened to the remaining budget.\n const budgetMs = options.timeoutMs;\n const hasDeadline = typeof budgetMs === \"number\" && Number.isFinite(budgetMs) && budgetMs > 0;\n const deadline = hasDeadline ? Date.now() + budgetMs : Number.POSITIVE_INFINITY;\n let attempt = 0;\n let attemptOptions = options;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n try {\n return await this.request<T>(method, pathname, body, attemptOptions);\n } catch (err) {\n if (attempt >= maxRetries || !isTransientNetworkError(err)) throw err;\n const delayMs = RETRY_BASE_DELAY_MS * 2 ** attempt;\n if (hasDeadline) {\n // The backoff sleep counts against the shared deadline; bail BEFORE\n // sleeping if the sleep alone would overshoot the remaining budget,\n // so a sub-backoff timeoutMs never blocks for the full backoff only\n // to then throw (cursor review).\n const remainingBeforeSleep = deadline - Date.now();\n if (remainingBeforeSleep <= delayMs) {\n throw new Error(\n `Remnic request exceeded the ${budgetMs}ms budget before retry ${attempt + 1} (${method} ${pathname})`,\n );\n }\n }\n await sleep(delayMs);\n attempt += 1;\n if (hasDeadline) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n throw new Error(\n `Remnic request exceeded the ${budgetMs}ms budget before retry ${attempt} (${method} ${pathname})`,\n );\n }\n attemptOptions = { ...options, timeoutMs: remaining };\n }\n }\n }\n }\n\n private async mcpRequest(\n method: string,\n params: Record<string, unknown>,\n options: RequestOptions = {},\n ): Promise<Record<string, unknown>> {\n this.requestId += 1;\n const payload = await this.request<Record<string, unknown>>(\"POST\", \"/mcp\", {\n jsonrpc: \"2.0\",\n id: this.requestId,\n method,\n params,\n }, options);\n if (payload.error) {\n throw new Error(JSON.stringify(payload.error));\n }\n return (payload.result && typeof payload.result === \"object\" ? payload.result : payload) as Record<string, unknown>;\n }\n}\n\nfunction isMcpTool(value: unknown): value is McpTool {\n return !!value && typeof value === \"object\" && \"name\" in value && typeof value.name === \"string\";\n}\n\nfunction isAbortError(err: unknown): boolean {\n return err instanceof Error && (err.name === \"AbortError\" || err.message === \"This operation was aborted\");\n}\n\n/**\n * Classify connection-level failures that are safe to retry: the request never\n * reached the daemon (or died mid-flight), so a retry is idempotent. Excludes\n * our own AbortController timeouts and HTTP responses (those carry meaning).\n */\nexport function isTransientNetworkError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (isAbortError(err)) return false;\n if (err instanceof RemnicHttpError) return false;\n const lower = (err.message ?? \"\").toLowerCase();\n // Bun fetch: \"The socket connection was closed unexpectedly.\"\n if (lower.includes(\"socket connection was closed\")) return true;\n if (lower.includes(\"socket closed\")) return true;\n // Node undici / OS codes surfaced in the message.\n if (lower.includes(\"econnreset\")) return true;\n if (lower.includes(\"epipe\")) return true;\n if (lower.includes(\"und_err_socket\")) return true;\n // Node wraps the real cause in err.cause (TypeError: fetch failed).\n if (lower.includes(\"fetch failed\")) return true;\n // Inspect the cause chain without an unchecked cast. Error.cause is\n // `unknown` in the ES2022 lib; narrow it before reading `.code`.\n const cause = err.cause;\n if (cause && typeof cause === \"object\" && \"code\" in cause) {\n const code = cause.code;\n if (typeof code === \"string\" && (code === \"ECONNRESET\" || code === \"EPIPE\" || code === \"UND_ERR_SOCKET\")) {\n return true;\n }\n }\n return false;\n}\n\nfunction sleep(ms: number): Promise<void> {\n // Plain Promise constructor: avoids Promise.withResolvers (ES2024 / Node 22+),\n // so retry backoff works on Node 20 and other runtimes that load plugin-pi.\n return new Promise<void>(resolve => setTimeout(resolve, ms));\n}\n\nfunction jsonBytes(value: unknown): number {\n return encoder.encode(JSON.stringify(value)).length;\n}\n\nfunction buildObserveEnvelope(config: RemnicPiConfig, sessionKey: string, cwd: string, messages: ObserveMessage[]): ObserveBody {\n return {\n sessionKey,\n cwd,\n namespace: config.namespace,\n skipExtraction: config.observeSkipExtraction,\n messages,\n };\n}\n\n/**\n * Split an observe batch into POST bodies whose serialized JSON stays under\n * `maxBytes`. Single messages that alone exceed the per-message budget are\n * truncated with a marker rather than dropped, so large tool outputs still\n * leave a trace in memory (#1600).\n */\nexport function chunkObservePayload(\n config: RemnicPiConfig,\n sessionKey: string,\n cwd: string,\n messages: ObserveMessage[],\n maxBytes: number,\n): ObserveBody[] {\n const envelopeOverhead = jsonBytes(buildObserveEnvelope(config, sessionKey, cwd, []));\n const messageBudget = maxBytes - envelopeOverhead;\n if (messageBudget <= 0) {\n // The envelope overhead alone meets/exceeds the cap, so no valid body can\n // fit — return a single chunk and let the daemon reject it visibly (this is\n // a degenerate/misconfigured cap, not the common case). A small-but-positive\n // budget (<=1024) is NOT degenerate: truncate/pack normally so oversized\n // messages are shrunk to fit instead of bypassing the #1600 safeguards\n // (cursor review).\n return [buildObserveEnvelope(config, sessionKey, cwd, messages)];\n }\n const chunks: ObserveMessage[][] = [];\n let current: ObserveMessage[] = [];\n let currentSize = 0;\n const flush = (): void => {\n if (current.length > 0) {\n chunks.push(current);\n current = [];\n currentSize = 0;\n }\n };\n for (const message of messages) {\n const size = jsonBytes(message);\n if (size > messageBudget) {\n flush();\n chunks.push([truncateObserveMessage(message, messageBudget)]);\n continue;\n }\n // Account for the JSON array comma separator before this message when it is\n // not the first in the chunk, so the serialized body never overshoots the\n // cap (review: cursor).\n if (current.length > 0 && currentSize + 1 + size > messageBudget) {\n flush();\n }\n if (current.length > 0) currentSize += 1;\n current.push(message);\n currentSize += size;\n }\n flush();\n if (chunks.length === 0) {\n return [buildObserveEnvelope(config, sessionKey, cwd, [])];\n }\n return chunks.map((msgs) => buildObserveEnvelope(config, sessionKey, cwd, msgs));\n}\n\nconst decoder = new TextDecoder();\n\nfunction truncateObserveMessage(message: ObserveMessage, budgetBytes: number): ObserveMessage {\n // A truncated observe keeps ONLY role + a content marker, dropping rawContent\n // and parts. Live Pi turns carry the full original message in rawContent and\n // parsed parts; those fields dominate the serialized size and would keep the\n // chunk over the cap (defeating #1600), so they are removed — the daemon\n // extracts from content. JSON-escape-aware: measures ACTUAL jsonBytes of each\n // candidate so escaping (\\n -> \\\\\\\\n) can't overshoot.\n const slim: ObserveMessage = { role: message.role, content: \"\" };\n const markerOnly = jsonBytes({ ...slim, content: TRUNCATION_MARKER });\n if (markerOnly > budgetBytes) {\n // Pathological: even the marker alone doesn't fit. Keep it anyway so the\n // turn isn't silently dropped.\n return { role: message.role, content: TRUNCATION_MARKER };\n }\n const fullContent = message.content + TRUNCATION_MARKER;\n if (jsonBytes({ ...slim, content: fullContent }) <= budgetBytes) {\n return { role: message.role, content: fullContent };\n }\n // Binary-search the largest content slice whose slim message fits. Slicing by\n // encoded bytes keeps multi-byte sequences intact where possible; the decoder\n // replaces any dangling tail with the replacement char.\n const encoded = encoder.encode(message.content);\n let lo = 0;\n let hi = encoded.length;\n while (lo < hi) {\n const mid = hi - Math.floor((hi - lo) / 2);\n const candidate = decoder.decode(encoded.subarray(0, mid)) + TRUNCATION_MARKER;\n if (jsonBytes({ ...slim, content: candidate }) <= budgetBytes) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n const truncated = lo > 0 ? decoder.decode(encoded.subarray(0, lo)) : \"\";\n return { role: message.role, content: truncated + TRUNCATION_MARKER };\n}\n\nfunction mergeObserveResults(results: Record<string, unknown>[]): Record<string, unknown> {\n if (results.length === 0) return {};\n if (results.length === 1) return results[0];\n const merged: Record<string, unknown> = {};\n let countSum = 0;\n let hasCount = false;\n for (const result of results) {\n for (const key of Object.keys(result)) {\n const value = result[key];\n if (key === \"count\" && typeof value === \"number\" && Number.isFinite(value)) {\n countSum += value;\n hasCount = true;\n } else {\n merged[key] = value;\n }\n }\n }\n if (hasCount) merged.count = countSum;\n return merged;\n}\n\nfunction responseErrorMessage(response: Response, text: string, payload: unknown, parseError: unknown): string {\n if (!parseError && payload && typeof payload === \"object\") {\n if (\"error\" in payload && typeof payload.error === \"string\" && payload.error.trim().length > 0) {\n return payload.error;\n }\n if (\"message\" in payload && typeof payload.message === \"string\" && payload.message.trim().length > 0) {\n return payload.message;\n }\n }\n\n const snippet = text.trim().replace(/\\s+/g, \" \").slice(0, 200);\n if (snippet.length > 0) {\n return response.statusText ? `${response.statusText}: ${snippet}` : snippet;\n }\n return response.statusText || `HTTP ${response.status}`;\n}\n","import { createHash } from \"node:crypto\";\n\n// Leaf submodule (not the `@remnic/core` barrel) so the omp pre-bundle's\n// `bun build` does not pull the rest of core into the extension bundle.\n// `message-parts` is a pure parser with no storage/native deps. See PR #1641.\nimport { parsePiMessageParts, type LcmMessagePartInput } from \"@remnic/core/message-parts\";\n\nimport type { ObserveMessage, ObserveMessagePart } from \"./client.js\";\n\ntype PiMessage = Record<string, unknown>;\n\nexport function sessionKeyFromContext(ctx: { sessionManager?: { getSessionId?: () => string } }): string {\n const id = ctx.sessionManager?.getSessionId?.();\n return id && id.trim().length > 0 ? `pi:${id}` : \"pi:default\";\n}\n\nexport function textFromMessage(message: unknown): string {\n if (!message || typeof message !== \"object\") return \"\";\n const obj = message as PiMessage;\n const role = typeof obj.role === \"string\" ? obj.role : \"message\";\n if (role === \"bashExecution\") {\n const command = typeof obj.command === \"string\" ? obj.command : \"\";\n const output = typeof obj.output === \"string\" ? obj.output : \"\";\n return [`Ran ${command}`, output].filter(Boolean).join(\"\\n\");\n }\n return textFromContent(obj.content).trim();\n}\n\nexport function latestUserQuery(messages: unknown[]): string {\n for (let index = messages.length - 1; index >= 0; index--) {\n const message = messages[index] as PiMessage;\n if (isExcludedFromContext(message) || isRemnicInjected(message)) continue;\n if (message?.role === \"user\") {\n const text = textFromMessage(message);\n if (text.length > 0) return text;\n }\n }\n return \"\";\n}\n\nexport function latestUserRecallTarget(\n messages: unknown[],\n): { query: string; dedupeKey: string } | null {\n for (let index = messages.length - 1; index >= 0; index--) {\n const message = messages[index] as PiMessage;\n if (isExcludedFromContext(message) || isRemnicInjected(message)) continue;\n if (message?.role !== \"user\") continue;\n const query = textFromMessage(message);\n if (query.length === 0) continue;\n const identity = stableObservedMessageIdentity(message);\n return {\n query,\n dedupeKey: identity ? `message:${identity}:${query}` : `query:${query}`,\n };\n }\n return null;\n}\n\nexport function toObserveMessage(message: unknown): ObserveMessage | null {\n if (!message || typeof message !== \"object\") return null;\n const obj = message as PiMessage;\n if (isExcludedFromContext(obj) || isRemnicInjected(obj)) return null;\n const role = obj.role === \"user\" || obj.role === \"bashExecution\" ? \"user\" : \"assistant\";\n const content = textFromMessage(obj);\n if (content.length === 0) return null;\n return {\n role,\n content,\n sourceFormat: \"pi\",\n rawContent: obj,\n parts: partsFromMessage(obj, content),\n };\n}\n\nexport function hashObservedMessage(message: ObserveMessage, sessionKey = \"\", identity = \"content\"): string {\n return createHash(\"sha256\")\n .update(sessionKey)\n .update(\"\\0\")\n .update(message.role)\n .update(\"\\0\")\n .update(identity)\n .update(\"\\0\")\n .update(message.content)\n .digest(\"hex\");\n}\n\nexport function observedMessageDedupeKey(\n message: ObserveMessage,\n sessionKey = \"\",\n): string | null {\n const identity = stableObservedMessageIdentity(message.rawContent);\n return identity ? hashObservedMessage(message, sessionKey, identity) : null;\n}\n\nexport function summarizeMessages(messages: unknown[], maxChars: number): string {\n const chunks: string[] = [];\n let used = 0;\n for (const message of messages) {\n if (isExcludedFromContext(message) || isRemnicInjected(message)) continue;\n const text = textFromMessage(message);\n if (!text) continue;\n const role = typeof (message as PiMessage)?.role === \"string\" ? (message as PiMessage).role : \"message\";\n const line = `[${role}] ${text}`;\n const separatorLength = chunks.length > 0 ? 2 : 0;\n const remaining = maxChars - used - separatorLength;\n if (remaining <= 0) break;\n const clipped = line.length > remaining ? line.slice(0, remaining) : line;\n if (clipped.length > 0) chunks.push(clipped);\n used += separatorLength + clipped.length;\n if (used >= maxChars) break;\n }\n return chunks.join(\"\\n\\n\");\n}\n\nexport function isExcludedFromContext(message: unknown): boolean {\n return !!message && typeof message === \"object\" && (message as PiMessage).excludeFromContext === true;\n}\n\nexport function isRemnicInjected(message: unknown): boolean {\n return !!message && typeof message === \"object\" && (message as PiMessage).remnicInjected === true;\n}\n\nfunction textFromContent(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n const chunks: string[] = [];\n for (const block of content) {\n if (!block || typeof block !== \"object\") continue;\n const obj = block as PiMessage;\n if (obj.type === \"text\" && typeof obj.text === \"string\") chunks.push(obj.text);\n if (obj.type === \"toolCall\" && typeof obj.name === \"string\") {\n chunks.push(`Tool ${obj.name} called with ${JSON.stringify(obj.arguments ?? {})}`);\n }\n }\n return chunks.join(\"\\n\");\n}\n\nfunction partsFromMessage(message: PiMessage, renderedContent: string): ObserveMessagePart[] {\n return parsePiMessageParts(message, {\n renderedContent,\n allowRenderedFallback: true,\n }).map(toObserveMessagePart);\n}\n\nfunction toObserveMessagePart(part: LcmMessagePartInput): ObserveMessagePart {\n return {\n ordinal: part.ordinal ?? undefined,\n kind: part.kind,\n payload: part.payload,\n toolName: part.toolName ?? part.tool_name ?? undefined,\n filePath: part.filePath ?? part.file_path ?? undefined,\n createdAt: part.createdAt ?? part.created_at ?? undefined,\n };\n}\n\nfunction stableObservedMessageIdentity(rawContent: unknown): string | null {\n if (rawContent && typeof rawContent === \"object\") {\n const obj = rawContent as PiMessage;\n const fields = [\n \"id\",\n \"entryId\",\n \"entry_id\",\n \"messageId\",\n \"message_id\",\n \"turnId\",\n \"turn_id\",\n \"timestamp\",\n \"createdAt\",\n \"created_at\",\n ];\n for (const field of fields) {\n const value = obj[field];\n if (typeof value === \"string\" && value.length > 0) return `${field}:${value}`;\n if (typeof value === \"number\" && Number.isFinite(value)) return `${field}:${value}`;\n }\n }\n return null;\n}\n"],"mappings":"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","names":["path","remnicPiExtension"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/plugin-pi",
3
- "version": "9.7.14",
3
+ "version": "9.8.0",
4
4
  "description": "Remnic memory extension for Pi Coding Agent",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@sinclair/typebox": "^0.34.0",
45
- "@remnic/core": "^9.7.14"
45
+ "@remnic/core": "^9.8.0"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "@earendil-works/pi-coding-agent": "*"