@remnic/plugin-pi 9.3.700 → 9.3.701

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -15,6 +15,32 @@ interface RemnicPiConfig {
15
15
  statusEnabled: boolean;
16
16
  requestTimeoutMs: number;
17
17
  startupRequestTimeoutMs: number;
18
+ /**
19
+ * Per-turn request budget for observe/recall. MUST stay below the host's
20
+ * in-handler kill budget (Pi/omp kills handlers at 30 s). Defaults to 20 s,
21
+ * capped at 25 s so a misconfiguration can never produce a structurally
22
+ * unsatisfiable timeout (issue #1626).
23
+ */
24
+ turnRequestTimeoutMs: number;
25
+ /**
26
+ * Soft cap on a single observe POST body in bytes. The client chunks observe
27
+ * batches to stay under this; individual oversized messages are truncated
28
+ * with a marker. Defaults to 100 KiB, safely under the daemon's default
29
+ * 128 KiB `maxBodyBytes` (issue #1600).
30
+ */
31
+ observeMaxBytes: number;
32
+ /**
33
+ * Maximum retry attempts for observe/recall on transient connection-level
34
+ * failures (socket close, ECONNRESET, EPIPE). Observe is dedupe-safe so
35
+ * retrying is harmless (issue #1602).
36
+ */
37
+ observeMaxRetries: number;
38
+ /**
39
+ * Cooldown base for the daemon-reachability circuit breaker. When observe/
40
+ * recall fails on a timeout or connection error, subsequent turns skip fast
41
+ * for an exponentially growing window starting at this value (issue #1626).
42
+ */
43
+ daemonCooldownMs: number;
18
44
  }
19
45
  interface LoadConfigOptions {
20
46
  configPath?: string;
@@ -51,27 +77,58 @@ interface McpTool {
51
77
  description?: string;
52
78
  inputSchema?: Record<string, unknown>;
53
79
  }
80
+ interface RequestOptions {
81
+ timeoutMs?: number;
82
+ /** Transient-retry budget for connection-level failures (socket close, ECONNRESET). */
83
+ maxRetries?: number;
84
+ }
85
+ interface ObserveOptions extends RequestOptions {
86
+ /** Soft cap on a single observe POST body in bytes; oversize batches are chunked. */
87
+ maxBytes?: number;
88
+ }
54
89
  declare class RemnicClient {
55
90
  private readonly config;
56
91
  private requestId;
92
+ private unreachableUntil;
93
+ private consecutiveFailures;
57
94
  constructor(config: RemnicPiConfig);
95
+ /** True when the daemon is not in a known-unreachable cooldown. */
96
+ isReachable(): boolean;
97
+ /** Clear the circuit breaker — call after any successful daemon interaction. */
98
+ markReachable(): void;
99
+ /**
100
+ * Enter (or extend) an unreachable cooldown. The cooldown grows exponentially
101
+ * with consecutive failures (base, 2×base, 4×base, …) capped at 60 s, so a
102
+ * flapping daemon is retried gently while a hard-down host backs off hard.
103
+ */
104
+ markUnreachable(baseCooldownMs: number): void;
58
105
  health(options?: RequestOptions): Promise<Record<string, unknown>>;
59
- recall(query: string, sessionKey: string, cwd: string): Promise<RecallResponse>;
60
- recallExplain(sessionKey: string): Promise<Record<string, unknown>>;
61
- observe(sessionKey: string, cwd: string, messages: ObserveMessage[]): Promise<Record<string, unknown>>;
62
- storeMemory(content: string, sessionKey: string): Promise<Record<string, unknown>>;
106
+ recall(query: string, sessionKey: string, cwd: string, options?: RequestOptions): Promise<RecallResponse>;
107
+ recallExplain(sessionKey: string, options?: RequestOptions): Promise<Record<string, unknown>>;
108
+ observe(sessionKey: string, cwd: string, messages: ObserveMessage[], options?: ObserveOptions): Promise<Record<string, unknown>>;
109
+ storeMemory(content: string, sessionKey: string, options?: RequestOptions): Promise<Record<string, unknown>>;
63
110
  lcmSearch(query: string, sessionKey: string, limit?: number): Promise<Record<string, unknown>>;
64
111
  lcmCompactionFlush(sessionKey: string): Promise<Record<string, unknown>>;
65
112
  lcmCompactionRecord(sessionKey: string, tokensBefore: number, tokensAfter: number): Promise<Record<string, unknown>>;
66
113
  contextCheckpoint(sessionKey: string, context: string): Promise<Record<string, unknown>>;
67
114
  mcpListTools(options?: RequestOptions): Promise<McpTool[]>;
68
115
  mcpTool(name: string, args: Record<string, unknown>): Promise<Record<string, unknown>>;
116
+ /**
117
+ * Single HTTP attempt with the configured timeout. No retry — retry of
118
+ * transient connection failures lives in {@link requestWithRetry}.
119
+ */
69
120
  private request;
121
+ /**
122
+ * Wrap {@link request} with a small bounded retry loop for transient
123
+ * connection-level failures (socket close mid-request, ECONNRESET, EPIPE).
124
+ * Timeouts (our own AbortController) and HTTP responses (4xx/5xx) are NOT
125
+ * retried here — timeouts already burned the full budget, and HTTP errors
126
+ * carry semantic meaning the caller must handle. Observe/recall are
127
+ * dedupe-safe so retrying a transiently-failed POST is harmless (#1602).
128
+ */
129
+ private requestWithRetry;
70
130
  private mcpRequest;
71
131
  }
72
- interface RequestOptions {
73
- timeoutMs?: number;
74
- }
75
132
 
76
133
  declare function textFromMessage(message: unknown): string;
77
134
 
@@ -94,5 +151,6 @@ declare function stripSessionOwnedSchemaFields(inputSchema: unknown): Record<str
94
151
  declare function stripSessionOwnedRuntimeFields(value: unknown): unknown;
95
152
  declare function observeMessages(ctx: any, client: RemnicClient, rawMessages: unknown[], observedHashes: Set<string>, liveObservedReplayKeys?: Map<string, number>): Promise<void>;
96
153
  declare function buildCompactionSummary(preparation: any): string;
154
+ declare function isDaemonUnreachableError(err: unknown): boolean;
97
155
 
98
- export { type RemnicPiExtensionOptions, buildCompactionSummary, createRemnicPiExtension, remnicPiExtension as default, observeMessages, stripSessionOwnedRuntimeFields, stripSessionOwnedSchemaFields, textFromMessage, toPiToolParametersSchema };
156
+ export { type RemnicPiExtensionOptions, buildCompactionSummary, createRemnicPiExtension, remnicPiExtension as default, isDaemonUnreachableError, observeMessages, stripSessionOwnedRuntimeFields, stripSessionOwnedSchemaFields, textFromMessage, toPiToolParametersSchema };
package/dist/index.js CHANGED
@@ -22,7 +22,14 @@ var DEFAULT_CONFIG = {
22
22
  mcpToolsEnabled: true,
23
23
  statusEnabled: true,
24
24
  requestTimeoutMs: 6e4,
25
- startupRequestTimeoutMs: 1e3
25
+ startupRequestTimeoutMs: 1e3,
26
+ // Default 20 s is comfortably under the Pi/omp 30 s handler budget (#1626).
27
+ turnRequestTimeoutMs: 2e4,
28
+ // Default 100 KiB leaves headroom under the daemon's 128 KiB default (#1600).
29
+ observeMaxBytes: 102400,
30
+ observeMaxRetries: 2,
31
+ // Base cooldown for the circuit breaker; doubles on consecutive failures (#1626).
32
+ daemonCooldownMs: 5e3
26
33
  };
27
34
  function defaultConfigPath(env) {
28
35
  return path.join(resolvePiAgentHome(env), "extensions", REMNIC_PI_EXTENSION_DIR_NAME, "remnic.config.json");
@@ -57,6 +64,26 @@ function coercePositiveInt(value, fallback, max, fieldName) {
57
64
  }
58
65
  return parsed;
59
66
  }
67
+ function coerceNonNegativeInt(value, fallback, max, fieldName) {
68
+ if (value === void 0 || value === null || value === "") return fallback;
69
+ let parsed;
70
+ if (typeof value === "number") {
71
+ parsed = value;
72
+ } else if (typeof value === "string") {
73
+ const trimmed = value.trim();
74
+ if (trimmed.length === 0) return fallback;
75
+ if (!/^[+-]?\d+$/.test(trimmed)) {
76
+ throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 0 to ${max}`);
77
+ }
78
+ parsed = Number(trimmed);
79
+ } else {
80
+ throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 0 to ${max}`);
81
+ }
82
+ if (!Number.isInteger(parsed) || parsed < 0 || parsed > max) {
83
+ throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 0 to ${max}`);
84
+ }
85
+ return parsed;
86
+ }
60
87
  function coerceOptionalNonEmptyString(value, fieldName) {
61
88
  if (value === void 0 || value === null) return void 0;
62
89
  if (typeof value === "string" && value.trim().length > 0) return value.trim();
@@ -122,6 +149,19 @@ function loadConfig(options = {}) {
122
149
  const daemonUrl = coerceOptionalHttpUrl(fileConfig.remnicDaemonUrl, "remnicDaemonUrl") ?? coerceOptionalHttpUrl(env.REMNIC_DAEMON_URL, "REMNIC_DAEMON_URL") ?? DEFAULT_CONFIG.remnicDaemonUrl;
123
150
  const authToken = coerceOptionalString(fileConfig.authToken, "authToken") ?? coerceOptionalString(env.REMNIC_PI_AUTH_TOKEN, "REMNIC_PI_AUTH_TOKEN");
124
151
  const namespace = coerceOptionalNonEmptyString(fileConfig.namespace, "namespace");
152
+ const requestTimeoutMs = coercePositiveInt(
153
+ fileConfig.requestTimeoutMs,
154
+ DEFAULT_CONFIG.requestTimeoutMs,
155
+ 6e4,
156
+ "requestTimeoutMs"
157
+ );
158
+ const turnFallback = Math.min(requestTimeoutMs, DEFAULT_CONFIG.turnRequestTimeoutMs);
159
+ const turnRequestTimeoutMs = coercePositiveInt(
160
+ fileConfig.turnRequestTimeoutMs,
161
+ turnFallback,
162
+ 25e3,
163
+ "turnRequestTimeoutMs"
164
+ );
125
165
  return {
126
166
  remnicDaemonUrl: daemonUrl,
127
167
  authToken,
@@ -135,13 +175,22 @@ function loadConfig(options = {}) {
135
175
  compactionEnabled: coerceBoolean(fileConfig.compactionEnabled, DEFAULT_CONFIG.compactionEnabled, "compactionEnabled"),
136
176
  mcpToolsEnabled: coerceBoolean(fileConfig.mcpToolsEnabled, DEFAULT_CONFIG.mcpToolsEnabled, "mcpToolsEnabled"),
137
177
  statusEnabled: coerceBoolean(fileConfig.statusEnabled, DEFAULT_CONFIG.statusEnabled, "statusEnabled"),
138
- requestTimeoutMs: coercePositiveInt(fileConfig.requestTimeoutMs, DEFAULT_CONFIG.requestTimeoutMs, 6e4, "requestTimeoutMs"),
178
+ requestTimeoutMs,
139
179
  startupRequestTimeoutMs: coercePositiveInt(
140
180
  fileConfig.startupRequestTimeoutMs,
141
181
  DEFAULT_CONFIG.startupRequestTimeoutMs,
142
182
  6e4,
143
183
  "startupRequestTimeoutMs"
144
- )
184
+ ),
185
+ turnRequestTimeoutMs,
186
+ observeMaxBytes: coercePositiveInt(
187
+ fileConfig.observeMaxBytes,
188
+ DEFAULT_CONFIG.observeMaxBytes,
189
+ 8388608,
190
+ "observeMaxBytes"
191
+ ),
192
+ observeMaxRetries: coerceNonNegativeInt(fileConfig.observeMaxRetries, DEFAULT_CONFIG.observeMaxRetries, 5, "observeMaxRetries"),
193
+ daemonCooldownMs: coercePositiveInt(fileConfig.daemonCooldownMs, DEFAULT_CONFIG.daemonCooldownMs, 6e4, "daemonCooldownMs")
145
194
  };
146
195
  }
147
196
 
@@ -153,51 +202,118 @@ var RemnicHttpError = class extends Error {
153
202
  }
154
203
  status;
155
204
  };
205
+ var encoder = new TextEncoder();
206
+ var RETRY_BASE_DELAY_MS = 200;
207
+ var MAX_COOLDOWN_MS = 6e4;
208
+ var TRUNCATION_MARKER = "\n\n[Remnic observe truncated: payload exceeded client size cap]";
156
209
  var RemnicClient = class {
157
210
  constructor(config) {
158
211
  this.config = config;
159
212
  }
160
213
  config;
161
214
  requestId = 0;
215
+ // Circuit-breaker state: when the daemon is known-unreachable, observe/recall
216
+ // callers skip fast instead of blocking every turn on a doomed request (#1626).
217
+ unreachableUntil = 0;
218
+ consecutiveFailures = 0;
219
+ /** True when the daemon is not in a known-unreachable cooldown. */
220
+ isReachable() {
221
+ return Date.now() >= this.unreachableUntil;
222
+ }
223
+ /** Clear the circuit breaker — call after any successful daemon interaction. */
224
+ markReachable() {
225
+ this.consecutiveFailures = 0;
226
+ this.unreachableUntil = 0;
227
+ }
228
+ /**
229
+ * Enter (or extend) an unreachable cooldown. The cooldown grows exponentially
230
+ * with consecutive failures (base, 2×base, 4×base, …) capped at 60 s, so a
231
+ * flapping daemon is retried gently while a hard-down host backs off hard.
232
+ */
233
+ markUnreachable(baseCooldownMs) {
234
+ this.consecutiveFailures += 1;
235
+ const factor = 2 ** Math.min(this.consecutiveFailures - 1, 4);
236
+ const cooldown = Math.min(baseCooldownMs * factor, MAX_COOLDOWN_MS);
237
+ this.unreachableUntil = Date.now() + cooldown;
238
+ }
162
239
  async health(options = {}) {
163
240
  return this.request("GET", "/engram/v1/health", void 0, options);
164
241
  }
165
- async recall(query, sessionKey, cwd) {
166
- return this.request("POST", "/engram/v1/recall", {
167
- query,
168
- sessionKey,
169
- cwd,
170
- namespace: this.config.namespace,
171
- topK: this.config.recallTopK,
172
- mode: this.config.recallMode
173
- });
242
+ async recall(query, sessionKey, cwd, options = {}) {
243
+ const merged = {
244
+ ...options,
245
+ timeoutMs: options.timeoutMs ?? this.config.turnRequestTimeoutMs,
246
+ maxRetries: options.maxRetries ?? this.config.observeMaxRetries
247
+ };
248
+ return this.requestWithRetry(
249
+ "POST",
250
+ "/engram/v1/recall",
251
+ {
252
+ query,
253
+ sessionKey,
254
+ cwd,
255
+ namespace: this.config.namespace,
256
+ topK: this.config.recallTopK,
257
+ mode: this.config.recallMode
258
+ },
259
+ merged
260
+ );
174
261
  }
175
- async recallExplain(sessionKey) {
176
- return this.request("POST", "/engram/v1/recall/explain", {
177
- sessionKey,
178
- namespace: this.config.namespace
179
- });
262
+ async recallExplain(sessionKey, options = {}) {
263
+ return this.requestWithRetry(
264
+ "POST",
265
+ "/engram/v1/recall/explain",
266
+ {
267
+ sessionKey,
268
+ namespace: this.config.namespace
269
+ },
270
+ options
271
+ );
180
272
  }
181
- async observe(sessionKey, cwd, messages) {
182
- return this.request("POST", "/engram/v1/observe", {
183
- sessionKey,
184
- cwd,
185
- namespace: this.config.namespace,
186
- skipExtraction: this.config.observeSkipExtraction,
187
- messages
188
- });
273
+ async observe(sessionKey, cwd, messages, options = {}) {
274
+ const maxBytes = options.maxBytes ?? this.config.observeMaxBytes;
275
+ const turnBudgetMs = options.timeoutMs ?? this.config.turnRequestTimeoutMs;
276
+ const retryOptions = {
277
+ timeoutMs: turnBudgetMs,
278
+ maxRetries: options.maxRetries ?? this.config.observeMaxRetries
279
+ };
280
+ const chunks = chunkObservePayload(this.config, sessionKey, cwd, messages, maxBytes);
281
+ if (chunks.length === 1) {
282
+ return this.requestWithRetry("POST", "/engram/v1/observe", chunks[0], retryOptions);
283
+ }
284
+ const deadline = Date.now() + turnBudgetMs;
285
+ const results = [];
286
+ for (let i = 0; i < chunks.length; i++) {
287
+ const remaining = deadline - Date.now();
288
+ if (remaining <= 0) {
289
+ throw new Error(
290
+ `Remnic observe exceeded the per-turn budget of ${turnBudgetMs}ms across ${chunks.length} chunks (completed ${i})`
291
+ );
292
+ }
293
+ const chunkOptions = { ...retryOptions, timeoutMs: remaining };
294
+ const result = await this.requestWithRetry(
295
+ "POST",
296
+ "/engram/v1/observe",
297
+ chunks[i],
298
+ chunkOptions
299
+ );
300
+ if (result && typeof result === "object") {
301
+ results.push(result);
302
+ }
303
+ }
304
+ return mergeObserveResults(results);
189
305
  }
190
- async storeMemory(content, sessionKey) {
191
- return this.request("POST", "/engram/v1/memories", {
306
+ async storeMemory(content, sessionKey, options = {}) {
307
+ return this.requestWithRetry("POST", "/engram/v1/memories", {
192
308
  content,
193
309
  category: "fact",
194
310
  sourceReason: "Captured from Pi via Remnic extension",
195
311
  sessionKey,
196
312
  namespace: this.config.namespace
197
- });
313
+ }, options);
198
314
  }
199
315
  async lcmSearch(query, sessionKey, limit = 10) {
200
- return this.request("POST", "/engram/v1/lcm/search", {
316
+ return this.requestWithRetry("POST", "/engram/v1/lcm/search", {
201
317
  query,
202
318
  sessionKey,
203
319
  namespace: this.config.namespace,
@@ -205,13 +321,13 @@ var RemnicClient = class {
205
321
  });
206
322
  }
207
323
  async lcmCompactionFlush(sessionKey) {
208
- return this.request("POST", "/engram/v1/lcm/compaction/flush", {
324
+ return this.requestWithRetry("POST", "/engram/v1/lcm/compaction/flush", {
209
325
  sessionKey,
210
326
  namespace: this.config.namespace
211
327
  });
212
328
  }
213
329
  async lcmCompactionRecord(sessionKey, tokensBefore, tokensAfter) {
214
- return this.request("POST", "/engram/v1/lcm/compaction/record", {
330
+ return this.requestWithRetry("POST", "/engram/v1/lcm/compaction/record", {
215
331
  sessionKey,
216
332
  namespace: this.config.namespace,
217
333
  tokensBefore,
@@ -236,6 +352,10 @@ var RemnicClient = class {
236
352
  arguments: args
237
353
  });
238
354
  }
355
+ /**
356
+ * Single HTTP attempt with the configured timeout. No retry — retry of
357
+ * transient connection failures lives in {@link requestWithRetry}.
358
+ */
239
359
  async request(method, pathname, body, options = {}) {
240
360
  const controller = new AbortController();
241
361
  const override = options.timeoutMs;
@@ -263,7 +383,12 @@ var RemnicClient = class {
263
383
  }
264
384
  }
265
385
  if (!response.ok) {
266
- throw new RemnicHttpError(response.status, responseErrorMessage(response, text, payload, parseError));
386
+ const message = responseErrorMessage(response, text, payload, parseError);
387
+ if (response.status === 413) {
388
+ const bodyBytes = body === void 0 ? 0 : jsonBytes(body);
389
+ throw new RemnicHttpError(response.status, `${message} (observed body ${bodyBytes} bytes; cap via observeMaxBytes)`);
390
+ }
391
+ throw new RemnicHttpError(response.status, message);
267
392
  }
268
393
  if (parseError) {
269
394
  const reason = parseError instanceof Error ? parseError.message : String(parseError);
@@ -279,6 +404,49 @@ var RemnicClient = class {
279
404
  clearTimeout(timeout);
280
405
  }
281
406
  }
407
+ /**
408
+ * Wrap {@link request} with a small bounded retry loop for transient
409
+ * connection-level failures (socket close mid-request, ECONNRESET, EPIPE).
410
+ * Timeouts (our own AbortController) and HTTP responses (4xx/5xx) are NOT
411
+ * retried here — timeouts already burned the full budget, and HTTP errors
412
+ * carry semantic meaning the caller must handle. Observe/recall are
413
+ * dedupe-safe so retrying a transiently-failed POST is harmless (#1602).
414
+ */
415
+ async requestWithRetry(method, pathname, body, options = {}) {
416
+ const maxRetries = options.maxRetries ?? 0;
417
+ const budgetMs = options.timeoutMs;
418
+ const hasDeadline = typeof budgetMs === "number" && Number.isFinite(budgetMs) && budgetMs > 0;
419
+ const deadline = hasDeadline ? Date.now() + budgetMs : Number.POSITIVE_INFINITY;
420
+ let attempt = 0;
421
+ let attemptOptions = options;
422
+ while (true) {
423
+ try {
424
+ return await this.request(method, pathname, body, attemptOptions);
425
+ } catch (err) {
426
+ if (attempt >= maxRetries || !isTransientNetworkError(err)) throw err;
427
+ const delayMs = RETRY_BASE_DELAY_MS * 2 ** attempt;
428
+ if (hasDeadline) {
429
+ const remainingBeforeSleep = deadline - Date.now();
430
+ if (remainingBeforeSleep <= delayMs) {
431
+ throw new Error(
432
+ `Remnic request exceeded the ${budgetMs}ms budget before retry ${attempt + 1} (${method} ${pathname})`
433
+ );
434
+ }
435
+ }
436
+ await sleep(delayMs);
437
+ attempt += 1;
438
+ if (hasDeadline) {
439
+ const remaining = deadline - Date.now();
440
+ if (remaining <= 0) {
441
+ throw new Error(
442
+ `Remnic request exceeded the ${budgetMs}ms budget before retry ${attempt} (${method} ${pathname})`
443
+ );
444
+ }
445
+ attemptOptions = { ...options, timeoutMs: remaining };
446
+ }
447
+ }
448
+ }
449
+ }
282
450
  async mcpRequest(method, params, options = {}) {
283
451
  this.requestId += 1;
284
452
  const payload = await this.request("POST", "/mcp", {
@@ -294,20 +462,135 @@ var RemnicClient = class {
294
462
  }
295
463
  };
296
464
  function isMcpTool(value) {
297
- return !!value && typeof value === "object" && typeof value.name === "string";
465
+ return !!value && typeof value === "object" && "name" in value && typeof value.name === "string";
298
466
  }
299
467
  function isAbortError(err) {
300
468
  return err instanceof Error && (err.name === "AbortError" || err.message === "This operation was aborted");
301
469
  }
470
+ function isTransientNetworkError(err) {
471
+ if (!(err instanceof Error)) return false;
472
+ if (isAbortError(err)) return false;
473
+ if (err instanceof RemnicHttpError) return false;
474
+ const lower = (err.message ?? "").toLowerCase();
475
+ if (lower.includes("socket connection was closed")) return true;
476
+ if (lower.includes("socket closed")) return true;
477
+ if (lower.includes("econnreset")) return true;
478
+ if (lower.includes("epipe")) return true;
479
+ if (lower.includes("und_err_socket")) return true;
480
+ if (lower.includes("fetch failed")) return true;
481
+ const cause = err.cause;
482
+ if (cause && typeof cause === "object" && "code" in cause) {
483
+ const code = cause.code;
484
+ if (typeof code === "string" && (code === "ECONNRESET" || code === "EPIPE" || code === "UND_ERR_SOCKET")) {
485
+ return true;
486
+ }
487
+ }
488
+ return false;
489
+ }
490
+ function sleep(ms) {
491
+ return new Promise((resolve) => setTimeout(resolve, ms));
492
+ }
493
+ function jsonBytes(value) {
494
+ return encoder.encode(JSON.stringify(value)).length;
495
+ }
496
+ function buildObserveEnvelope(config, sessionKey, cwd, messages) {
497
+ return {
498
+ sessionKey,
499
+ cwd,
500
+ namespace: config.namespace,
501
+ skipExtraction: config.observeSkipExtraction,
502
+ messages
503
+ };
504
+ }
505
+ function chunkObservePayload(config, sessionKey, cwd, messages, maxBytes) {
506
+ const envelopeOverhead = jsonBytes(buildObserveEnvelope(config, sessionKey, cwd, []));
507
+ const messageBudget = maxBytes - envelopeOverhead;
508
+ if (messageBudget <= 0) {
509
+ return [buildObserveEnvelope(config, sessionKey, cwd, messages)];
510
+ }
511
+ const chunks = [];
512
+ let current = [];
513
+ let currentSize = 0;
514
+ const flush = () => {
515
+ if (current.length > 0) {
516
+ chunks.push(current);
517
+ current = [];
518
+ currentSize = 0;
519
+ }
520
+ };
521
+ for (const message of messages) {
522
+ const size = jsonBytes(message);
523
+ if (size > messageBudget) {
524
+ flush();
525
+ chunks.push([truncateObserveMessage(message, messageBudget)]);
526
+ continue;
527
+ }
528
+ if (current.length > 0 && currentSize + 1 + size > messageBudget) {
529
+ flush();
530
+ }
531
+ if (current.length > 0) currentSize += 1;
532
+ current.push(message);
533
+ currentSize += size;
534
+ }
535
+ flush();
536
+ if (chunks.length === 0) {
537
+ return [buildObserveEnvelope(config, sessionKey, cwd, [])];
538
+ }
539
+ return chunks.map((msgs) => buildObserveEnvelope(config, sessionKey, cwd, msgs));
540
+ }
541
+ var decoder = new TextDecoder();
542
+ function truncateObserveMessage(message, budgetBytes) {
543
+ const slim = { role: message.role, content: "" };
544
+ const markerOnly = jsonBytes({ ...slim, content: TRUNCATION_MARKER });
545
+ if (markerOnly > budgetBytes) {
546
+ return { role: message.role, content: TRUNCATION_MARKER };
547
+ }
548
+ const fullContent = message.content + TRUNCATION_MARKER;
549
+ if (jsonBytes({ ...slim, content: fullContent }) <= budgetBytes) {
550
+ return { role: message.role, content: fullContent };
551
+ }
552
+ const encoded = encoder.encode(message.content);
553
+ let lo = 0;
554
+ let hi = encoded.length;
555
+ while (lo < hi) {
556
+ const mid = hi - Math.floor((hi - lo) / 2);
557
+ const candidate = decoder.decode(encoded.subarray(0, mid)) + TRUNCATION_MARKER;
558
+ if (jsonBytes({ ...slim, content: candidate }) <= budgetBytes) {
559
+ lo = mid;
560
+ } else {
561
+ hi = mid - 1;
562
+ }
563
+ }
564
+ const truncated = lo > 0 ? decoder.decode(encoded.subarray(0, lo)) : "";
565
+ return { role: message.role, content: truncated + TRUNCATION_MARKER };
566
+ }
567
+ function mergeObserveResults(results) {
568
+ if (results.length === 0) return {};
569
+ if (results.length === 1) return results[0];
570
+ const merged = {};
571
+ let countSum = 0;
572
+ let hasCount = false;
573
+ for (const result of results) {
574
+ for (const key of Object.keys(result)) {
575
+ const value = result[key];
576
+ if (key === "count" && typeof value === "number" && Number.isFinite(value)) {
577
+ countSum += value;
578
+ hasCount = true;
579
+ } else {
580
+ merged[key] = value;
581
+ }
582
+ }
583
+ }
584
+ if (hasCount) merged.count = countSum;
585
+ return merged;
586
+ }
302
587
  function responseErrorMessage(response, text, payload, parseError) {
303
588
  if (!parseError && payload && typeof payload === "object") {
304
- const error = payload.error;
305
- if (typeof error === "string" && error.trim().length > 0) {
306
- return error;
589
+ if ("error" in payload && typeof payload.error === "string" && payload.error.trim().length > 0) {
590
+ return payload.error;
307
591
  }
308
- const message = payload.message;
309
- if (typeof message === "string" && message.trim().length > 0) {
310
- return message;
592
+ if ("message" in payload && typeof payload.message === "string" && payload.message.trim().length > 0) {
593
+ return payload.message;
311
594
  }
312
595
  }
313
596
  const snippet = text.trim().replace(/\s+/g, " ").slice(0, 200);
@@ -475,13 +758,17 @@ function createRemnicPiExtension(options = {}) {
475
758
  const session = snapshotPiContext(ctx);
476
759
  if (!session) return;
477
760
  if (!config.recallEnabled || !config.authToken) return;
761
+ if (!client.isReachable()) return;
478
762
  const recallTarget = latestUserRecallTarget(Array.isArray(event.messages) ? event.messages : []);
479
763
  if (!recallTarget) return;
480
764
  const { query } = recallTarget;
481
765
  const { state } = getSessionState(session.sessionKey, sessionStates);
482
766
  if (recallTarget.dedupeKey === state.lastInjectedRecallKey) return;
483
767
  try {
484
- const recalled = await client.recall(query, session.sessionKey, session.cwd);
768
+ const recalled = await client.recall(query, session.sessionKey, session.cwd, {
769
+ timeoutMs: config.turnRequestTimeoutMs
770
+ });
771
+ client.markReachable();
485
772
  const context = trimContext(recalled.context ?? "", config.recallBudgetChars);
486
773
  if (!context) return;
487
774
  state.lastInjectedRecallKey = recallTarget.dedupeKey;
@@ -499,6 +786,7 @@ ${context}` }],
499
786
  ]
