@remnic/plugin-pi 9.69.17 → 9.69.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +17 -1
- package/dist/index.js +43 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -185,6 +185,22 @@ type RemnicPiConfigInput = Omit<RemnicPiConfig, "recallTimeoutThreshold" | "reca
|
|
|
185
185
|
interface RemnicPiExtensionOptions extends LoadConfigOptions {
|
|
186
186
|
config?: RemnicPiConfigInput;
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Match an MCP tool name against the recognized Remnic namespace and derive the
|
|
190
|
+
* Pi-safe registered name. Two catalog shapes are supported:
|
|
191
|
+
*
|
|
192
|
+
* - legacy dotted form: `remnic.recall` -> pi: `remnic_recall`
|
|
193
|
+
* - current underscore: `remnic_recall` -> pi: `remnic_recall`
|
|
194
|
+
*
|
|
195
|
+
* Only names starting with the exact `remnic.` or `remnic_` prefix and a
|
|
196
|
+
* nonempty alphanumeric/underscore suffix are accepted. The server-side name
|
|
197
|
+
* is echoed verbatim in tools/call so the request is accepted by whichever
|
|
198
|
+
* server shape produced the catalog.
|
|
199
|
+
*/
|
|
200
|
+
declare function matchRemnicMcpToolName(name: unknown): {
|
|
201
|
+
serverToolName: string;
|
|
202
|
+
piToolName: string;
|
|
203
|
+
} | null;
|
|
188
204
|
declare function resetProcessRecallBreakerForTest(config: Pick<RemnicPiConfig, "remnicDaemonUrl" | "namespace" | "authToken" | "recallTimeoutThreshold" | "recallTimeoutWindow">): void;
|
|
189
205
|
declare function createRemnicPiExtension(options?: RemnicPiExtensionOptions): (pi: PiApi) => Promise<void>;
|
|
190
206
|
declare function remnicPiExtension(pi: PiApi): Promise<void>;
|
|
@@ -195,4 +211,4 @@ declare function observeMessages(ctx: any, client: RemnicClient, rawMessages: un
|
|
|
195
211
|
declare function buildCompactionSummary(preparation: any): string;
|
|
196
212
|
declare function isDaemonUnreachableError(err: unknown): boolean;
|
|
197
213
|
|
|
198
|
-
export { type PiApi, type RemnicPiConfigInput, type RemnicPiExtensionOptions, buildCompactionSummary, createRemnicPiExtension, remnicPiExtension as default, isDaemonUnreachableError, observeMessages, resetProcessRecallBreakerForTest, stripSessionOwnedRuntimeFields, stripSessionOwnedSchemaFields, textFromMessage, toPiToolParametersSchema };
|
|
214
|
+
export { type PiApi, type RemnicPiConfigInput, type RemnicPiExtensionOptions, buildCompactionSummary, createRemnicPiExtension, remnicPiExtension as default, isDaemonUnreachableError, matchRemnicMcpToolName, observeMessages, resetProcessRecallBreakerForTest, stripSessionOwnedRuntimeFields, stripSessionOwnedSchemaFields, textFromMessage, toPiToolParametersSchema };
|
package/dist/index.js
CHANGED
|
@@ -684,6 +684,23 @@ var RECALL_PRODUCING_TOOL_NAMES = {
|
|
|
684
684
|
"remnic.recall": true,
|
|
685
685
|
"remnic.recall_xray": true
|
|
686
686
|
};
|
|
687
|
+
function matchRemnicMcpToolName(name) {
|
|
688
|
+
if (typeof name !== "string") return null;
|
|
689
|
+
let rest = null;
|
|
690
|
+
if (name.startsWith("remnic.")) {
|
|
691
|
+
rest = name.slice("remnic.".length);
|
|
692
|
+
} else if (name.startsWith("remnic_")) {
|
|
693
|
+
rest = name.slice("remnic_".length);
|
|
694
|
+
}
|
|
695
|
+
if (rest === null || !/^[a-zA-Z0-9_]+$/.test(rest)) return null;
|
|
696
|
+
return {
|
|
697
|
+
serverToolName: name,
|
|
698
|
+
piToolName: `remnic_${rest}`
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
function isRecallProducingToolName(serverToolName) {
|
|
702
|
+
return RECALL_PRODUCING_TOOL_NAMES[serverToolName] === true || RECALL_PRODUCING_TOOL_NAMES[serverToolName.replace(/^remnic_/, "remnic.")] === true;
|
|
703
|
+
}
|
|
687
704
|
function recallTimeoutBreakerCacheKey(config) {
|
|
688
705
|
return JSON.stringify([
|
|
689
706
|
config.remnicDaemonUrl,
|
|
@@ -936,12 +953,17 @@ async function registerMcpTools(pi, client, config, recallTimeoutBreaker) {
|
|
|
936
953
|
} catch {
|
|
937
954
|
return;
|
|
938
955
|
}
|
|
956
|
+
let registeredCount = 0;
|
|
957
|
+
const seenPiToolNames = /* @__PURE__ */ new Set();
|
|
939
958
|
for (const tool of tools) {
|
|
940
|
-
|
|
941
|
-
|
|
959
|
+
const match = matchRemnicMcpToolName(tool.name);
|
|
960
|
+
if (!match) continue;
|
|
961
|
+
if (seenPiToolNames.has(match.piToolName)) continue;
|
|
962
|
+
seenPiToolNames.add(match.piToolName);
|
|
963
|
+
registeredCount++;
|
|
942
964
|
pi.registerTool({
|
|
943
|
-
name: piToolName,
|
|
944
|
-
label:
|
|
965
|
+
name: match.piToolName,
|
|
966
|
+
label: match.serverToolName,
|
|
945
967
|
description: tool.description ?? `Call ${tool.name}`,
|
|
946
968
|
parameters: toPiToolParametersSchema(tool.inputSchema),
|
|
947
969
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
@@ -952,7 +974,7 @@ async function registerMcpTools(pi, client, config, recallTimeoutBreaker) {
|
|
|
952
974
|
details: { skipped: true, reason: "stale_context" }
|
|
953
975
|
};
|
|
954
976
|
}
|
|
955
|
-
if (recallTimeoutBreaker.isTripped() &&
|
|
977
|
+
if (recallTimeoutBreaker.isTripped() && isRecallProducingToolName(tool.name)) {
|
|
956
978
|
const message = notifyRecallDisabled(session);
|
|
957
979
|
return {
|
|
958
980
|
content: [{ type: "text", text: message }],
|
|
@@ -966,15 +988,15 @@ async function registerMcpTools(pi, client, config, recallTimeoutBreaker) {
|
|
|
966
988
|
namespace: config.namespace,
|
|
967
989
|
cwd: session.cwd
|
|
968
990
|
};
|
|
969
|
-
const result =
|
|
970
|
-
(breakerSignal) => client.mcpTool(
|
|
991
|
+
const result = isRecallProducingToolName(tool.name) ? await executeRecallWithBreaker(
|
|
992
|
+
(breakerSignal) => client.mcpTool(match.serverToolName, toolArguments, {
|
|
971
993
|
signal: signal ? AbortSignal.any([signal, breakerSignal]) : breakerSignal
|
|
972
994
|
}),
|
|
973
995
|
recallTimeoutBreaker,
|
|
974
996
|
pi,
|
|
975
997
|
session,
|
|
976
998
|
config
|
|
977
|
-
) : await client.mcpTool(
|
|
999
|
+
) : await client.mcpTool(match.serverToolName, toolArguments, { signal });
|
|
978
1000
|
return {
|
|
979
1001
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
980
1002
|
details: result
|
|
@@ -982,6 +1004,18 @@ async function registerMcpTools(pi, client, config, recallTimeoutBreaker) {
|
|
|
982
1004
|
}
|
|
983
1005
|
});
|
|
984
1006
|
}
|
|
1007
|
+
if (tools.length > 0 && registeredCount === 0) {
|
|
1008
|
+
const observedNames = tools.map((tool) => tool.name).filter(isString);
|
|
1009
|
+
const observed = observedNames.slice(0, 10).join(", ");
|
|
1010
|
+
const message = `Remnic MCP tools/list returned ${tools.length} tool(s) but none matched the Remnic namespace ("remnic." or "remnic_" prefix); no Remnic MCP tools were registered. Observed: ${observed}${observedNames.length > 10 ? ", \u2026" : ""}. Check Remnic server/plugin version compatibility.`;
|
|
1011
|
+
console.warn(`[remnic] ${message}`);
|
|
1012
|
+
pi.appendEntry(STATE_CUSTOM_TYPE, {
|
|
1013
|
+
level: "warning",
|
|
1014
|
+
code: "MCP_TOOLS_FILTERED",
|
|
1015
|
+
message,
|
|
1016
|
+
observedToolNames: observedNames
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
985
1019
|
}
|
|
986
1020
|
function toPiToolParametersSchema(inputSchema) {
|
|
987
1021
|
return Type.Unsafe(stripSessionOwnedSchemaFields(inputSchema));
|
|
@@ -1399,6 +1433,7 @@ export {
|
|
|
1399
1433
|
createRemnicPiExtension,
|
|
1400
1434
|
remnicPiExtension as default,
|
|
1401
1435
|
isDaemonUnreachableError,
|
|
1436
|
+
matchRemnicMcpToolName,
|
|
1402
1437
|
observeMessages,
|
|
1403
1438
|
resetProcessRecallBreakerForTest,
|
|
1404
1439
|
stripSessionOwnedRuntimeFields,
|
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 { Type, type TSchema } from \"@sinclair/typebox\";\nimport { 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\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 for (const tool of tools) {\n if (!tool.name.startsWith(\"remnic.\")) continue;\n const piToolName = tool.name.replace(/^remnic\\./, \"remnic_\").replace(/[^a-zA-Z0-9_]/g, \"_\");\n pi.registerTool({\n name: piToolName,\n label: tool.name,\n description: tool.description ?? `Call ${tool.name}`,\n parameters: toPiToolParametersSchema(tool.inputSchema),\n async execute(_toolCallId: string, params: Record<string, unknown>, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: any) {\n const session = snapshotPiContext(ctx);\n if (!session) {\n return {\n content: [{ type: \"text\", text: \"Remnic tool skipped because the Pi context is no longer active.\" }],\n details: { skipped: true, reason: \"stale_context\" },\n };\n }\n if (recallTimeoutBreaker.isTripped() && RECALL_PRODUCING_TOOL_NAMES[tool.name] === true) {\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 = RECALL_PRODUCING_TOOL_NAMES[tool.name] === true\n ? await executeRecallWithBreaker(\n (breakerSignal) =>\n client.mcpTool(tool.name, toolArguments, {\n signal: signal ? AbortSignal.any([signal, breakerSignal]) : breakerSignal,\n }),\n recallTimeoutBreaker,\n pi,\n session,\n config,\n )\n : await client.mcpTool(tool.name, toolArguments, { signal });\n return {\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\n details: result,\n };\n },\n });\n }\n}\n\nexport function toPiToolParametersSchema(inputSchema: unknown): TSchema {\n return Type.Unsafe(stripSessionOwnedSchemaFields(inputSchema));\n}\n\nexport function stripSessionOwnedSchemaFields(inputSchema: unknown): Record<string, unknown> {\n if (!isRecord(inputSchema)) {\n return { type: \"object\", properties: {}, additionalProperties: true };\n }\n return stripSessionOwnedSchemaNode(inputSchema) as Record<string, unknown>;\n}\n\nfunction stripSessionOwnedSchemaNode(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripSessionOwnedSchemaNode(entry));\n }\n if (!isRecord(value)) {\n return value;\n }\n const schema: Record<string, unknown> = { ...value };\n if (isRecord(value.properties)) {\n const properties: Record<string, unknown> = {};\n for (const [key, property] of Object.entries(value.properties)) {\n if (SESSION_OWNED_FIELDS.has(key)) continue;\n properties[key] = stripSessionOwnedSchemaNode(property);\n }\n schema.properties = properties;\n }\n if (Array.isArray(value.required)) {\n schema.required = value.required.filter(\n (field) => typeof field !== \"string\" || !SESSION_OWNED_FIELDS.has(field),\n );\n }\n for (const key of [\"items\", \"additionalProperties\", \"not\"] as const) {\n if (isRecord(value[key])) {\n schema[key] = stripSessionOwnedSchemaNode(value[key]);\n }\n }\n for (const key of [\"oneOf\", \"anyOf\", \"allOf\"] as const) {\n if (Array.isArray(value[key])) {\n schema[key] = value[key].map((entry) => stripSessionOwnedSchemaNode(entry));\n }\n }\n return schema;\n}\n\nexport function stripSessionOwnedRuntimeFields(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripSessionOwnedRuntimeFields(entry));\n }\n if (!isRecord(value)) {\n return value;\n }\n const sanitized: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n if (SESSION_OWNED_FIELDS.has(key)) continue;\n sanitized[key] = stripSessionOwnedRuntimeFields(child);\n }\n return sanitized;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isUserMessage(message: unknown): boolean {\n return isRecord(message) && message.role === \"user\";\n}\n\nfunction getSessionState(sessionKey: string, states: Map<string, PiSessionState>): { sessionKey: string; state: PiSessionState } {\n let state = states.get(sessionKey);\n if (!state) {\n state = {\n observedHashes: new Set<string>(),\n liveObservedReplayKeys: new Map<string, number>(),\n 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 { Type, type TSchema } from \"@sinclair/typebox\";\nimport { 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\nexport function toPiToolParametersSchema(inputSchema: unknown): TSchema {\n return Type.Unsafe(stripSessionOwnedSchemaFields(inputSchema));\n}\n\nexport function stripSessionOwnedSchemaFields(inputSchema: unknown): Record<string, unknown> {\n if (!isRecord(inputSchema)) {\n return { type: \"object\", properties: {}, additionalProperties: true };\n }\n return stripSessionOwnedSchemaNode(inputSchema) as Record<string, unknown>;\n}\n\nfunction stripSessionOwnedSchemaNode(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripSessionOwnedSchemaNode(entry));\n }\n if (!isRecord(value)) {\n return value;\n }\n const schema: Record<string, unknown> = { ...value };\n if (isRecord(value.properties)) {\n const properties: Record<string, unknown> = {};\n for (const [key, property] of Object.entries(value.properties)) {\n if (SESSION_OWNED_FIELDS.has(key)) continue;\n properties[key] = stripSessionOwnedSchemaNode(property);\n }\n schema.properties = properties;\n }\n if (Array.isArray(value.required)) {\n schema.required = value.required.filter(\n (field) => typeof field !== \"string\" || !SESSION_OWNED_FIELDS.has(field),\n );\n }\n for (const key of [\"items\", \"additionalProperties\", \"not\"] as const) {\n if (isRecord(value[key])) {\n schema[key] = stripSessionOwnedSchemaNode(value[key]);\n }\n }\n for (const key of [\"oneOf\", \"anyOf\", \"allOf\"] as const) {\n if (Array.isArray(value[key])) {\n schema[key] = value[key].map((entry) => stripSessionOwnedSchemaNode(entry));\n }\n }\n return schema;\n}\n\nexport function stripSessionOwnedRuntimeFields(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripSessionOwnedRuntimeFields(entry));\n }\n if (!isRecord(value)) {\n return value;\n }\n const sanitized: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n if (SESSION_OWNED_FIELDS.has(key)) continue;\n sanitized[key] = stripSessionOwnedRuntimeFields(child);\n }\n return sanitized;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isUserMessage(message: unknown): boolean {\n return isRecord(message) && message.role === \"user\";\n}\n\nfunction getSessionState(sessionKey: string, states: Map<string, PiSessionState>): { sessionKey: string; state: PiSessionState } {\n let state = states.get(sessionKey);\n if (!state) {\n state = {\n observedHashes: new Set<string>(),\n liveObservedReplayKeys: new Map<string, number>(),\n 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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/plugin-pi",
|
|
3
|
-
"version": "9.69.
|
|
3
|
+
"version": "9.69.19",
|
|
4
4
|
"description": "Remnic memory extension for Pi Coding Agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@sinclair/typebox": "^0.34.0",
|
|
45
|
-
"@remnic/core": "^9.69.
|
|
45
|
+
"@remnic/core": "^9.69.19"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@earendil-works/pi-coding-agent": "*"
|