@remnic/plugin-pi 9.69.59 → 9.69.61
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.js +22 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -620,12 +620,14 @@ function isRecallTimeoutError(err) {
|
|
|
620
620
|
}
|
|
621
621
|
var RecallTimeoutBreaker = class {
|
|
622
622
|
tripped = false;
|
|
623
|
+
trippedAt = 0;
|
|
623
624
|
results = [];
|
|
624
625
|
threshold;
|
|
625
626
|
windowSize;
|
|
627
|
+
resetAfterMs;
|
|
626
628
|
abortController = new AbortController();
|
|
627
629
|
constructor(options) {
|
|
628
|
-
const { threshold, window } = options;
|
|
630
|
+
const { threshold, window, resetAfterMs } = options;
|
|
629
631
|
if (typeof threshold !== "number" || !Number.isInteger(threshold) || threshold <= 0 || typeof window !== "number" || !Number.isInteger(window) || window <= 0 || threshold > window) {
|
|
630
632
|
throw new Error(
|
|
631
633
|
`Invalid RecallTimeoutBreaker options: threshold and window must be positive integers with threshold <= window (got threshold=${threshold}, window=${window})`
|
|
@@ -633,11 +635,24 @@ var RecallTimeoutBreaker = class {
|
|
|
633
635
|
}
|
|
634
636
|
this.threshold = threshold;
|
|
635
637
|
this.windowSize = window;
|
|
638
|
+
if (resetAfterMs !== void 0 && (typeof resetAfterMs !== "number" || !Number.isInteger(resetAfterMs) || resetAfterMs <= 0)) {
|
|
639
|
+
throw new Error(
|
|
640
|
+
`Invalid RecallTimeoutBreaker options: resetAfterMs must be a positive integer (got ${resetAfterMs})`
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
this.resetAfterMs = resetAfterMs ?? 3e5;
|
|
636
644
|
}
|
|
637
645
|
get signal() {
|
|
638
646
|
return this.abortController.signal;
|
|
639
647
|
}
|
|
640
648
|
isTripped() {
|
|
649
|
+
if (this.tripped && Date.now() - this.trippedAt >= this.resetAfterMs) {
|
|
650
|
+
this.tripped = false;
|
|
651
|
+
this.trippedAt = 0;
|
|
652
|
+
this.results.length = 0;
|
|
653
|
+
this.abortController = new AbortController();
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
641
656
|
return this.tripped;
|
|
642
657
|
}
|
|
643
658
|
recordSuccess() {
|
|
@@ -655,6 +670,7 @@ var RecallTimeoutBreaker = class {
|
|
|
655
670
|
if (this.results.length > this.windowSize) this.results.shift();
|
|
656
671
|
if (this.timeoutCount() >= this.threshold) {
|
|
657
672
|
this.tripped = true;
|
|
673
|
+
this.trippedAt = Date.now();
|
|
658
674
|
this.abortController.abort();
|
|
659
675
|
return true;
|
|
660
676
|
}
|
|
@@ -1385,13 +1401,13 @@ function errorMessage(err) {
|
|
|
1385
1401
|
return err instanceof Error ? err.message : String(err);
|
|
1386
1402
|
}
|
|
1387
1403
|
function notifyRecallDisabled(session) {
|
|
1388
|
-
const message = "Remnic recall is
|
|
1404
|
+
const message = "Remnic recall is paused for this process because timeouts delayed operations; recall retries automatically after a 5 minute cooldown.";
|
|
1389
1405
|
session.notify(message, "warning");
|
|
1390
1406
|
return message;
|
|
1391
1407
|
}
|
|
1392
|
-
var RECALL_DISABLED_STATUS = "Remnic recall
|
|
1408
|
+
var RECALL_DISABLED_STATUS = "Remnic recall paused (timeouts delayed operations; auto-retry after cooldown)";
|
|
1393
1409
|
function emitRecallTimeoutTrip(pi, session, config, err) {
|
|
1394
|
-
const message = `Remnic recall has been
|
|
1410
|
+
const message = `Remnic recall has been paused because repeated timeouts delayed operations (${config.recallTimeoutThreshold} timeouts in the last ${config.recallTimeoutWindow} recall attempts). It retries automatically after a 5 minute cooldown.`;
|
|
1395
1411
|
session.notify(message, "warning");
|
|
1396
1412
|
if (config.statusEnabled) {
|
|
1397
1413
|
session.setStatus("remnic", RECALL_DISABLED_STATUS);
|
|
@@ -1401,7 +1417,7 @@ function emitRecallTimeoutTrip(pi, session, config, err) {
|
|
|
1401
1417
|
code: "RECALL_TIMEOUT_TRIP",
|
|
1402
1418
|
threshold: config.recallTimeoutThreshold,
|
|
1403
1419
|
window: config.recallTimeoutWindow,
|
|
1404
|
-
reason: "Recall
|
|
1420
|
+
reason: "Recall paused because timeouts delayed operations; auto-retries after cooldown",
|
|
1405
1421
|
message,
|
|
1406
1422
|
error: errorMessage(err),
|
|
1407
1423
|
disabledAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1409,6 +1425,7 @@ function emitRecallTimeoutTrip(pi, session, config, err) {
|
|
|
1409
1425
|
});
|
|
1410
1426
|
}
|
|
1411
1427
|
async function executeRecallWithBreaker(operation, breaker, pi, session, config) {
|
|
1428
|
+
if (breaker.isTripped()) throw new RemnicRequestAbortedError();
|
|
1412
1429
|
try {
|
|
1413
1430
|
const result = await operation(breaker.signal);
|
|
1414
1431
|
breaker.recordSuccess();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/messages.ts","../src/recall-timeout-breaker.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\n\nimport { DEFAULT_CONFIG, loadConfig, type LoadConfigOptions, type RemnicPiConfig } from \"./config.js\";\nimport {\n RemnicClient,\n RemnicHttpError,\n RemnicRequestAbortedError,\n isTransientNetworkError,\n type McpTool,\n type ObserveMessage,\n} from \"./client.js\";\nimport {\n hashObservedMessage,\n observedMessageDedupeKey,\n sessionKeyFromContext,\n summarizeMessages,\n textFromMessage,\n toObserveMessage,\n} from \"./messages.js\";\nimport { RecallTimeoutBreaker, isRecallTimeoutError } from \"./recall-timeout-breaker.js\";\n\nexport type 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 type RemnicPiConfigInput = Omit<RemnicPiConfig, \"recallTimeoutThreshold\" | \"recallTimeoutWindow\"> &\n Partial<Pick<RemnicPiConfig, \"recallTimeoutThreshold\" | \"recallTimeoutWindow\">>;\n\nexport interface RemnicPiExtensionOptions extends LoadConfigOptions {\n config?: RemnicPiConfigInput;\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\"]);\nconst RECALL_BREAKER_REGISTRY_KEY = Symbol.for(\"remnic.plugin-pi.recall-timeout-breakers\");\nconst recallBreakerRegistry = globalThis as { [key: symbol]: Map<string, RecallTimeoutBreaker> | undefined };\nconst PROCESS_SCOPED_RECALL_BREAKERS =\n recallBreakerRegistry[RECALL_BREAKER_REGISTRY_KEY] ??= new Map<string, RecallTimeoutBreaker>();\nconst RECALL_PRODUCING_TOOL_NAMES: Record<string, true> = {\n \"remnic.recall\": true,\n \"remnic.recall_xray\": true,\n};\n\n/**\n * Match an MCP tool name against the recognized Remnic namespace and derive the\n * Pi-safe registered name. Two catalog shapes are supported:\n *\n * - legacy dotted form: `remnic.recall` -> pi: `remnic_recall`\n * - current underscore: `remnic_recall` -> pi: `remnic_recall`\n *\n * Only names starting with the exact `remnic.` or `remnic_` prefix and a\n * nonempty alphanumeric/underscore suffix are accepted. The server-side name\n * is echoed verbatim in tools/call so the request is accepted by whichever\n * server shape produced the catalog.\n */\nexport function matchRemnicMcpToolName(name: unknown): { serverToolName: string; piToolName: string } | null {\n if (typeof name !== \"string\") return null;\n let rest: string | null = null;\n if (name.startsWith(\"remnic.\")) {\n rest = name.slice(\"remnic.\".length);\n } else if (name.startsWith(\"remnic_\")) {\n rest = name.slice(\"remnic_\".length);\n }\n if (rest === null || !/^[a-zA-Z0-9_]+$/.test(rest)) return null;\n return {\n serverToolName: name,\n piToolName: `remnic_${rest}`,\n };\n}\n\n/** Breaker lookup tolerant of both catalog shapes (`remnic_recall` <-> `remnic.recall`). */\nfunction isRecallProducingToolName(serverToolName: string): boolean {\n return (\n RECALL_PRODUCING_TOOL_NAMES[serverToolName] === true ||\n RECALL_PRODUCING_TOOL_NAMES[serverToolName.replace(/^remnic_/, \"remnic.\")] === true\n );\n}\n\ntype PiSessionState = {\n observedHashes: Set<string>;\n liveObservedReplayKeys: Map<string, number>;\n /** Cached recall context — populated from the first context event's user prompt,\n * reused byte-identically across subsequent turns for KV cache prefix stability. */\n cachedContext: string | null;\n /** True once recall has been attempted (success or failure) for this session. */\n recallCompleted: boolean;\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\nfunction recallTimeoutBreakerCacheKey(\n config: Pick<RemnicPiConfig, \"remnicDaemonUrl\" | \"namespace\" | \"authToken\" | \"recallTimeoutThreshold\" | \"recallTimeoutWindow\">,\n): string {\n return JSON.stringify([\n config.remnicDaemonUrl,\n config.namespace ?? \"\",\n createHash(\"sha256\").update(config.authToken ?? \"\").digest(\"hex\"),\n config.recallTimeoutThreshold,\n config.recallTimeoutWindow,\n ]);\n}\n\nexport function resetProcessRecallBreakerForTest(\n config: Pick<RemnicPiConfig, \"remnicDaemonUrl\" | \"namespace\" | \"authToken\" | \"recallTimeoutThreshold\" | \"recallTimeoutWindow\">,\n): void {\n PROCESS_SCOPED_RECALL_BREAKERS.delete(recallTimeoutBreakerCacheKey(config));\n}\n\nexport function createRemnicPiExtension(options: RemnicPiExtensionOptions = {}) {\n const config: RemnicPiConfig = { ...DEFAULT_CONFIG, ...(options.config ?? loadConfig(options)) };\n const client = new RemnicClient(config);\n const sessionStates = new Map<string, PiSessionState>();\n const breakerKey = recallTimeoutBreakerCacheKey(config);\n let recallTimeoutBreaker = PROCESS_SCOPED_RECALL_BREAKERS.get(breakerKey);\n if (!recallTimeoutBreaker) {\n recallTimeoutBreaker = new RecallTimeoutBreaker({\n threshold: config.recallTimeoutThreshold,\n window: config.recallTimeoutWindow,\n });\n PROCESS_SCOPED_RECALL_BREAKERS.set(breakerKey, recallTimeoutBreaker);\n }\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 state.cachedContext = null;\n state.recallCompleted = false;\n\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 probe = await probeDaemonHealth(client, config);\n if (config.statusEnabled) {\n session.setStatus(\n \"remnic\",\n recallTimeoutBreaker.isTripped() ? RECALL_DISABLED_STATUS : remnicStatusLabel(probe, config.namespace),\n );\n }\n await runNamespacePreflight(pi, session, client, config);\n });\n\n pi.on(\"before_agent_start\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n\n // On the first before_agent_start, populate the cache using the user's\n // prompt text so semantic recall produces relevant results. Once\n // populated, the cached context is appended to the system prompt on\n // every turn for KV cache prefix stability (PR #2208).\n if (!state.recallCompleted && config.recallEnabled && config.authToken && client.isReachable()) {\n const promptText = typeof event.prompt === \"string\" && event.prompt.trim().length > 0 ? event.prompt : \"\";\n if (promptText) {\n state.recallCompleted = true;\n try {\n const recalled = await executeRecallWithBreaker(\n (signal) =>\n client.recall(promptText, session.sessionKey, session.cwd, {\n timeoutMs: config.turnRequestTimeoutMs,\n signal,\n }),\n recallTimeoutBreaker,\n pi,\n session,\n config,\n );\n client.markReachable();\n const context = trimContext(recalled.context ?? \"\", config.recallBudgetChars);\n if (context) {\n state.cachedContext = context;\n }\n } catch (err) {\n if (recallTimeoutBreaker.isTripped()) return;\n if (isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);\n session.notify(`Remnic recall unavailable: ${errorMessage(err)}`, \"warning\");\n }\n }\n }\n\n // Inject cached context into the system prompt — byte-identical across\n // turns so the KV cache prefix is preserved. This avoids the\n // system-role message issue: Pi's AgentMessage union does not include\n // \"system\", so injecting via the context hook produced an invalid\n // provider payload that Pi drops (codex review).\n if (!state.cachedContext) return;\n const basePrompt = typeof event.systemPrompt === \"string\" ? event.systemPrompt : \"\";\n return {\n systemPrompt: `${basePrompt}\\n\\n${state.cachedContext}`,\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, recallTimeoutBreaker);\n if (config.mcpToolsEnabled && config.authToken) {\n await registerMcpTools(pi, client, config, recallTimeoutBreaker);\n }\n };\n}\n\nexport default async function remnicPiExtension(pi: PiApi): Promise<void> {\n await createRemnicPiExtension()(pi);\n}\n\nfunction registerCommands(\n pi: PiApi,\n client: RemnicClient,\n config: RemnicPiConfig,\n recallTimeoutBreaker: RecallTimeoutBreaker,\n): void {\n pi.registerCommand(\"remnic-status\", {\n description: \"Check Remnic daemon status\",\n handler: commandHandler(async (_args, _ctx, session) => {\n if (recallTimeoutBreaker.isTripped()) notifyRecallDisabled(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 if (recallTimeoutBreaker.isTripped()) {\n notifyRecallDisabled(session);\n return;\n }\n const result = await executeRecallWithBreaker(\n (signal) =>\n client.recall(query, session.sessionKey, session.cwd, {\n timeoutMs: config.requestTimeoutMs,\n signal,\n }),\n recallTimeoutBreaker,\n pi,\n session,\n config,\n );\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(\n pi: PiApi,\n client: RemnicClient,\n config: RemnicPiConfig,\n recallTimeoutBreaker: RecallTimeoutBreaker,\n): Promise<void> {\n let tools: McpTool[] = [];\n try {\n tools = await client.mcpListTools({ timeoutMs: config.startupRequestTimeoutMs });\n } catch {\n return;\n }\n let registeredCount = 0;\n const seenPiToolNames = new Set<string>();\n for (const tool of tools) {\n const match = matchRemnicMcpToolName(tool.name);\n if (!match) continue;\n // A catalog that mixes shapes could map two names onto one Pi-safe name;\n // register the first occurrence only.\n if (seenPiToolNames.has(match.piToolName)) continue;\n seenPiToolNames.add(match.piToolName);\n registeredCount++;\n pi.registerTool({\n name: match.piToolName,\n label: match.serverToolName,\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 if (recallTimeoutBreaker.isTripped() && isRecallProducingToolName(tool.name)) {\n const message = notifyRecallDisabled(session);\n return {\n content: [{ type: \"text\", text: message }],\n details: { skipped: true, reason: \"recall_timeout_breaker\" },\n };\n }\n const safeParams = stripSessionOwnedRuntimeFields(params ?? {}) as Record<string, unknown>;\n const toolArguments = {\n ...safeParams,\n sessionKey: session.sessionKey,\n namespace: config.namespace,\n cwd: session.cwd,\n };\n const result = isRecallProducingToolName(tool.name)\n ? await executeRecallWithBreaker(\n (breakerSignal) =>\n client.mcpTool(match.serverToolName, toolArguments, {\n signal: signal ? AbortSignal.any([signal, breakerSignal]) : breakerSignal,\n }),\n recallTimeoutBreaker,\n pi,\n session,\n config,\n )\n : await client.mcpTool(match.serverToolName, toolArguments, { signal });\n return {\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\n details: result,\n };\n },\n });\n }\n if (tools.length > 0 && registeredCount === 0) {\n const observedNames = tools.map((tool) => tool.name).filter(isString);\n const observed = observedNames.slice(0, 10).join(\", \");\n const message =\n `Remnic MCP tools/list returned ${tools.length} tool(s) but none matched the Remnic namespace` +\n ` (\"remnic.\" or \"remnic_\" prefix); no Remnic MCP tools were registered. Observed: ${observed}` +\n `${observedNames.length > 10 ? \", …\" : \"\"}. Check Remnic server/plugin version compatibility.`;\n console.warn(`[remnic] ${message}`);\n pi.appendEntry(STATE_CUSTOM_TYPE, {\n level: \"warning\",\n code: \"MCP_TOOLS_FILTERED\",\n message,\n observedToolNames: observedNames,\n });\n }\n}\n\n/** Plain JSON Schema: Pi validates tool parameters as plain JSON Schema. */\nexport type PiToolParametersSchema = Record<string, unknown>;\n\nexport function toPiToolParametersSchema(inputSchema: unknown): PiToolParametersSchema {\n return 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 cachedContext: null,\n recallCompleted: false,\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/** Result of the session_start daemon probe. */\ntype DaemonProbeResult = \"ready\" | \"starting\" | \"unreachable\";\n\n/**\n * Probe the daemon and update the shared circuit breaker. Returns the probe\n * outcome so the caller can render a status label. This runs at session_start\n * regardless of `statusEnabled`: the breaker update is a data-path concern (a\n * down daemon must be marked unreachable so the namespace preflight and every\n * later hook fast-skip instead of each burning a full request budget),\n * independent of whether the status UI is shown.\n *\n * A 503 `not_ready` answer is NOT offline (issue #2215): the daemon responded\n * — it is up and serving recall via fallback retrieval while startup search\n * warm-up is still running — so it counts as reachable and renders as\n * \"starting\" instead of tripping the breaker or claiming the service is down.\n */\nasync function probeDaemonHealth(client: RemnicClient, config: RemnicPiConfig): Promise<DaemonProbeResult> {\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 \"ready\";\n } catch (err) {\n if (err instanceof RemnicHttpError && err.status === 503 && err.code === \"not_ready\") {\n client.markReachable();\n return \"starting\";\n }\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 \"unreachable\";\n }\n}\n\n/** Status-line label for the session_start probe outcome. */\nfunction remnicStatusLabel(probe: DaemonProbeResult, namespace: string | undefined): string {\n if (probe === \"unreachable\") return \"Remnic offline\";\n if (probe === \"starting\") return \"Remnic starting\";\n return `Remnic ${namespace ? `(${namespace})` : \"ready\"}`;\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 notifyRecallDisabled(session: PiContextSnapshot): string {\n const message =\n \"Remnic recall is disabled for this process because timeouts delayed operations; \" +\n \"memory recall is unavailable until restart.\";\n session.notify(message, \"warning\");\n return message;\n}\n\nconst RECALL_DISABLED_STATUS = \"Remnic recall disabled until restart (timeouts delayed operations)\";\n\nfunction emitRecallTimeoutTrip(pi: PiApi, session: PiContextSnapshot, config: RemnicPiConfig, err: unknown): void {\n const message =\n `Remnic recall has been disabled for this process because repeated timeouts delayed operations. ` +\n `Memory recall is unavailable until restart (${config.recallTimeoutThreshold} timeouts in the last ` +\n `${config.recallTimeoutWindow} recall attempts).`;\n session.notify(message, \"warning\");\n if (config.statusEnabled) {\n session.setStatus(\"remnic\", RECALL_DISABLED_STATUS);\n }\n pi.appendEntry(STATE_CUSTOM_TYPE, {\n level: \"warning\",\n code: \"RECALL_TIMEOUT_TRIP\",\n threshold: config.recallTimeoutThreshold,\n window: config.recallTimeoutWindow,\n reason: \"Recall disabled until restart because timeouts delayed operations\",\n message,\n error: errorMessage(err),\n disabledAt: new Date().toISOString(),\n persistent: true,\n });\n}\n\nasync function executeRecallWithBreaker<T>(\n operation: (signal: AbortSignal) => Promise<T>,\n breaker: RecallTimeoutBreaker,\n pi: PiApi,\n session: PiContextSnapshot,\n config: RemnicPiConfig,\n): Promise<T> {\n try {\n const result = await operation(breaker.signal);\n breaker.recordSuccess();\n return result;\n } catch (err) {\n if (!(breaker.isTripped() && err instanceof RemnicRequestAbortedError)) {\n const tripped = breaker.record(isRecallTimeoutError(err) ? \"timeout\" : \"failure\");\n if (tripped) emitRecallTimeoutTrip(pi, session, config, err);\n }\n throw err;\n }\n}\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 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 /** Optional caller abort signal (e.g. shared breaker trip). */\n signal?: AbortSignal;\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 /** Machine-readable error code from the daemon's JSON error body (e.g. `not_ready`). */\n readonly code?: string,\n ) {\n super(message);\n }\n}\n\nexport class RemnicRequestTimeoutError extends Error {\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Remnic request timed out after ${timeoutMs}ms`);\n this.name = \"RemnicRequestTimeoutError\";\n this.timeoutMs = timeoutMs;\n }\n}\n\nexport class RemnicRequestAbortedError extends Error {\n constructor() {\n super(\"Remnic request aborted\");\n this.name = \"RemnicRequestAbortedError\";\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 signal: options.signal,\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(\n name: string,\n args: Record<string, unknown>,\n options: RequestOptions = {},\n ): Promise<Record<string, unknown>> {\n return this.mcpRequest(\"tools/call\", {\n name,\n arguments: args,\n }, options);\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 let timedOut = false;\n const onExternalAbort = () => controller.abort();\n if (options.signal?.aborted) {\n throw new RemnicRequestAbortedError();\n }\n options.signal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n const override = options.timeoutMs;\n const timeoutMs =\n override === undefined\n ? this.config.requestTimeoutMs\n : (() => {\n if (!Number.isInteger(override) || override <= 0) {\n throw new TypeError(\"Request timeoutMs must be a positive integer\");\n }\n return override;\n })();\n const timeout = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, 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 const code = responseErrorCode(payload, parseError);\n if (response.status === 413) {\n const bodyBytes = body === undefined ? 0 : jsonBytes(body);\n throw new RemnicHttpError(response.status, `${message} (observed body ${bodyBytes} bytes; cap via observeMaxBytes)`, code);\n }\n throw new RemnicHttpError(response.status, message, code);\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 (timedOut) throw new RemnicRequestTimeoutError(timeoutMs);\n if (options.signal?.aborted) throw new RemnicRequestAbortedError();\n throw err;\n } finally {\n clearTimeout(timeout);\n options.signal?.removeEventListener(\"abort\", onExternalAbort);\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 // 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, options.signal);\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 if (lower.includes(\"socket connection was closed\")) return true;\n if (lower.includes(\"socket closed\")) return true;\n if (lower.includes(\"econnreset\")) return true;\n if (lower.includes(\"epipe\")) return true;\n if (lower.includes(\"und_err_socket\")) return true;\n if (lower.includes(\"fetch failed\")) return true;\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, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) return Promise.reject(new RemnicRequestAbortedError());\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(new RemnicRequestAbortedError());\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\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\nfunction responseErrorCode(payload: unknown, parseError: unknown): string | undefined {\n if (parseError || !payload || typeof payload !== \"object\") return undefined;\n if (\"code\" in payload && typeof payload.code === \"string\" && payload.code.trim().length > 0) {\n return payload.code;\n }\n return undefined;\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","import { RemnicRequestTimeoutError } from \"./client.js\";\n\n/**\n * In-memory rolling-window circuit breaker for automatic Pi context recalls.\n *\n * Only explicit Remnic request timeouts (the abort-to-timeout conversion from\n * {@link RemnicClient.request}) count toward the trip threshold. Other recall\n * failures (HTTP responses, auth errors, transient network failures) are still\n * recorded in the rolling window so they age out older timeouts, but they do\n * not increment the timeout counter.\n *\n * Once tripped, the breaker stays open for the lifetime of the process; it is\n * intentionally not reset by later successes.\n */\nexport type RecallResult = \"success\" | \"timeout\" | \"failure\";\n\n/**\n * True when an error is the explicit abort-to-timeout error produced by the\n * HTTP request layer. This deliberately excludes budget-exceeded, network,\n * HTTP/auth failures, and lookalike server-controlled messages.\n */\nexport function isRecallTimeoutError(err: unknown): boolean {\n return err instanceof RemnicRequestTimeoutError;\n}\n\nexport interface RecallTimeoutBreakerOptions {\n /** Number of timeouts in the last {@link window} recall calls that trips the breaker. */\n threshold: number;\n /** Number of recent recall calls kept in the rolling window. */\n window: number;\n}\n\nexport class RecallTimeoutBreaker {\n private tripped = false;\n private readonly results: boolean[] = [];\n private readonly threshold: number;\n private readonly windowSize: number;\n private readonly abortController = new AbortController();\n\n constructor(options: RecallTimeoutBreakerOptions) {\n const { threshold, window } = options;\n if (\n typeof threshold !== \"number\" ||\n !Number.isInteger(threshold) ||\n threshold <= 0 ||\n typeof window !== \"number\" ||\n !Number.isInteger(window) ||\n window <= 0 ||\n threshold > window\n ) {\n throw new Error(\n `Invalid RecallTimeoutBreaker options: threshold and window must be positive integers with threshold <= window (got threshold=${threshold}, window=${window})`,\n );\n }\n this.threshold = threshold;\n this.windowSize = window;\n }\n\n get signal(): AbortSignal {\n return this.abortController.signal;\n }\n\n isTripped(): boolean {\n return this.tripped;\n }\n\n recordSuccess(): void {\n this.record(\"success\");\n }\n\n recordTimeout(): boolean {\n return this.record(\"timeout\");\n }\n\n recordFailure(): void {\n this.record(\"failure\");\n }\n\n record(result: RecallResult): boolean {\n if (this.tripped) return false;\n\n this.results.push(result === \"timeout\");\n if (this.results.length > this.windowSize) this.results.shift();\n\n if (this.timeoutCount() >= this.threshold) {\n this.tripped = true;\n this.abortController.abort();\n return true;\n }\n\n return false;\n }\n\n timeoutCount(): number {\n let count = 0;\n for (const timedOut of this.results) {\n if (timedOut) count += 1;\n }\n return count;\n }\n}\n\n"],"mappings":"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","names":["createHash","createHash","remnicPiExtension"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/messages.ts","../src/recall-timeout-breaker.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\n\nimport { DEFAULT_CONFIG, loadConfig, type LoadConfigOptions, type RemnicPiConfig } from \"./config.js\";\nimport {\n RemnicClient,\n RemnicHttpError,\n RemnicRequestAbortedError,\n isTransientNetworkError,\n type McpTool,\n type ObserveMessage,\n} from \"./client.js\";\nimport {\n hashObservedMessage,\n observedMessageDedupeKey,\n sessionKeyFromContext,\n summarizeMessages,\n textFromMessage,\n toObserveMessage,\n} from \"./messages.js\";\nimport { RecallTimeoutBreaker, isRecallTimeoutError } from \"./recall-timeout-breaker.js\";\n\nexport type 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 type RemnicPiConfigInput = Omit<RemnicPiConfig, \"recallTimeoutThreshold\" | \"recallTimeoutWindow\"> &\n Partial<Pick<RemnicPiConfig, \"recallTimeoutThreshold\" | \"recallTimeoutWindow\">>;\n\nexport interface RemnicPiExtensionOptions extends LoadConfigOptions {\n config?: RemnicPiConfigInput;\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\"]);\nconst RECALL_BREAKER_REGISTRY_KEY = Symbol.for(\"remnic.plugin-pi.recall-timeout-breakers\");\nconst recallBreakerRegistry = globalThis as { [key: symbol]: Map<string, RecallTimeoutBreaker> | undefined };\nconst PROCESS_SCOPED_RECALL_BREAKERS =\n recallBreakerRegistry[RECALL_BREAKER_REGISTRY_KEY] ??= new Map<string, RecallTimeoutBreaker>();\nconst RECALL_PRODUCING_TOOL_NAMES: Record<string, true> = {\n \"remnic.recall\": true,\n \"remnic.recall_xray\": true,\n};\n\n/**\n * Match an MCP tool name against the recognized Remnic namespace and derive the\n * Pi-safe registered name. Two catalog shapes are supported:\n *\n * - legacy dotted form: `remnic.recall` -> pi: `remnic_recall`\n * - current underscore: `remnic_recall` -> pi: `remnic_recall`\n *\n * Only names starting with the exact `remnic.` or `remnic_` prefix and a\n * nonempty alphanumeric/underscore suffix are accepted. The server-side name\n * is echoed verbatim in tools/call so the request is accepted by whichever\n * server shape produced the catalog.\n */\nexport function matchRemnicMcpToolName(name: unknown): { serverToolName: string; piToolName: string } | null {\n if (typeof name !== \"string\") return null;\n let rest: string | null = null;\n if (name.startsWith(\"remnic.\")) {\n rest = name.slice(\"remnic.\".length);\n } else if (name.startsWith(\"remnic_\")) {\n rest = name.slice(\"remnic_\".length);\n }\n if (rest === null || !/^[a-zA-Z0-9_]+$/.test(rest)) return null;\n return {\n serverToolName: name,\n piToolName: `remnic_${rest}`,\n };\n}\n\n/** Breaker lookup tolerant of both catalog shapes (`remnic_recall` <-> `remnic.recall`). */\nfunction isRecallProducingToolName(serverToolName: string): boolean {\n return (\n RECALL_PRODUCING_TOOL_NAMES[serverToolName] === true ||\n RECALL_PRODUCING_TOOL_NAMES[serverToolName.replace(/^remnic_/, \"remnic.\")] === true\n );\n}\n\ntype PiSessionState = {\n observedHashes: Set<string>;\n liveObservedReplayKeys: Map<string, number>;\n /** Cached recall context — populated from the first context event's user prompt,\n * reused byte-identically across subsequent turns for KV cache prefix stability. */\n cachedContext: string | null;\n /** True once recall has been attempted (success or failure) for this session. */\n recallCompleted: boolean;\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\nfunction recallTimeoutBreakerCacheKey(\n config: Pick<RemnicPiConfig, \"remnicDaemonUrl\" | \"namespace\" | \"authToken\" | \"recallTimeoutThreshold\" | \"recallTimeoutWindow\">,\n): string {\n return JSON.stringify([\n config.remnicDaemonUrl,\n config.namespace ?? \"\",\n createHash(\"sha256\").update(config.authToken ?? \"\").digest(\"hex\"),\n config.recallTimeoutThreshold,\n config.recallTimeoutWindow,\n ]);\n}\n\nexport function resetProcessRecallBreakerForTest(\n config: Pick<RemnicPiConfig, \"remnicDaemonUrl\" | \"namespace\" | \"authToken\" | \"recallTimeoutThreshold\" | \"recallTimeoutWindow\">,\n): void {\n PROCESS_SCOPED_RECALL_BREAKERS.delete(recallTimeoutBreakerCacheKey(config));\n}\n\nexport function createRemnicPiExtension(options: RemnicPiExtensionOptions = {}) {\n const config: RemnicPiConfig = { ...DEFAULT_CONFIG, ...(options.config ?? loadConfig(options)) };\n const client = new RemnicClient(config);\n const sessionStates = new Map<string, PiSessionState>();\n const breakerKey = recallTimeoutBreakerCacheKey(config);\n let recallTimeoutBreaker = PROCESS_SCOPED_RECALL_BREAKERS.get(breakerKey);\n if (!recallTimeoutBreaker) {\n recallTimeoutBreaker = new RecallTimeoutBreaker({\n threshold: config.recallTimeoutThreshold,\n window: config.recallTimeoutWindow,\n });\n PROCESS_SCOPED_RECALL_BREAKERS.set(breakerKey, recallTimeoutBreaker);\n }\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 state.cachedContext = null;\n state.recallCompleted = false;\n\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 probe = await probeDaemonHealth(client, config);\n if (config.statusEnabled) {\n session.setStatus(\n \"remnic\",\n recallTimeoutBreaker.isTripped() ? RECALL_DISABLED_STATUS : remnicStatusLabel(probe, config.namespace),\n );\n }\n await runNamespacePreflight(pi, session, client, config);\n });\n\n pi.on(\"before_agent_start\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n\n // On the first before_agent_start, populate the cache using the user's\n // prompt text so semantic recall produces relevant results. Once\n // populated, the cached context is appended to the system prompt on\n // every turn for KV cache prefix stability (PR #2208).\n if (!state.recallCompleted && config.recallEnabled && config.authToken && client.isReachable()) {\n const promptText = typeof event.prompt === \"string\" && event.prompt.trim().length > 0 ? event.prompt : \"\";\n if (promptText) {\n state.recallCompleted = true;\n try {\n const recalled = await executeRecallWithBreaker(\n (signal) =>\n client.recall(promptText, session.sessionKey, session.cwd, {\n timeoutMs: config.turnRequestTimeoutMs,\n signal,\n }),\n recallTimeoutBreaker,\n pi,\n session,\n config,\n );\n client.markReachable();\n const context = trimContext(recalled.context ?? \"\", config.recallBudgetChars);\n if (context) {\n state.cachedContext = context;\n }\n } catch (err) {\n if (recallTimeoutBreaker.isTripped()) return;\n if (isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);\n session.notify(`Remnic recall unavailable: ${errorMessage(err)}`, \"warning\");\n }\n }\n }\n\n // Inject cached context into the system prompt — byte-identical across\n // turns so the KV cache prefix is preserved. This avoids the\n // system-role message issue: Pi's AgentMessage union does not include\n // \"system\", so injecting via the context hook produced an invalid\n // provider payload that Pi drops (codex review).\n if (!state.cachedContext) return;\n const basePrompt = typeof event.systemPrompt === \"string\" ? event.systemPrompt : \"\";\n return {\n systemPrompt: `${basePrompt}\\n\\n${state.cachedContext}`,\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, recallTimeoutBreaker);\n if (config.mcpToolsEnabled && config.authToken) {\n await registerMcpTools(pi, client, config, recallTimeoutBreaker);\n }\n };\n}\n\nexport default async function remnicPiExtension(pi: PiApi): Promise<void> {\n await createRemnicPiExtension()(pi);\n}\n\nfunction registerCommands(\n pi: PiApi,\n client: RemnicClient,\n config: RemnicPiConfig,\n recallTimeoutBreaker: RecallTimeoutBreaker,\n): void {\n pi.registerCommand(\"remnic-status\", {\n description: \"Check Remnic daemon status\",\n handler: commandHandler(async (_args, _ctx, session) => {\n if (recallTimeoutBreaker.isTripped()) notifyRecallDisabled(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 if (recallTimeoutBreaker.isTripped()) {\n notifyRecallDisabled(session);\n return;\n }\n const result = await executeRecallWithBreaker(\n (signal) =>\n client.recall(query, session.sessionKey, session.cwd, {\n timeoutMs: config.requestTimeoutMs,\n signal,\n }),\n recallTimeoutBreaker,\n pi,\n session,\n config,\n );\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(\n pi: PiApi,\n client: RemnicClient,\n config: RemnicPiConfig,\n recallTimeoutBreaker: RecallTimeoutBreaker,\n): Promise<void> {\n let tools: McpTool[] = [];\n try {\n tools = await client.mcpListTools({ timeoutMs: config.startupRequestTimeoutMs });\n } catch {\n return;\n }\n let registeredCount = 0;\n const seenPiToolNames = new Set<string>();\n for (const tool of tools) {\n const match = matchRemnicMcpToolName(tool.name);\n if (!match) continue;\n // A catalog that mixes shapes could map two names onto one Pi-safe name;\n // register the first occurrence only.\n if (seenPiToolNames.has(match.piToolName)) continue;\n seenPiToolNames.add(match.piToolName);\n registeredCount++;\n pi.registerTool({\n name: match.piToolName,\n label: match.serverToolName,\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 if (recallTimeoutBreaker.isTripped() && isRecallProducingToolName(tool.name)) {\n const message = notifyRecallDisabled(session);\n return {\n content: [{ type: \"text\", text: message }],\n details: { skipped: true, reason: \"recall_timeout_breaker\" },\n };\n }\n const safeParams = stripSessionOwnedRuntimeFields(params ?? {}) as Record<string, unknown>;\n const toolArguments = {\n ...safeParams,\n sessionKey: session.sessionKey,\n namespace: config.namespace,\n cwd: session.cwd,\n };\n const result = isRecallProducingToolName(tool.name)\n ? await executeRecallWithBreaker(\n (breakerSignal) =>\n client.mcpTool(match.serverToolName, toolArguments, {\n signal: signal ? AbortSignal.any([signal, breakerSignal]) : breakerSignal,\n }),\n recallTimeoutBreaker,\n pi,\n session,\n config,\n )\n : await client.mcpTool(match.serverToolName, toolArguments, { signal });\n return {\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\n details: result,\n };\n },\n });\n }\n if (tools.length > 0 && registeredCount === 0) {\n const observedNames = tools.map((tool) => tool.name).filter(isString);\n const observed = observedNames.slice(0, 10).join(\", \");\n const message =\n `Remnic MCP tools/list returned ${tools.length} tool(s) but none matched the Remnic namespace` +\n ` (\"remnic.\" or \"remnic_\" prefix); no Remnic MCP tools were registered. Observed: ${observed}` +\n `${observedNames.length > 10 ? \", …\" : \"\"}. Check Remnic server/plugin version compatibility.`;\n console.warn(`[remnic] ${message}`);\n pi.appendEntry(STATE_CUSTOM_TYPE, {\n level: \"warning\",\n code: \"MCP_TOOLS_FILTERED\",\n message,\n observedToolNames: observedNames,\n });\n }\n}\n\n/** Plain JSON Schema: Pi validates tool parameters as plain JSON Schema. */\nexport type PiToolParametersSchema = Record<string, unknown>;\n\nexport function toPiToolParametersSchema(inputSchema: unknown): PiToolParametersSchema {\n return 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 cachedContext: null,\n recallCompleted: false,\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/** Result of the session_start daemon probe. */\ntype DaemonProbeResult = \"ready\" | \"starting\" | \"unreachable\";\n\n/**\n * Probe the daemon and update the shared circuit breaker. Returns the probe\n * outcome so the caller can render a status label. This runs at session_start\n * regardless of `statusEnabled`: the breaker update is a data-path concern (a\n * down daemon must be marked unreachable so the namespace preflight and every\n * later hook fast-skip instead of each burning a full request budget),\n * independent of whether the status UI is shown.\n *\n * A 503 `not_ready` answer is NOT offline (issue #2215): the daemon responded\n * — it is up and serving recall via fallback retrieval while startup search\n * warm-up is still running — so it counts as reachable and renders as\n * \"starting\" instead of tripping the breaker or claiming the service is down.\n */\nasync function probeDaemonHealth(client: RemnicClient, config: RemnicPiConfig): Promise<DaemonProbeResult> {\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 \"ready\";\n } catch (err) {\n if (err instanceof RemnicHttpError && err.status === 503 && err.code === \"not_ready\") {\n client.markReachable();\n return \"starting\";\n }\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 \"unreachable\";\n }\n}\n\n/** Status-line label for the session_start probe outcome. */\nfunction remnicStatusLabel(probe: DaemonProbeResult, namespace: string | undefined): string {\n if (probe === \"unreachable\") return \"Remnic offline\";\n if (probe === \"starting\") return \"Remnic starting\";\n return `Remnic ${namespace ? `(${namespace})` : \"ready\"}`;\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 notifyRecallDisabled(session: PiContextSnapshot): string {\n const message =\n \"Remnic recall is paused for this process because timeouts delayed operations; \" +\n \"recall retries automatically after a 5 minute cooldown.\";\n session.notify(message, \"warning\");\n return message;\n}\n\nconst RECALL_DISABLED_STATUS = \"Remnic recall paused (timeouts delayed operations; auto-retry after cooldown)\";\n\nfunction emitRecallTimeoutTrip(pi: PiApi, session: PiContextSnapshot, config: RemnicPiConfig, err: unknown): void {\n const message =\n `Remnic recall has been paused because repeated timeouts delayed operations ` +\n `(${config.recallTimeoutThreshold} timeouts in the last ${config.recallTimeoutWindow} recall attempts). ` +\n `It retries automatically after a 5 minute cooldown.`;\n session.notify(message, \"warning\");\n if (config.statusEnabled) {\n session.setStatus(\"remnic\", RECALL_DISABLED_STATUS);\n }\n pi.appendEntry(STATE_CUSTOM_TYPE, {\n level: \"warning\",\n code: \"RECALL_TIMEOUT_TRIP\",\n threshold: config.recallTimeoutThreshold,\n window: config.recallTimeoutWindow,\n reason: \"Recall paused because timeouts delayed operations; auto-retries after cooldown\",\n message,\n error: errorMessage(err),\n disabledAt: new Date().toISOString(),\n persistent: true,\n });\n}\n\nasync function executeRecallWithBreaker<T>(\n operation: (signal: AbortSignal) => Promise<T>,\n breaker: RecallTimeoutBreaker,\n pi: PiApi,\n session: PiContextSnapshot,\n config: RemnicPiConfig,\n): Promise<T> {\n if (breaker.isTripped()) throw new RemnicRequestAbortedError();\n try {\n const result = await operation(breaker.signal);\n breaker.recordSuccess();\n return result;\n } catch (err) {\n if (!(breaker.isTripped() && err instanceof RemnicRequestAbortedError)) {\n const tripped = breaker.record(isRecallTimeoutError(err) ? \"timeout\" : \"failure\");\n if (tripped) emitRecallTimeoutTrip(pi, session, config, err);\n }\n throw err;\n }\n}\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 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 /** Optional caller abort signal (e.g. shared breaker trip). */\n signal?: AbortSignal;\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 /** Machine-readable error code from the daemon's JSON error body (e.g. `not_ready`). */\n readonly code?: string,\n ) {\n super(message);\n }\n}\n\nexport class RemnicRequestTimeoutError extends Error {\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Remnic request timed out after ${timeoutMs}ms`);\n this.name = \"RemnicRequestTimeoutError\";\n this.timeoutMs = timeoutMs;\n }\n}\n\nexport class RemnicRequestAbortedError extends Error {\n constructor() {\n super(\"Remnic request aborted\");\n this.name = \"RemnicRequestAbortedError\";\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 signal: options.signal,\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(\n name: string,\n args: Record<string, unknown>,\n options: RequestOptions = {},\n ): Promise<Record<string, unknown>> {\n return this.mcpRequest(\"tools/call\", {\n name,\n arguments: args,\n }, options);\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 let timedOut = false;\n const onExternalAbort = () => controller.abort();\n if (options.signal?.aborted) {\n throw new RemnicRequestAbortedError();\n }\n options.signal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n const override = options.timeoutMs;\n const timeoutMs =\n override === undefined\n ? this.config.requestTimeoutMs\n : (() => {\n if (!Number.isInteger(override) || override <= 0) {\n throw new TypeError(\"Request timeoutMs must be a positive integer\");\n }\n return override;\n })();\n const timeout = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, 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 const code = responseErrorCode(payload, parseError);\n if (response.status === 413) {\n const bodyBytes = body === undefined ? 0 : jsonBytes(body);\n throw new RemnicHttpError(response.status, `${message} (observed body ${bodyBytes} bytes; cap via observeMaxBytes)`, code);\n }\n throw new RemnicHttpError(response.status, message, code);\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 (timedOut) throw new RemnicRequestTimeoutError(timeoutMs);\n if (options.signal?.aborted) throw new RemnicRequestAbortedError();\n throw err;\n } finally {\n clearTimeout(timeout);\n options.signal?.removeEventListener(\"abort\", onExternalAbort);\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 // 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, options.signal);\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 if (lower.includes(\"socket connection was closed\")) return true;\n if (lower.includes(\"socket closed\")) return true;\n if (lower.includes(\"econnreset\")) return true;\n if (lower.includes(\"epipe\")) return true;\n if (lower.includes(\"und_err_socket\")) return true;\n if (lower.includes(\"fetch failed\")) return true;\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, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) return Promise.reject(new RemnicRequestAbortedError());\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(new RemnicRequestAbortedError());\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\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\nfunction responseErrorCode(payload: unknown, parseError: unknown): string | undefined {\n if (parseError || !payload || typeof payload !== \"object\") return undefined;\n if (\"code\" in payload && typeof payload.code === \"string\" && payload.code.trim().length > 0) {\n return payload.code;\n }\n return undefined;\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","import { RemnicRequestTimeoutError } from \"./client.js\";\n\n/**\n * In-memory rolling-window circuit breaker for automatic Pi context recalls.\n *\n * Only explicit Remnic request timeouts (the abort-to-timeout conversion from\n * {@link RemnicClient.request}) count toward the trip threshold. Other recall\n * failures (HTTP responses, auth errors, transient network failures) are still\n * recorded in the rolling window so they age out older timeouts, but they do\n * not increment the timeout counter.\n *\n * Once tripped, the breaker pauses recall for a cooldown (default 5 minutes,\n * tunable via {@link RecallTimeoutBreakerOptions.resetAfterMs}) and then\n * re-arms itself: the rolling window clears, a fresh abort controller is\n * installed, and the next recall acts as the probe. Sustained saturation\n * simply re-trips it; a recovered daemon resumes recall without a process\n * restart.\n */\nexport type RecallResult = \"success\" | \"timeout\" | \"failure\";\n\n/**\n * True when an error is the explicit abort-to-timeout error produced by the\n * HTTP request layer. This deliberately excludes budget-exceeded, network,\n * HTTP/auth failures, and lookalike server-controlled messages.\n */\nexport function isRecallTimeoutError(err: unknown): boolean {\n return err instanceof RemnicRequestTimeoutError;\n}\n\nexport interface RecallTimeoutBreakerOptions {\n /** Number of timeouts in the last {@link window} recall calls that trips the breaker. */\n threshold: number;\n /** Number of recent recall calls kept in the rolling window. */\n window: number;\n /**\n * Cooldown in milliseconds after a trip before the breaker re-arms itself.\n * Defaults to 300000 (5 minutes).\n */\n resetAfterMs?: number;\n}\n\nexport class RecallTimeoutBreaker {\n private tripped = false;\n private trippedAt = 0;\n private readonly results: boolean[] = [];\n private readonly threshold: number;\n private readonly windowSize: number;\n private readonly resetAfterMs: number;\n private abortController = new AbortController();\n\n constructor(options: RecallTimeoutBreakerOptions) {\n const { threshold, window, resetAfterMs } = options;\n if (\n typeof threshold !== \"number\" ||\n !Number.isInteger(threshold) ||\n threshold <= 0 ||\n typeof window !== \"number\" ||\n !Number.isInteger(window) ||\n window <= 0 ||\n threshold > window\n ) {\n throw new Error(\n `Invalid RecallTimeoutBreaker options: threshold and window must be positive integers with threshold <= window (got threshold=${threshold}, window=${window})`,\n );\n }\n this.threshold = threshold;\n this.windowSize = window;\n if (\n resetAfterMs !== undefined &&\n (typeof resetAfterMs !== \"number\" || !Number.isInteger(resetAfterMs) || resetAfterMs <= 0)\n ) {\n throw new Error(\n `Invalid RecallTimeoutBreaker options: resetAfterMs must be a positive integer (got ${resetAfterMs})`,\n );\n }\n this.resetAfterMs = resetAfterMs ?? 300000;\n }\n\n get signal(): AbortSignal {\n return this.abortController.signal;\n }\n\n isTripped(): boolean {\n if (this.tripped && Date.now() - this.trippedAt >= this.resetAfterMs) {\n this.tripped = false;\n this.trippedAt = 0;\n this.results.length = 0;\n this.abortController = new AbortController();\n return false;\n }\n return this.tripped;\n }\n\n recordSuccess(): void {\n this.record(\"success\");\n }\n\n recordTimeout(): boolean {\n return this.record(\"timeout\");\n }\n\n recordFailure(): void {\n this.record(\"failure\");\n }\n\n record(result: RecallResult): boolean {\n if (this.tripped) return false;\n\n this.results.push(result === \"timeout\");\n if (this.results.length > this.windowSize) this.results.shift();\n\n if (this.timeoutCount() >= this.threshold) {\n this.tripped = true;\n this.trippedAt = Date.now();\n this.abortController.abort();\n return true;\n }\n\n return false;\n }\n\n timeoutCount(): number {\n let count = 0;\n for (const timedOut of this.results) {\n if (timedOut) count += 1;\n }\n return count;\n }\n}\n\n"],"mappings":"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","names":["createHash","createHash","remnicPiExtension"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/plugin-pi",
|
|
3
|
-
"version": "9.69.
|
|
3
|
+
"version": "9.69.61",
|
|
4
4
|
"description": "Remnic memory extension for Pi Coding Agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"provenance": true
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@remnic/core": "^9.69.
|
|
44
|
+
"@remnic/core": "^9.69.61"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@earendil-works/pi-coding-agent": "*"
|