500
787
  };
501
788
  } catch (err) {
789
+ if (isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);
502
790
  session.notify(`Remnic recall unavailable: ${errorMessage(err)}`, "warning");
503
791
  }
504
792
  });
@@ -507,7 +795,7 @@ ${context}` }],
507
795
  if (!session) return;
508
796
  if (!config.observeEnabled || !isUserMessage(event.message)) return;
509
797
  const { state } = getSessionState(session.sessionKey, sessionStates);
510
- await observeMessagesForSession(session, client, [event.message], state.observedHashes, state.liveObservedReplayKeys);
798
+ await observeMessagesForSession(session, client, [event.message], state.observedHashes, state.liveObservedReplayKeys, config);
511
799
  });
512
800
  pi.on("turn_end", async (event, ctx) => {
513
801
  const session = snapshotPiContext(ctx);
@@ -515,7 +803,7 @@ ${context}` }],
515
803
  if (!config.observeEnabled) return;
516
804
  const messages = [event.message, ...Array.isArray(event.toolResults) ? event.toolResults : []];
517
805
  const { state } = getSessionState(session.sessionKey, sessionStates);
518
- await observeMessagesForSession(session, client, messages, state.observedHashes, state.liveObservedReplayKeys);
806
+ await observeMessagesForSession(session, client, messages, state.observedHashes, state.liveObservedReplayKeys, config);
519
807
  });
520
808
  pi.on("session_shutdown", async (_event, ctx) => {
521
809
  const session = snapshotPiContext(ctx, { includeSessionHistory: true });
@@ -525,7 +813,7 @@ ${context}` }],
525
813
  const branchMessages = branchMessagesWithEntryIdentity(session.branch);
526
814
  const unobservedBranchMessages = skipLiveObservedReplayMessages(session.sessionKey, branchMessages, state.liveObservedReplayKeys);
527
815
  if (unobservedBranchMessages.length > 0) {
528
- await observeMessagesForSession(session, client, unobservedBranchMessages, state.observedHashes);
816
+ await observeMessagesForSession(session, client, unobservedBranchMessages, state.observedHashes, void 0, config, true);
529
817
  }
530
818
  }
531
819
  persistObservedState(pi, state.observedHashes);
@@ -585,6 +873,7 @@ function registerCommands(pi, client, config) {
585
873
  description: "Check Remnic daemon status",
586
874
  handler: commandHandler(async (_args, _ctx, session) => {
587
875
  const health = await client.health();
876
+ client.markReachable();
588
877
  session.notify(`Remnic ${health.ok ? "healthy" : "unhealthy"} at ${config.remnicDaemonUrl}`, health.ok ? "success" : "warning");
589
878
  })
590
879
  });
@@ -596,7 +885,10 @@ function registerCommands(pi, client, config) {
596
885
  session.notify("Usage: /remnic-recall <query>", "warning");
597
886
  return;
598
887
  }
599
- const result = await client.recall(query, session.sessionKey, session.cwd);
888
+ const result = await client.recall(query, session.sessionKey, session.cwd, {
889
+ timeoutMs: config.requestTimeoutMs
890
+ });
891
+ client.markReachable();
600
892
  session.notify(trimContext(result.context ?? "(no Remnic context)", MAX_CONTEXT_CHARS), "info");
601
893
  })
602
894
  });
@@ -775,7 +1067,7 @@ async function observeMessages(ctx, client, rawMessages, observedHashes, liveObs
775
1067
  if (!session) return;
776
1068
  await observeMessagesForSession(session, client, rawMessages, observedHashes, liveObservedReplayKeys);
777
1069
  }
778
- async function observeMessagesForSession(session, client, rawMessages, observedHashes, liveObservedReplayKeys) {
1070
+ async function observeMessagesForSession(session, client, rawMessages, observedHashes, liveObservedReplayKeys, config, forceAttempt = false) {
779
1071
  const messages = [];
780
1072
  const pendingHashes = /* @__PURE__ */ new Set();
781
1073
  for (const raw of rawMessages) {
@@ -787,8 +1079,11 @@ async function observeMessagesForSession(session, client, rawMessages, observedH
787
1079
  messages.push(message);
788
1080
  }
789
1081
  if (messages.length === 0) return;
1082
+ if (config && !forceAttempt && !client.isReachable()) return;
1083
+ const observeOptions = config ? { timeoutMs: forceAttempt ? config.requestTimeoutMs : config.turnRequestTimeoutMs } : void 0;
790
1084
  try {
791
- await client.observe(session.sessionKey, session.cwd, messages);
1085
+ await client.observe(session.sessionKey, session.cwd, messages, observeOptions);
1086
+ if (config) client.markReachable();
792
1087
  for (const hash of pendingHashes) rememberObservedHash(observedHashes, hash);
793
1088
  if (liveObservedReplayKeys) {
794
1089
  for (const message of messages) {
@@ -796,6 +1091,7 @@ async function observeMessagesForSession(session, client, rawMessages, observedH
796
1091
  }
797
1092
  }
798
1093
  } catch (err) {
1094
+ if (config && isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);
799
1095
  session.notify(`Remnic observe failed: ${errorMessage(err)}`, "warning");
800
1096
  }
801
1097
  }
@@ -887,8 +1183,10 @@ function persistObservedState(pi, observedHashes) {
887
1183
  async function setStatus(session, client, config) {
888
1184
  try {
889
1185
  await client.health({ timeoutMs: config.startupRequestTimeoutMs });
1186
+ client.markReachable();
890
1187
  session.setStatus("remnic", `Remnic ${config.namespace ? `(${config.namespace})` : "ready"}`);
891
- } catch {
1188
+ } catch (err) {
1189
+ if (isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);
892
1190
  session.setStatus("remnic", "Remnic offline");
893
1191
  }
894
1192
  }
@@ -1001,6 +1299,13 @@ function trimContext(value, budget) {
1001
1299
  if (budget <= TRUNCATION_NOTICE.length) return TRUNCATION_NOTICE.slice(0, budget);
1002
1300
  return `${value.slice(0, budget - TRUNCATION_NOTICE.length)}${TRUNCATION_NOTICE}`;
1003
1301
  }
1302
+ function isDaemonUnreachableError(err) {
1303
+ if (!(err instanceof Error)) return false;
1304
+ if (/Remnic request timed out/.test(err.message)) return true;
1305
+ if (/Remnic request exceeded the \d+ms budget before retry/.test(err.message)) return true;
1306
+ if (/Remnic observe exceeded the per-turn budget of \d+ms/.test(err.message)) return true;
1307
+ return isTransientNetworkError(err);
1308
+ }
1004
1309
  function errorMessage(err) {
1005
1310
  return err instanceof Error ? err.message : String(err);
1006
1311
  }
@@ -1014,6 +1319,7 @@ export {
1014
1319
  buildCompactionSummary,
1015
1320
  createRemnicPiExtension,
1016
1321
  remnicPiExtension as default,
1322
+ isDaemonUnreachableError,
1017
1323
  observeMessages,
1018
1324
  stripSessionOwnedRuntimeFields,
1019
1325
  stripSessionOwnedSchemaFields,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/client.ts","../src/messages.ts"],"sourcesContent":["import { Type, type TSchema } from \"@sinclair/typebox\";\n\nimport { loadConfig, type LoadConfigOptions, type RemnicPiConfig } from \"./config.js\";\nimport { RemnicClient, type McpTool, type ObserveMessage } from \"./client.js\";\nimport {\n hashObservedMessage,\n latestUserRecallTarget,\n observedMessageDedupeKey,\n sessionKeyFromContext,\n summarizeMessages,\n textFromMessage,\n toObserveMessage,\n} from \"./messages.js\";\n\ntype PiApi = {\n on(event: string, handler: (event: any, ctx: any) => unknown | Promise<unknown>): void;\n registerCommand(name: string, options: { description?: string; handler: (args: string, ctx: any) => Promise<void> }): void;\n registerTool(tool: Record<string, unknown>): void;\n appendEntry<T = unknown>(customType: string, data?: T): void;\n};\n\nexport interface RemnicPiExtensionOptions extends LoadConfigOptions {\n config?: RemnicPiConfig;\n}\n\nconst STATE_CUSTOM_TYPE = \"remnic_state\";\nconst MAX_OBSERVED_HASHES = 2000;\nconst MAX_SESSION_STATES = 50;\nconst MAX_CONTEXT_CHARS = 12000;\nconst TRUNCATION_NOTICE = \"\\n\\n[Remnic context truncated]\";\nconst SESSION_OWNED_FIELDS = new Set([\"sessionKey\", \"namespace\", \"cwd\"]);\n\ntype PiSessionState = {\n observedHashes: Set<string>;\n liveObservedReplayKeys: Map<string, number>;\n lastInjectedRecallKey: string;\n};\n\ntype NotifyLevel = \"info\" | \"success\" | \"warning\" | \"error\";\ntype NotifyFn = (message: string, level: NotifyLevel) => void;\n\ntype PiContextSnapshot = {\n sessionKey: string;\n cwd: string;\n entries: any[];\n branch: any[];\n notify: NotifyFn;\n setStatus: (key: string, value: string) => void;\n compact?: () => unknown;\n};\ntype PiContextSnapshotOptions = {\n includeSessionHistory?: boolean;\n};\n\nexport function createRemnicPiExtension(options: RemnicPiExtensionOptions = {}) {\n const config = options.config ?? loadConfig(options);\n const client = new RemnicClient(config);\n const sessionStates = new Map<string, PiSessionState>();\n\n return async function remnicPiExtension(pi: PiApi): Promise<void> {\n pi.on(\"session_start\", async (_event, ctx) => {\n const session = snapshotPiContext(ctx, { includeSessionHistory: true });\n if (!session) return;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n restoreObservedState(session, state.observedHashes);\n if (!config.statusEnabled) return;\n await setStatus(session, client, config);\n });\n\n pi.on(\"context\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.recallEnabled || !config.authToken) return;\n const recallTarget = latestUserRecallTarget(Array.isArray(event.messages) ? event.messages : []);\n if (!recallTarget) return;\n const { query } = recallTarget;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n if (recallTarget.dedupeKey === state.lastInjectedRecallKey) return;\n\n try {\n const recalled = await client.recall(query, session.sessionKey, session.cwd);\n const context = trimContext(recalled.context ?? \"\", config.recallBudgetChars);\n if (!context) return;\n state.lastInjectedRecallKey = recallTarget.dedupeKey;\n return {\n messages: [\n ...event.messages,\n {\n role: \"user\",\n content: [{ type: \"text\", text: `Remnic recalled context for this turn:\\n\\n${context}` }],\n remnicInjected: true,\n timestamp: Date.now(),\n },\n ],\n };\n } catch (err) {\n session.notify(`Remnic recall unavailable: ${errorMessage(err)}`, \"warning\");\n }\n });\n\n pi.on(\"message_end\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.observeEnabled || !isUserMessage(event.message)) return;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n await observeMessagesForSession(session, client, [event.message], state.observedHashes, state.liveObservedReplayKeys);\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);\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);\n }\n }\n persistObservedState(pi, state.observedHashes);\n sessionStates.delete(sessionKey);\n });\n\n pi.on(\"session_before_compact\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.compactionEnabled || !config.authToken) return;\n const preparation = event.preparation ?? {};\n try {\n await client.lcmCompactionFlush(session.sessionKey);\n } catch (err) {\n session.notify(`Remnic LCM flush failed: ${errorMessage(err)}`, \"warning\");\n return;\n }\n\n const tokensBefore = finiteTokenCount(preparation.tokensBefore);\n const tokensAfter = finiteTokenCount(preparation.tokensAfter);\n if (tokensBefore !== null && tokensAfter !== null) {\n try {\n await client.lcmCompactionRecord(session.sessionKey, tokensBefore, tokensAfter);\n } catch (err) {\n session.notify(`Remnic LCM compaction token record failed: ${errorMessage(err)}`, \"warning\");\n }\n }\n\n const summary = buildCompactionSummary(preparation);\n if (!summary.trim()) return;\n try {\n await client.contextCheckpoint(session.sessionKey, summary);\n } catch (err) {\n session.notify(`Remnic context checkpoint failed: ${errorMessage(err)}`, \"warning\");\n }\n const details = fileDetailsFromPreparation(preparation);\n return {\n compaction: {\n summary,\n firstKeptEntryId: preparation.firstKeptEntryId,\n tokensBefore: preparation.tokensBefore,\n details: {\n ...details,\n remnic: { version: 1, source: \"pi\" },\n },\n },\n };\n });\n\n registerCommands(pi, client, config);\n if (config.mcpToolsEnabled && config.authToken) {\n await registerMcpTools(pi, client, config);\n }\n };\n}\n\nexport default async function remnicPiExtension(pi: PiApi): Promise<void> {\n await createRemnicPiExtension()(pi);\n}\n\nfunction registerCommands(pi: PiApi, client: RemnicClient, config: RemnicPiConfig): void {\n pi.registerCommand(\"remnic-status\", {\n description: \"Check Remnic daemon status\",\n handler: commandHandler(async (_args, _ctx, session) => {\n const health = await client.health();\n 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 const result = await client.recall(query, session.sessionKey, session.cwd);\n session.notify(trimContext(result.context ?? \"(no Remnic context)\", MAX_CONTEXT_CHARS), \"info\");\n }),\n });\n\n pi.registerCommand(\"remnic-remember\", {\n description: \"Store a Remnic memory\",\n handler: commandHandler(async (args, _ctx, session) => {\n const content = args.trim();\n if (!content) {\n session.notify(\"Usage: /remnic-remember <memory>\", \"warning\");\n return;\n }\n await client.storeMemory(content, session.sessionKey);\n session.notify(\"Stored Remnic memory\", \"success\");\n }),\n });\n\n pi.registerCommand(\"remnic-lcm-search\", {\n description: \"Search Remnic LCM archived Pi context\",\n handler: commandHandler(async (args, _ctx, session) => {\n const query = args.trim();\n if (!query) {\n session.notify(\"Usage: /remnic-lcm-search <query>\", \"warning\");\n return;\n }\n const result = await client.lcmSearch(query, session.sessionKey);\n session.notify(JSON.stringify(result, null, 2), \"info\");\n }),\n });\n\n pi.registerCommand(\"remnic-why\", {\n description: \"Explain the last Remnic recall\",\n handler: commandHandler(async (_args, _ctx, session) => {\n const result = await client.recallExplain(session.sessionKey);\n session.notify(JSON.stringify(result, null, 2), \"info\");\n }),\n });\n\n pi.registerCommand(\"remnic-compact\", {\n description: \"Trigger Pi compaction with Remnic LCM coordination\",\n handler: commandHandler(async (_args, _ctx, session) => {\n session.compact?.();\n session.notify(\"Compaction requested\", \"info\");\n }),\n });\n}\n\nfunction commandHandler(\n handler: (args: string, ctx: any, session: PiContextSnapshot) => Promise<void>,\n): (args: string, ctx: any) => Promise<void> {\n return async (args, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n try {\n await handler(args, ctx, session);\n } catch (err) {\n session.notify(`Remnic command failed: ${errorMessage(err)}`, \"warning\");\n }\n };\n}\n\nasync function registerMcpTools(pi: PiApi, client: RemnicClient, config: RemnicPiConfig): Promise<void> {\n let tools: McpTool[] = [];\n try {\n tools = await client.mcpListTools({ timeoutMs: config.startupRequestTimeoutMs });\n } catch {\n return;\n }\n for (const tool of tools) {\n if (!tool.name.startsWith(\"remnic.\")) continue;\n const piToolName = tool.name.replace(/^remnic\\./, \"remnic_\").replace(/[^a-zA-Z0-9_]/g, \"_\");\n pi.registerTool({\n name: piToolName,\n label: tool.name,\n description: tool.description ?? `Call ${tool.name}`,\n parameters: toPiToolParametersSchema(tool.inputSchema),\n async execute(_toolCallId: string, params: Record<string, unknown>, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: any) {\n const session = snapshotPiContext(ctx);\n if (!session) {\n return {\n content: [{ type: \"text\", text: \"Remnic tool skipped because the Pi context is no longer active.\" }],\n details: { skipped: true, reason: \"stale_context\" },\n };\n }\n const safeParams = stripSessionOwnedRuntimeFields(params ?? {}) as Record<string, unknown>;\n const result = await client.mcpTool(tool.name, {\n ...safeParams,\n sessionKey: session.sessionKey,\n namespace: config.namespace,\n cwd: session.cwd,\n });\n return {\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\n details: result,\n };\n },\n });\n }\n}\n\nexport function toPiToolParametersSchema(inputSchema: unknown): TSchema {\n return Type.Unsafe(stripSessionOwnedSchemaFields(inputSchema));\n}\n\nexport function stripSessionOwnedSchemaFields(inputSchema: unknown): Record<string, unknown> {\n if (!isRecord(inputSchema)) {\n return { type: \"object\", properties: {}, additionalProperties: true };\n }\n return stripSessionOwnedSchemaNode(inputSchema) as Record<string, unknown>;\n}\n\nfunction stripSessionOwnedSchemaNode(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripSessionOwnedSchemaNode(entry));\n }\n if (!isRecord(value)) {\n return value;\n }\n const schema: Record<string, unknown> = { ...value };\n if (isRecord(value.properties)) {\n const properties: Record<string, unknown> = {};\n for (const [key, property] of Object.entries(value.properties)) {\n if (SESSION_OWNED_FIELDS.has(key)) continue;\n properties[key] = stripSessionOwnedSchemaNode(property);\n }\n schema.properties = properties;\n }\n if (Array.isArray(value.required)) {\n schema.required = value.required.filter(\n (field) => typeof field !== \"string\" || !SESSION_OWNED_FIELDS.has(field),\n );\n }\n for (const key of [\"items\", \"additionalProperties\", \"not\"] as const) {\n if (isRecord(value[key])) {\n schema[key] = stripSessionOwnedSchemaNode(value[key]);\n }\n }\n for (const key of [\"oneOf\", \"anyOf\", \"allOf\"] as const) {\n if (Array.isArray(value[key])) {\n schema[key] = value[key].map((entry) => stripSessionOwnedSchemaNode(entry));\n }\n }\n return schema;\n}\n\nexport function stripSessionOwnedRuntimeFields(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripSessionOwnedRuntimeFields(entry));\n }\n if (!isRecord(value)) {\n return value;\n }\n const sanitized: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n if (SESSION_OWNED_FIELDS.has(key)) continue;\n sanitized[key] = stripSessionOwnedRuntimeFields(child);\n }\n return sanitized;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isUserMessage(message: unknown): boolean {\n return isRecord(message) && message.role === \"user\";\n}\n\nfunction getSessionState(sessionKey: string, states: Map<string, PiSessionState>): { sessionKey: string; state: PiSessionState } {\n let state = states.get(sessionKey);\n if (!state) {\n state = {\n observedHashes: new Set<string>(),\n liveObservedReplayKeys: new Map<string, number>(),\n lastInjectedRecallKey: \"\",\n };\n states.set(sessionKey, state);\n pruneSessionStates(states);\n }\n return { sessionKey, state };\n}\n\nfunction pruneSessionStates(states: Map<string, PiSessionState>): void {\n while (states.size > MAX_SESSION_STATES) {\n const oldest = states.keys().next().value;\n if (typeof oldest !== \"string\") return;\n states.delete(oldest);\n }\n}\n\nexport async function observeMessages(\n ctx: any,\n client: RemnicClient,\n rawMessages: unknown[],\n observedHashes: Set<string>,\n liveObservedReplayKeys?: Map<string, number>,\n): Promise<void> {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n await observeMessagesForSession(session, client, rawMessages, observedHashes, liveObservedReplayKeys);\n}\n\nasync function observeMessagesForSession(\n session: PiContextSnapshot,\n client: RemnicClient,\n rawMessages: unknown[],\n observedHashes: Set<string>,\n liveObservedReplayKeys?: Map<string, number>,\n): 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 try {\n await client.observe(session.sessionKey, session.cwd, messages);\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 session.notify(`Remnic observe failed: ${errorMessage(err)}`, \"warning\");\n }\n}\n\nexport function buildCompactionSummary(preparation: any): string {\n const previousSummary = typeof preparation.previousSummary === \"string\"\n ? preparation.previousSummary.trim()\n : \"\";\n const messages = [\n ...(Array.isArray(preparation.messagesToSummarize) ? preparation.messagesToSummarize : []),\n ...(Array.isArray(preparation.turnPrefixMessages) ? preparation.turnPrefixMessages : []),\n ];\n const transcript = summarizeMessages(messages, 24000);\n const details = fileDetailsFromPreparation(preparation);\n\n if (\n !previousSummary &&\n !transcript &&\n details.readFiles.length === 0 &&\n details.modifiedFiles.length === 0\n ) {\n return \"\";\n }\n\n const sections: string[] = [\n \"## Remnic Pi Context Checkpoint\",\n \"\",\n \"This checkpoint was created by Remnic during Pi context compaction.\",\n ];\n if (previousSummary) sections.push(\"\", \"## Previous Summary\", previousSummary);\n if (transcript) sections.push(\"\", \"## Conversation Excerpt\", transcript);\n if (details.readFiles.length > 0) sections.push(\"\", \"<read-files>\", ...details.readFiles, \"</read-files>\");\n if (details.modifiedFiles.length > 0) sections.push(\"\", \"<modified-files>\", ...details.modifiedFiles, \"</modified-files>\");\n return sections.join(\"\\n\");\n}\n\nfunction fileDetailsFromPreparation(preparation: any): { readFiles: string[]; modifiedFiles: string[] } {\n const fileOps = preparation?.fileOps;\n const read = fileOps?.read instanceof Set ? Array.from(fileOps.read).filter(isString) : [];\n const edited = fileOps?.edited instanceof Set ? Array.from(fileOps.edited).filter(isString) : [];\n const written = fileOps?.written instanceof Set ? Array.from(fileOps.written).filter(isString) : [];\n const modified = new Set([...edited, ...written]);\n return {\n readFiles: read.filter((file) => !modified.has(file)).sort(),\n modifiedFiles: Array.from(modified).sort(),\n };\n}\n\nfunction restoreObservedState(session: PiContextSnapshot, observedHashes: Set<string>): void {\n for (const entry of session.entries) {\n if (entry?.type !== \"custom\" || entry.customType !== STATE_CUSTOM_TYPE) continue;\n const hashes = entry.data?.observedHashes;\n if (Array.isArray(hashes)) {\n for (const hash of hashes) {\n if (typeof hash === \"string\") rememberObservedHash(observedHashes, hash);\n }\n }\n }\n}\n\nfunction rememberObservedHash(observedHashes: Set<string>, hash: string): void {\n if (observedHashes.has(hash)) return;\n while (observedHashes.size >= MAX_OBSERVED_HASHES) {\n const oldest = observedHashes.keys().next().value;\n if (typeof oldest !== \"string\") break;\n observedHashes.delete(oldest);\n }\n observedHashes.add(hash);\n}\n\nfunction rememberLiveObservedReplayKey(liveObservedReplayKeys: Map<string, number>, key: string): void {\n liveObservedReplayKeys.set(key, (liveObservedReplayKeys.get(key) ?? 0) + 1);\n}\n\nfunction consumeLiveObservedReplayKey(liveObservedReplayKeys: Map<string, number>, key: string): boolean {\n const count = liveObservedReplayKeys.get(key) ?? 0;\n if (count <= 0) return false;\n if (count === 1) liveObservedReplayKeys.delete(key);\n else liveObservedReplayKeys.set(key, count - 1);\n return true;\n}\n\nfunction skipLiveObservedReplayMessages(\n sessionKey: string,\n rawMessages: unknown[],\n liveObservedReplayKeys: Map<string, number>,\n): unknown[] {\n if (liveObservedReplayKeys.size === 0) return rawMessages;\n const unobserved: unknown[] = [];\n for (const raw of rawMessages) {\n const message = toObserveMessage(raw);\n if (message && consumeLiveObservedReplayKey(liveObservedReplayKeys, liveReplayKey(message, sessionKey))) {\n continue;\n }\n unobserved.push(raw);\n }\n return unobserved;\n}\n\nfunction liveReplayKey(message: ObserveMessage, sessionKey: string): string {\n return hashObservedMessage(message, sessionKey, \"live-replay\");\n}\n\nfunction persistObservedState(pi: PiApi, observedHashes: Set<string>): void {\n const observed = Array.from(observedHashes).slice(-MAX_OBSERVED_HASHES);\n pi.appendEntry(STATE_CUSTOM_TYPE, {\n observedHashes: observed,\n recordedAt: new Date().toISOString(),\n });\n}\n\nasync function setStatus(session: PiContextSnapshot, client: RemnicClient, config: RemnicPiConfig): Promise<void> {\n try {\n await client.health({ timeoutMs: config.startupRequestTimeoutMs });\n session.setStatus(\"remnic\", `Remnic ${config.namespace ? `(${config.namespace})` : \"ready\"}`);\n } catch {\n session.setStatus(\"remnic\", \"Remnic offline\");\n }\n}\n\nfunction snapshotPiContext(ctx: any, options: PiContextSnapshotOptions = {}): PiContextSnapshot | null {\n const sessionKey = safeSessionKeyFromContext(ctx);\n if (!sessionKey) return null;\n const cwd = safeStringRead(() => ctx?.cwd, \"\");\n const hasUI = safeRead(() => ctx?.hasUI, undefined) === false;\n const ui = hasUI ? undefined : safeRead(() => ctx?.ui, undefined);\n const compact = safeRead(() => ctx?.compact, undefined);\n const includeSessionHistory = options.includeSessionHistory === true;\n return {\n sessionKey,\n cwd,\n entries: includeSessionHistory ? safeEntries(ctx) : [],\n branch: includeSessionHistory ? safeBranch(ctx) : [],\n notify: makeNotifier(ui, hasUI),\n setStatus: makeStatusSetter(ui, hasUI),\n compact: typeof compact === \"function\" ? () => compact.call(ctx) : undefined,\n };\n}\n\nfunction safeSessionKeyFromContext(ctx: any): string | null {\n try {\n return sessionKeyFromContext(ctx);\n } catch {\n return null;\n }\n}\n\nfunction makeNotifier(ui: unknown, hasUI: boolean): NotifyFn {\n if (hasUI || !isRecord(ui) || typeof ui.notify !== \"function\") {\n return () => undefined;\n }\n const notifyFn = ui.notify;\n return (message, level) => {\n try {\n notifyFn.call(ui, message, level);\n } catch {\n // Pi invalidates session-bound UI objects during reload/replacement. A\n // notification failure must not tear down Remnic's hooks.\n }\n };\n}\n\nfunction makeStatusSetter(ui: unknown, hasUI: boolean): PiContextSnapshot[\"setStatus\"] {\n if (hasUI || !isRecord(ui) || typeof ui.setStatus !== \"function\") {\n return () => undefined;\n }\n const setStatusFn = ui.setStatus;\n return (key, value) => {\n try {\n setStatusFn.call(ui, key, value);\n } catch {\n // See makeNotifier: stale UI should not make extension startup fail.\n }\n };\n}\n\nfunction safeRead<T>(read: () => T, fallback: T): T {\n try {\n return read();\n } catch {\n return fallback;\n }\n}\n\nfunction safeStringRead(read: () => unknown, fallback: string): string {\n const value = safeRead(read, fallback);\n return typeof value === \"string\" ? value : fallback;\n}\n\nfunction safeEntries(ctx: any): any[] {\n try {\n const entries = ctx.sessionManager?.getEntries?.();\n return Array.isArray(entries) ? entries : [];\n } catch {\n return [];\n }\n}\n\nfunction safeBranch(ctx: any): any[] {\n try {\n const branch = ctx.sessionManager?.getBranch?.();\n return Array.isArray(branch) ? branch : [];\n } catch {\n return [];\n }\n}\n\nfunction branchMessagesWithEntryIdentity(branch: any[]): unknown[] {\n const messages: unknown[] = [];\n for (const entry of branch) {\n const message = messageWithEntryIdentity(entry);\n if (message) messages.push(message);\n }\n return messages;\n}\n\nfunction messageWithEntryIdentity(entry: any): unknown | null {\n const message = entry?.message;\n if (!message || typeof message !== \"object\" || Array.isArray(message)) return message ?? null;\n\n const source = isRecord(entry) ? entry : {};\n const enriched: Record<string, unknown> = { ...(message as Record<string, unknown>) };\n assignMissingIdentity(enriched, \"entryId\", source.id ?? source.entryId ?? source.entry_id);\n assignMissingIdentity(enriched, \"timestamp\", source.timestamp);\n assignMissingIdentity(enriched, \"createdAt\", source.createdAt ?? source.created_at);\n return enriched;\n}\n\nfunction assignMissingIdentity(target: Record<string, unknown>, field: string, value: unknown): void {\n if (target[field] !== undefined) return;\n if (typeof value === \"string\" && value.length > 0) {\n target[field] = value;\n return;\n }\n if (typeof value === \"number\" && Number.isFinite(value)) {\n target[field] = value;\n }\n}\n\nfunction trimContext(value: string, budget: number): string {\n if (value.length <= budget) return value;\n if (budget <= TRUNCATION_NOTICE.length) return TRUNCATION_NOTICE.slice(0, budget);\n return `${value.slice(0, budget - TRUNCATION_NOTICE.length)}${TRUNCATION_NOTICE}`;\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction finiteTokenCount(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 ? value : null;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\nexport { textFromMessage };\n","import { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport { expandTildePath } from \"@remnic/core\";\n\nimport { REMNIC_PI_EXTENSION_DIR_NAME, resolvePiAgentHome } from \"./paths.js\";\n\nexport interface RemnicPiConfig {\n remnicDaemonUrl: string;\n authToken?: string;\n namespace?: string;\n recallMode: \"auto\" | \"minimal\" | \"full\" | \"graph_mode\" | \"no_recall\";\n recallTopK: number;\n recallBudgetChars: number;\n recallEnabled: boolean;\n observeEnabled: boolean;\n observeSkipExtraction: boolean;\n compactionEnabled: boolean;\n mcpToolsEnabled: boolean;\n statusEnabled: boolean;\n requestTimeoutMs: number;\n startupRequestTimeoutMs: number;\n}\n\nexport interface LoadConfigOptions {\n configPath?: string;\n env?: NodeJS.ProcessEnv;\n}\n\nconst DEFAULT_CONFIG: RemnicPiConfig = {\n remnicDaemonUrl: \"http://127.0.0.1:4318\",\n recallMode: \"auto\",\n recallTopK: 8,\n recallBudgetChars: 12000,\n recallEnabled: true,\n observeEnabled: true,\n observeSkipExtraction: false,\n compactionEnabled: true,\n mcpToolsEnabled: true,\n statusEnabled: true,\n requestTimeoutMs: 60000,\n startupRequestTimeoutMs: 1000,\n};\n\nfunction defaultConfigPath(env: NodeJS.ProcessEnv): string {\n return path.join(resolvePiAgentHome(env), \"extensions\", REMNIC_PI_EXTENSION_DIR_NAME, \"remnic.config.json\");\n}\n\nfunction coerceBoolean(value: unknown, fallback: boolean, fieldName: string): boolean {\n if (value === undefined || value === null) return fallback;\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true;\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false;\n }\n throw new Error(`Invalid boolean value for Remnic Pi config field ${fieldName}`);\n}\n\nfunction coercePositiveInt(value: unknown, fallback: number, max: number, fieldName: string): number {\n if (value === undefined || value === null || value === \"\") return fallback;\n let parsed: number;\n if (typeof value === \"number\") {\n parsed = value;\n } else if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.length === 0) return fallback;\n if (!/^[+-]?\\d+$/.test(trimmed)) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 1 to ${max}`);\n }\n parsed = Number(trimmed);\n } else {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 1 to ${max}`);\n }\n if (!Number.isInteger(parsed) || parsed <= 0 || parsed > max) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 1 to ${max}`);\n }\n return parsed;\n}\n\nfunction coerceOptionalNonEmptyString(value: unknown, fieldName: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === \"string\" && value.trim().length > 0) return value.trim();\n throw new Error(`Invalid string value for Remnic Pi config field ${fieldName}`);\n}\n\nfunction coerceOptionalString(value: unknown, fieldName: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n throw new Error(`Invalid string value for Remnic Pi config field ${fieldName}`);\n}\n\nfunction coerceOptionalHttpUrl(value: unknown, fieldName: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value !== \"string\") {\n throw new Error(`Invalid URL value for Remnic Pi config field ${fieldName}: expected an http or https URL`);\n }\n const trimmed = value.trim();\n if (trimmed.length === 0) return undefined;\n try {\n const parsed = new URL(trimmed);\n if (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") return trimTrailingSlashes(trimmed);\n } catch {\n // Fall through to the shared error below.\n }\n throw new Error(`Invalid URL value for Remnic Pi config field ${fieldName}: expected an http or https URL`);\n}\n\nfunction coerceRecallMode(value: unknown): RemnicPiConfig[\"recallMode\"] {\n if (value === undefined || value === null || value === \"\") return DEFAULT_CONFIG.recallMode;\n if (\n value === \"minimal\" ||\n value === \"full\" ||\n value === \"graph_mode\" ||\n value === \"no_recall\" ||\n value === \"auto\"\n ) {\n return value;\n }\n throw new Error(`Invalid recallMode value for Remnic Pi config: ${JSON.stringify(value)}`);\n}\n\nfunction readConfigFile(configPath: string): Record<string, unknown> {\n if (!existsSync(configPath)) return {};\n try {\n const raw = readFileSync(configPath, \"utf-8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n throw new Error(\"expected a JSON object\");\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to load Remnic Pi config at ${configPath}: ${reason}`);\n }\n}\n\nfunction trimTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;\n return value.slice(0, end);\n}\n\nexport function resolveConfigPath(options: LoadConfigOptions = {}): string {\n const env = options.env ?? process.env;\n // REMNIC_PI_CONFIG keeps precedence for upstream Pi; REMNIC_OMP_CONFIG lets an\n // omp (oh-my-pi) direct load (`omp -e npm:@remnic/plugin-pi`) point the shared\n // runtime module at its own config without an explicit configPath. Connector\n // installs always pass an explicit configPath, so this only affects direct loads.\n return expandTildePath(\n options.configPath || env.REMNIC_PI_CONFIG || env.REMNIC_OMP_CONFIG || defaultConfigPath(env),\n );\n}\n\nexport function loadConfig(options: LoadConfigOptions = {}): RemnicPiConfig {\n const env = options.env ?? process.env;\n const fileConfig = readConfigFile(resolveConfigPath(options));\n const daemonUrl =\n coerceOptionalHttpUrl(fileConfig.remnicDaemonUrl, \"remnicDaemonUrl\") ??\n coerceOptionalHttpUrl(env.REMNIC_DAEMON_URL, \"REMNIC_DAEMON_URL\") ??\n DEFAULT_CONFIG.remnicDaemonUrl;\n const authToken =\n coerceOptionalString(fileConfig.authToken, \"authToken\") ??\n coerceOptionalString(env.REMNIC_PI_AUTH_TOKEN, \"REMNIC_PI_AUTH_TOKEN\");\n const namespace = coerceOptionalNonEmptyString(fileConfig.namespace, \"namespace\");\n\n return {\n remnicDaemonUrl: daemonUrl,\n authToken,\n namespace,\n recallMode: coerceRecallMode(fileConfig.recallMode),\n recallTopK: coercePositiveInt(fileConfig.recallTopK, DEFAULT_CONFIG.recallTopK, 50, \"recallTopK\"),\n recallBudgetChars: coercePositiveInt(fileConfig.recallBudgetChars, DEFAULT_CONFIG.recallBudgetChars, 64000, \"recallBudgetChars\"),\n recallEnabled: coerceBoolean(fileConfig.recallEnabled, DEFAULT_CONFIG.recallEnabled, \"recallEnabled\"),\n observeEnabled: coerceBoolean(fileConfig.observeEnabled, DEFAULT_CONFIG.observeEnabled, \"observeEnabled\"),\n observeSkipExtraction: coerceBoolean(fileConfig.observeSkipExtraction, DEFAULT_CONFIG.observeSkipExtraction, \"observeSkipExtraction\"),\n compactionEnabled: coerceBoolean(fileConfig.compactionEnabled, DEFAULT_CONFIG.compactionEnabled, \"compactionEnabled\"),\n mcpToolsEnabled: coerceBoolean(fileConfig.mcpToolsEnabled, DEFAULT_CONFIG.mcpToolsEnabled, \"mcpToolsEnabled\"),\n statusEnabled: coerceBoolean(fileConfig.statusEnabled, DEFAULT_CONFIG.statusEnabled, \"statusEnabled\"),\n requestTimeoutMs: coercePositiveInt(fileConfig.requestTimeoutMs, DEFAULT_CONFIG.requestTimeoutMs, 60_000, \"requestTimeoutMs\"),\n startupRequestTimeoutMs: coercePositiveInt(\n fileConfig.startupRequestTimeoutMs,\n DEFAULT_CONFIG.startupRequestTimeoutMs,\n 60_000,\n \"startupRequestTimeoutMs\",\n ),\n };\n}\n","import type { RemnicPiConfig } from \"./config.js\";\n\nexport interface RecallResponse {\n context?: string;\n results?: Array<{ id?: string; content?: string; score?: number; category?: string }>;\n count?: number;\n}\n\nexport interface ObserveMessagePart {\n ordinal?: number;\n kind: \"text\" | \"tool_call\" | \"tool_result\" | \"patch\" | \"file_read\" | \"file_write\" | \"step_start\" | \"step_finish\" | \"snapshot\" | \"retry\";\n payload: Record<string, unknown>;\n toolName?: string | null;\n filePath?: string | null;\n createdAt?: string | null;\n}\n\nexport interface ObserveMessage {\n role: \"user\" | \"assistant\";\n content: string;\n sourceFormat?: \"pi\";\n rawContent?: unknown;\n parts?: ObserveMessagePart[];\n}\n\nexport interface McpTool {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n}\n\nexport class RemnicHttpError extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(message);\n }\n}\n\nexport class RemnicClient {\n private requestId = 0;\n\n constructor(private readonly config: RemnicPiConfig) {}\n\n async health(options: RequestOptions = {}): Promise<Record<string, unknown>> {\n return this.request(\"GET\", \"/engram/v1/health\", undefined, options);\n }\n\n async recall(query: string, sessionKey: string, cwd: string): Promise<RecallResponse> {\n return this.request(\"POST\", \"/engram/v1/recall\", {\n query,\n sessionKey,\n cwd,\n namespace: this.config.namespace,\n topK: this.config.recallTopK,\n mode: this.config.recallMode,\n });\n }\n\n async recallExplain(sessionKey: string): Promise<Record<string, unknown>> {\n return this.request(\"POST\", \"/engram/v1/recall/explain\", {\n sessionKey,\n namespace: this.config.namespace,\n });\n }\n\n async observe(sessionKey: string, cwd: string, messages: ObserveMessage[]): Promise<Record<string, unknown>> {\n return this.request(\"POST\", \"/engram/v1/observe\", {\n sessionKey,\n cwd,\n namespace: this.config.namespace,\n skipExtraction: this.config.observeSkipExtraction,\n messages,\n });\n }\n\n async storeMemory(content: string, sessionKey: string): Promise<Record<string, unknown>> {\n return this.request(\"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 });\n }\n\n async lcmSearch(query: string, sessionKey: string, limit = 10): Promise<Record<string, unknown>> {\n return this.request(\"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.request(\"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.request(\"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 as { tools?: unknown }).tools;\n return Array.isArray(tools) ? tools.filter(isMcpTool) : [];\n }\n\n async mcpTool(name: string, args: Record<string, unknown>): Promise<Record<string, unknown>> {\n return this.mcpRequest(\"tools/call\", {\n name,\n arguments: args,\n });\n }\n\n private async request<T = Record<string, unknown>>(\n method: string,\n pathname: string,\n body?: unknown,\n options: RequestOptions = {},\n ): Promise<T> {\n const controller = new AbortController();\n // A per-request override is honored only when it is a finite positive number;\n // 0, negative, NaN, or non-finite values would make setTimeout abort\n // immediately (or behave erratically), so fall back to the general budget.\n // In practice the override is always sourced from the validated\n // `startupRequestTimeoutMs` config, but this keeps the client robust to any\n // future caller (Copilot review).\n const override = options.timeoutMs;\n const timeoutMs =\n typeof override === \"number\" && Number.isFinite(override) && override > 0\n ? override\n : this.config.requestTimeoutMs;\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(`${this.config.remnicDaemonUrl}${pathname}`, {\n method,\n headers: {\n ...(body === undefined ? {} : { \"Content-Type\": \"application/json\" }),\n ...(this.config.authToken ? { Authorization: `Bearer ${this.config.authToken}` } : {}),\n \"X-Engram-Client-Id\": \"pi\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller.signal,\n });\n const text = await response.text();\n let payload: unknown = {};\n let parseError: unknown;\n if (text) {\n try {\n payload = JSON.parse(text);\n } catch (err) {\n parseError = err;\n }\n }\n if (!response.ok) {\n throw new RemnicHttpError(response.status, responseErrorMessage(response, text, payload, parseError));\n }\n if (parseError) {\n const reason = parseError instanceof Error ? parseError.message : String(parseError);\n throw new Error(`Invalid JSON response from Remnic daemon (${response.status} ${response.statusText || \"OK\"}): ${reason}`);\n }\n return payload as T;\n } catch (err) {\n if (isAbortError(err)) {\n throw new Error(`Remnic request timed out after ${timeoutMs}ms`);\n }\n throw err;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n 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\ninterface RequestOptions {\n timeoutMs?: number;\n}\n\nfunction isMcpTool(value: unknown): value is McpTool {\n return !!value && typeof value === \"object\" && typeof (value as { name?: unknown }).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\nfunction responseErrorMessage(response: Response, text: string, payload: unknown, parseError: unknown): string {\n if (!parseError && payload && typeof payload === \"object\") {\n const error = (payload as { error?: unknown }).error;\n if (typeof error === \"string\" && error.trim().length > 0) {\n return error;\n }\n const message = (payload as { message?: unknown }).message;\n if (typeof message === \"string\" && message.trim().length > 0) {\n return message;\n }\n }\n\n const snippet = text.trim().replace(/\\s+/g, \" \").slice(0, 200);\n if (snippet.length > 0) {\n return response.statusText ? `${response.statusText}: ${snippet}` : snippet;\n }\n return response.statusText || `HTTP ${response.status}`;\n}\n","import { createHash } from \"node:crypto\";\n\nimport { parsePiMessageParts, type LcmMessagePartInput } from \"@remnic/core\";\n\nimport type { ObserveMessage, ObserveMessagePart } from \"./client.js\";\n\ntype PiMessage = Record<string, unknown>;\n\nexport function sessionKeyFromContext(ctx: { sessionManager?: { getSessionId?: () => string } }): string {\n const id = ctx.sessionManager?.getSessionId?.();\n return id && id.trim().length > 0 ? `pi:${id}` : \"pi:default\";\n}\n\nexport function textFromMessage(message: unknown): string {\n if (!message || typeof message !== \"object\") return \"\";\n const obj = message as PiMessage;\n const role = typeof obj.role === \"string\" ? obj.role : \"message\";\n if (role === \"bashExecution\") {\n const command = typeof obj.command === \"string\" ? obj.command : \"\";\n const output = typeof obj.output === \"string\" ? obj.output : \"\";\n return [`Ran ${command}`, output].filter(Boolean).join(\"\\n\");\n }\n return textFromContent(obj.content).trim();\n}\n\nexport function latestUserQuery(messages: unknown[]): string {\n for (let index = messages.length - 1; index >= 0; index--) {\n const message = messages[index] as PiMessage;\n if (isExcludedFromContext(message) || isRemnicInjected(message)) continue;\n if (message?.role === \"user\") {\n const text = textFromMessage(message);\n if (text.length > 0) return text;\n }\n }\n return \"\";\n}\n\nexport function latestUserRecallTarget(\n messages: unknown[],\n): { query: string; dedupeKey: string } | null {\n for (let index = messages.length - 1; index >= 0; index--) {\n const message = messages[index] as PiMessage;\n if (isExcludedFromContext(message) || isRemnicInjected(message)) continue;\n if (message?.role !== \"user\") continue;\n const query = textFromMessage(message);\n if (query.length === 0) continue;\n const identity = stableObservedMessageIdentity(message);\n return {\n query,\n dedupeKey: identity ? `message:${identity}:${query}` : `query:${query}`,\n };\n }\n return null;\n}\n\nexport function toObserveMessage(message: unknown): ObserveMessage | null {\n if (!message || typeof message !== \"object\") return null;\n const obj = message as PiMessage;\n if (isExcludedFromContext(obj) || isRemnicInjected(obj)) return null;\n const role = obj.role === \"user\" || obj.role === \"bashExecution\" ? \"user\" : \"assistant\";\n const content = textFromMessage(obj);\n if (content.length === 0) return null;\n return {\n role,\n content,\n sourceFormat: \"pi\",\n rawContent: obj,\n parts: partsFromMessage(obj, content),\n };\n}\n\nexport function hashObservedMessage(message: ObserveMessage, sessionKey = \"\", identity = \"content\"): string {\n return createHash(\"sha256\")\n .update(sessionKey)\n .update(\"\\0\")\n .update(message.role)\n .update(\"\\0\")\n .update(identity)\n .update(\"\\0\")\n .update(message.content)\n .digest(\"hex\");\n}\n\nexport function observedMessageDedupeKey(\n message: ObserveMessage,\n sessionKey = \"\",\n): string | null {\n const identity = stableObservedMessageIdentity(message.rawContent);\n return identity ? hashObservedMessage(message, sessionKey, identity) : null;\n}\n\nexport function summarizeMessages(messages: unknown[], maxChars: number): string {\n const chunks: string[] = [];\n let used = 0;\n for (const message of messages) {\n if (isExcludedFromContext(message) || isRemnicInjected(message)) continue;\n const text = textFromMessage(message);\n if (!text) continue;\n const role = typeof (message as PiMessage)?.role === \"string\" ? (message as PiMessage).role : \"message\";\n const line = `[${role}] ${text}`;\n const separatorLength = chunks.length > 0 ? 2 : 0;\n const remaining = maxChars - used - separatorLength;\n if (remaining <= 0) break;\n const clipped = line.length > remaining ? line.slice(0, remaining) : line;\n if (clipped.length > 0) chunks.push(clipped);\n used += separatorLength + clipped.length;\n if (used >= maxChars) break;\n }\n return chunks.join(\"\\n\\n\");\n}\n\nexport function isExcludedFromContext(message: unknown): boolean {\n return !!message && typeof message === \"object\" && (message as PiMessage).excludeFromContext === true;\n}\n\nexport function isRemnicInjected(message: unknown): boolean {\n return !!message && typeof message === \"object\" && (message as PiMessage).remnicInjected === true;\n}\n\nfunction textFromContent(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n const chunks: string[] = [];\n for (const block of content) {\n if (!block || typeof block !== \"object\") continue;\n const obj = block as PiMessage;\n if (obj.type === \"text\" && typeof obj.text === \"string\") chunks.push(obj.text);\n if (obj.type === \"toolCall\" && typeof obj.name === \"string\") {\n chunks.push(`Tool ${obj.name} called with ${JSON.stringify(obj.arguments ?? {})}`);\n }\n }\n return chunks.join(\"\\n\");\n}\n\nfunction partsFromMessage(message: PiMessage, renderedContent: string): ObserveMessagePart[] {\n return parsePiMessageParts(message, {\n renderedContent,\n allowRenderedFallback: true,\n }).map(toObserveMessagePart);\n}\n\nfunction toObserveMessagePart(part: LcmMessagePartInput): ObserveMessagePart {\n return {\n ordinal: part.ordinal ?? undefined,\n kind: part.kind,\n payload: part.payload,\n toolName: part.toolName ?? part.tool_name ?? undefined,\n filePath: part.filePath ?? part.file_path ?? undefined,\n createdAt: part.createdAt ?? part.created_at ?? undefined,\n };\n}\n\nfunction stableObservedMessageIdentity(rawContent: unknown): string | null {\n if (rawContent && typeof rawContent === \"object\") {\n const obj = rawContent as PiMessage;\n const fields = [\n \"id\",\n \"entryId\",\n \"entry_id\",\n \"messageId\",\n \"message_id\",\n \"turnId\",\n \"turn_id\",\n \"timestamp\",\n \"createdAt\",\n \"created_at\",\n ];\n for (const field of fields) {\n const value = obj[field];\n if (typeof value === \"string\" && value.length > 0) return `${field}:${value}`;\n if (typeof value === \"number\" && Number.isFinite(value)) return `${field}:${value}`;\n }\n }\n return null;\n}\n"],"mappings":";;;;;;AAAA,SAAS,YAA0B;;;ACAnC,SAAS,YAAY,oBAAoB;AACzC,OAAO,UAAU;AAEjB,SAAS,uBAAuB;AA0BhC,IAAM,iBAAiC;AAAA,EACrC,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,uBAAuB;AAAA,EACvB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,yBAAyB;AAC3B;AAEA,SAAS,kBAAkB,KAAgC;AACzD,SAAO,KAAK,KAAK,mBAAmB,GAAG,GAAG,cAAc,8BAA8B,oBAAoB;AAC5G;AAEA,SAAS,cAAc,OAAgB,UAAmB,WAA4B;AACpF,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,MAAI,OAAO,UAAU,UAAW,QAAO;AACvC,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,aAAa,MAAM,KAAK,EAAE,YAAY;AAC5C,QAAI,CAAC,QAAQ,KAAK,OAAO,IAAI,EAAE,SAAS,UAAU,EAAG,QAAO;AAC5D,QAAI,CAAC,SAAS,KAAK,MAAM,KAAK,EAAE,SAAS,UAAU,EAAG,QAAO;AAAA,EAC/D;AACA,QAAM,IAAI,MAAM,oDAAoD,SAAS,EAAE;AACjF;AAEA,SAAS,kBAAkB,OAAgB,UAAkB,KAAa,WAA2B;AACnG,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,GAAI,QAAO;AAClE,MAAI;AACJ,MAAI,OAAO,UAAU,UAAU;AAC7B,aAAS;AAAA,EACX,WAAW,OAAO,UAAU,UAAU;AACpC,UAAM,UAAU,MAAM,KAAK;AAC3B,QAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAI,CAAC,aAAa,KAAK,OAAO,GAAG;AAC/B,YAAM,IAAI,MAAM,oDAAoD,SAAS,mCAAmC,GAAG,EAAE;AAAA,IACvH;AACA,aAAS,OAAO,OAAO;AAAA,EACzB,OAAO;AACL,UAAM,IAAI,MAAM,oDAAoD,SAAS,mCAAmC,GAAG,EAAE;AAAA,EACvH;AACA,MAAI,CAAC,OAAO,UAAU,MAAM,KAAK,UAAU,KAAK,SAAS,KAAK;AAC5D,UAAM,IAAI,MAAM,oDAAoD,SAAS,mCAAmC,GAAG,EAAE;AAAA,EACvH;AACA,SAAO;AACT;AAEA,SAAS,6BAA6B,OAAgB,WAAuC;AAC3F,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,EAAG,QAAO,MAAM,KAAK;AAC5E,QAAM,IAAI,MAAM,mDAAmD,SAAS,EAAE;AAChF;AAEA,SAAS,qBAAqB,OAAgB,WAAuC;AACnF,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,UAAU,MAAM,KAAK;AAC3B,WAAO,QAAQ,SAAS,IAAI,UAAU;AAAA,EACxC;AACA,QAAM,IAAI,MAAM,mDAAmD,SAAS,EAAE;AAChF;AAEA,SAAS,sBAAsB,OAAgB,WAAuC;AACpF,MAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,IAAI,MAAM,gDAAgD,SAAS,iCAAiC;AAAA,EAC5G;AACA,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,OAAO;AAC9B,QAAI,OAAO,aAAa,WAAW,OAAO,aAAa,SAAU,QAAO,oBAAoB,OAAO;AAAA,EACrG,QAAQ;AAAA,EAER;AACA,QAAM,IAAI,MAAM,gDAAgD,SAAS,iCAAiC;AAC5G;AAEA,SAAS,iBAAiB,OAA8C;AACtE,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,GAAI,QAAO,eAAe;AACjF,MACE,UAAU,aACV,UAAU,UACV,UAAU,gBACV,UAAU,eACV,UAAU,QACV;AACA,WAAO;AAAA,EACT;AACA,QAAM,IAAI,MAAM,kDAAkD,KAAK,UAAU,KAAK,CAAC,EAAE;AAC3F;AAEA,SAAS,eAAe,YAA6C;AACnE,MAAI,CAAC,WAAW,UAAU,EAAG,QAAO,CAAC;AACrC,MAAI;AACF,UAAM,MAAM,aAAa,YAAY,OAAO;AAC5C,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAAG;AAClE,aAAO;AAAA,IACT;AACA,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C,SAAS,KAAK;AACZ,UAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,UAAM,IAAI,MAAM,sCAAsC,UAAU,KAAK,MAAM,EAAE;AAAA,EAC/E;AACF;AAEA,SAAS,oBAAoB,OAAuB;AAClD,MAAI,MAAM,MAAM;AAChB,SAAO,MAAM,KAAK,MAAM,WAAW,MAAM,CAAC,MAAM,GAAI,QAAO;AAC3D,SAAO,MAAM,MAAM,GAAG,GAAG;AAC3B;AAEO,SAAS,kBAAkB,UAA6B,CAAC,GAAW;AACzE,QAAM,MAAM,QAAQ,OAAO,QAAQ;AAKnC,SAAO;AAAA,IACL,QAAQ,cAAc,IAAI,oBAAoB,IAAI,qBAAqB,kBAAkB,GAAG;AAAA,EAC9F;AACF;AAEO,SAAS,WAAW,UAA6B,CAAC,GAAmB;AAC1E,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAM,aAAa,eAAe,kBAAkB,OAAO,CAAC;AAC5D,QAAM,YACJ,sBAAsB,WAAW,iBAAiB,iBAAiB,KACnE,sBAAsB,IAAI,mBAAmB,mBAAmB,KAChE,eAAe;AACjB,QAAM,YACJ,qBAAqB,WAAW,WAAW,WAAW,KACtD,qBAAqB,IAAI,sBAAsB,sBAAsB;AACvE,QAAM,YAAY,6BAA6B,WAAW,WAAW,WAAW;AAEhF,SAAO;AAAA,IACL,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,IACA,YAAY,iBAAiB,WAAW,UAAU;AAAA,IAClD,YAAY,kBAAkB,WAAW,YAAY,eAAe,YAAY,IAAI,YAAY;AAAA,IAChG,mBAAmB,kBAAkB,WAAW,mBAAmB,eAAe,mBAAmB,MAAO,mBAAmB;AAAA,IAC/H,eAAe,cAAc,WAAW,eAAe,eAAe,eAAe,eAAe;AAAA,IACpG,gBAAgB,cAAc,WAAW,gBAAgB,eAAe,gBAAgB,gBAAgB;AAAA,IACxG,uBAAuB,cAAc,WAAW,uBAAuB,eAAe,uBAAuB,uBAAuB;AAAA,IACpI,mBAAmB,cAAc,WAAW,mBAAmB,eAAe,mBAAmB,mBAAmB;AAAA,IACpH,iBAAiB,cAAc,WAAW,iBAAiB,eAAe,iBAAiB,iBAAiB;AAAA,IAC5G,eAAe,cAAc,WAAW,eAAe,eAAe,eAAe,eAAe;AAAA,IACpG,kBAAkB,kBAAkB,WAAW,kBAAkB,eAAe,kBAAkB,KAAQ,kBAAkB;AAAA,IAC5H,yBAAyB;AAAA,MACvB,WAAW;AAAA,MACX,eAAe;AAAA,MACf;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;;;AC/JO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EACzC,YACW,QACT,SACA;AACA,UAAM,OAAO;AAHJ;AAAA,EAIX;AAAA,EAJW;AAKb;AAEO,IAAM,eAAN,MAAmB;AAAA,EAGxB,YAA6B,QAAwB;AAAxB;AAAA,EAAyB;AAAA,EAAzB;AAAA,EAFrB,YAAY;AAAA,EAIpB,MAAM,OAAO,UAA0B,CAAC,GAAqC;AAC3E,WAAO,KAAK,QAAQ,OAAO,qBAAqB,QAAW,OAAO;AAAA,EACpE;AAAA,EAEA,MAAM,OAAO,OAAe,YAAoB,KAAsC;AACpF,WAAO,KAAK,QAAQ,QAAQ,qBAAqB;AAAA,MAC/C;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,KAAK,OAAO;AAAA,MACvB,MAAM,KAAK,OAAO;AAAA,MAClB,MAAM,KAAK,OAAO;AAAA,IACpB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,cAAc,YAAsD;AACxE,WAAO,KAAK,QAAQ,QAAQ,6BAA6B;AAAA,MACvD;AAAA,MACA,WAAW,KAAK,OAAO;AAAA,IACzB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAQ,YAAoB,KAAa,UAA8D;AAC3G,WAAO,KAAK,QAAQ,QAAQ,sBAAsB;AAAA,MAChD;AAAA,MACA;AAAA,MACA,WAAW,KAAK,OAAO;AAAA,MACvB,gBAAgB,KAAK,OAAO;AAAA,MAC5B;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YAAY,SAAiB,YAAsD;AACvF,WAAO,KAAK,QAAQ,QAAQ,uBAAuB;AAAA,MACjD;AAAA,MACA,UAAU;AAAA,MACV,cAAc;AAAA,MACd;AAAA,MACA,WAAW,KAAK,OAAO;AAAA,IACzB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,UAAU,OAAe,YAAoB,QAAQ,IAAsC;AAC/F,WAAO,KAAK,QAAQ,QAAQ,yBAAyB;AAAA,MACnD;AAAA,MACA;AAAA,MACA,WAAW,KAAK,OAAO;AAAA,MACvB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,mBAAmB,YAAsD;AAC7E,WAAO,KAAK,QAAQ,QAAQ,mCAAmC;AAAA,MAC7D;AAAA,MACA,WAAW,KAAK,OAAO;AAAA,IACzB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,oBAAoB,YAAoB,cAAsB,aAAuD;AACzH,WAAO,KAAK,QAAQ,QAAQ,oCAAoC;AAAA,MAC9D;AAAA,MACA,WAAW,KAAK,OAAO;AAAA,MACvB;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,kBAAkB,YAAoB,SAAmD;AAC7F,WAAO,KAAK,QAAQ,6BAA6B;AAAA,MAC/C;AAAA,MACA;AAAA,MACA,WAAW,KAAK,OAAO;AAAA,IACzB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,aAAa,UAA0B,CAAC,GAAuB;AACnE,UAAM,SAAS,MAAM,KAAK,WAAW,cAAc,CAAC,GAAG,OAAO;AAC9D,UAAM,QAAS,OAA+B;AAC9C,WAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,OAAO,SAAS,IAAI,CAAC;AAAA,EAC3D;AAAA,EAEA,MAAM,QAAQ,MAAc,MAAiE;AAC3F,WAAO,KAAK,WAAW,cAAc;AAAA,MACnC;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,QACZ,QACA,UACA,MACA,UAA0B,CAAC,GACf;AACZ,UAAM,aAAa,IAAI,gBAAgB;AAOvC,UAAM,WAAW,QAAQ;AACzB,UAAM,YACJ,OAAO,aAAa,YAAY,OAAO,SAAS,QAAQ,KAAK,WAAW,IACpE,WACA,KAAK,OAAO;AAClB,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAC9D,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,eAAe,GAAG,QAAQ,IAAI;AAAA,QACxE;AAAA,QACA,SAAS;AAAA,UACP,GAAI,SAAS,SAAY,CAAC,IAAI,EAAE,gBAAgB,mBAAmB;AAAA,UACnE,GAAI,KAAK,OAAO,YAAY,EAAE,eAAe,UAAU,KAAK,OAAO,SAAS,GAAG,IAAI,CAAC;AAAA,UACpF,sBAAsB;AAAA,QACxB;AAAA,QACA,MAAM,SAAS,SAAY,SAAY,KAAK,UAAU,IAAI;AAAA,QAC1D,QAAQ,WAAW;AAAA,MACrB,CAAC;AACD,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAI,UAAmB,CAAC;AACxB,UAAI;AACJ,UAAI,MAAM;AACR,YAAI;AACF,oBAAU,KAAK,MAAM,IAAI;AAAA,QAC3B,SAAS,KAAK;AACZ,uBAAa;AAAA,QACf;AAAA,MACF;AACA,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,gBAAgB,SAAS,QAAQ,qBAAqB,UAAU,MAAM,SAAS,UAAU,CAAC;AAAA,MACtG;AACA,UAAI,YAAY;AACd,cAAM,SAAS,sBAAsB,QAAQ,WAAW,UAAU,OAAO,UAAU;AACnF,cAAM,IAAI,MAAM,6CAA6C,SAAS,MAAM,IAAI,SAAS,cAAc,IAAI,MAAM,MAAM,EAAE;AAAA,MAC3H;AACA,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,UAAI,aAAa,GAAG,GAAG;AACrB,cAAM,IAAI,MAAM,kCAAkC,SAAS,IAAI;AAAA,MACjE;AACA,YAAM;AAAA,IACR,UAAE;AACA,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAc,WACZ,QACA,QACA,UAA0B,CAAC,GACO;AAClC,SAAK,aAAa;AAClB,UAAM,UAAU,MAAM,KAAK,QAAiC,QAAQ,QAAQ;AAAA,MAC1E,SAAS;AAAA,MACT,IAAI,KAAK;AAAA,MACT;AAAA,MACA;AAAA,IACF,GAAG,OAAO;AACV,QAAI,QAAQ,OAAO;AACjB,YAAM,IAAI,MAAM,KAAK,UAAU,QAAQ,KAAK,CAAC;AAAA,IAC/C;AACA,WAAQ,QAAQ,UAAU,OAAO,QAAQ,WAAW,WAAW,QAAQ,SAAS;AAAA,EAClF;AACF;AAMA,SAAS,UAAU,OAAkC;AACnD,SAAO,CAAC,CAAC,SAAS,OAAO,UAAU,YAAY,OAAQ,MAA6B,SAAS;AAC/F;AAEA,SAAS,aAAa,KAAuB;AAC3C,SAAO,eAAe,UAAU,IAAI,SAAS,gBAAgB,IAAI,YAAY;AAC/E;AAEA,SAAS,qBAAqB,UAAoB,MAAc,SAAkB,YAA6B;AAC7G,MAAI,CAAC,cAAc,WAAW,OAAO,YAAY,UAAU;AACzD,UAAM,QAAS,QAAgC;AAC/C,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,GAAG;AACxD,aAAO;AAAA,IACT;AACA,UAAM,UAAW,QAAkC;AACnD,QAAI,OAAO,YAAY,YAAY,QAAQ,KAAK,EAAE,SAAS,GAAG;AAC5D,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,UAAU,KAAK,KAAK,EAAE,QAAQ,QAAQ,GAAG,EAAE,MAAM,GAAG,GAAG;AAC7D,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO,SAAS,aAAa,GAAG,SAAS,UAAU,KAAK,OAAO,KAAK;AAAA,EACtE;AACA,SAAO,SAAS,cAAc,QAAQ,SAAS,MAAM;AACvD;;;AC/OA,SAAS,kBAAkB;AAE3B,SAAS,2BAAqD;AAMvD,SAAS,sBAAsB,KAAmE;AACvG,QAAM,KAAK,IAAI,gBAAgB,eAAe;AAC9C,SAAO,MAAM,GAAG,KAAK,EAAE,SAAS,IAAI,MAAM,EAAE,KAAK;AACnD;AAEO,SAAS,gBAAgB,SAA0B;AACxD,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAM,MAAM;AACZ,QAAM,OAAO,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;AACvD,MAAI,SAAS,iBAAiB;AAC5B,UAAM,UAAU,OAAO,IAAI,YAAY,WAAW,IAAI,UAAU;AAChE,UAAM,SAAS,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS;AAC7D,WAAO,CAAC,OAAO,OAAO,IAAI,MAAM,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAAA,EAC7D;AACA,SAAO,gBAAgB,IAAI,OAAO,EAAE,KAAK;AAC3C;AAcO,SAAS,uBACd,UAC6C;AAC7C,WAAS,QAAQ,SAAS,SAAS,GAAG,SAAS,GAAG,SAAS;AACzD,UAAM,UAAU,SAAS,KAAK;AAC9B,QAAI,sBAAsB,OAAO,KAAK,iBAAiB,OAAO,EAAG;AACjE,QAAI,SAAS,SAAS,OAAQ;AAC9B,UAAM,QAAQ,gBAAgB,OAAO;AACrC,QAAI,MAAM,WAAW,EAAG;AACxB,UAAM,WAAW,8BAA8B,OAAO;AACtD,WAAO;AAAA,MACL;AAAA,MACA,WAAW,WAAW,WAAW,QAAQ,IAAI,KAAK,KAAK,SAAS,KAAK;AAAA,IACvE;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,SAAyC;AACxE,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAM,MAAM;AACZ,MAAI,sBAAsB,GAAG,KAAK,iBAAiB,GAAG,EAAG,QAAO;AAChE,QAAM,OAAO,IAAI,SAAS,UAAU,IAAI,SAAS,kBAAkB,SAAS;AAC5E,QAAM,UAAU,gBAAgB,GAAG;AACnC,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,OAAO,iBAAiB,KAAK,OAAO;AAAA,EACtC;AACF;AAEO,SAAS,oBAAoB,SAAyB,aAAa,IAAI,WAAW,WAAmB;AAC1G,SAAO,WAAW,QAAQ,EACvB,OAAO,UAAU,EACjB,OAAO,IAAI,EACX,OAAO,QAAQ,IAAI,EACnB,OAAO,IAAI,EACX,OAAO,QAAQ,EACf,OAAO,IAAI,EACX,OAAO,QAAQ,OAAO,EACtB,OAAO,KAAK;AACjB;AAEO,SAAS,yBACd,SACA,aAAa,IACE;AACf,QAAM,WAAW,8BAA8B,QAAQ,UAAU;AACjE,SAAO,WAAW,oBAAoB,SAAS,YAAY,QAAQ,IAAI;AACzE;AAEO,SAAS,kBAAkB,UAAqB,UAA0B;AAC/E,QAAM,SAAmB,CAAC;AAC1B,MAAI,OAAO;AACX,aAAW,WAAW,UAAU;AAC9B,QAAI,sBAAsB,OAAO,KAAK,iBAAiB,OAAO,EAAG;AACjE,UAAM,OAAO,gBAAgB,OAAO;AACpC,QAAI,CAAC,KAAM;AACX,UAAM,OAAO,OAAQ,SAAuB,SAAS,WAAY,QAAsB,OAAO;AAC9F,UAAM,OAAO,IAAI,IAAI,KAAK,IAAI;AAC9B,UAAM,kBAAkB,OAAO,SAAS,IAAI,IAAI;AAChD,UAAM,YAAY,WAAW,OAAO;AACpC,QAAI,aAAa,EAAG;AACpB,UAAM,UAAU,KAAK,SAAS,YAAY,KAAK,MAAM,GAAG,SAAS,IAAI;AACrE,QAAI,QAAQ,SAAS,EAAG,QAAO,KAAK,OAAO;AAC3C,YAAQ,kBAAkB,QAAQ;AAClC,QAAI,QAAQ,SAAU;AAAA,EACxB;AACA,SAAO,OAAO,KAAK,MAAM;AAC3B;AAEO,SAAS,sBAAsB,SAA2B;AAC/D,SAAO,CAAC,CAAC,WAAW,OAAO,YAAY,YAAa,QAAsB,uBAAuB;AACnG;AAEO,SAAS,iBAAiB,SAA2B;AAC1D,SAAO,CAAC,CAAC,WAAW,OAAO,YAAY,YAAa,QAAsB,mBAAmB;AAC/F;AAEA,SAAS,gBAAgB,SAA0B;AACjD,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,QAAM,SAAmB,CAAC;AAC1B,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AACzC,UAAM,MAAM;AACZ,QAAI,IAAI,SAAS,UAAU,OAAO,IAAI,SAAS,SAAU,QAAO,KAAK,IAAI,IAAI;AAC7E,QAAI,IAAI,SAAS,cAAc,OAAO,IAAI,SAAS,UAAU;AAC3D,aAAO,KAAK,QAAQ,IAAI,IAAI,gBAAgB,KAAK,UAAU,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE;AAAA,IACnF;AAAA,EACF;AACA,SAAO,OAAO,KAAK,IAAI;AACzB;AAEA,SAAS,iBAAiB,SAAoB,iBAA+C;AAC3F,SAAO,oBAAoB,SAAS;AAAA,IAClC;AAAA,IACA,uBAAuB;AAAA,EACzB,CAAC,EAAE,IAAI,oBAAoB;AAC7B;AAEA,SAAS,qBAAqB,MAA+C;AAC3E,SAAO;AAAA,IACL,SAAS,KAAK,WAAW;AAAA,IACzB,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,UAAU,KAAK,YAAY,KAAK,aAAa;AAAA,IAC7C,UAAU,KAAK,YAAY,KAAK,aAAa;AAAA,IAC7C,WAAW,KAAK,aAAa,KAAK,cAAc;AAAA,EAClD;AACF;AAEA,SAAS,8BAA8B,YAAoC;AACzE,MAAI,cAAc,OAAO,eAAe,UAAU;AAChD,UAAM,MAAM;AACZ,UAAM,SAAS;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,eAAW,SAAS,QAAQ;AAC1B,YAAM,QAAQ,IAAI,KAAK;AACvB,UAAI,OAAO,UAAU,YAAY,MAAM,SAAS,EAAG,QAAO,GAAG,KAAK,IAAI,KAAK;AAC3E,UAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO,GAAG,KAAK,IAAI,KAAK;AAAA,IACnF;AAAA,EACF;AACA,SAAO;AACT;;;AHrJA,IAAM,oBAAoB;AAC1B,IAAM,sBAAsB;AAC5B,IAAM,qBAAqB;AAC3B,IAAM,oBAAoB;AAC1B,IAAM,oBAAoB;AAC1B,IAAM,uBAAuB,oBAAI,IAAI,CAAC,cAAc,aAAa,KAAK,CAAC;AAwBhE,SAAS,wBAAwB,UAAoC,CAAC,GAAG;AAC9E,QAAM,SAAS,QAAQ,UAAU,WAAW,OAAO;AACnD,QAAM,SAAS,IAAI,aAAa,MAAM;AACtC,QAAM,gBAAgB,oBAAI,IAA4B;AAEtD,SAAO,eAAeA,mBAAkB,IAA0B;AAChE,OAAG,GAAG,iBAAiB,OAAO,QAAQ,QAAQ;AAC5C,YAAM,UAAU,kBAAkB,KAAK,EAAE,uBAAuB,KAAK,CAAC;AACtE,UAAI,CAAC,QAAS;AACd,YAAM,EAAE,MAAM,IAAI,gBAAgB,QAAQ,YAAY,aAAa;AACnE,2BAAqB,SAAS,MAAM,cAAc;AAClD,UAAI,CAAC,OAAO,cAAe;AAC3B,YAAM,UAAU,SAAS,QAAQ,MAAM;AAAA,IACzC,CAAC;AAED,OAAG,GAAG,WAAW,OAAO,OAAO,QAAQ;AACrC,YAAM,UAAU,kBAAkB,GAAG;AACrC,UAAI,CAAC,QAAS;AACd,UAAI,CAAC,OAAO,iBAAiB,CAAC,OAAO,UAAW;AAChD,YAAM,eAAe,uBAAuB,MAAM,QAAQ,MAAM,QAAQ,IAAI,MAAM,WAAW,CAAC,CAAC;AAC/F,UAAI,CAAC,aAAc;AACnB,YAAM,EAAE,MAAM,IAAI;AAClB,YAAM,EAAE,MAAM,IAAI,gBAAgB,QAAQ,YAAY,aAAa;AACnE,UAAI,aAAa,cAAc,MAAM,sBAAuB;AAE5D,UAAI;AACF,cAAM,WAAW,MAAM,OAAO,OAAO,OAAO,QAAQ,YAAY,QAAQ,GAAG;AAC3E,cAAM,UAAU,YAAY,SAAS,WAAW,IAAI,OAAO,iBAAiB;AAC5E,YAAI,CAAC,QAAS;AACd,cAAM,wBAAwB,aAAa;AAC3C,eAAO;AAAA,UACL,UAAU;AAAA,YACR,GAAG,MAAM;AAAA,YACT;AAAA,cACE,MAAM;AAAA,cACN,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM;AAAA;AAAA,EAA6C,OAAO,GAAG,CAAC;AAAA,cACxF,gBAAgB;AAAA,cAChB,WAAW,KAAK,IAAI;AAAA,YACtB;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AACZ,gBAAQ,OAAO,8BAA8B,aAAa,GAAG,CAAC,IAAI,SAAS;AAAA,MAC7E;AAAA,IACF,CAAC;AAED,OAAG,GAAG,eAAe,OAAO,OAAO,QAAQ;AACzC,YAAM,UAAU,kBAAkB,GAAG;AACrC,UAAI,CAAC,QAAS;AACd,UAAI,CAAC,OAAO,kBAAkB,CAAC,cAAc,MAAM,OAAO,EAAG;AAC7D,YAAM,EAAE,MAAM,IAAI,gBAAgB,QAAQ,YAAY,aAAa;AACnE,YAAM,0BAA0B,SAAS,QAAQ,CAAC,MAAM,OAAO,GAAG,MAAM,gBAAgB,MAAM,sBAAsB;AAAA,IACtH,CAAC;AAED,OAAG,GAAG,YAAY,OAAO,OAAO,QAAQ;AACtC,YAAM,UAAU,kBAAkB,GAAG;AACrC,UAAI,CAAC,QAAS;AACd,UAAI,CAAC,OAAO,eAAgB;AAC5B,YAAM,WAAW,CAAC,MAAM,SAAS,GAAI,MAAM,QAAQ,MAAM,WAAW,IAAI,MAAM,cAAc,CAAC,CAAE;AAC/F,YAAM,EAAE,MAAM,IAAI,gBAAgB,QAAQ,YAAY,aAAa;AACnE,YAAM,0BAA0B,SAAS,QAAQ,UAAU,MAAM,gBAAgB,MAAM,sBAAsB;AAAA,IAC/G,CAAC;AAED,OAAG,GAAG,oBAAoB,OAAO,QAAQ,QAAQ;AAC/C,YAAM,UAAU,kBAAkB,KAAK,EAAE,uBAAuB,KAAK,CAAC;AACtE,UAAI,CAAC,QAAS;AACd,YAAM,EAAE,YAAY,MAAM,IAAI,gBAAgB,QAAQ,YAAY,aAAa;AAC/E,UAAI,OAAO,gBAAgB;AACzB,cAAM,iBAAiB,gCAAgC,QAAQ,MAAM;AACrE,cAAM,2BAA2B,+BAA+B,QAAQ,YAAY,gBAAgB,MAAM,sBAAsB;AAChI,YAAI,yBAAyB,SAAS,GAAG;AACvC,gBAAM,0BAA0B,SAAS,QAAQ,0BAA0B,MAAM,cAAc;AAAA,QACjG;AAAA,MACF;AACA,2BAAqB,IAAI,MAAM,cAAc;AAC7C,oBAAc,OAAO,UAAU;AAAA,IACjC,CAAC;AAED,OAAG,GAAG,0BAA0B,OAAO,OAAO,QAAQ;AACpD,YAAM,UAAU,kBAAkB,GAAG;AACrC,UAAI,CAAC,QAAS;AACd,UAAI,CAAC,OAAO,qBAAqB,CAAC,OAAO,UAAW;AACpD,YAAM,cAAc,MAAM,eAAe,CAAC;AAC1C,UAAI;AACF,cAAM,OAAO,mBAAmB,QAAQ,UAAU;AAAA,MACpD,SAAS,KAAK;AACZ,gBAAQ,OAAO,4BAA4B,aAAa,GAAG,CAAC,IAAI,SAAS;AACzE;AAAA,MACF;AAEA,YAAM,eAAe,iBAAiB,YAAY,YAAY;AAC9D,YAAM,cAAc,iBAAiB,YAAY,WAAW;AAC5D,UAAI,iBAAiB,QAAQ,gBAAgB,MAAM;AACjD,YAAI;AACF,gBAAM,OAAO,oBAAoB,QAAQ,YAAY,cAAc,WAAW;AAAA,QAChF,SAAS,KAAK;AACZ,kBAAQ,OAAO,8CAA8C,aAAa,GAAG,CAAC,IAAI,SAAS;AAAA,QAC7F;AAAA,MACF;AAEA,YAAM,UAAU,uBAAuB,WAAW;AAClD,UAAI,CAAC,QAAQ,KAAK,EAAG;AACrB,UAAI;AACF,cAAM,OAAO,kBAAkB,QAAQ,YAAY,OAAO;AAAA,MAC5D,SAAS,KAAK;AACZ,gBAAQ,OAAO,qCAAqC,aAAa,GAAG,CAAC,IAAI,SAAS;AAAA,MACpF;AACA,YAAM,UAAU,2BAA2B,WAAW;AACtD,aAAO;AAAA,QACL,YAAY;AAAA,UACV;AAAA,UACA,kBAAkB,YAAY;AAAA,UAC9B,cAAc,YAAY;AAAA,UAC1B,SAAS;AAAA,YACP,GAAG;AAAA,YACH,QAAQ,EAAE,SAAS,GAAG,QAAQ,KAAK;AAAA,UACrC;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAED,qBAAiB,IAAI,QAAQ,MAAM;AACnC,QAAI,OAAO,mBAAmB,OAAO,WAAW;AAC9C,YAAM,iBAAiB,IAAI,QAAQ,MAAM;AAAA,IAC3C;AAAA,EACF;AACF;AAEA,eAAO,kBAAyC,IAA0B;AACxE,QAAM,wBAAwB,EAAE,EAAE;AACpC;AAEA,SAAS,iBAAiB,IAAW,QAAsB,QAA8B;AACvF,KAAG,gBAAgB,iBAAiB;AAAA,IAClC,aAAa;AAAA,IACb,SAAS,eAAe,OAAO,OAAO,MAAM,YAAY;AACtD,YAAM,SAAS,MAAM,OAAO,OAAO;AACnC,cAAQ,OAAO,UAAU,OAAO,KAAK,YAAY,WAAW,OAAO,OAAO,eAAe,IAAI,OAAO,KAAK,YAAY,SAAS;AAAA,IAChI,CAAC;AAAA,EACH,CAAC;AAED,KAAG,gBAAgB,iBAAiB;AAAA,IAClC,aAAa;AAAA,IACb,SAAS,eAAe,OAAO,MAAM,MAAM,YAAY;AACrD,YAAM,QAAQ,KAAK,KAAK;AACxB,UAAI,CAAC,OAAO;AACV,gBAAQ,OAAO,iCAAiC,SAAS;AACzD;AAAA,MACF;AACA,YAAM,SAAS,MAAM,OAAO,OAAO,OAAO,QAAQ,YAAY,QAAQ,GAAG;AACzE,cAAQ,OAAO,YAAY,OAAO,WAAW,uBAAuB,iBAAiB,GAAG,MAAM;AAAA,IAChG,CAAC;AAAA,EACH,CAAC;AAED,KAAG,gBAAgB,mBAAmB;AAAA,IACpC,aAAa;AAAA,IACb,SAAS,eAAe,OAAO,MAAM,MAAM,YAAY;AACrD,YAAM,UAAU,KAAK,KAAK;AAC1B,UAAI,CAAC,SAAS;AACZ,gBAAQ,OAAO,oCAAoC,SAAS;AAC5D;AAAA,MACF;AACA,YAAM,OAAO,YAAY,SAAS,QAAQ,UAAU;AACpD,cAAQ,OAAO,wBAAwB,SAAS;AAAA,IAClD,CAAC;AAAA,EACH,CAAC;AAED,KAAG,gBAAgB,qBAAqB;AAAA,IACtC,aAAa;AAAA,IACb,SAAS,eAAe,OAAO,MAAM,MAAM,YAAY;AACrD,YAAM,QAAQ,KAAK,KAAK;AACxB,UAAI,CAAC,OAAO;AACV,gBAAQ,OAAO,qCAAqC,SAAS;AAC7D;AAAA,MACF;AACA,YAAM,SAAS,MAAM,OAAO,UAAU,OAAO,QAAQ,UAAU;AAC/D,cAAQ,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,MAAM;AAAA,IACxD,CAAC;AAAA,EACH,CAAC;AAED,KAAG,gBAAgB,cAAc;AAAA,IAC/B,aAAa;AAAA,IACb,SAAS,eAAe,OAAO,OAAO,MAAM,YAAY;AACtD,YAAM,SAAS,MAAM,OAAO,cAAc,QAAQ,UAAU;AAC5D,cAAQ,OAAO,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,MAAM;AAAA,IACxD,CAAC;AAAA,EACH,CAAC;AAED,KAAG,gBAAgB,kBAAkB;AAAA,IACnC,aAAa;AAAA,IACb,SAAS,eAAe,OAAO,OAAO,MAAM,YAAY;AACtD,cAAQ,UAAU;AAClB,cAAQ,OAAO,wBAAwB,MAAM;AAAA,IAC/C,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,eACP,SAC2C;AAC3C,SAAO,OAAO,MAAM,QAAQ;AAC1B,UAAM,UAAU,kBAAkB,GAAG;AACrC,QAAI,CAAC,QAAS;AACd,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,OAAO;AAAA,IAClC,SAAS,KAAK;AACZ,cAAQ,OAAO,0BAA0B,aAAa,GAAG,CAAC,IAAI,SAAS;AAAA,IACzE;AAAA,EACF;AACF;AAEA,eAAe,iBAAiB,IAAW,QAAsB,QAAuC;AACtG,MAAI,QAAmB,CAAC;AACxB,MAAI;AACF,YAAQ,MAAM,OAAO,aAAa,EAAE,WAAW,OAAO,wBAAwB,CAAC;AAAA,EACjF,QAAQ;AACN;AAAA,EACF;AACA,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,KAAK,KAAK,WAAW,SAAS,EAAG;AACtC,UAAM,aAAa,KAAK,KAAK,QAAQ,aAAa,SAAS,EAAE,QAAQ,kBAAkB,GAAG;AAC1F,OAAG,aAAa;AAAA,MACd,MAAM;AAAA,MACN,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK,eAAe,QAAQ,KAAK,IAAI;AAAA,MAClD,YAAY,yBAAyB,KAAK,WAAW;AAAA,MACrD,MAAM,QAAQ,aAAqB,QAAiC,SAAkC,WAAoB,KAAU;AAClI,cAAM,UAAU,kBAAkB,GAAG;AACrC,YAAI,CAAC,SAAS;AACZ,iBAAO;AAAA,YACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,kEAAkE,CAAC;AAAA,YACnG,SAAS,EAAE,SAAS,MAAM,QAAQ,gBAAgB;AAAA,UACpD;AAAA,QACF;AACA,cAAM,aAAa,+BAA+B,UAAU,CAAC,CAAC;AAC9D,cAAM,SAAS,MAAM,OAAO,QAAQ,KAAK,MAAM;AAAA,UAC7C,GAAG;AAAA,UACH,YAAY,QAAQ;AAAA,UACpB,WAAW,OAAO;AAAA,UAClB,KAAK,QAAQ;AAAA,QACf,CAAC;AACD,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,CAAC;AAAA,UACjE,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEO,SAAS,yBAAyB,aAA+B;AACtE,SAAO,KAAK,OAAO,8BAA8B,WAAW,CAAC;AAC/D;AAEO,SAAS,8BAA8B,aAA+C;AAC3F,MAAI,CAAC,SAAS,WAAW,GAAG;AAC1B,WAAO,EAAE,MAAM,UAAU,YAAY,CAAC,GAAG,sBAAsB,KAAK;AAAA,EACtE;AACA,SAAO,4BAA4B,WAAW;AAChD;AAEA,SAAS,4BAA4B,OAAyB;AAC5D,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,UAAU,4BAA4B,KAAK,CAAC;AAAA,EAChE;AACA,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO;AAAA,EACT;AACA,QAAM,SAAkC,EAAE,GAAG,MAAM;AACnD,MAAI,SAAS,MAAM,UAAU,GAAG;AAC9B,UAAM,aAAsC,CAAC;AAC7C,eAAW,CAAC,KAAK,QAAQ,KAAK,OAAO,QAAQ,MAAM,UAAU,GAAG;AAC9D,UAAI,qBAAqB,IAAI,GAAG,EAAG;AACnC,iBAAW,GAAG,IAAI,4BAA4B,QAAQ;AAAA,IACxD;AACA,WAAO,aAAa;AAAA,EACtB;AACA,MAAI,MAAM,QAAQ,MAAM,QAAQ,GAAG;AACjC,WAAO,WAAW,MAAM,SAAS;AAAA,MAC/B,CAAC,UAAU,OAAO,UAAU,YAAY,CAAC,qBAAqB,IAAI,KAAK;AAAA,IACzE;AAAA,EACF;AACA,aAAW,OAAO,CAAC,SAAS,wBAAwB,KAAK,GAAY;AACnE,QAAI,SAAS,MAAM,GAAG,CAAC,GAAG;AACxB,aAAO,GAAG,IAAI,4BAA4B,MAAM,GAAG,CAAC;AAAA,IACtD;AAAA,EACF;AACA,aAAW,OAAO,CAAC,SAAS,SAAS,OAAO,GAAY;AACtD,QAAI,MAAM,QAAQ,MAAM,GAAG,CAAC,GAAG;AAC7B,aAAO,GAAG,IAAI,MAAM,GAAG,EAAE,IAAI,CAAC,UAAU,4BAA4B,KAAK,CAAC;AAAA,IAC5E;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,+BAA+B,OAAyB;AACtE,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,CAAC,UAAU,+BAA+B,KAAK,CAAC;AAAA,EACnE;AACA,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB,WAAO;AAAA,EACT;AACA,QAAM,YAAqC,CAAC;AAC5C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,qBAAqB,IAAI,GAAG,EAAG;AACnC,cAAU,GAAG,IAAI,+BAA+B,KAAK;AAAA,EACvD;AACA,SAAO;AACT;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,CAAC,CAAC,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AACrE;AAEA,SAAS,cAAc,SAA2B;AAChD,SAAO,SAAS,OAAO,KAAK,QAAQ,SAAS;AAC/C;AAEA,SAAS,gBAAgB,YAAoB,QAAoF;AAC/H,MAAI,QAAQ,OAAO,IAAI,UAAU;AACjC,MAAI,CAAC,OAAO;AACV,YAAQ;AAAA,MACN,gBAAgB,oBAAI,IAAY;AAAA,MAChC,wBAAwB,oBAAI,IAAoB;AAAA,MAChD,uBAAuB;AAAA,IACzB;AACA,WAAO,IAAI,YAAY,KAAK;AAC5B,uBAAmB,MAAM;AAAA,EAC3B;AACA,SAAO,EAAE,YAAY,MAAM;AAC7B;AAEA,SAAS,mBAAmB,QAA2C;AACrE,SAAO,OAAO,OAAO,oBAAoB;AACvC,UAAM,SAAS,OAAO,KAAK,EAAE,KAAK,EAAE;AACpC,QAAI,OAAO,WAAW,SAAU;AAChC,WAAO,OAAO,MAAM;AAAA,EACtB;AACF;AAEA,eAAsB,gBACpB,KACA,QACA,aACA,gBACA,wBACe;AACf,QAAM,UAAU,kBAAkB,GAAG;AACrC,MAAI,CAAC,QAAS;AACd,QAAM,0BAA0B,SAAS,QAAQ,aAAa,gBAAgB,sBAAsB;AACtG;AAEA,eAAe,0BACb,SACA,QACA,aACA,gBACA,wBACe;AACf,QAAM,WAA6B,CAAC;AACpC,QAAM,gBAAgB,oBAAI,IAAY;AACtC,aAAW,OAAO,aAAa;AAC7B,UAAM,UAAU,iBAAiB,GAAG;AACpC,QAAI,CAAC,QAAS;AACd,UAAM,OAAO,yBAAyB,SAAS,QAAQ,UAAU;AACjE,QAAI,SAAS,eAAe,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,GAAI;AACnE,QAAI,KAAM,eAAc,IAAI,IAAI;AAChC,aAAS,KAAK,OAAO;AAAA,EACvB;AACA,MAAI,SAAS,WAAW,EAAG;AAC3B,MAAI;AACF,UAAM,OAAO,QAAQ,QAAQ,YAAY,QAAQ,KAAK,QAAQ;AAC9D,eAAW,QAAQ,cAAe,sBAAqB,gBAAgB,IAAI;AAC3E,QAAI,wBAAwB;AAC1B,iBAAW,WAAW,UAAU;AAC9B,sCAA8B,wBAAwB,cAAc,SAAS,QAAQ,UAAU,CAAC;AAAA,MAClG;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,OAAO,0BAA0B,aAAa,GAAG,CAAC,IAAI,SAAS;AAAA,EACzE;AACF;AAEO,SAAS,uBAAuB,aAA0B;AAC/D,QAAM,kBAAkB,OAAO,YAAY,oBAAoB,WAC3D,YAAY,gBAAgB,KAAK,IACjC;AACJ,QAAM,WAAW;AAAA,IACf,GAAI,MAAM,QAAQ,YAAY,mBAAmB,IAAI,YAAY,sBAAsB,CAAC;AAAA,IACxF,GAAI,MAAM,QAAQ,YAAY,kBAAkB,IAAI,YAAY,qBAAqB,CAAC;AAAA,EACxF;AACA,QAAM,aAAa,kBAAkB,UAAU,IAAK;AACpD,QAAM,UAAU,2BAA2B,WAAW;AAEtD,MACE,CAAC,mBACD,CAAC,cACD,QAAQ,UAAU,WAAW,KAC7B,QAAQ,cAAc,WAAW,GACjC;AACA,WAAO;AAAA,EACT;AAEA,QAAM,WAAqB;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,MAAI,gBAAiB,UAAS,KAAK,IAAI,uBAAuB,eAAe;AAC7E,MAAI,WAAY,UAAS,KAAK,IAAI,2BAA2B,UAAU;AACvE,MAAI,QAAQ,UAAU,SAAS,EAAG,UAAS,KAAK,IAAI,gBAAgB,GAAG,QAAQ,WAAW,eAAe;AACzG,MAAI,QAAQ,cAAc,SAAS,EAAG,UAAS,KAAK,IAAI,oBAAoB,GAAG,QAAQ,eAAe,mBAAmB;AACzH,SAAO,SAAS,KAAK,IAAI;AAC3B;AAEA,SAAS,2BAA2B,aAAoE;AACtG,QAAM,UAAU,aAAa;AAC7B,QAAM,OAAO,SAAS,gBAAgB,MAAM,MAAM,KAAK,QAAQ,IAAI,EAAE,OAAO,QAAQ,IAAI,CAAC;AACzF,QAAM,SAAS,SAAS,kBAAkB,MAAM,MAAM,KAAK,QAAQ,MAAM,EAAE,OAAO,QAAQ,IAAI,CAAC;AAC/F,QAAM,UAAU,SAAS,mBAAmB,MAAM,MAAM,KAAK,QAAQ,OAAO,EAAE,OAAO,QAAQ,IAAI,CAAC;AAClG,QAAM,WAAW,oBAAI,IAAI,CAAC,GAAG,QAAQ,GAAG,OAAO,CAAC;AAChD,SAAO;AAAA,IACL,WAAW,KAAK,OAAO,CAAC,SAAS,CAAC,SAAS,IAAI,IAAI,CAAC,EAAE,KAAK;AAAA,IAC3D,eAAe,MAAM,KAAK,QAAQ,EAAE,KAAK;AAAA,EAC3C;AACF;AAEA,SAAS,qBAAqB,SAA4B,gBAAmC;AAC3F,aAAW,SAAS,QAAQ,SAAS;AACnC,QAAI,OAAO,SAAS,YAAY,MAAM,eAAe,kBAAmB;AACxE,UAAM,SAAS,MAAM,MAAM;AAC3B,QAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,iBAAW,QAAQ,QAAQ;AACzB,YAAI,OAAO,SAAS,SAAU,sBAAqB,gBAAgB,IAAI;AAAA,MACzE;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,qBAAqB,gBAA6B,MAAoB;AAC7E,MAAI,eAAe,IAAI,IAAI,EAAG;AAC9B,SAAO,eAAe,QAAQ,qBAAqB;AACjD,UAAM,SAAS,eAAe,KAAK,EAAE,KAAK,EAAE;AAC5C,QAAI,OAAO,WAAW,SAAU;AAChC,mBAAe,OAAO,MAAM;AAAA,EAC9B;AACA,iBAAe,IAAI,IAAI;AACzB;AAEA,SAAS,8BAA8B,wBAA6C,KAAmB;AACrG,yBAAuB,IAAI,MAAM,uBAAuB,IAAI,GAAG,KAAK,KAAK,CAAC;AAC5E;AAEA,SAAS,6BAA6B,wBAA6C,KAAsB;AACvG,QAAM,QAAQ,uBAAuB,IAAI,GAAG,KAAK;AACjD,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,UAAU,EAAG,wBAAuB,OAAO,GAAG;AAAA,MAC7C,wBAAuB,IAAI,KAAK,QAAQ,CAAC;AAC9C,SAAO;AACT;AAEA,SAAS,+BACP,YACA,aACA,wBACW;AACX,MAAI,uBAAuB,SAAS,EAAG,QAAO;AAC9C,QAAM,aAAwB,CAAC;AAC/B,aAAW,OAAO,aAAa;AAC7B,UAAM,UAAU,iBAAiB,GAAG;AACpC,QAAI,WAAW,6BAA6B,wBAAwB,cAAc,SAAS,UAAU,CAAC,GAAG;AACvG;AAAA,IACF;AACA,eAAW,KAAK,GAAG;AAAA,EACrB;AACA,SAAO;AACT;AAEA,SAAS,cAAc,SAAyB,YAA4B;AAC1E,SAAO,oBAAoB,SAAS,YAAY,aAAa;AAC/D;AAEA,SAAS,qBAAqB,IAAW,gBAAmC;AAC1E,QAAM,WAAW,MAAM,KAAK,cAAc,EAAE,MAAM,CAAC,mBAAmB;AACtE,KAAG,YAAY,mBAAmB;AAAA,IAChC,gBAAgB;AAAA,IAChB,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,EACrC,CAAC;AACH;AAEA,eAAe,UAAU,SAA4B,QAAsB,QAAuC;AAChH,MAAI;AACF,UAAM,OAAO,OAAO,EAAE,WAAW,OAAO,wBAAwB,CAAC;AACjE,YAAQ,UAAU,UAAU,UAAU,OAAO,YAAY,IAAI,OAAO,SAAS,MAAM,OAAO,EAAE;AAAA,EAC9F,QAAQ;AACN,YAAQ,UAAU,UAAU,gBAAgB;AAAA,EAC9C;AACF;AAEA,SAAS,kBAAkB,KAAU,UAAoC,CAAC,GAA6B;AACrG,QAAM,aAAa,0BAA0B,GAAG;AAChD,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,MAAM,eAAe,MAAM,KAAK,KAAK,EAAE;AAC7C,QAAM,QAAQ,SAAS,MAAM,KAAK,OAAO,MAAS,MAAM;AACxD,QAAM,KAAK,QAAQ,SAAY,SAAS,MAAM,KAAK,IAAI,MAAS;AAChE,QAAM,UAAU,SAAS,MAAM,KAAK,SAAS,MAAS;AACtD,QAAM,wBAAwB,QAAQ,0BAA0B;AAChE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS,wBAAwB,YAAY,GAAG,IAAI,CAAC;AAAA,IACrD,QAAQ,wBAAwB,WAAW,GAAG,IAAI,CAAC;AAAA,IACnD,QAAQ,aAAa,IAAI,KAAK;AAAA,IAC9B,WAAW,iBAAiB,IAAI,KAAK;AAAA,IACrC,SAAS,OAAO,YAAY,aAAa,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,EACrE;AACF;AAEA,SAAS,0BAA0B,KAAyB;AAC1D,MAAI;AACF,WAAO,sBAAsB,GAAG;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,IAAa,OAA0B;AAC3D,MAAI,SAAS,CAAC,SAAS,EAAE,KAAK,OAAO,GAAG,WAAW,YAAY;AAC7D,WAAO,MAAM;AAAA,EACf;AACA,QAAM,WAAW,GAAG;AACpB,SAAO,CAAC,SAAS,UAAU;AACzB,QAAI;AACF,eAAS,KAAK,IAAI,SAAS,KAAK;AAAA,IAClC,QAAQ;AAAA,IAGR;AAAA,EACF;AACF;AAEA,SAAS,iBAAiB,IAAa,OAAgD;AACrF,MAAI,SAAS,CAAC,SAAS,EAAE,KAAK,OAAO,GAAG,cAAc,YAAY;AAChE,WAAO,MAAM;AAAA,EACf;AACA,QAAM,cAAc,GAAG;AACvB,SAAO,CAAC,KAAK,UAAU;AACrB,QAAI;AACF,kBAAY,KAAK,IAAI,KAAK,KAAK;AAAA,IACjC,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAEA,SAAS,SAAY,MAAe,UAAgB;AAClD,MAAI;AACF,WAAO,KAAK;AAAA,EACd,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,eAAe,MAAqB,UAA0B;AACrE,QAAM,QAAQ,SAAS,MAAM,QAAQ;AACrC,SAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;AAEA,SAAS,YAAY,KAAiB;AACpC,MAAI;AACF,UAAM,UAAU,IAAI,gBAAgB,aAAa;AACjD,WAAO,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC;AAAA,EAC7C,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,WAAW,KAAiB;AACnC,MAAI;AACF,UAAM,SAAS,IAAI,gBAAgB,YAAY;AAC/C,WAAO,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC;AAAA,EAC3C,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,gCAAgC,QAA0B;AACjE,QAAM,WAAsB,CAAC;AAC7B,aAAW,SAAS,QAAQ;AAC1B,UAAM,UAAU,yBAAyB,KAAK;AAC9C,QAAI,QAAS,UAAS,KAAK,OAAO;AAAA,EACpC;AACA,SAAO;AACT;AAEA,SAAS,yBAAyB,OAA4B;AAC5D,QAAM,UAAU,OAAO;AACvB,MAAI,CAAC,WAAW,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,EAAG,QAAO,WAAW;AAEzF,QAAM,SAAS,SAAS,KAAK,IAAI,QAAQ,CAAC;AAC1C,QAAM,WAAoC,EAAE,GAAI,QAAoC;AACpF,wBAAsB,UAAU,WAAW,OAAO,MAAM,OAAO,WAAW,OAAO,QAAQ;AACzF,wBAAsB,UAAU,aAAa,OAAO,SAAS;AAC7D,wBAAsB,UAAU,aAAa,OAAO,aAAa,OAAO,UAAU;AAClF,SAAO;AACT;AAEA,SAAS,sBAAsB,QAAiC,OAAe,OAAsB;AACnG,MAAI,OAAO,KAAK,MAAM,OAAW;AACjC,MAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AACjD,WAAO,KAAK,IAAI;AAChB;AAAA,EACF;AACA,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,GAAG;AACvD,WAAO,KAAK,IAAI;AAAA,EAClB;AACF;AAEA,SAAS,YAAY,OAAe,QAAwB;AAC1D,MAAI,MAAM,UAAU,OAAQ,QAAO;AACnC,MAAI,UAAU,kBAAkB,OAAQ,QAAO,kBAAkB,MAAM,GAAG,MAAM;AAChF,SAAO,GAAG,MAAM,MAAM,GAAG,SAAS,kBAAkB,MAAM,CAAC,GAAG,iBAAiB;AACjF;AAEA,SAAS,aAAa,KAAsB;AAC1C,SAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACxD;AAEA,SAAS,iBAAiB,OAA+B;AACvD,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,SAAS,IAAI,QAAQ;AACrF;AAEA,SAAS,SAAS,OAAiC;AACjD,SAAO,OAAO,UAAU;AAC1B;","names":["remnicPiExtension"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/client.ts","../src/messages.ts"],"sourcesContent":["import { Type, type TSchema } from \"@sinclair/typebox\";\n\nimport { loadConfig, type LoadConfigOptions, type RemnicPiConfig } from \"./config.js\";\nimport { RemnicClient, isTransientNetworkError, type McpTool, type ObserveMessage } from \"./client.js\";\nimport {\n hashObservedMessage,\n latestUserRecallTarget,\n observedMessageDedupeKey,\n sessionKeyFromContext,\n summarizeMessages,\n textFromMessage,\n toObserveMessage,\n} from \"./messages.js\";\n\ntype PiApi = {\n on(event: string, handler: (event: any, ctx: any) => unknown | Promise<unknown>): void;\n registerCommand(name: string, options: { description?: string; handler: (args: string, ctx: any) => Promise<void> }): void;\n registerTool(tool: Record<string, unknown>): void;\n appendEntry<T = unknown>(customType: string, data?: T): void;\n};\n\nexport interface RemnicPiExtensionOptions extends LoadConfigOptions {\n config?: RemnicPiConfig;\n}\n\nconst STATE_CUSTOM_TYPE = \"remnic_state\";\nconst MAX_OBSERVED_HASHES = 2000;\nconst MAX_SESSION_STATES = 50;\nconst MAX_CONTEXT_CHARS = 12000;\nconst TRUNCATION_NOTICE = \"\\n\\n[Remnic context truncated]\";\nconst SESSION_OWNED_FIELDS = new Set([\"sessionKey\", \"namespace\", \"cwd\"]);\n\ntype PiSessionState = {\n observedHashes: Set<string>;\n liveObservedReplayKeys: Map<string, number>;\n lastInjectedRecallKey: string;\n};\n\ntype NotifyLevel = \"info\" | \"success\" | \"warning\" | \"error\";\ntype NotifyFn = (message: string, level: NotifyLevel) => void;\n\ntype PiContextSnapshot = {\n sessionKey: string;\n cwd: string;\n entries: any[];\n branch: any[];\n notify: NotifyFn;\n setStatus: (key: string, value: string) => void;\n compact?: () => unknown;\n};\ntype PiContextSnapshotOptions = {\n includeSessionHistory?: boolean;\n};\n\nexport function createRemnicPiExtension(options: RemnicPiExtensionOptions = {}) {\n const config = options.config ?? loadConfig(options);\n const client = new RemnicClient(config);\n const sessionStates = new Map<string, PiSessionState>();\n\n return async function remnicPiExtension(pi: PiApi): Promise<void> {\n pi.on(\"session_start\", async (_event, ctx) => {\n const session = snapshotPiContext(ctx, { includeSessionHistory: true });\n if (!session) return;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n restoreObservedState(session, state.observedHashes);\n if (!config.statusEnabled) return;\n await setStatus(session, client, config);\n });\n\n pi.on(\"context\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.recallEnabled || !config.authToken) return;\n // Circuit breaker: skip recall fast while the daemon is known-down so a\n // dead host doesn't block every turn on a doomed request (#1626).\n if (!client.isReachable()) return;\n const recallTarget = latestUserRecallTarget(Array.isArray(event.messages) ? event.messages : []);\n if (!recallTarget) return;\n const { query } = recallTarget;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n if (recallTarget.dedupeKey === state.lastInjectedRecallKey) return;\n\n try {\n const recalled = await client.recall(query, session.sessionKey, session.cwd, {\n timeoutMs: config.turnRequestTimeoutMs,\n });\n client.markReachable();\n const context = trimContext(recalled.context ?? \"\", config.recallBudgetChars);\n if (!context) return;\n state.lastInjectedRecallKey = recallTarget.dedupeKey;\n return {\n messages: [\n ...event.messages,\n {\n role: \"user\",\n content: [{ type: \"text\", text: `Remnic recalled context for this turn:\\n\\n${context}` }],\n remnicInjected: true,\n timestamp: Date.now(),\n },\n ],\n };\n } catch (err) {\n // Only trip the breaker when the daemon is genuinely unreachable\n // (timeout or connection-level failure) — not on a transient HTTP\n // error, so a one-off failure still retries on the next turn (#1626).\n if (isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);\n session.notify(`Remnic recall unavailable: ${errorMessage(err)}`, \"warning\");\n }\n });\n\n pi.on(\"message_end\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.observeEnabled || !isUserMessage(event.message)) return;\n const { state } = getSessionState(session.sessionKey, sessionStates);\n await observeMessagesForSession(session, client, [event.message], state.observedHashes, state.liveObservedReplayKeys, config);\n });\n\n pi.on(\"turn_end\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.observeEnabled) return;\n const messages = [event.message, ...(Array.isArray(event.toolResults) ? event.toolResults : [])];\n const { state } = getSessionState(session.sessionKey, sessionStates);\n await observeMessagesForSession(session, client, messages, state.observedHashes, state.liveObservedReplayKeys, config);\n });\n\n pi.on(\"session_shutdown\", async (_event, ctx) => {\n const session = snapshotPiContext(ctx, { includeSessionHistory: true });\n if (!session) return;\n const { sessionKey, state } = getSessionState(session.sessionKey, sessionStates);\n if (config.observeEnabled) {\n const branchMessages = branchMessagesWithEntryIdentity(session.branch);\n const unobservedBranchMessages = skipLiveObservedReplayMessages(session.sessionKey, branchMessages, state.liveObservedReplayKeys);\n if (unobservedBranchMessages.length > 0) {\n await observeMessagesForSession(session, client, unobservedBranchMessages, state.observedHashes, undefined, config, true);\n }\n }\n persistObservedState(pi, state.observedHashes);\n sessionStates.delete(sessionKey);\n });\n\n pi.on(\"session_before_compact\", async (event, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n if (!config.compactionEnabled || !config.authToken) return;\n const preparation = event.preparation ?? {};\n try {\n await client.lcmCompactionFlush(session.sessionKey);\n } catch (err) {\n session.notify(`Remnic LCM flush failed: ${errorMessage(err)}`, \"warning\");\n return;\n }\n\n const tokensBefore = finiteTokenCount(preparation.tokensBefore);\n const tokensAfter = finiteTokenCount(preparation.tokensAfter);\n if (tokensBefore !== null && tokensAfter !== null) {\n try {\n await client.lcmCompactionRecord(session.sessionKey, tokensBefore, tokensAfter);\n } catch (err) {\n session.notify(`Remnic LCM compaction token record failed: ${errorMessage(err)}`, \"warning\");\n }\n }\n\n const summary = buildCompactionSummary(preparation);\n if (!summary.trim()) return;\n try {\n await client.contextCheckpoint(session.sessionKey, summary);\n } catch (err) {\n session.notify(`Remnic context checkpoint failed: ${errorMessage(err)}`, \"warning\");\n }\n const details = fileDetailsFromPreparation(preparation);\n return {\n compaction: {\n summary,\n firstKeptEntryId: preparation.firstKeptEntryId,\n tokensBefore: preparation.tokensBefore,\n details: {\n ...details,\n remnic: { version: 1, source: \"pi\" },\n },\n },\n };\n });\n\n registerCommands(pi, client, config);\n if (config.mcpToolsEnabled && config.authToken) {\n await registerMcpTools(pi, client, config);\n }\n };\n}\n\nexport default async function remnicPiExtension(pi: PiApi): Promise<void> {\n await createRemnicPiExtension()(pi);\n}\n\nfunction registerCommands(pi: PiApi, client: RemnicClient, config: RemnicPiConfig): void {\n pi.registerCommand(\"remnic-status\", {\n description: \"Check Remnic daemon status\",\n handler: commandHandler(async (_args, _ctx, session) => {\n const health = await client.health();\n // The daemon responded (any HTTP result), so clear any stale cooldown a\n // prior timeout left on the shared client (cursor review).\n client.markReachable();\n session.notify(`Remnic ${health.ok ? \"healthy\" : \"unhealthy\"} at ${config.remnicDaemonUrl}`, health.ok ? \"success\" : \"warning\");\n }),\n });\n\n pi.registerCommand(\"remnic-recall\", {\n description: \"Recall Remnic context for a query\",\n handler: commandHandler(async (args, _ctx, session) => {\n const query = args.trim();\n if (!query) {\n session.notify(\"Usage: /remnic-recall <query>\", \"warning\");\n return;\n }\n // Pass the general request budget so requestWithRetry shares ONE deadline\n // across retries (total <= requestTimeoutMs) instead of looping through\n // observeMaxRetries full timeouts and blocking the interactive command\n // for several minutes on a flaky connection (cursor review).\n const result = await client.recall(query, session.sessionKey, session.cwd, {\n timeoutMs: config.requestTimeoutMs,\n });\n // The daemon responded, so clear any stale cooldown a prior timeout left\n // on the shared client (cursor review).\n client.markReachable();\n session.notify(trimContext(result.context ?? \"(no Remnic context)\", MAX_CONTEXT_CHARS), \"info\");\n }),\n });\n\n pi.registerCommand(\"remnic-remember\", {\n description: \"Store a Remnic memory\",\n handler: commandHandler(async (args, _ctx, session) => {\n const content = args.trim();\n if (!content) {\n session.notify(\"Usage: /remnic-remember <memory>\", \"warning\");\n return;\n }\n await client.storeMemory(content, session.sessionKey);\n session.notify(\"Stored Remnic memory\", \"success\");\n }),\n });\n\n pi.registerCommand(\"remnic-lcm-search\", {\n description: \"Search Remnic LCM archived Pi context\",\n handler: commandHandler(async (args, _ctx, session) => {\n const query = args.trim();\n if (!query) {\n session.notify(\"Usage: /remnic-lcm-search <query>\", \"warning\");\n return;\n }\n const result = await client.lcmSearch(query, session.sessionKey);\n session.notify(JSON.stringify(result, null, 2), \"info\");\n }),\n });\n\n pi.registerCommand(\"remnic-why\", {\n description: \"Explain the last Remnic recall\",\n handler: commandHandler(async (_args, _ctx, session) => {\n const result = await client.recallExplain(session.sessionKey);\n session.notify(JSON.stringify(result, null, 2), \"info\");\n }),\n });\n\n pi.registerCommand(\"remnic-compact\", {\n description: \"Trigger Pi compaction with Remnic LCM coordination\",\n handler: commandHandler(async (_args, _ctx, session) => {\n session.compact?.();\n session.notify(\"Compaction requested\", \"info\");\n }),\n });\n}\n\nfunction commandHandler(\n handler: (args: string, ctx: any, session: PiContextSnapshot) => Promise<void>,\n): (args: string, ctx: any) => Promise<void> {\n return async (args, ctx) => {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n try {\n await handler(args, ctx, session);\n } catch (err) {\n session.notify(`Remnic command failed: ${errorMessage(err)}`, \"warning\");\n }\n };\n}\n\nasync function registerMcpTools(pi: PiApi, client: RemnicClient, config: RemnicPiConfig): Promise<void> {\n let tools: McpTool[] = [];\n try {\n tools = await client.mcpListTools({ timeoutMs: config.startupRequestTimeoutMs });\n } catch {\n return;\n }\n for (const tool of tools) {\n if (!tool.name.startsWith(\"remnic.\")) continue;\n const piToolName = tool.name.replace(/^remnic\\./, \"remnic_\").replace(/[^a-zA-Z0-9_]/g, \"_\");\n pi.registerTool({\n name: piToolName,\n label: tool.name,\n description: tool.description ?? `Call ${tool.name}`,\n parameters: toPiToolParametersSchema(tool.inputSchema),\n async execute(_toolCallId: string, params: Record<string, unknown>, _signal: AbortSignal | undefined, _onUpdate: unknown, ctx: any) {\n const session = snapshotPiContext(ctx);\n if (!session) {\n return {\n content: [{ type: \"text\", text: \"Remnic tool skipped because the Pi context is no longer active.\" }],\n details: { skipped: true, reason: \"stale_context\" },\n };\n }\n const safeParams = stripSessionOwnedRuntimeFields(params ?? {}) as Record<string, unknown>;\n const result = await client.mcpTool(tool.name, {\n ...safeParams,\n sessionKey: session.sessionKey,\n namespace: config.namespace,\n cwd: session.cwd,\n });\n return {\n content: [{ type: \"text\", text: JSON.stringify(result, null, 2) }],\n details: result,\n };\n },\n });\n }\n}\n\nexport function toPiToolParametersSchema(inputSchema: unknown): TSchema {\n return Type.Unsafe(stripSessionOwnedSchemaFields(inputSchema));\n}\n\nexport function stripSessionOwnedSchemaFields(inputSchema: unknown): Record<string, unknown> {\n if (!isRecord(inputSchema)) {\n return { type: \"object\", properties: {}, additionalProperties: true };\n }\n return stripSessionOwnedSchemaNode(inputSchema) as Record<string, unknown>;\n}\n\nfunction stripSessionOwnedSchemaNode(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripSessionOwnedSchemaNode(entry));\n }\n if (!isRecord(value)) {\n return value;\n }\n const schema: Record<string, unknown> = { ...value };\n if (isRecord(value.properties)) {\n const properties: Record<string, unknown> = {};\n for (const [key, property] of Object.entries(value.properties)) {\n if (SESSION_OWNED_FIELDS.has(key)) continue;\n properties[key] = stripSessionOwnedSchemaNode(property);\n }\n schema.properties = properties;\n }\n if (Array.isArray(value.required)) {\n schema.required = value.required.filter(\n (field) => typeof field !== \"string\" || !SESSION_OWNED_FIELDS.has(field),\n );\n }\n for (const key of [\"items\", \"additionalProperties\", \"not\"] as const) {\n if (isRecord(value[key])) {\n schema[key] = stripSessionOwnedSchemaNode(value[key]);\n }\n }\n for (const key of [\"oneOf\", \"anyOf\", \"allOf\"] as const) {\n if (Array.isArray(value[key])) {\n schema[key] = value[key].map((entry) => stripSessionOwnedSchemaNode(entry));\n }\n }\n return schema;\n}\n\nexport function stripSessionOwnedRuntimeFields(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripSessionOwnedRuntimeFields(entry));\n }\n if (!isRecord(value)) {\n return value;\n }\n const sanitized: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n if (SESSION_OWNED_FIELDS.has(key)) continue;\n sanitized[key] = stripSessionOwnedRuntimeFields(child);\n }\n return sanitized;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isUserMessage(message: unknown): boolean {\n return isRecord(message) && message.role === \"user\";\n}\n\nfunction getSessionState(sessionKey: string, states: Map<string, PiSessionState>): { sessionKey: string; state: PiSessionState } {\n let state = states.get(sessionKey);\n if (!state) {\n state = {\n observedHashes: new Set<string>(),\n liveObservedReplayKeys: new Map<string, number>(),\n lastInjectedRecallKey: \"\",\n };\n states.set(sessionKey, state);\n pruneSessionStates(states);\n }\n return { sessionKey, state };\n}\n\nfunction pruneSessionStates(states: Map<string, PiSessionState>): void {\n while (states.size > MAX_SESSION_STATES) {\n const oldest = states.keys().next().value;\n if (typeof oldest !== \"string\") return;\n states.delete(oldest);\n }\n}\n\nexport async function observeMessages(\n ctx: any,\n client: RemnicClient,\n rawMessages: unknown[],\n observedHashes: Set<string>,\n liveObservedReplayKeys?: Map<string, number>,\n): Promise<void> {\n const session = snapshotPiContext(ctx);\n if (!session) return;\n await observeMessagesForSession(session, client, rawMessages, observedHashes, liveObservedReplayKeys);\n}\n\nasync function observeMessagesForSession(\n session: PiContextSnapshot,\n client: RemnicClient,\n rawMessages: unknown[],\n observedHashes: Set<string>,\n liveObservedReplayKeys?: Map<string, number>,\n config?: RemnicPiConfig,\n forceAttempt = false,\n): Promise<void> {\n const messages: ObserveMessage[] = [];\n const pendingHashes = new Set<string>();\n for (const raw of rawMessages) {\n const message = toObserveMessage(raw);\n if (!message) continue;\n const hash = observedMessageDedupeKey(message, session.sessionKey);\n if (hash && (observedHashes.has(hash) || pendingHashes.has(hash))) continue;\n if (hash) pendingHashes.add(hash);\n messages.push(message);\n }\n if (messages.length === 0) return;\n // Circuit breaker: when config is wired (the live Pi handlers always pass\n // it), skip observe fast while the daemon is known-down so a dead host\n // doesn't burn the full per-turn budget on every turn (#1626). Shutdown is\n // the exception — it is the last chance to observe the branch before the\n // session tears down, so force the attempt even mid-cooldown; a failure\n // still trips the breaker normally (codex review).\n if (config && !forceAttempt && !client.isReachable()) return;\n // Live turn hooks are bounded by the per-turn budget to protect the host's\n // ~30s handler window (#1626). Shutdown is teardown with no such constraint,\n // so the forced replay uses the general request budget — otherwise a large\n // unobserved branch would time out exactly when forceAttempt tried to save it\n // (cursor review).\n const observeOptions = config\n ? { timeoutMs: forceAttempt ? config.requestTimeoutMs : config.turnRequestTimeoutMs }\n : undefined;\n try {\n await client.observe(session.sessionKey, session.cwd, messages, observeOptions);\n if (config) client.markReachable();\n for (const hash of pendingHashes) rememberObservedHash(observedHashes, hash);\n if (liveObservedReplayKeys) {\n for (const message of messages) {\n rememberLiveObservedReplayKey(liveObservedReplayKeys, liveReplayKey(message, session.sessionKey));\n }\n }\n } catch (err) {\n if (config && isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);\n session.notify(`Remnic observe failed: ${errorMessage(err)}`, \"warning\");\n }\n}\n\nexport function buildCompactionSummary(preparation: any): string {\n const previousSummary = typeof preparation.previousSummary === \"string\"\n ? preparation.previousSummary.trim()\n : \"\";\n const messages = [\n ...(Array.isArray(preparation.messagesToSummarize) ? preparation.messagesToSummarize : []),\n ...(Array.isArray(preparation.turnPrefixMessages) ? preparation.turnPrefixMessages : []),\n ];\n const transcript = summarizeMessages(messages, 24000);\n const details = fileDetailsFromPreparation(preparation);\n\n if (\n !previousSummary &&\n !transcript &&\n details.readFiles.length === 0 &&\n details.modifiedFiles.length === 0\n ) {\n return \"\";\n }\n\n const sections: string[] = [\n \"## Remnic Pi Context Checkpoint\",\n \"\",\n \"This checkpoint was created by Remnic during Pi context compaction.\",\n ];\n if (previousSummary) sections.push(\"\", \"## Previous Summary\", previousSummary);\n if (transcript) sections.push(\"\", \"## Conversation Excerpt\", transcript);\n if (details.readFiles.length > 0) sections.push(\"\", \"<read-files>\", ...details.readFiles, \"</read-files>\");\n if (details.modifiedFiles.length > 0) sections.push(\"\", \"<modified-files>\", ...details.modifiedFiles, \"</modified-files>\");\n return sections.join(\"\\n\");\n}\n\nfunction fileDetailsFromPreparation(preparation: any): { readFiles: string[]; modifiedFiles: string[] } {\n const fileOps = preparation?.fileOps;\n const read = fileOps?.read instanceof Set ? Array.from(fileOps.read).filter(isString) : [];\n const edited = fileOps?.edited instanceof Set ? Array.from(fileOps.edited).filter(isString) : [];\n const written = fileOps?.written instanceof Set ? Array.from(fileOps.written).filter(isString) : [];\n const modified = new Set([...edited, ...written]);\n return {\n readFiles: read.filter((file) => !modified.has(file)).sort(),\n modifiedFiles: Array.from(modified).sort(),\n };\n}\n\nfunction restoreObservedState(session: PiContextSnapshot, observedHashes: Set<string>): void {\n for (const entry of session.entries) {\n if (entry?.type !== \"custom\" || entry.customType !== STATE_CUSTOM_TYPE) continue;\n const hashes = entry.data?.observedHashes;\n if (Array.isArray(hashes)) {\n for (const hash of hashes) {\n if (typeof hash === \"string\") rememberObservedHash(observedHashes, hash);\n }\n }\n }\n}\n\nfunction rememberObservedHash(observedHashes: Set<string>, hash: string): void {\n if (observedHashes.has(hash)) return;\n while (observedHashes.size >= MAX_OBSERVED_HASHES) {\n const oldest = observedHashes.keys().next().value;\n if (typeof oldest !== \"string\") break;\n observedHashes.delete(oldest);\n }\n observedHashes.add(hash);\n}\n\nfunction rememberLiveObservedReplayKey(liveObservedReplayKeys: Map<string, number>, key: string): void {\n liveObservedReplayKeys.set(key, (liveObservedReplayKeys.get(key) ?? 0) + 1);\n}\n\nfunction consumeLiveObservedReplayKey(liveObservedReplayKeys: Map<string, number>, key: string): boolean {\n const count = liveObservedReplayKeys.get(key) ?? 0;\n if (count <= 0) return false;\n if (count === 1) liveObservedReplayKeys.delete(key);\n else liveObservedReplayKeys.set(key, count - 1);\n return true;\n}\n\nfunction skipLiveObservedReplayMessages(\n sessionKey: string,\n rawMessages: unknown[],\n liveObservedReplayKeys: Map<string, number>,\n): unknown[] {\n if (liveObservedReplayKeys.size === 0) return rawMessages;\n const unobserved: unknown[] = [];\n for (const raw of rawMessages) {\n const message = toObserveMessage(raw);\n if (message && consumeLiveObservedReplayKey(liveObservedReplayKeys, liveReplayKey(message, sessionKey))) {\n continue;\n }\n unobserved.push(raw);\n }\n return unobserved;\n}\n\nfunction liveReplayKey(message: ObserveMessage, sessionKey: string): string {\n return hashObservedMessage(message, sessionKey, \"live-replay\");\n}\n\nfunction persistObservedState(pi: PiApi, observedHashes: Set<string>): void {\n const observed = Array.from(observedHashes).slice(-MAX_OBSERVED_HASHES);\n pi.appendEntry(STATE_CUSTOM_TYPE, {\n observedHashes: observed,\n recordedAt: new Date().toISOString(),\n });\n}\n\nasync function setStatus(session: PiContextSnapshot, client: RemnicClient, config: RemnicPiConfig): Promise<void> {\n try {\n await client.health({ timeoutMs: config.startupRequestTimeoutMs });\n // A successful health probe means the daemon is reachable, so clear any\n // stale cooldown a prior recall/observe timeout left on the shared client —\n // otherwise the next live hook fast-skips even though the daemon is up\n // (codex review).\n client.markReachable();\n session.setStatus(\"remnic\", `Remnic ${config.namespace ? `(${config.namespace})` : \"ready\"}`);\n } catch (err) {\n // Startup just proved the daemon is unreachable, so trip the fast-skip\n // breaker too — otherwise the first live context/observe hook still spends\n // the full turn budget on a doomed request before the breaker would trip\n // on its own (codex review).\n if (isDaemonUnreachableError(err)) client.markUnreachable(config.daemonCooldownMs);\n session.setStatus(\"remnic\", \"Remnic offline\");\n }\n}\n\nfunction snapshotPiContext(ctx: any, options: PiContextSnapshotOptions = {}): PiContextSnapshot | null {\n const sessionKey = safeSessionKeyFromContext(ctx);\n if (!sessionKey) return null;\n const cwd = safeStringRead(() => ctx?.cwd, \"\");\n const hasUI = safeRead(() => ctx?.hasUI, undefined) === false;\n const ui = hasUI ? undefined : safeRead(() => ctx?.ui, undefined);\n const compact = safeRead(() => ctx?.compact, undefined);\n const includeSessionHistory = options.includeSessionHistory === true;\n return {\n sessionKey,\n cwd,\n entries: includeSessionHistory ? safeEntries(ctx) : [],\n branch: includeSessionHistory ? safeBranch(ctx) : [],\n notify: makeNotifier(ui, hasUI),\n setStatus: makeStatusSetter(ui, hasUI),\n compact: typeof compact === \"function\" ? () => compact.call(ctx) : undefined,\n };\n}\n\nfunction safeSessionKeyFromContext(ctx: any): string | null {\n try {\n return sessionKeyFromContext(ctx);\n } catch {\n return null;\n }\n}\n\nfunction makeNotifier(ui: unknown, hasUI: boolean): NotifyFn {\n if (hasUI || !isRecord(ui) || typeof ui.notify !== \"function\") {\n return () => undefined;\n }\n const notifyFn = ui.notify;\n return (message, level) => {\n try {\n notifyFn.call(ui, message, level);\n } catch {\n // Pi invalidates session-bound UI objects during reload/replacement. A\n // notification failure must not tear down Remnic's hooks.\n }\n };\n}\n\nfunction makeStatusSetter(ui: unknown, hasUI: boolean): PiContextSnapshot[\"setStatus\"] {\n if (hasUI || !isRecord(ui) || typeof ui.setStatus !== \"function\") {\n return () => undefined;\n }\n const setStatusFn = ui.setStatus;\n return (key, value) => {\n try {\n setStatusFn.call(ui, key, value);\n } catch {\n // See makeNotifier: stale UI should not make extension startup fail.\n }\n };\n}\n\nfunction safeRead<T>(read: () => T, fallback: T): T {\n try {\n return read();\n } catch {\n return fallback;\n }\n}\n\nfunction safeStringRead(read: () => unknown, fallback: string): string {\n const value = safeRead(read, fallback);\n return typeof value === \"string\" ? value : fallback;\n}\n\nfunction safeEntries(ctx: any): any[] {\n try {\n const entries = ctx.sessionManager?.getEntries?.();\n return Array.isArray(entries) ? entries : [];\n } catch {\n return [];\n }\n}\n\nfunction safeBranch(ctx: any): any[] {\n try {\n const branch = ctx.sessionManager?.getBranch?.();\n return Array.isArray(branch) ? branch : [];\n } catch {\n return [];\n }\n}\n\nfunction branchMessagesWithEntryIdentity(branch: any[]): unknown[] {\n const messages: unknown[] = [];\n for (const entry of branch) {\n const message = messageWithEntryIdentity(entry);\n if (message) messages.push(message);\n }\n return messages;\n}\n\nfunction messageWithEntryIdentity(entry: any): unknown | null {\n const message = entry?.message;\n if (!message || typeof message !== \"object\" || Array.isArray(message)) return message ?? null;\n\n const source = isRecord(entry) ? entry : {};\n const enriched: Record<string, unknown> = { ...(message as Record<string, unknown>) };\n assignMissingIdentity(enriched, \"entryId\", source.id ?? source.entryId ?? source.entry_id);\n assignMissingIdentity(enriched, \"timestamp\", source.timestamp);\n assignMissingIdentity(enriched, \"createdAt\", source.createdAt ?? source.created_at);\n return enriched;\n}\n\nfunction assignMissingIdentity(target: Record<string, unknown>, field: string, value: unknown): void {\n if (target[field] !== undefined) return;\n if (typeof value === \"string\" && value.length > 0) {\n target[field] = value;\n return;\n }\n if (typeof value === \"number\" && Number.isFinite(value)) {\n target[field] = value;\n }\n}\n\nfunction trimContext(value: string, budget: number): string {\n if (value.length <= budget) return value;\n if (budget <= TRUNCATION_NOTICE.length) return TRUNCATION_NOTICE.slice(0, budget);\n return `${value.slice(0, budget - TRUNCATION_NOTICE.length)}${TRUNCATION_NOTICE}`;\n}\n\n\nexport function isDaemonUnreachableError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (/Remnic request timed out/.test(err.message)) return true;\n // Retry-budget exhaustion means transient failures ate the whole per-turn\n // deadline inside requestWithRetry — the daemon is effectively unreachable\n // for this turn, so trip the breaker and cool down instead of burning another\n // full budget on the next hook (codex review). This error only arises from\n // transient connection failures, never from a semantic HTTP response.\n if (/Remnic request exceeded the \\d+ms budget before retry/.test(err.message)) return true;\n // Multi-chunk observe throws its own budget-exceeded message when the shared\n // per-turn deadline is exhausted across chunks; that is also an effectively-\n // unreachable condition for the turn, so trip the breaker and fast-skip\n // subsequent turns instead of piling on more doomed chunked observes (cursor).\n if (/Remnic observe exceeded the per-turn budget of \\d+ms/.test(err.message)) return true;\n return isTransientNetworkError(err);\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction finiteTokenCount(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) && value >= 0 ? value : null;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\nexport { textFromMessage };\n","import { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\nimport { expandTildePath } from \"@remnic/core\";\n\nimport { REMNIC_PI_EXTENSION_DIR_NAME, resolvePiAgentHome } from \"./paths.js\";\n\nexport interface RemnicPiConfig {\n remnicDaemonUrl: string;\n authToken?: string;\n namespace?: string;\n recallMode: \"auto\" | \"minimal\" | \"full\" | \"graph_mode\" | \"no_recall\";\n recallTopK: number;\n recallBudgetChars: number;\n recallEnabled: boolean;\n observeEnabled: boolean;\n observeSkipExtraction: boolean;\n compactionEnabled: boolean;\n mcpToolsEnabled: boolean;\n statusEnabled: boolean;\n requestTimeoutMs: number;\n startupRequestTimeoutMs: number;\n /**\n * Per-turn request budget for observe/recall. MUST stay below the host's\n * in-handler kill budget (Pi/omp kills handlers at 30 s). Defaults to 20 s,\n * capped at 25 s so a misconfiguration can never produce a structurally\n * unsatisfiable timeout (issue #1626).\n */\n turnRequestTimeoutMs: number;\n /**\n * Soft cap on a single observe POST body in bytes. The client chunks observe\n * batches to stay under this; individual oversized messages are truncated\n * with a marker. Defaults to 100 KiB, safely under the daemon's default\n * 128 KiB `maxBodyBytes` (issue #1600).\n */\n observeMaxBytes: number;\n /**\n * Maximum retry attempts for observe/recall on transient connection-level\n * failures (socket close, ECONNRESET, EPIPE). Observe is dedupe-safe so\n * retrying is harmless (issue #1602).\n */\n observeMaxRetries: number;\n /**\n * Cooldown base for the daemon-reachability circuit breaker. When observe/\n * recall fails on a timeout or connection error, subsequent turns skip fast\n * for an exponentially growing window starting at this value (issue #1626).\n */\n daemonCooldownMs: number;\n}\n\nexport interface LoadConfigOptions {\n configPath?: string;\n env?: NodeJS.ProcessEnv;\n}\n\nconst DEFAULT_CONFIG: RemnicPiConfig = {\n remnicDaemonUrl: \"http://127.0.0.1:4318\",\n recallMode: \"auto\",\n recallTopK: 8,\n recallBudgetChars: 12000,\n recallEnabled: true,\n observeEnabled: true,\n observeSkipExtraction: false,\n compactionEnabled: true,\n mcpToolsEnabled: true,\n statusEnabled: true,\n requestTimeoutMs: 60000,\n startupRequestTimeoutMs: 1000,\n // Default 20 s is comfortably under the Pi/omp 30 s handler budget (#1626).\n turnRequestTimeoutMs: 20000,\n // Default 100 KiB leaves headroom under the daemon's 128 KiB default (#1600).\n observeMaxBytes: 102400,\n observeMaxRetries: 2,\n // Base cooldown for the circuit breaker; doubles on consecutive failures (#1626).\n daemonCooldownMs: 5000,\n};\n\nfunction defaultConfigPath(env: NodeJS.ProcessEnv): string {\n return path.join(resolvePiAgentHome(env), \"extensions\", REMNIC_PI_EXTENSION_DIR_NAME, \"remnic.config.json\");\n}\n\nfunction coerceBoolean(value: unknown, fallback: boolean, fieldName: string): boolean {\n if (value === undefined || value === null) return fallback;\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"string\") {\n const normalized = value.trim().toLowerCase();\n if ([\"true\", \"1\", \"yes\", \"on\"].includes(normalized)) return true;\n if ([\"false\", \"0\", \"no\", \"off\"].includes(normalized)) return false;\n }\n throw new Error(`Invalid boolean value for Remnic Pi config field ${fieldName}`);\n}\n\nfunction coercePositiveInt(value: unknown, fallback: number, max: number, fieldName: string): number {\n if (value === undefined || value === null || value === \"\") return fallback;\n let parsed: number;\n if (typeof value === \"number\") {\n parsed = value;\n } else if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.length === 0) return fallback;\n if (!/^[+-]?\\d+$/.test(trimmed)) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 1 to ${max}`);\n }\n parsed = Number(trimmed);\n } else {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 1 to ${max}`);\n }\n if (!Number.isInteger(parsed) || parsed <= 0 || parsed > max) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 1 to ${max}`);\n }\n return parsed;\n}\n\n/**\n * Like {@link coercePositiveInt} but allows 0, for knobs where 0 is a\n * meaningful \"disabled\" value (e.g. observeMaxRetries). Still rejects\n * negatives, non-integers, and values above the cap.\n */\nfunction coerceNonNegativeInt(value: unknown, fallback: number, max: number, fieldName: string): number {\n if (value === undefined || value === null || value === \"\") return fallback;\n let parsed: number;\n if (typeof value === \"number\") {\n parsed = value;\n } else if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.length === 0) return fallback;\n if (!/^[+-]?\\d+$/.test(trimmed)) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 0 to ${max}`);\n }\n parsed = Number(trimmed);\n } else {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 0 to ${max}`);\n }\n if (!Number.isInteger(parsed) || parsed < 0 || parsed > max) {\n throw new Error(`Invalid numeric value for Remnic Pi config field ${fieldName}: expected an integer from 0 to ${max}`);\n }\n return parsed;\n}\n\nfunction coerceOptionalNonEmptyString(value: unknown, fieldName: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === \"string\" && value.trim().length > 0) return value.trim();\n throw new Error(`Invalid string value for Remnic Pi config field ${fieldName}`);\n}\n\nfunction coerceOptionalString(value: unknown, fieldName: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n throw new Error(`Invalid string value for Remnic Pi config field ${fieldName}`);\n}\n\nfunction coerceOptionalHttpUrl(value: unknown, fieldName: string): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value !== \"string\") {\n throw new Error(`Invalid URL value for Remnic Pi config field ${fieldName}: expected an http or https URL`);\n }\n const trimmed = value.trim();\n if (trimmed.length === 0) return undefined;\n try {\n const parsed = new URL(trimmed);\n if (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") return trimTrailingSlashes(trimmed);\n } catch {\n // Fall through to the shared error below.\n }\n throw new Error(`Invalid URL value for Remnic Pi config field ${fieldName}: expected an http or https URL`);\n}\n\nfunction coerceRecallMode(value: unknown): RemnicPiConfig[\"recallMode\"] {\n if (value === undefined || value === null || value === \"\") return DEFAULT_CONFIG.recallMode;\n if (\n value === \"minimal\" ||\n value === \"full\" ||\n value === \"graph_mode\" ||\n value === \"no_recall\" ||\n value === \"auto\"\n ) {\n return value;\n }\n throw new Error(`Invalid recallMode value for Remnic Pi config: ${JSON.stringify(value)}`);\n}\n\nfunction readConfigFile(configPath: string): Record<string, unknown> {\n if (!existsSync(configPath)) return {};\n try {\n const raw = readFileSync(configPath, \"utf-8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n throw new Error(\"expected a JSON object\");\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n throw new Error(`Failed to load Remnic Pi config at ${configPath}: ${reason}`);\n }\n}\n\nfunction trimTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;\n return value.slice(0, end);\n}\n\nexport function resolveConfigPath(options: LoadConfigOptions = {}): string {\n const env = options.env ?? process.env;\n // REMNIC_PI_CONFIG keeps precedence for upstream Pi; REMNIC_OMP_CONFIG lets an\n // omp (oh-my-pi) direct load (`omp -e npm:@remnic/plugin-pi`) point the shared\n // runtime module at its own config without an explicit configPath. Connector\n // installs always pass an explicit configPath, so this only affects direct loads.\n return expandTildePath(\n options.configPath || env.REMNIC_PI_CONFIG || env.REMNIC_OMP_CONFIG || defaultConfigPath(env),\n );\n}\n\nexport function loadConfig(options: LoadConfigOptions = {}): RemnicPiConfig {\n const env = options.env ?? process.env;\n const fileConfig = readConfigFile(resolveConfigPath(options));\n const daemonUrl =\n coerceOptionalHttpUrl(fileConfig.remnicDaemonUrl, \"remnicDaemonUrl\") ??\n coerceOptionalHttpUrl(env.REMNIC_DAEMON_URL, \"REMNIC_DAEMON_URL\") ??\n DEFAULT_CONFIG.remnicDaemonUrl;\n const authToken =\n coerceOptionalString(fileConfig.authToken, \"authToken\") ??\n coerceOptionalString(env.REMNIC_PI_AUTH_TOKEN, \"REMNIC_PI_AUTH_TOKEN\");\n const namespace = coerceOptionalNonEmptyString(fileConfig.namespace, \"namespace\");\n\n const requestTimeoutMs = coercePositiveInt(\n fileConfig.requestTimeoutMs,\n DEFAULT_CONFIG.requestTimeoutMs,\n 60_000,\n \"requestTimeoutMs\",\n );\n // When turnRequestTimeoutMs is not explicitly set, derive it from the\n // configured requestTimeoutMs (capped at the default turn budget) so an\n // existing install that lowered requestTimeoutMs below 20s keeps its tighter\n // per-turn budget instead of being silently raised back to 20s (codex review).\n const turnFallback = Math.min(requestTimeoutMs, DEFAULT_CONFIG.turnRequestTimeoutMs);\n const turnRequestTimeoutMs = coercePositiveInt(\n fileConfig.turnRequestTimeoutMs,\n turnFallback,\n 25_000,\n \"turnRequestTimeoutMs\",\n );\n\n return {\n remnicDaemonUrl: daemonUrl,\n authToken,\n namespace,\n recallMode: coerceRecallMode(fileConfig.recallMode),\n recallTopK: coercePositiveInt(fileConfig.recallTopK, DEFAULT_CONFIG.recallTopK, 50, \"recallTopK\"),\n recallBudgetChars: coercePositiveInt(fileConfig.recallBudgetChars, DEFAULT_CONFIG.recallBudgetChars, 64000, \"recallBudgetChars\"),\n recallEnabled: coerceBoolean(fileConfig.recallEnabled, DEFAULT_CONFIG.recallEnabled, \"recallEnabled\"),\n observeEnabled: coerceBoolean(fileConfig.observeEnabled, DEFAULT_CONFIG.observeEnabled, \"observeEnabled\"),\n observeSkipExtraction: coerceBoolean(fileConfig.observeSkipExtraction, DEFAULT_CONFIG.observeSkipExtraction, \"observeSkipExtraction\"),\n compactionEnabled: coerceBoolean(fileConfig.compactionEnabled, DEFAULT_CONFIG.compactionEnabled, \"compactionEnabled\"),\n mcpToolsEnabled: coerceBoolean(fileConfig.mcpToolsEnabled, DEFAULT_CONFIG.mcpToolsEnabled, \"mcpToolsEnabled\"),\n statusEnabled: coerceBoolean(fileConfig.statusEnabled, DEFAULT_CONFIG.statusEnabled, \"statusEnabled\"),\n requestTimeoutMs,\n startupRequestTimeoutMs: coercePositiveInt(\n fileConfig.startupRequestTimeoutMs,\n DEFAULT_CONFIG.startupRequestTimeoutMs,\n 60_000,\n \"startupRequestTimeoutMs\",\n ),\n turnRequestTimeoutMs,\n observeMaxBytes: coercePositiveInt(\n fileConfig.observeMaxBytes,\n DEFAULT_CONFIG.observeMaxBytes,\n 8_388_608,\n \"observeMaxBytes\",\n ),\n observeMaxRetries: coerceNonNegativeInt(fileConfig.observeMaxRetries, DEFAULT_CONFIG.observeMaxRetries, 5, \"observeMaxRetries\"),\n daemonCooldownMs: coercePositiveInt(fileConfig.daemonCooldownMs, DEFAULT_CONFIG.daemonCooldownMs, 60_000, \"daemonCooldownMs\"),\n };\n}\n","import type { RemnicPiConfig } from \"./config.js\";\n\nexport interface RecallResponse {\n context?: string;\n results?: Array<{ id?: string; content?: string; score?: number; category?: string }>;\n count?: number;\n}\n\nexport interface ObserveMessagePart {\n ordinal?: number;\n kind: \"text\" | \"tool_call\" | \"tool_result\" | \"patch\" | \"file_read\" | \"file_write\" | \"step_start\" | \"step_finish\" | \"snapshot\" | \"retry\";\n payload: Record<string, unknown>;\n toolName?: string | null;\n filePath?: string | null;\n createdAt?: string | null;\n}\n\nexport interface ObserveMessage {\n role: \"user\" | \"assistant\";\n content: string;\n sourceFormat?: \"pi\";\n rawContent?: unknown;\n parts?: ObserveMessagePart[];\n}\n\nexport interface McpTool {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n}\n\nexport interface RequestOptions {\n timeoutMs?: number;\n /** Transient-retry budget for connection-level failures (socket close, ECONNRESET). */\n maxRetries?: number;\n}\n\nexport interface ObserveOptions extends RequestOptions {\n /** Soft cap on a single observe POST body in bytes; oversize batches are chunked. */\n maxBytes?: number;\n}\n\nexport class RemnicHttpError extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(message);\n }\n}\n\ninterface ObserveBody {\n sessionKey: string;\n cwd: string;\n namespace?: string;\n skipExtraction: boolean;\n messages: ObserveMessage[];\n}\n\nconst encoder = new TextEncoder();\nconst RETRY_BASE_DELAY_MS = 200;\nconst MAX_COOLDOWN_MS = 60_000;\nconst TRUNCATION_MARKER = \"\\n\\n[Remnic observe truncated: payload exceeded client size cap]\";\n\nexport class RemnicClient {\n private requestId = 0;\n // Circuit-breaker state: when the daemon is known-unreachable, observe/recall\n // callers skip fast instead of blocking every turn on a doomed request (#1626).\n private unreachableUntil = 0;\n private consecutiveFailures = 0;\n\n constructor(private readonly config: RemnicPiConfig) {}\n\n /** True when the daemon is not in a known-unreachable cooldown. */\n isReachable(): boolean {\n return Date.now() >= this.unreachableUntil;\n }\n\n /** Clear the circuit breaker — call after any successful daemon interaction. */\n markReachable(): void {\n this.consecutiveFailures = 0;\n this.unreachableUntil = 0;\n }\n\n /**\n * Enter (or extend) an unreachable cooldown. The cooldown grows exponentially\n * with consecutive failures (base, 2×base, 4×base, …) capped at 60 s, so a\n * flapping daemon is retried gently while a hard-down host backs off hard.\n */\n markUnreachable(baseCooldownMs: number): void {\n this.consecutiveFailures += 1;\n const factor = 2 ** Math.min(this.consecutiveFailures - 1, 4);\n const cooldown = Math.min(baseCooldownMs * factor, MAX_COOLDOWN_MS);\n this.unreachableUntil = Date.now() + cooldown;\n }\n\n async health(options: RequestOptions = {}): Promise<Record<string, unknown>> {\n return this.request(\"GET\", \"/engram/v1/health\", undefined, options);\n }\n\n async recall(\n query: string,\n sessionKey: string,\n cwd: string,\n options: RequestOptions = {},\n ): Promise<RecallResponse> {\n // Recall is a read-only query; retry transient connection failures with the\n // same budget as observe (#1602). Default to the per-turn budget when the\n // caller omits timeoutMs so the retries share one deadline (like observe)\n // instead of each attempt reusing the full general request timeout (cursor\n // review). Callers that need more (e.g. the manual /remnic-recall command)\n // pass an explicit timeoutMs.\n const merged: RequestOptions = {\n ...options,\n timeoutMs: options.timeoutMs ?? this.config.turnRequestTimeoutMs,\n maxRetries: options.maxRetries ?? this.config.observeMaxRetries,\n };\n return this.requestWithRetry(\n \"POST\",\n \"/engram/v1/recall\",\n {\n query,\n sessionKey,\n cwd,\n namespace: this.config.namespace,\n topK: this.config.recallTopK,\n mode: this.config.recallMode,\n },\n merged,\n );\n }\n\n async recallExplain(sessionKey: string, options: RequestOptions = {}): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\n \"POST\",\n \"/engram/v1/recall/explain\",\n {\n sessionKey,\n namespace: this.config.namespace,\n },\n options,\n );\n }\n\n async observe(\n sessionKey: string,\n cwd: string,\n messages: ObserveMessage[],\n options: ObserveOptions = {},\n ): Promise<Record<string, unknown>> {\n const maxBytes = options.maxBytes ?? this.config.observeMaxBytes;\n // Observe runs on the live turn hooks, so bound it by the per-turn budget\n // (#1626) regardless of how many chunks the payload splits into. A missing\n // override previously let single-chunk observe fall back to the 60s general\n // budget while multi-chunk used the 20s turn budget (cursor review). An\n // explicit override is honored so callers outside the turn (shutdown replay,\n // tests) can extend it.\n const turnBudgetMs = options.timeoutMs ?? this.config.turnRequestTimeoutMs;\n const retryOptions: RequestOptions = {\n timeoutMs: turnBudgetMs,\n maxRetries: options.maxRetries ?? this.config.observeMaxRetries,\n };\n const chunks = chunkObservePayload(this.config, sessionKey, cwd, messages, maxBytes);\n if (chunks.length === 1) {\n return this.requestWithRetry(\"POST\", \"/engram/v1/observe\", chunks[0], retryOptions);\n }\n // Multiple chunks: send sequentially within the SAME per-turn deadline so\n // the TOTAL observe time stays under turnBudgetMs (not per-chunk), which\n // keeps it inside the host's ~30s handler budget (#1626). Each chunk is\n // retried independently on transient connection failures; observe is\n // dedupe-safe, so a partial failure just re-sends on the next turn.\n const deadline = Date.now() + turnBudgetMs;\n const results: Record<string, unknown>[] = [];\n for (let i = 0; i < chunks.length; i++) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n throw new Error(\n `Remnic observe exceeded the per-turn budget of ${turnBudgetMs}ms across ${chunks.length} chunks (completed ${i})`,\n );\n }\n const chunkOptions: RequestOptions = { ...retryOptions, timeoutMs: remaining };\n const result = await this.requestWithRetry<Record<string, unknown>>(\n \"POST\",\n \"/engram/v1/observe\",\n chunks[i],\n chunkOptions,\n );\n if (result && typeof result === \"object\") {\n results.push(result);\n }\n }\n return mergeObserveResults(results);\n }\n\n async storeMemory(content: string, sessionKey: string, options: RequestOptions = {}): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\"POST\", \"/engram/v1/memories\", {\n content,\n category: \"fact\",\n sourceReason: \"Captured from Pi via Remnic extension\",\n sessionKey,\n namespace: this.config.namespace,\n }, options);\n }\n\n async lcmSearch(query: string, sessionKey: string, limit = 10): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\"POST\", \"/engram/v1/lcm/search\", {\n query,\n sessionKey,\n namespace: this.config.namespace,\n limit,\n });\n }\n\n async lcmCompactionFlush(sessionKey: string): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\"POST\", \"/engram/v1/lcm/compaction/flush\", {\n sessionKey,\n namespace: this.config.namespace,\n });\n }\n\n async lcmCompactionRecord(sessionKey: string, tokensBefore: number, tokensAfter: number): Promise<Record<string, unknown>> {\n return this.requestWithRetry(\"POST\", \"/engram/v1/lcm/compaction/record\", {\n sessionKey,\n namespace: this.config.namespace,\n tokensBefore,\n tokensAfter,\n });\n }\n\n async contextCheckpoint(sessionKey: string, context: string): Promise<Record<string, unknown>> {\n return this.mcpTool(\"remnic.context_checkpoint\", {\n sessionKey,\n context,\n namespace: this.config.namespace,\n });\n }\n\n async mcpListTools(options: RequestOptions = {}): Promise<McpTool[]> {\n const result = await this.mcpRequest(\"tools/list\", {}, options);\n const tools = result.tools;\n return Array.isArray(tools) ? tools.filter(isMcpTool) : [];\n }\n\n async mcpTool(name: string, args: Record<string, unknown>): Promise<Record<string, unknown>> {\n return this.mcpRequest(\"tools/call\", {\n name,\n arguments: args,\n });\n }\n\n /**\n * Single HTTP attempt with the configured timeout. No retry — retry of\n * transient connection failures lives in {@link requestWithRetry}.\n */\n private async request<T = Record<string, unknown>>(\n method: string,\n pathname: string,\n body?: unknown,\n options: RequestOptions = {},\n ): Promise<T> {\n const controller = new AbortController();\n // A per-request override is honored only when it is a finite positive number;\n // 0, negative, NaN, or non-finite values would make setTimeout abort\n // immediately (or behave erratically), so fall back to the general budget.\n // In practice the override is always sourced from the validated\n // `startupRequestTimeoutMs` / `turnRequestTimeoutMs` config, but this keeps\n // the client robust to any future caller (Copilot review).\n const override = options.timeoutMs;\n const timeoutMs =\n typeof override === \"number\" && Number.isFinite(override) && override > 0\n ? override\n : this.config.requestTimeoutMs;\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(`${this.config.remnicDaemonUrl}${pathname}`, {\n method,\n headers: {\n ...(body === undefined ? {} : { \"Content-Type\": \"application/json\" }),\n ...(this.config.authToken ? { Authorization: `Bearer ${this.config.authToken}` } : {}),\n \"X-Engram-Client-Id\": \"pi\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller.signal,\n });\n const text = await response.text();\n let payload: unknown = {};\n let parseError: unknown;\n if (text) {\n try {\n payload = JSON.parse(text);\n } catch (err) {\n parseError = err;\n }\n }\n if (!response.ok) {\n const message = responseErrorMessage(response, text, payload, parseError);\n if (response.status === 413) {\n // Surface the body size so operators can tune the cap (#1600).\n const bodyBytes = body === undefined ? 0 : jsonBytes(body);\n throw new RemnicHttpError(response.status, `${message} (observed body ${bodyBytes} bytes; cap via observeMaxBytes)`);\n }\n throw new RemnicHttpError(response.status, message);\n }\n if (parseError) {\n const reason = parseError instanceof Error ? parseError.message : String(parseError);\n throw new Error(`Invalid JSON response from Remnic daemon (${response.status} ${response.statusText || \"OK\"}): ${reason}`);\n }\n return payload as T;\n } catch (err) {\n if (isAbortError(err)) {\n throw new Error(`Remnic request timed out after ${timeoutMs}ms`);\n }\n throw err;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n /**\n * Wrap {@link request} with a small bounded retry loop for transient\n * connection-level failures (socket close mid-request, ECONNRESET, EPIPE).\n * Timeouts (our own AbortController) and HTTP responses (4xx/5xx) are NOT\n * retried here — timeouts already burned the full budget, and HTTP errors\n * carry semantic meaning the caller must handle. Observe/recall are\n * dedupe-safe so retrying a transiently-failed POST is harmless (#1602).\n */\n private async requestWithRetry<T = Record<string, unknown>>(\n method: string,\n pathname: string,\n body: unknown,\n options: RequestOptions = {},\n ): Promise<T> {\n const maxRetries = options.maxRetries ?? 0;\n // Share ONE deadline across all attempts (including backoff sleeps) when the\n // caller passes a per-turn/per-operation budget, so a late transient failure\n // cannot burn a full timeout on every retry and overshoot the host's ~30s\n // handler window (#1602/#1626 — cursor + codex reviews). The first attempt\n // keeps the original timeoutMs verbatim (preserving error messages/timing);\n // only retries are tightened to the remaining budget.\n const budgetMs = options.timeoutMs;\n const hasDeadline = typeof budgetMs === \"number\" && Number.isFinite(budgetMs) && budgetMs > 0;\n const deadline = hasDeadline ? Date.now() + budgetMs : Number.POSITIVE_INFINITY;\n let attempt = 0;\n let attemptOptions = options;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n try {\n return await this.request<T>(method, pathname, body, attemptOptions);\n } catch (err) {\n if (attempt >= maxRetries || !isTransientNetworkError(err)) throw err;\n const delayMs = RETRY_BASE_DELAY_MS * 2 ** attempt;\n if (hasDeadline) {\n // The backoff sleep counts against the shared deadline; bail BEFORE\n // sleeping if the sleep alone would overshoot the remaining budget,\n // so a sub-backoff timeoutMs never blocks for the full backoff only\n // to then throw (cursor review).\n const remainingBeforeSleep = deadline - Date.now();\n if (remainingBeforeSleep <= delayMs) {\n throw new Error(\n `Remnic request exceeded the ${budgetMs}ms budget before retry ${attempt + 1} (${method} ${pathname})`,\n );\n }\n }\n await sleep(delayMs);\n attempt += 1;\n if (hasDeadline) {\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n throw new Error(\n `Remnic request exceeded the ${budgetMs}ms budget before retry ${attempt} (${method} ${pathname})`,\n );\n }\n attemptOptions = { ...options, timeoutMs: remaining };\n }\n }\n }\n }\n\n private async mcpRequest(\n method: string,\n params: Record<string, unknown>,\n options: RequestOptions = {},\n ): Promise<Record<string, unknown>> {\n this.requestId += 1;\n const payload = await this.request<Record<string, unknown>>(\"POST\", \"/mcp\", {\n jsonrpc: \"2.0\",\n id: this.requestId,\n method,\n params,\n }, options);\n if (payload.error) {\n throw new Error(JSON.stringify(payload.error));\n }\n return (payload.result && typeof payload.result === \"object\" ? payload.result : payload) as Record<string, unknown>;\n }\n}\n\nfunction isMcpTool(value: unknown): value is McpTool {\n return !!value && typeof value === \"object\" && \"name\" in value && typeof value.name === \"string\";\n}\n\nfunction isAbortError(err: unknown): boolean {\n return err instanceof Error && (err.name === \"AbortError\" || err.message === \"This operation was aborted\");\n}\n\n/**\n * Classify connection-level failures that are safe to retry: the request never\n * reached the daemon (or died mid-flight), so a retry is idempotent. Excludes\n * our own AbortController timeouts and HTTP responses (those carry meaning).\n */\nexport function isTransientNetworkError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n if (isAbortError(err)) return false;\n if (err instanceof RemnicHttpError) return false;\n const lower = (err.message ?? \"\").toLowerCase();\n // Bun fetch: \"The socket connection was closed unexpectedly.\"\n if (lower.includes(\"socket connection was closed\")) return true;\n if (lower.includes(\"socket closed\")) return true;\n // Node undici / OS codes surfaced in the message.\n if (lower.includes(\"econnreset\")) return true;\n if (lower.includes(\"epipe\")) return true;\n if (lower.includes(\"und_err_socket\")) return true;\n // Node wraps the real cause in err.cause (TypeError: fetch failed).\n if (lower.includes(\"fetch failed\")) return true;\n // Inspect the cause chain without an unchecked cast. Error.cause is\n // `unknown` in the ES2022 lib; narrow it before reading `.code`.\n const cause = err.cause;\n if (cause && typeof cause === \"object\" && \"code\" in cause) {\n const code = cause.code;\n if (typeof code === \"string\" && (code === \"ECONNRESET\" || code === \"EPIPE\" || code === \"UND_ERR_SOCKET\")) {\n return true;\n }\n }\n return false;\n}\n\nfunction sleep(ms: number): Promise<void> {\n // Plain Promise constructor: avoids Promise.withResolvers (ES2024 / Node 22+),\n // so retry backoff works on Node 20 and other runtimes that load plugin-pi.\n return new Promise<void>(resolve => setTimeout(resolve, ms));\n}\n\nfunction jsonBytes(value: unknown): number {\n return encoder.encode(JSON.stringify(value)).length;\n}\n\nfunction buildObserveEnvelope(config: RemnicPiConfig, sessionKey: string, cwd: string, messages: ObserveMessage[]): ObserveBody {\n return {\n sessionKey,\n cwd,\n namespace: config.namespace,\n skipExtraction: config.observeSkipExtraction,\n messages,\n };\n}\n\n/**\n * Split an observe batch into POST bodies whose serialized JSON stays under\n * `maxBytes`. Single messages that alone exceed the per-message budget are\n * truncated with a marker rather than dropped, so large tool outputs still\n * leave a trace in memory (#1600).\n */\nexport function chunkObservePayload(\n config: RemnicPiConfig,\n sessionKey: string,\n cwd: string,\n messages: ObserveMessage[],\n maxBytes: number,\n): ObserveBody[] {\n const envelopeOverhead = jsonBytes(buildObserveEnvelope(config, sessionKey, cwd, []));\n const messageBudget = maxBytes - envelopeOverhead;\n if (messageBudget <= 0) {\n // The envelope overhead alone meets/exceeds the cap, so no valid body can\n // fit — return a single chunk and let the daemon reject it visibly (this is\n // a degenerate/misconfigured cap, not the common case). A small-but-positive\n // budget (<=1024) is NOT degenerate: truncate/pack normally so oversized\n // messages are shrunk to fit instead of bypassing the #1600 safeguards\n // (cursor review).\n return [buildObserveEnvelope(config, sessionKey, cwd, messages)];\n }\n const chunks: ObserveMessage[][] = [];\n let current: ObserveMessage[] = [];\n let currentSize = 0;\n const flush = (): void => {\n if (current.length > 0) {\n chunks.push(current);\n current = [];\n currentSize = 0;\n }\n };\n for (const message of messages) {\n const size = jsonBytes(message);\n if (size > messageBudget) {\n flush();\n chunks.push([truncateObserveMessage(message, messageBudget)]);\n continue;\n }\n // Account for the JSON array comma separator before this message when it is\n // not the first in the chunk, so the serialized body never overshoots the\n // cap (review: cursor).\n if (current.length > 0 && currentSize + 1 + size > messageBudget) {\n flush();\n }\n if (current.length > 0) currentSize += 1;\n current.push(message);\n currentSize += size;\n }\n flush();\n if (chunks.length === 0) {\n return [buildObserveEnvelope(config, sessionKey, cwd, [])];\n }\n return chunks.map((msgs) => buildObserveEnvelope(config, sessionKey, cwd, msgs));\n}\n\nconst decoder = new TextDecoder();\n\nfunction truncateObserveMessage(message: ObserveMessage, budgetBytes: number): ObserveMessage {\n // A truncated observe keeps ONLY role + a content marker, dropping rawContent\n // and parts. Live Pi turns carry the full original message in rawContent and\n // parsed parts; those fields dominate the serialized size and would keep the\n // chunk over the cap (defeating #1600), so they are removed — the daemon\n // extracts from content. JSON-escape-aware: measures ACTUAL jsonBytes of each\n // candidate so escaping (\\n -> \\\\\\\\n) can't overshoot.\n const slim: ObserveMessage = { role: message.role, content: \"\" };\n const markerOnly = jsonBytes({ ...slim, content: TRUNCATION_MARKER });\n if (markerOnly > budgetBytes) {\n // Pathological: even the marker alone doesn't fit. Keep it anyway so the\n // turn isn't silently dropped.\n return { role: message.role, content: TRUNCATION_MARKER };\n }\n const fullContent = message.content + TRUNCATION_MARKER;\n if (jsonBytes({ ...slim, content: fullContent }) <= budgetBytes) {\n return { role: message.role, content: fullContent };\n }\n // Binary-search the largest content slice whose slim message fits. Slicing by\n // encoded bytes keeps multi-byte sequences intact where possible; the decoder\n // replaces any dangling tail with the replacement char.\n const encoded = encoder.encode(message.content);\n let lo = 0;\n let hi = encoded.length;\n while (lo < hi) {\n const mid = hi - Math.floor((hi - lo) / 2);\n const candidate = decoder.decode(encoded.subarray(0, mid)) + TRUNCATION_MARKER;\n if (jsonBytes({ ...slim, content: candidate }) <= budgetBytes) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n const truncated = lo > 0 ? decoder.decode(encoded.subarray(0, lo)) : \"\";\n return { role: message.role, content: truncated + TRUNCATION_MARKER };\n}\n\nfunction mergeObserveResults(results: Record<string, unknown>[]): Record<string, unknown> {\n if (results.length === 0) return {};\n if (results.length === 1) return results[0];\n const merged: Record<string, unknown> = {};\n let countSum = 0;\n let hasCount = false;\n for (const result of results) {\n for (const key of Object.keys(result)) {\n const value = result[key];\n if (key === \"count\" && typeof value === \"number\" && Number.isFinite(value)) {\n countSum += value;\n hasCount = true;\n } else {\n merged[key] = value;\n }\n }\n }\n if (hasCount) merged.count = countSum;\n return merged;\n}\n\nfunction responseErrorMessage(response: Response, text: string, payload: unknown, parseError: unknown): string {\n if (!parseError && payload && typeof payload === \"object\") {\n if (\"error\" in payload && typeof payload.error === \"string\" && payload.error.trim().length > 0) {\n return payload.error;\n }\n if (\"message\" in payload && typeof payload.message === \"string\" && payload.message.trim().length > 0) {\n return payload.message;\n }\n }\n\n const snippet = text.trim().replace(/\\s+/g, \" \").slice(0, 200);\n if (snippet.length > 0) {\n return response.statusText ? `${response.statusText}: ${snippet}` : snippet;\n }\n return response.statusText || `HTTP ${response.status}`;\n}\n","import { createHash } from \"node:crypto\";\n\nimport { parsePiMessageParts, type LcmMessagePartInput } from \"@remnic/core\";\n\nimport type { ObserveMessage, ObserveMessagePart } from \"./client.js\";\n\ntype PiMessage = Record<string, unknown>;\n\nexport function sessionKeyFromContext(ctx: { sessionManager?: { getSessionId?: () => string } }): string {\n const id = ctx.sessionManager?.getSessionId?.();\n return id && id.trim().length > 0 ? `pi:${id}` : \"pi:default\";\n}\n\nexport function textFromMessage(message: unknown): string {\n if (!message || typeof message !== \"object\") return \"\";\n const obj = message as PiMessage;\n const role = typeof obj.role === \"string\" ? obj.role : \"message\";\n if (role === \"bashExecution\") {\n const command = typeof obj.command === \"string\" ? obj.command : \"\";\n const output = typeof obj.output === \"string\" ? obj.output : \"\";\n return [`Ran ${command}`, output].filter(Boolean).join(\"\\n\");\n }\n return textFromContent(obj.content).trim();\n}\n\nexport function latestUserQuery(messages: unknown[]): string {\n for (let index = messages.length - 1; index >= 0; index--) {\n const message = messages[index] as PiMessage;\n if (isExcludedFromContext(message) || isRemnicInjected(message)) continue;\n if (message?.role === \"user\") {\n const text = textFromMessage(message);\n if (text.length > 0) return text;\n }\n }\n return \"\";\n}\n\nexport function latestUserRecallTarget(\n messages: unknown[],\n): { query: string; dedupeKey: string } | null {\n for (let index = messages.length - 1; index >= 0; index--) {\n const message = messages[index] as PiMessage;\n if (isExcludedFromContext(message) || isRemnicInjected(message)) continue;\n if (message?.role !== \"user\") continue;\n const query = textFromMessage(message);\n if (query.length === 0) continue;\n const identity = stableObservedMessageIdentity(message);\n return {\n query,\n dedupeKey: identity ? `message:${identity}:${query}` : `query:${query}`,\n };\n }\n return null;\n}\n\nexport function toObserveMessage(message: unknown): ObserveMessage | null {\n if (!message || typeof message !== \"object\") return null;\n const obj = message as PiMessage;\n if (isExcludedFromContext(obj) || isRemnicInjected(obj)) return null;\n const role = obj.role === \"user\" || obj.role === \"bashExecution\" ? \"user\" : \"assistant\";\n const content = textFromMessage(obj);\n if (content.length === 0) return null;\n return {\n role,\n content,\n sourceFormat: \"pi\",\n rawContent: obj,\n parts: partsFromMessage(obj, content),\n };\n}\n\nexport function hashObservedMessage(message: ObserveMessage, sessionKey = \"\", identity = \"content\"): string {\n return createHash(\"sha256\")\n .update(sessionKey)\n .update(\"\\0\")\n .update(message.role)\n .update(\"\\0\")\n .update(identity)\n .update(\"\\0\")\n .update(message.content)\n .digest(\"hex\");\n}\n\nexport function observedMessageDedupeKey(\n message: ObserveMessage,\n sessionKey = \"\",\n): string | null {\n const identity = stableObservedMessageIdentity(message.rawContent);\n return identity ? hashObservedMessage(message, sessionKey, identity) : null;\n}\n\nexport function summarizeMessages(messages: unknown[], maxChars: number): string {\n const chunks: string[] = [];\n let used = 0;\n for (const message of messages) {\n if (isExcludedFromContext(message) || isRemnicInjected(message)) continue;\n const text = textFromMessage(message);\n if (!text) continue;\n const role = typeof (message as PiMessage)?.role === \"string\" ? (message as PiMessage).role : \"message\";\n const line = `[${role}] ${text}`;\n const separatorLength = chunks.length > 0 ? 2 : 0;\n const remaining = maxChars - used - separatorLength;\n if (remaining <= 0) break;\n const clipped = line.length > remaining ? line.slice(0, remaining) : line;\n if (clipped.length > 0) chunks.push(clipped);\n used += separatorLength + clipped.length;\n if (used >= maxChars) break;\n }\n return chunks.join(\"\\n\\n\");\n}\n\nexport function isExcludedFromContext(message: unknown): boolean {\n return !!message && typeof message === \"object\" && (message as PiMessage).excludeFromContext === true;\n}\n\nexport function isRemnicInjected(message: unknown): boolean {\n return !!message && typeof message === \"object\" && (message as PiMessage).remnicInjected === true;\n}\n\nfunction textFromContent(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n const chunks: string[] = [];\n for (const block of content) {\n if (!block || typeof block !== \"object\") continue;\n const obj = block as PiMessage;\n if (obj.type === \"text\" && typeof obj.text === \"string\") chunks.push(obj.text);\n if (obj.type === \"toolCall\" && typeof obj.name === \"string\") {\n chunks.push(`Tool ${obj.name} called with ${JSON.stringify(obj.arguments ?? {})}`);\n }\n }\n return chunks.join(\"\\n\");\n}\n\nfunction partsFromMessage(message: PiMessage, renderedContent: string): ObserveMessagePart[] {\n return parsePiMessageParts(message, {\n renderedContent,\n allowRenderedFallback: true,\n }).map(toObserveMessagePart);\n}\n\nfunction toObserveMessagePart(part: LcmMessagePartInput): ObserveMessagePart {\n return {\n ordinal: part.ordinal ?? undefined,\n kind: part.kind,\n payload: part.payload,\n toolName: part.toolName ?? part.tool_name ?? undefined,\n filePath: part.filePath ?? part.file_path ?? undefined,\n createdAt: part.createdAt ?? part.created_at ?? undefined,\n };\n}\n\nfunction stableObservedMessageIdentity(rawContent: unknown): string | null {\n if (rawContent && typeof rawContent === \"object\") {\n const obj = rawContent as PiMessage;\n const fields = [\n \"id\",\n \"entryId\",\n \"entry_id\",\n \"messageId\",\n \"message_id\",\n \"turnId\",\n \"turn_id\",\n \"timestamp\",\n \"createdAt\",\n \"created_at\",\n ];\n for (const field of fields) {\n const value = obj[field];\n if (typeof value === \"string\" && value.length > 0) return `${field}:${value}`;\n if (typeof value === \"number\" && Number.isFinite(value)) return `${field}:${value}`;\n }\n }\n return null;\n}\n"],"mappings":"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","names":["remnicPiExtension"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/plugin-pi",
3
- "version": "9.3.700",
3
+ "version": "9.3.701",
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.3.700"
45
+ "@remnic/core": "^9.3.701"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "@earendil-works/pi-coding-agent": "*"