@remnic/plugin-pi 9.63.11 → 9.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,6 +39,16 @@ function resolveOmpAgentHome(env) {
39
39
  function resolveOmpExtensionRoot(env) {
40
40
  return path.join(resolveOmpAgentHome(env), "extensions", REMNIC_PI_EXTENSION_DIR_NAME);
41
41
  }
42
+ function resolvePrimeAgentAgentHome(env) {
43
+ const explicitCodingAgentDir = env.PRIME_AGENT_CODING_AGENT_DIR?.trim();
44
+ if (explicitCodingAgentDir) {
45
+ return path.resolve(expandTildePath(explicitCodingAgentDir));
46
+ }
47
+ return path.join(env.HOME ?? env.USERPROFILE ?? os.homedir(), ".prime", "agent");
48
+ }
49
+ function resolvePrimeAgentExtensionRoot(env) {
50
+ return path.join(resolvePrimeAgentAgentHome(env), "extensions", REMNIC_PI_EXTENSION_DIR_NAME);
51
+ }
42
52
 
43
53
  // src/config.ts
44
54
  import { existsSync, readFileSync } from "fs";
@@ -256,7 +266,9 @@ export {
256
266
  resolveOmpConfigRoot,
257
267
  resolveOmpAgentHome,
258
268
  resolveOmpExtensionRoot,
269
+ resolvePrimeAgentAgentHome,
270
+ resolvePrimeAgentExtensionRoot,
259
271
  DEFAULT_CONFIG,
260
272
  loadConfig
261
273
  };
262
- //# sourceMappingURL=chunk-HRZBFDYV.js.map
274
+ //# sourceMappingURL=chunk-C6JW4C3O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/paths.ts","../src/config.ts"],"sourcesContent":["import os from \"node:os\";\nimport path from \"node:path\";\n\n// Leaf submodule (not the `@remnic/core` barrel) so the omp pre-bundle's\n// `bun build` does not pull the rest of core into the extension bundle.\nimport { expandTildePath } from \"@remnic/core/utils/path\";\n\nexport const REMNIC_PI_EXTENSION_DIR_NAME = \"remnic\";\n\nexport function resolvePiAgentHome(env: NodeJS.ProcessEnv): string {\n const explicitCodingAgentDir = env.PI_CODING_AGENT_DIR?.trim();\n if (explicitCodingAgentDir) return path.resolve(expandTildePath(explicitCodingAgentDir));\n\n const explicitAgentHome = env.PI_AGENT_HOME?.trim();\n if (explicitAgentHome) return path.resolve(expandTildePath(explicitAgentHome));\n\n const explicitPiHome = env.PI_HOME?.trim();\n if (explicitPiHome) return path.join(path.resolve(expandTildePath(explicitPiHome)), \"agent\");\n\n return path.join(env.HOME ?? env.USERPROFILE ?? os.homedir(), \".pi\", \"agent\");\n}\n\nexport function resolvePiExtensionRoot(env: NodeJS.ProcessEnv): string {\n return path.join(resolvePiAgentHome(env), \"extensions\", REMNIC_PI_EXTENSION_DIR_NAME);\n}\n\n/**\n * Resolve the active omp profile from the environment, mirroring omp's\n * `resolveProfileEnv`: `OMP_PROFILE` is authoritative, and `PI_PROFILE` is a\n * compatibility fallback consulted **only** when `OMP_PROFILE` is undefined\n * (an explicitly-empty `OMP_PROFILE` therefore selects the default profile).\n * The reserved name \"default\" and blank values resolve to the base (no profile).\n */\nfunction resolveOmpProfile(env: NodeJS.ProcessEnv): string | undefined {\n const raw = env.OMP_PROFILE !== undefined ? env.OMP_PROFILE : env.PI_PROFILE;\n const trimmed = raw?.trim();\n if (!trimmed || trimmed === \"default\") return undefined;\n return trimmed;\n}\n\n/**\n * Resolve the omp (oh-my-pi) agent home directory that omp auto-discovers\n * extensions from. Mirrors omp's `DirResolver` (packages/utils/src/dirs.ts):\n *\n * - The config dir name is `PI_CONFIG_DIR` (default `.omp`).\n * - When a profile (`OMP_PROFILE`, falling back to `PI_PROFILE`) is active it\n * wins and resolves to `<configRoot>/profiles/<name>/agent`; omp discards\n * the `PI_CODING_AGENT_DIR` override while a profile is active.\n * - Otherwise `PI_CODING_AGENT_DIR` overrides the whole agent dir.\n * - Otherwise the base agent dir is `<configRoot>/agent`.\n *\n * Note: omp's XDG redirection (`XDG_DATA_HOME`, etc.) applies to the `data`,\n * `state`, and `cache` categories (sessions/state/cache) — NOT to the base\n * agent dir that extensions are discovered from — so it is intentionally not\n * consulted here.\n */\n/**\n * The omp config root (`~/<PI_CONFIG_DIR or .omp>`), which contains the base\n * `agent/` dir and any `profiles/<name>/agent/` dirs.\n */\nexport function resolveOmpConfigRoot(env: NodeJS.ProcessEnv): string {\n const home = env.HOME ?? env.USERPROFILE ?? os.homedir();\n const configDirName = env.PI_CONFIG_DIR?.trim() || \".omp\";\n return path.join(home, configDirName);\n}\n\nexport function resolveOmpAgentHome(env: NodeJS.ProcessEnv): string {\n const configRoot = resolveOmpConfigRoot(env);\n\n const profile = resolveOmpProfile(env);\n if (profile) {\n return path.join(configRoot, \"profiles\", profile, \"agent\");\n }\n\n const explicitCodingAgentDir = env.PI_CODING_AGENT_DIR?.trim();\n if (explicitCodingAgentDir) return path.resolve(expandTildePath(explicitCodingAgentDir));\n\n return path.join(configRoot, \"agent\");\n}\n\nexport function resolveOmpExtensionRoot(env: NodeJS.ProcessEnv): string {\n return path.join(resolveOmpAgentHome(env), \"extensions\", REMNIC_PI_EXTENSION_DIR_NAME);\n}\n\n/**\n * Prime Agent agent home (a Pi-fork coding agent). Honors only\n * `PRIME_AGENT_CODING_AGENT_DIR`; the Pi-family env vars (`PI_CODING_AGENT_DIR`,\n * `PI_CONFIG_DIR`, …) deliberately do NOT apply — Prime Agent is a separate\n * install tree at `~/.prime/agent`.\n */\nexport function resolvePrimeAgentAgentHome(env: NodeJS.ProcessEnv): string {\n const explicitCodingAgentDir = env.PRIME_AGENT_CODING_AGENT_DIR?.trim();\n if (explicitCodingAgentDir) {\n return path.resolve(expandTildePath(explicitCodingAgentDir));\n }\n return path.join(env.HOME ?? env.USERPROFILE ?? os.homedir(), \".prime\", \"agent\");\n}\n\nexport function resolvePrimeAgentExtensionRoot(env: NodeJS.ProcessEnv): string {\n return path.join(resolvePrimeAgentAgentHome(env), \"extensions\", REMNIC_PI_EXTENSION_DIR_NAME);\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\n// Leaf submodule (not the `@remnic/core` barrel) so the omp pre-bundle's\n// `bun build` does not pull the rest of core — including the LanceDB native\n// asset — into the extension bundle. See PR #1641.\nimport { expandTildePath } from \"@remnic/core/utils/path\";\n\nimport { REMNIC_PI_EXTENSION_DIR_NAME, resolvePiAgentHome } from \"./paths.js\";\n\nexport interface RemnicPiConfig {\n remnicDaemonUrl: string;\n authToken?: string;\n namespace?: string;\n recallMode: \"auto\" | \"minimal\" | \"full\" | \"graph_mode\" | \"no_recall\";\n recallTopK: number;\n recallBudgetChars: number;\n recallEnabled: boolean;\n observeEnabled: boolean;\n observeSkipExtraction: boolean;\n compactionEnabled: boolean;\n mcpToolsEnabled: boolean;\n statusEnabled: boolean;\n requestTimeoutMs: number;\n startupRequestTimeoutMs: number;\n /**\n * Per-turn request budget for observe/recall. MUST stay below the host's\n * in-handler kill budget (Pi/omp kills handlers at 30 s). Defaults to 20 s,\n * capped at 25 s so a misconfiguration can never produce a structurally\n * unsatisfiable timeout (issue #1626).\n */\n turnRequestTimeoutMs: number;\n /**\n * Soft cap on a single observe POST body in bytes. The client chunks observe\n * batches to stay under this; individual oversized messages are truncated\n * with a marker. Defaults to 100 KiB, safely under the daemon's default\n * 128 KiB `maxBodyBytes` (issue #1600).\n */\n observeMaxBytes: number;\n /**\n * Maximum retry attempts for observe/recall on transient connection-level\n * failures (socket close, ECONNRESET, EPIPE). Observe is dedupe-safe so\n * retrying is harmless (issue #1602).\n */\n observeMaxRetries: number;\n /**\n * Cooldown base for the daemon-reachability circuit breaker. When observe/\n * recall fails on a timeout or connection error, subsequent turns skip fast\n * for an exponentially growing window starting at this value (issue #1626).\n */\n daemonCooldownMs: number;\n /**\n * Number of explicit recall timeout errors in the last {@link recallTimeoutWindow}\n * recall calls that permanently disables automatic recall for the process lifetime.\n */\n recallTimeoutThreshold: number;\n /**\n * Size of the rolling window of recent recall calls used by the recall-timeout\n * circuit breaker.\n */\n recallTimeoutWindow: number;\n}\n\nexport interface LoadConfigOptions {\n configPath?: string;\n env?: NodeJS.ProcessEnv;\n}\n\nexport const 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 // Recall-timeout circuit breaker: 7 timeouts in the last 10 recall calls trip permanently.\n recallTimeoutThreshold: 7,\n recallTimeoutWindow: 10,\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 const recallTimeoutThreshold = coercePositiveInt(\n fileConfig.recallTimeoutThreshold,\n DEFAULT_CONFIG.recallTimeoutThreshold,\n 1000,\n \"recallTimeoutThreshold\",\n );\n const recallTimeoutWindow = coercePositiveInt(\n fileConfig.recallTimeoutWindow,\n DEFAULT_CONFIG.recallTimeoutWindow,\n 1000,\n \"recallTimeoutWindow\",\n );\n if (recallTimeoutThreshold > recallTimeoutWindow) {\n throw new Error(\n `Invalid recall timeout circuit breaker config: threshold (${recallTimeoutThreshold}) cannot exceed window (${recallTimeoutWindow})`,\n );\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 recallTimeoutThreshold,\n recallTimeoutWindow,\n };\n}\n"],"mappings":"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","names":["path","expandTildePath","path","expandTildePath"]}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DEFAULT_CONFIG,
3
3
  loadConfig
4
- } from "./chunk-HRZBFDYV.js";
4
+ } from "./chunk-C6JW4C3O.js";
5
5
 
6
6
  // src/index.ts
7
7
  import { Type } from "@sinclair/typebox";
@@ -26,7 +26,7 @@ interface HostPublisherDescriptor {
26
26
  listRemovalAgentHomes?(env: NodeJS.ProcessEnv): string[];
27
27
  }
28
28
  /**
29
- * Shared publisher for Pi-family hosts. Concrete hosts (Pi, omp) subclass this
29
+ * Shared publisher for Pi-family hosts. Concrete hosts (Pi, omp, Prime Agent) subclass this
30
30
  * with a {@link HostPublisherDescriptor}; the install/rollback machinery is
31
31
  * identical across hosts.
32
32
  */
@@ -75,6 +75,18 @@ declare class HostMemoryExtensionPublisher implements MemoryExtensionPublisher {
75
75
  declare class PiMemoryExtensionPublisher extends HostMemoryExtensionPublisher {
76
76
  constructor();
77
77
  }
78
+ /**
79
+ * Publisher for Prime Agent (`~/.prime/agent/extensions/remnic`), a Pi-fork
80
+ * coding agent. It loads the plain `index.ts` wrapper directly (no bun
81
+ * pre-bundle, so no loader/dist-bundle machinery), but discovers extensions
82
+ * through a package manifest — the install therefore also writes a
83
+ * `package.json` depending on `@remnic/plugin-pi`.
84
+ */
85
+ declare class PrimeAgentMemoryExtensionPublisher extends HostMemoryExtensionPublisher {
86
+ constructor();
87
+ protected get ownedFileNames(): readonly string[];
88
+ protected finalizePublish(_ctx: PublishContext, extensionRoot: string): void;
89
+ }
78
90
  /** Publisher for Oh My Pi / omp (`~/.omp/agent/extensions/remnic`). */
79
91
  declare class OmpMemoryExtensionPublisher extends HostMemoryExtensionPublisher {
80
92
  constructor();
@@ -135,4 +147,4 @@ declare function resolveBunOnPath(): string | null;
135
147
  */
136
148
  declare function resolveBunBinary(): string | null;
137
149
 
138
- export { HostMemoryExtensionPublisher, type HostPublisherDescriptor, OmpMemoryExtensionPublisher, PiMemoryExtensionPublisher, resolveBunBinary, resolveBunOnPath, resolveOmpWrapperImportSpecifier };
150
+ export { HostMemoryExtensionPublisher, type HostPublisherDescriptor, OmpMemoryExtensionPublisher, PiMemoryExtensionPublisher, PrimeAgentMemoryExtensionPublisher, resolveBunBinary, resolveBunOnPath, resolveOmpWrapperImportSpecifier };
package/dist/publisher.js CHANGED
@@ -4,8 +4,10 @@ import {
4
4
  resolveOmpConfigRoot,
5
5
  resolveOmpExtensionRoot,
6
6
  resolvePiAgentHome,
7
- resolvePiExtensionRoot
8
- } from "./chunk-HRZBFDYV.js";
7
+ resolvePiExtensionRoot,
8
+ resolvePrimeAgentAgentHome,
9
+ resolvePrimeAgentExtensionRoot
10
+ } from "./chunk-C6JW4C3O.js";
9
11
 
10
12
  // src/publisher.ts
11
13
  import fs from "fs";
@@ -18,6 +20,157 @@ import {
18
20
  loadTokenStore,
19
21
  saveTokenStore
20
22
  } from "@remnic/core";
23
+
24
+ // src/omp-loader-templates.ts
25
+ function renderOmpLoader(pluginPiDistPath, bunBin) {
26
+ return [
27
+ "// Auto-generated by Remnic's OmpMemoryExtensionPublisher.",
28
+ "// omp's embedded runtime cannot resolve bare npm specifiers from this",
29
+ "// extension's node_modules, so we pre-bundle with `bun build` and import",
30
+ "// the self-contained bundle here. Rebuilt automatically when index.ts or",
31
+ "// the underlying @remnic/plugin-pi dist changes.",
32
+ "",
33
+ 'import { existsSync, renameSync, rmSync, statSync } from "node:fs";',
34
+ 'import { spawnSync } from "node:child_process";',
35
+ 'import { dirname, join } from "node:path";',
36
+ 'import { fileURLToPath, pathToFileURL } from "node:url";',
37
+ "",
38
+ "const here = dirname(fileURLToPath(import.meta.url));",
39
+ 'const bundleDir = join(here, "dist-bundle");',
40
+ 'const bundleEntry = join(bundleDir, "index.js");',
41
+ 'const sourceEntry = join(here, "index.ts");',
42
+ `const pluginPiEntry = ${JSON.stringify(pluginPiDistPath)};`,
43
+ // Reuse the bun path resolved at install time (REMNIC_OMP_BUN_BIN, PATH,
44
+ // or a common absolute location). Fall back to "bun" on PATH if the
45
+ // resolved path no longer exists (e.g. the extension tree was moved), so
46
+ // self-healing still works when bun is reachable only via PATH.
47
+ `const resolvedBunBin = ${JSON.stringify(bunBin)};`,
48
+ 'const bunForRebuild = resolvedBunBin && existsSync(resolvedBunBin) ? resolvedBunBin : "bun";',
49
+ "",
50
+ "function bundleIsStale() {",
51
+ " if (!existsSync(bundleEntry)) return true;",
52
+ " const bundleMtime = statSync(bundleEntry).mtimeMs;",
53
+ " if (existsSync(sourceEntry) && bundleMtime < statSync(sourceEntry).mtimeMs) return true;",
54
+ " if (pluginPiEntry && existsSync(pluginPiEntry) && bundleMtime < statSync(pluginPiEntry).mtimeMs) return true;",
55
+ " return false;",
56
+ "}",
57
+ "",
58
+ "function rebuildBundle() {",
59
+ " // Build to a temp dir and swap, mirroring the install-time build, so a",
60
+ " // failed self-heal rebuild never corrupts the working bundle.",
61
+ ' var tmp = join(here, ".dist-bundle.tmp-" + process.pid + "-" + Date.now());',
62
+ " var result = spawnSync(bunForRebuild, [",
63
+ ' "build",',
64
+ " sourceEntry,",
65
+ ' "--target=bun",',
66
+ ' "--outdir=" + tmp',
67
+ " ], {",
68
+ " cwd: here,",
69
+ ' stdio: "inherit",',
70
+ " });",
71
+ " if (result.status !== 0 || result.error) {",
72
+ " try { rmSync(tmp, { recursive: true, force: true }); } catch (e) {}",
73
+ " throw new Error(",
74
+ ' "Remnic omp extension: bundle is stale or missing and could not be rebuilt. " +',
75
+ ' "Install bun (https://bun.sh), then run " +',
76
+ ' "`bun build index.ts --target=bun --outdir=dist-bundle` inside " + here',
77
+ " );",
78
+ " }",
79
+ " var backup = null;",
80
+ " try {",
81
+ " if (existsSync(bundleDir)) {",
82
+ ' backup = join(here, ".dist-bundle.bak-" + process.pid + "-" + Date.now());',
83
+ " renameSync(bundleDir, backup);",
84
+ " }",
85
+ " renameSync(tmp, bundleDir);",
86
+ " if (backup) { try { rmSync(backup, { recursive: true, force: true }); } catch (e) {} }",
87
+ " } catch (err) {",
88
+ " try { rmSync(tmp, { recursive: true, force: true }); } catch (e) {}",
89
+ " if (backup && existsSync(backup) && !existsSync(bundleDir)) { try { renameSync(backup, bundleDir); } catch (e) {} }",
90
+ " throw new Error(",
91
+ ' "Remnic omp extension: failed to finalize rebuilt bundle - " + (err && err.message ? err.message : err)',
92
+ " );",
93
+ " }",
94
+ "}",
95
+ "",
96
+ "if (bundleIsStale()) rebuildBundle();",
97
+ "",
98
+ "// Cache-bust so a freshly rebuilt bundle is loaded instead of a stale cached copy.",
99
+ 'const bundle = await import(pathToFileURL(bundleEntry).href + "?t=" + Date.now());',
100
+ "export default bundle.default;",
101
+ ""
102
+ ].join("\n");
103
+ }
104
+ function renderOmpPackageJson() {
105
+ const manifest = {
106
+ name: "remnic-omp-extension",
107
+ version: "0.0.0",
108
+ private: true,
109
+ type: "module",
110
+ omp: { extensions: ["./loader.js"] },
111
+ // Legacy key so older omp builds that only read `pi.extensions` also
112
+ // resolve loader.js instead of falling through to index.ts.
113
+ pi: { extensions: ["./loader.js"] },
114
+ scripts: { postinstall: "node postinstall-bundle.cjs" }
115
+ };
116
+ return `${JSON.stringify(manifest, null, 2)}
117
+ `;
118
+ }
119
+ function renderOmpPostinstall(bunBin) {
120
+ return `// Auto-generated by Remnic's OmpMemoryExtensionPublisher.
121
+ // Re-bundles the omp extension after npm install (e.g. a plugin-pi upgrade)
122
+ // using the bun path resolved at install time, with a PATH fallback. Node-only
123
+ // so it runs under npm's default cmd.exe shell on Windows as well as POSIX bash.
124
+ "use strict";
125
+ var fs = require("node:fs");
126
+ var cp = require("node:child_process");
127
+ var path = require("node:path");
128
+
129
+ var RESOLVED_BUN = ${JSON.stringify(bunBin)};
130
+ var dir = __dirname;
131
+ var entry = path.join(dir, "index.ts");
132
+ var out = path.join(dir, "dist-bundle");
133
+
134
+ function pickBun() {
135
+ var env = process.env.REMNIC_OMP_BUN_BIN;
136
+ if (env && fs.existsSync(env)) return env;
137
+ if (RESOLVED_BUN && fs.existsSync(RESOLVED_BUN)) return RESOLVED_BUN;
138
+ return "bun";
139
+ }
140
+
141
+ function rebuild() {
142
+ var bun = pickBun();
143
+ var tmp = path.join(dir, ".dist-bundle.tmp-" + process.pid + "-" + Date.now());
144
+ var r = cp.spawnSync(bun, ["build", entry, "--target=bun", "--outdir=" + tmp], { cwd: dir, stdio: "inherit" });
145
+ if (r.error || r.status !== 0) {
146
+ try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (e) {}
147
+ throw new Error("Remnic omp extension: postinstall bun build failed (bun=" + bun + "). Run bun build index.ts --target=bun --outdir=dist-bundle manually inside " + dir);
148
+ }
149
+ var backup = null;
150
+ try {
151
+ if (fs.existsSync(out)) {
152
+ backup = path.join(dir, ".dist-bundle.bak-" + process.pid + "-" + Date.now());
153
+ fs.renameSync(out, backup);
154
+ }
155
+ fs.renameSync(tmp, out);
156
+ if (backup) { try { fs.rmSync(backup, { recursive: true, force: true }); } catch (e) {} }
157
+ } catch (err) {
158
+ try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (e) {}
159
+ if (backup && fs.existsSync(backup) && !fs.existsSync(out)) { try { fs.renameSync(backup, out); } catch (e) {} }
160
+ throw err;
161
+ }
162
+ }
163
+
164
+ try {
165
+ rebuild();
166
+ } catch (err) {
167
+ console.error(err && err.message ? err.message : err);
168
+ process.exit(1);
169
+ }
170
+ `;
171
+ }
172
+
173
+ // src/publisher.ts
21
174
  var DEFAULT_DAEMON_PORT = 4318;
22
175
  var BASE_OWNED_FILES = ["remnic.config.json", "index.ts", "README.md"];
23
176
  var EXTENSION_OWNED_TEMP_FILE_SUFFIX = /\.tmp-\d+-\d+$/u;
@@ -38,6 +191,20 @@ var OMP_HOST = {
38
191
  resolveExtensionRoot: resolveOmpExtensionRoot,
39
192
  listRemovalAgentHomes: ompRemovalAgentHomes
40
193
  };
194
+ var PRIME_AGENT_HOST = {
195
+ hostId: "prime-agent",
196
+ connectorId: "prime-agent",
197
+ displayName: "Prime Agent",
198
+ tokenGenerateHint: "remnic token generate prime-agent",
199
+ resolveAgentHome: resolvePrimeAgentAgentHome,
200
+ resolveExtensionRoot: resolvePrimeAgentExtensionRoot,
201
+ listRemovalAgentHomes: primeAgentRemovalAgentHomes
202
+ };
203
+ function primeAgentRemovalAgentHomes(env) {
204
+ const homes = /* @__PURE__ */ new Set([resolvePrimeAgentAgentHome(env)]);
205
+ homes.add(resolvePrimeAgentAgentHome({ HOME: env.HOME, USERPROFILE: env.USERPROFILE }));
206
+ return [...homes];
207
+ }
41
208
  function ompRemovalAgentHomes(env) {
42
209
  const homes = /* @__PURE__ */ new Set([resolveOmpAgentHome(env)]);
43
210
  const configRoot = resolveOmpConfigRoot(env);
@@ -280,6 +447,51 @@ var PiMemoryExtensionPublisher = class extends HostMemoryExtensionPublisher {
280
447
  super(PI_HOST);
281
448
  }
282
449
  };
450
+ var PrimeAgentMemoryExtensionPublisher = class extends HostMemoryExtensionPublisher {
451
+ constructor() {
452
+ super(PRIME_AGENT_HOST);
453
+ }
454
+ get ownedFileNames() {
455
+ return [...BASE_OWNED_FILES, "package.json"];
456
+ }
457
+ finalizePublish(_ctx, extensionRoot) {
458
+ atomicWriteFile(
459
+ path.join(extensionRoot, "package.json"),
460
+ renderPrimeAgentPackageJson(),
461
+ 420
462
+ );
463
+ }
464
+ };
465
+ function renderPrimeAgentPackageJson() {
466
+ const manifest = {
467
+ name: "remnic-prime-agent-extension",
468
+ version: "0.0.0",
469
+ private: true,
470
+ type: "module",
471
+ dependencies: {
472
+ "@remnic/plugin-pi": `^${readPluginPiVersion()}`
473
+ }
474
+ };
475
+ return `${JSON.stringify(manifest, null, 2)}
476
+ `;
477
+ }
478
+ function readPluginPiVersion() {
479
+ const manifestPath = path.resolve(
480
+ path.dirname(fileURLToPath(import.meta.url)),
481
+ "..",
482
+ "package.json"
483
+ );
484
+ try {
485
+ const parsed = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
486
+ if (typeof parsed.version === "string" && parsed.version.length > 0) {
487
+ return parsed.version;
488
+ }
489
+ } catch {
490
+ }
491
+ throw new Error(
492
+ `Remnic prime-agent extension: cannot read the @remnic/plugin-pi version from ${manifestPath}.`
493
+ );
494
+ }
283
495
  var OmpPreBundleError = class extends Error {
284
496
  };
285
497
  var OmpMemoryExtensionPublisher = class extends HostMemoryExtensionPublisher {
@@ -434,153 +646,6 @@ function renderWrapper(extensionModulePath, configPath, wrapperDir) {
434
646
  ""
435
647
  ].join("\n");
436
648
  }
437
- function renderOmpLoader(pluginPiDistPath, bunBin) {
438
- return [
439
- "// Auto-generated by Remnic's OmpMemoryExtensionPublisher.",
440
- "// omp's embedded runtime cannot resolve bare npm specifiers from this",
441
- "// extension's node_modules, so we pre-bundle with `bun build` and import",
442
- "// the self-contained bundle here. Rebuilt automatically when index.ts or",
443
- "// the underlying @remnic/plugin-pi dist changes.",
444
- "",
445
- 'import { existsSync, renameSync, rmSync, statSync } from "node:fs";',
446
- 'import { spawnSync } from "node:child_process";',
447
- 'import { dirname, join } from "node:path";',
448
- 'import { fileURLToPath, pathToFileURL } from "node:url";',
449
- "",
450
- "const here = dirname(fileURLToPath(import.meta.url));",
451
- 'const bundleDir = join(here, "dist-bundle");',
452
- 'const bundleEntry = join(bundleDir, "index.js");',
453
- 'const sourceEntry = join(here, "index.ts");',
454
- `const pluginPiEntry = ${JSON.stringify(pluginPiDistPath)};`,
455
- // Reuse the bun path resolved at install time (REMNIC_OMP_BUN_BIN, PATH,
456
- // or a common absolute location). Fall back to "bun" on PATH if the
457
- // resolved path no longer exists (e.g. the extension tree was moved), so
458
- // self-healing still works when bun is reachable only via PATH.
459
- `const resolvedBunBin = ${JSON.stringify(bunBin)};`,
460
- 'const bunForRebuild = resolvedBunBin && existsSync(resolvedBunBin) ? resolvedBunBin : "bun";',
461
- "",
462
- "function bundleIsStale() {",
463
- " if (!existsSync(bundleEntry)) return true;",
464
- " const bundleMtime = statSync(bundleEntry).mtimeMs;",
465
- " if (existsSync(sourceEntry) && bundleMtime < statSync(sourceEntry).mtimeMs) return true;",
466
- " if (pluginPiEntry && existsSync(pluginPiEntry) && bundleMtime < statSync(pluginPiEntry).mtimeMs) return true;",
467
- " return false;",
468
- "}",
469
- "",
470
- "function rebuildBundle() {",
471
- " // Build to a temp dir and swap, mirroring the install-time build, so a",
472
- " // failed self-heal rebuild never corrupts the working bundle.",
473
- ' var tmp = join(here, ".dist-bundle.tmp-" + process.pid + "-" + Date.now());',
474
- " var result = spawnSync(bunForRebuild, [",
475
- ' "build",',
476
- " sourceEntry,",
477
- ' "--target=bun",',
478
- ' "--outdir=" + tmp',
479
- " ], {",
480
- " cwd: here,",
481
- ' stdio: "inherit",',
482
- " });",
483
- " if (result.status !== 0 || result.error) {",
484
- " try { rmSync(tmp, { recursive: true, force: true }); } catch (e) {}",
485
- " throw new Error(",
486
- ' "Remnic omp extension: bundle is stale or missing and could not be rebuilt. " +',
487
- ' "Install bun (https://bun.sh), then run " +',
488
- ' "`bun build index.ts --target=bun --outdir=dist-bundle` inside " + here',
489
- " );",
490
- " }",
491
- " var backup = null;",
492
- " try {",
493
- " if (existsSync(bundleDir)) {",
494
- ' backup = join(here, ".dist-bundle.bak-" + process.pid + "-" + Date.now());',
495
- " renameSync(bundleDir, backup);",
496
- " }",
497
- " renameSync(tmp, bundleDir);",
498
- " if (backup) { try { rmSync(backup, { recursive: true, force: true }); } catch (e) {} }",
499
- " } catch (err) {",
500
- " try { rmSync(tmp, { recursive: true, force: true }); } catch (e) {}",
501
- " if (backup && existsSync(backup) && !existsSync(bundleDir)) { try { renameSync(backup, bundleDir); } catch (e) {} }",
502
- " throw new Error(",
503
- ' "Remnic omp extension: failed to finalize rebuilt bundle - " + (err && err.message ? err.message : err)',
504
- " );",
505
- " }",
506
- "}",
507
- "",
508
- "if (bundleIsStale()) rebuildBundle();",
509
- "",
510
- "// Cache-bust so a freshly rebuilt bundle is loaded instead of a stale cached copy.",
511
- 'const bundle = await import(pathToFileURL(bundleEntry).href + "?t=" + Date.now());',
512
- "export default bundle.default;",
513
- ""
514
- ].join("\n");
515
- }
516
- function renderOmpPackageJson() {
517
- const manifest = {
518
- name: "remnic-omp-extension",
519
- version: "0.0.0",
520
- private: true,
521
- type: "module",
522
- omp: { extensions: ["./loader.js"] },
523
- // Legacy key so older omp builds that only read `pi.extensions` also
524
- // resolve loader.js instead of falling through to index.ts.
525
- pi: { extensions: ["./loader.js"] },
526
- scripts: { postinstall: "node postinstall-bundle.cjs" }
527
- };
528
- return `${JSON.stringify(manifest, null, 2)}
529
- `;
530
- }
531
- function renderOmpPostinstall(bunBin) {
532
- return `// Auto-generated by Remnic's OmpMemoryExtensionPublisher.
533
- // Re-bundles the omp extension after npm install (e.g. a plugin-pi upgrade)
534
- // using the bun path resolved at install time, with a PATH fallback. Node-only
535
- // so it runs under npm's default cmd.exe shell on Windows as well as POSIX bash.
536
- "use strict";
537
- var fs = require("node:fs");
538
- var cp = require("node:child_process");
539
- var path = require("node:path");
540
-
541
- var RESOLVED_BUN = ${JSON.stringify(bunBin)};
542
- var dir = __dirname;
543
- var entry = path.join(dir, "index.ts");
544
- var out = path.join(dir, "dist-bundle");
545
-
546
- function pickBun() {
547
- var env = process.env.REMNIC_OMP_BUN_BIN;
548
- if (env && fs.existsSync(env)) return env;
549
- if (RESOLVED_BUN && fs.existsSync(RESOLVED_BUN)) return RESOLVED_BUN;
550
- return "bun";
551
- }
552
-
553
- function rebuild() {
554
- var bun = pickBun();
555
- var tmp = path.join(dir, ".dist-bundle.tmp-" + process.pid + "-" + Date.now());
556
- var r = cp.spawnSync(bun, ["build", entry, "--target=bun", "--outdir=" + tmp], { cwd: dir, stdio: "inherit" });
557
- if (r.error || r.status !== 0) {
558
- try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (e) {}
559
- throw new Error("Remnic omp extension: postinstall bun build failed (bun=" + bun + "). Run bun build index.ts --target=bun --outdir=dist-bundle manually inside " + dir);
560
- }
561
- var backup = null;
562
- try {
563
- if (fs.existsSync(out)) {
564
- backup = path.join(dir, ".dist-bundle.bak-" + process.pid + "-" + Date.now());
565
- fs.renameSync(out, backup);
566
- }
567
- fs.renameSync(tmp, out);
568
- if (backup) { try { fs.rmSync(backup, { recursive: true, force: true }); } catch (e) {} }
569
- } catch (err) {
570
- try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (e) {}
571
- if (backup && fs.existsSync(backup) && !fs.existsSync(out)) { try { fs.renameSync(backup, out); } catch (e) {} }
572
- throw err;
573
- }
574
- }
575
-
576
- try {
577
- rebuild();
578
- } catch (err) {
579
- console.error(err && err.message ? err.message : err);
580
- process.exit(1);
581
- }
582
- `;
583
- }
584
649
  function isExecutableFile(candidate) {
585
650
  try {
586
651
  const stat = fs.statSync(candidate);
@@ -844,6 +909,7 @@ export {
844
909
  HostMemoryExtensionPublisher,
845
910
  OmpMemoryExtensionPublisher,
846
911
  PiMemoryExtensionPublisher,
912
+ PrimeAgentMemoryExtensionPublisher,
847
913
  resolveBunBinary,
848
914
  resolveBunOnPath,
849
915
  resolveOmpWrapperImportSpecifier
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/publisher.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport { spawnSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport os from \"node:os\";\n\nimport {\n type MemoryExtensionPublisher,\n type PublishContext,\n type PublishResult,\n type PublisherCapabilities,\n type TokenEntry,\n getConnectorToken,\n loadTokenStore,\n saveTokenStore,\n} from \"@remnic/core\";\n\nimport {\n resolveOmpAgentHome,\n resolveOmpConfigRoot,\n resolveOmpExtensionRoot,\n resolvePiAgentHome,\n resolvePiExtensionRoot,\n} from \"./paths.js\";\nimport { DEFAULT_CONFIG } from \"./config.js\";\n\nconst DEFAULT_DAEMON_PORT = 4318;\nconst BASE_OWNED_FILES = [\"remnic.config.json\", \"index.ts\", \"README.md\"] as const;\nconst EXTENSION_OWNED_TEMP_FILE_SUFFIX = /\\.tmp-\\d+-\\d+$/u;\n\ntype FileSnapshot = {\n path: string;\n existed: boolean;\n content?: Buffer;\n mode?: number;\n};\n\ntype DirSnapshot = {\n path: string;\n existed: boolean;\n};\n\n/**\n * Host-specific parameters for a Pi-family memory extension publisher.\n *\n * The Remnic runtime extension is host-neutral (it only uses Pi's extension\n * hooks, which omp preserves as a superset), so the only things that vary\n * between hosts are *where* the extension is installed, *which* connector\n * token it uses, and *how* it is labelled. Everything else — atomic writes,\n * rollback, symlink guards, config merge — is shared.\n */\nexport interface HostPublisherDescriptor {\n readonly hostId: string;\n readonly connectorId: string;\n readonly displayName: string;\n readonly tokenGenerateHint: string;\n resolveAgentHome(env: NodeJS.ProcessEnv): string;\n resolveExtensionRoot(env: NodeJS.ProcessEnv): string;\n /**\n * Optional: every agent home `unpublish` should sweep for a stale extension,\n * beyond the one resolved from the current env. Hosts with env-sensitive\n * install locations (e.g. omp profiles) provide this so `remnic connectors\n * remove` cleans up even when the remove-time env differs from install time.\n */\n listRemovalAgentHomes?(env: NodeJS.ProcessEnv): string[];\n}\n\nconst PI_HOST: HostPublisherDescriptor = {\n hostId: \"pi\",\n connectorId: \"pi\",\n displayName: \"Pi Coding Agent\",\n tokenGenerateHint: \"remnic token generate pi\",\n resolveAgentHome: resolvePiAgentHome,\n resolveExtensionRoot: resolvePiExtensionRoot,\n};\n\nconst OMP_HOST: HostPublisherDescriptor = {\n hostId: \"omp\",\n connectorId: \"omp\",\n displayName: \"Oh My Pi (omp)\",\n tokenGenerateHint: \"remnic token generate omp\",\n resolveAgentHome: resolveOmpAgentHome,\n resolveExtensionRoot: resolveOmpExtensionRoot,\n listRemovalAgentHomes: ompRemovalAgentHomes,\n};\n\n/**\n * Every omp agent home a stale extension might live under, so `unpublish` cleans\n * up regardless of the profile/env active at remove time: the env-resolved home,\n * the base `<configRoot>/agent`, an explicit `PI_CODING_AGENT_DIR`, and every\n * existing `<configRoot>/profiles/<name>/agent`. Symlinked profile dirs are\n * skipped defensively.\n */\nfunction ompRemovalAgentHomes(env: NodeJS.ProcessEnv): string[] {\n const homes = new Set<string>([resolveOmpAgentHome(env)]);\n const configRoot = resolveOmpConfigRoot(env);\n homes.add(path.join(configRoot, \"agent\"));\n\n const explicit = env.PI_CODING_AGENT_DIR?.trim();\n if (explicit) homes.add(path.resolve(explicit));\n\n const profilesDir = path.join(configRoot, \"profiles\");\n let entries: fs.Dirent[] = [];\n try {\n entries = fs.readdirSync(profilesDir, { withFileTypes: true });\n } catch {\n entries = [];\n }\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.isSymbolicLink()) {\n homes.add(path.join(profilesDir, entry.name, \"agent\"));\n }\n }\n return [...homes];\n}\n\n/**\n * Shared publisher for Pi-family hosts. Concrete hosts (Pi, omp) subclass this\n * with a {@link HostPublisherDescriptor}; the install/rollback machinery is\n * identical across hosts.\n */\nexport class HostMemoryExtensionPublisher implements MemoryExtensionPublisher {\n static readonly capabilities: PublisherCapabilities = {\n // Real publisher: writes host config + wrapper + readme, just no\n // instructions.md/skills/citation/read-path-template artefacts. The\n // explicit flag prevents the parity gate from mis-inferring \"all flags\n // false ⇒ stub\" for this host (#1518).\n isStub: false,\n instructionsMd: false,\n skillsFolder: false,\n citationFormat: false,\n readPathTemplate: false,\n };\n\n protected constructor(private readonly host: HostPublisherDescriptor) {}\n\n /**\n * File basenames this publisher owns inside the extension root. The shared\n * set is config + wrapper + readme; subclasses add host-specific files\n * (e.g. omp's pre-bundle loader + package manifest). Used for snapshot,\n * atomic-write rollback, and unpublish cleanup.\n */\n protected get ownedFileNames(): readonly string[] {\n return BASE_OWNED_FILES;\n }\n\n /**\n * Directory names this publisher owns inside the extension root (build\n * outputs). Recursively removed on unpublish and on publish rollback when\n * newly created.\n */\n protected get ownedDirNames(): readonly string[] {\n return [];\n }\n\n /**\n * Whether the generated wrapper must use a bun-buildable import specifier\n * (relative path) instead of a file:// URL. omp pre-bundles the wrapper with\n * `bun build`, which cannot resolve file:// specifiers; pi loads the wrapper\n * directly via tsx and keeps the file:// URL.\n */\n protected get usesBundledWrapper(): boolean {\n return false;\n }\n\n /**\n * Hook for subclasses to write host-specific files and run install-time\n * build steps after the shared config/wrapper/readme are written. Runs\n * inside the publish try-block: a throw triggers full rollback.\n */\n protected finalizePublish(\n _ctx: PublishContext,\n _extensionRoot: string,\n _paths: { configPath: string; wrapperPath: string; pluginPiDistPath: string },\n ): void {\n // No-op by default; subclasses override.\n }\n\n get hostId(): string {\n return this.host.hostId;\n }\n\n async resolveExtensionRoot(env?: NodeJS.ProcessEnv): Promise<string> {\n return this.host.resolveExtensionRoot(env ?? process.env);\n }\n\n async isHostAvailable(): Promise<boolean> {\n // Pi-family agents auto-discover extensions from their agent extensions\n // directory. The directory can be created before the agent has been\n // launched, so availability should not block first-time installation.\n return true;\n }\n\n async renderInstructions(ctx: PublishContext): Promise<string> {\n const namespace = ctx.config.namespace ?? \"default\";\n const daemonUrl = resolveDaemonUrl(ctx);\n return [\n `# Remnic for ${this.host.displayName}`,\n \"\",\n `Remnic provides memory, retrieval, observation, MCP tools, and long-context compaction coordination for ${this.host.displayName}.`,\n \"\",\n \"## Installed Capabilities\",\n \"\",\n \"- Recall relevant Remnic context in the `before_agent_start` hook via system prompt injection.\",\n '- Observe user, assistant, and tool messages with `sourceFormat: \"pi\"`.',\n \"- Coordinate `session_before_compact` with Remnic LCM flush and checkpoint recording.\",\n \"- Register Remnic MCP tools as host tools when daemon authentication is configured.\",\n \"- Persist lightweight dedupe state in custom entries via `appendEntry`.\",\n \"\",\n \"## Runtime\",\n \"\",\n `- Remnic daemon: \\`${daemonUrl}\\``,\n `- Namespace: \\`${namespace}\\``,\n `- Memory directory: \\`${ctx.config.memoryDir}\\``,\n \"\",\n \"The private `remnic.config.json` file stores the daemon URL, namespace, and connector auth token with owner-only permissions.\",\n ].join(\"\\n\");\n }\n\n async publish(ctx: PublishContext): Promise<PublishResult> {\n const extensionRoot = await this.resolveExtensionRoot();\n const agentHome = this.host.resolveAgentHome(process.env);\n assertSafeExtensionRoot(extensionRoot, agentHome);\n const filesWritten: string[] = [];\n const skipped: string[] = [];\n\n ctx.log.info(`Publishing ${this.host.displayName} memory extension to ${extensionRoot}`);\n\n const ownedFilePaths = this.ownedFileNames.map((fileName) => path.join(extensionRoot, fileName));\n const configPath = ownedFilePaths[0];\n const wrapperPath = ownedFilePaths[1];\n const readmePath = ownedFilePaths[2];\n const pluginPiDistPath = resolveExtensionModulePath();\n const rootExisted = fs.existsSync(extensionRoot);\n const fileSnapshots = snapshotFiles(ownedFilePaths);\n const dirSnapshots = snapshotDirs(\n this.ownedDirNames.map((dirName) => path.join(extensionRoot, dirName)),\n );\n const priorTokenEntry =\n ctx.rollbackTokenEntry === undefined\n ? snapshotTokenEntry(this.host.connectorId)\n : cloneTokenEntry(ctx.rollbackTokenEntry);\n\n const token = getConnectorToken(this.host.connectorId);\n if (!token) {\n skipped.push(\n `auth token unavailable; run \\`${this.host.tokenGenerateHint}\\` and reinstall the connector`,\n );\n }\n\n try {\n const priorConfig = readPriorConfig(configPath);\n const config: Record<string, unknown> = {\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 recallTimeoutThreshold: DEFAULT_CONFIG.recallTimeoutThreshold,\n recallTimeoutWindow: DEFAULT_CONFIG.recallTimeoutWindow,\n ...priorConfig,\n remnicDaemonUrl: resolveDaemonUrl(ctx),\n };\n if (token) {\n config.authToken = token;\n }\n if (ctx.config.namespace) {\n config.namespace = ctx.config.namespace;\n }\n\n mkdirExtensionRoot(extensionRoot, agentHome);\n\n atomicWriteFile(configPath, `${JSON.stringify(config, null, 2)}\\n`, 0o600);\n filesWritten.push(configPath);\n\n atomicWriteFile(\n wrapperPath,\n renderWrapper(\n pluginPiDistPath,\n configPath,\n this.usesBundledWrapper ? extensionRoot : undefined,\n ),\n 0o644,\n );\n filesWritten.push(wrapperPath);\n\n atomicWriteFile(readmePath, `${await this.renderInstructions(ctx)}\\n`, 0o644);\n filesWritten.push(readmePath);\n\n this.finalizePublish(ctx, extensionRoot, { configPath, wrapperPath, pluginPiDistPath });\n for (let i = BASE_OWNED_FILES.length; i < ownedFilePaths.length; i++) {\n filesWritten.push(ownedFilePaths[i]);\n }\n } catch (err) {\n try {\n // Remove newly created owned dirs (e.g. dist-bundle) BEFORE\n // restorePublishSnapshot's removeEmptyDirectory check, otherwise a\n // first-time publish that created dist-bundle would leave an empty\n // extension root behind on rollback.\n restoreDirSnapshots(dirSnapshots);\n restorePublishSnapshot(extensionRoot, rootExisted, fileSnapshots);\n } catch (restoreErr) {\n ctx.log.warn(\n `${this.host.displayName} extension rollback failed: ${restoreErr instanceof Error ? restoreErr.message : String(restoreErr)}`,\n );\n }\n // A failed omp pre-bundle (bun missing or `bun build` failing) is\n // recoverable: the runtime loader self-heals dist-bundle on first load,\n // and the connector token is already committed by the CLI. Rolling it\n // back here would leave the connector registered with no credential and\n // block a non-`--force` reinstall (AGENTS.md #14 — don't destroy\n // committed state before the new state is confirmed). File/dir rollback\n // above still runs, so a failed first-time publish still cleans its root.\n if (!(err instanceof OmpPreBundleError)) {\n try {\n restoreTokenEntry(priorTokenEntry, this.host.connectorId);\n } catch (tokenErr) {\n ctx.log.warn(\n `${this.host.displayName} connector token rollback failed: ${tokenErr instanceof Error ? tokenErr.message : String(tokenErr)}`,\n );\n }\n }\n throw err;\n }\n\n return {\n hostId: this.host.hostId,\n extensionRoot,\n filesWritten,\n skipped,\n };\n }\n\n async unpublish(): Promise<void> {\n const agentHomes = this.host.listRemovalAgentHomes\n ? this.host.listRemovalAgentHomes(process.env)\n : [this.host.resolveAgentHome(process.env)];\n\n const ownedFileNames = this.ownedFileNames;\n const ownedDirNames = this.ownedDirNames;\n const seen = new Set<string>();\n for (const agentHome of agentHomes) {\n const extensionRoot = path.join(path.resolve(agentHome), \"extensions\", \"remnic\");\n if (seen.has(extensionRoot)) continue;\n seen.add(extensionRoot);\n if (!fs.existsSync(extensionRoot)) continue;\n\n assertSafeExtensionRoot(extensionRoot, agentHome);\n const removableFiles = removableOwnedExtensionFiles(\n extensionOwnedUnpublishPaths(extensionRoot, ownedFileNames),\n );\n for (const filePath of removableFiles) {\n fs.rmSync(filePath, { force: true });\n }\n for (const dirName of ownedDirNames) {\n const dirPath = path.join(extensionRoot, dirName);\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(dirPath);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") continue;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${dirPath}`);\n }\n if (stat.isDirectory()) {\n fs.rmSync(dirPath, { recursive: true, force: true });\n }\n }\n removeEmptyDirectory(extensionRoot);\n }\n }\n}\n\n/** Publisher for upstream Pi (`~/.pi/agent/extensions/remnic`). */\nexport class PiMemoryExtensionPublisher extends HostMemoryExtensionPublisher {\n constructor() {\n super(PI_HOST);\n }\n}\n\n/**\n * Marks a failure originating from the omp pre-bundle step (bun missing, or the\n * `bun build` itself failing). {@link HostMemoryExtensionPublisher.publish}\n * catches this and rolls back the written files but SKIPS the connector-token\n * rollback: the pre-bundle runs after the install (config + wrapper + token) is\n * already committed, and the runtime loader self-heals `dist-bundle` on first\n * load, so destroying the just-generated token would leave the connector\n * registered with no credential and block a non-`--force` reinstall\n * (AGENTS.md #14 — don't destroy committed state before the new state is\n * confirmed). The message is preserved verbatim so existing `/requires \\`bun\\`/`\n * and `/bun build failed/` assertions still match.\n */\nclass OmpPreBundleError extends Error {}\n\n/** Publisher for Oh My Pi / omp (`~/.omp/agent/extensions/remnic`). */\nexport class OmpMemoryExtensionPublisher extends HostMemoryExtensionPublisher {\n constructor() {\n super(OMP_HOST);\n }\n\n protected get ownedFileNames(): readonly string[] {\n return [...BASE_OWNED_FILES, \"loader.js\", \"package.json\", \"postinstall-bundle.cjs\"];\n }\n\n protected get ownedDirNames(): readonly string[] {\n return [\"dist-bundle\"];\n }\n\n // omp pre-bundles index.ts with `bun build`; the wrapper must use a relative\n // import specifier (bun's bundler cannot resolve file:// URLs).\n protected get usesBundledWrapper(): boolean {\n return true;\n }\n\n protected finalizePublish(\n ctx: PublishContext,\n extensionRoot: string,\n paths: { configPath: string; wrapperPath: string; pluginPiDistPath: string },\n ): void {\n // Resolve once so the install-time build and the generated loader share the\n // same bun path — a loader that hardcodes \"bun\" cannot self-heal when bun\n // is reachable only via REMNIC_OMP_BUN_BIN or a common absolute install\n // path that is not on omp's PATH at runtime.\n const bunBin = resolveBunBinary();\n if (!bunBin) {\n // OmpPreBundleError so publish() keeps the connector token intact (see\n // the class doc); the runtime loader self-heals the bundle once bun is\n // installed.\n throw new OmpPreBundleError(\n \"Remnic omp extension requires `bun` to pre-bundle the extension: omp's embedded \" +\n \"runtime cannot resolve bare npm specifiers from the extension's node_modules. \" +\n \"Install bun from https://bun.sh, then re-run `remnic connectors install omp`.\",\n );\n }\n\n const loaderPath = path.join(extensionRoot, \"loader.js\");\n const packageJsonPath = path.join(extensionRoot, \"package.json\");\n\n const postinstallPath = path.join(extensionRoot, \"postinstall-bundle.cjs\");\n\n atomicWriteFile(loaderPath, renderOmpLoader(paths.pluginPiDistPath, bunBin), 0o644);\n // Cross-platform postinstall helper (Node-only) so npm's default cmd.exe\n // shell on Windows re-bundles after `npm install`; the POSIX one-liner it\n // replaces only ran under bash.\n atomicWriteFile(postinstallPath, renderOmpPostinstall(bunBin), 0o644);\n atomicWriteFile(packageJsonPath, renderOmpPackageJson(), 0o644);\n\n try {\n this.runBundleBuild(ctx, extensionRoot, bunBin);\n } catch (err) {\n // OmpPreBundleError so publish() keeps the connector token intact (see\n // the class doc); the runtime loader self-heals the bundle on next load.\n const message = err instanceof Error ? err.message : String(err);\n throw new OmpPreBundleError(message);\n }\n }\n\n /**\n * Pre-bundles the omp extension with `bun build` so omp's embedded runtime\n * never resolves bare npm specifiers (e.g. @sinclair/typebox) from the\n * extension's node_modules at load time. The bundle is written to a temp\n * directory and swapped into dist-bundle/ on success. The pre-existing\n * dist-bundle is renamed aside (not removed) before the swap, so a failure\n * during the final rename restores the previously working bundle rather than\n * leaving the install with no bundle at all.\n *\n * Override in tests to skip the real bun invocation.\n */\n protected runBundleBuild(ctx: PublishContext, extensionRoot: string, bunBin: string): void {\n const sourceEntry = path.join(extensionRoot, \"index.ts\");\n const tmpOutDir = path.join(extensionRoot, `.dist-bundle.tmp-${process.pid}-${Date.now()}`);\n const finalOutDir = path.join(extensionRoot, \"dist-bundle\");\n\n const result = spawnSync(bunBin, [\"build\", sourceEntry, \"--target=bun\", `--outdir=${tmpOutDir}`], {\n cwd: extensionRoot,\n encoding: \"utf-8\",\n });\n\n if (result.error || result.status !== 0) {\n try {\n fs.rmSync(tmpOutDir, { recursive: true, force: true });\n } catch {\n // best-effort tmp cleanup\n }\n const detail =\n (typeof result.stderr === \"string\" ? result.stderr.trim() : \"\") ||\n (result.error instanceof Error ? result.error.message : \"\") ||\n `bun exited with status ${result.status ?? \"null\"}`;\n throw new Error(\n `Remnic omp extension: bun build failed (${detail}). Resolve the error and re-run ` +\n \"`remnic connectors install omp`, or build manually with \" +\n \"`bun build index.ts --target=bun --outdir=dist-bundle` inside \" +\n `${extensionRoot}.`,\n );\n }\n\n // Swap the freshly built bundle into place without ever leaving the install\n // bundle-less. Rename the existing dist-bundle aside, move the new one in,\n // and only then discard the backup. On any failure mid-swap, restore the\n // backup so the previously working bundle survives (the publish-level\n // rollback only removes newly created dirs — it never restores a removed\n // dist-bundle, so we must not remove it here).\n let backupDir: string | null = null;\n try {\n if (fs.existsSync(finalOutDir)) {\n backupDir = path.join(extensionRoot, `.dist-bundle.bak-${process.pid}-${Date.now()}`);\n fs.renameSync(finalOutDir, backupDir);\n }\n fs.renameSync(tmpOutDir, finalOutDir);\n if (backupDir) {\n try {\n fs.rmSync(backupDir, { recursive: true, force: true });\n } catch {\n // best-effort backup cleanup; leaving it does not break the install\n }\n }\n } catch (err) {\n try {\n if (fs.existsSync(tmpOutDir)) fs.rmSync(tmpOutDir, { recursive: true, force: true });\n } catch {\n // best-effort tmp cleanup\n }\n // Restore the previously working bundle if we moved it aside and the\n // final swap did not land.\n if (backupDir && fs.existsSync(backupDir) && !fs.existsSync(finalOutDir)) {\n try {\n fs.renameSync(backupDir, finalOutDir);\n } catch {\n // best-effort restore; the loader's self-heal rebuilds on next start\n }\n }\n throw new Error(\n `Remnic omp extension: failed to finalize bundle output — ${err instanceof Error ? err.message : String(err)}.`,\n );\n }\n\n ctx.log.info(`Pre-bundled omp extension into ${finalOutDir}`);\n }\n}\n\nfunction extensionOwnedPaths(extensionRoot: string, ownedFileNames: readonly string[]): string[] {\n return ownedFileNames.map((fileName) => path.join(extensionRoot, fileName));\n}\n\nfunction extensionOwnedUnpublishPaths(extensionRoot: string, ownedFileNames: readonly string[]): string[] {\n const ownedBaseNames = new Set(ownedFileNames);\n const ownedPaths = extensionOwnedPaths(extensionRoot, ownedFileNames);\n for (const fileName of fs.readdirSync(extensionRoot)) {\n const match = EXTENSION_OWNED_TEMP_FILE_SUFFIX.exec(fileName);\n if (match && ownedBaseNames.has(fileName.slice(0, match.index))) {\n ownedPaths.push(path.join(extensionRoot, fileName));\n }\n }\n return ownedPaths;\n}\n\nfunction resolveDaemonUrl(ctx: PublishContext): string {\n if (ctx.config.daemonUrl && ctx.config.daemonUrl.trim().length > 0) {\n return trimTrailingSlashes(ctx.config.daemonUrl.trim());\n }\n return `http://127.0.0.1:${ctx.config.daemonPort ?? DEFAULT_DAEMON_PORT}`;\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\nfunction resolveExtensionModulePath(): string {\n const moduleDir = path.dirname(fileURLToPath(import.meta.url));\n const built = path.join(moduleDir, \"index.js\");\n if (fs.existsSync(built)) return built;\n\n const source = path.join(moduleDir, \"index.ts\");\n if (fs.existsSync(source)) return source;\n\n return built;\n}\n\n/**\n * Resolves the import specifier the omp wrapper uses to reach the\n * `@remnic/plugin-pi` dist entry from the generated `index.ts`. omp pre-bundles\n * that wrapper with `bun build`, whose bundler cannot resolve `file://`\n * specifiers (\"Could not resolve: file://…\" on Bun 1.2–1.3, verified), so the\n * specifier must be a relative path. On Windows, when the extension directory\n * and the plugin-pi install sit on different drives, `path.relative` cannot\n * express a relative path and returns an absolute drive path (e.g. `D:\\…`);\n * prefixing `./` then yields an invalid module specifier that fails `bun build`\n * with a cryptic error. Detect that layout and fail fast with an actionable\n * message instead. (Cross-drive omp installs are unsupported because neither a\n * relative specifier nor a `file://` URL is acceptable to `bun build`.) Drive\n * roots are compared case-insensitively so a same-drive Windows install is not\n * falsely rejected when the agent home and the plugin-pi install report the\n * drive letter in different casing (`C:\\\\` vs `c:\\\\`).\n *\n * Exported so the cross-drive guard can be exercised on non-Windows hosts via\n * `path.win32`.\n */\nexport function resolveOmpWrapperImportSpecifier(\n extensionModulePath: string,\n wrapperDir: string,\n pathApi: typeof path = path,\n): string {\n // Windows drive roots are case-insensitive: `C:\\\\…` (e.g. from the omp agent\n // home) and `c:\\\\…` (e.g. from fileURLToPath(import.meta.url)) are the SAME\n // drive, and path.win32.relative yields a valid relative specifier between\n // them. Compare the parsed roots case-insensitively so a same-drive install\n // isn't falsely rejected as \"different drives\". posix roots (`/`) are\n // unaffected by toLowerCase().\n if (pathApi.parse(wrapperDir).root.toLowerCase() !== pathApi.parse(extensionModulePath).root.toLowerCase()) {\n throw new Error(\n \"Remnic omp extension cannot pre-bundle: the extension directory \" +\n `(${wrapperDir}) and the @remnic/plugin-pi install (${extensionModulePath}) ` +\n \"are on different drives, so no relative import specifier can be generated \" +\n \"for `bun build` (and `bun build` cannot resolve a `file://` specifier). \" +\n \"Move the omp agent home and the Remnic install onto the same drive.\",\n );\n }\n let rel = pathApi.relative(wrapperDir, extensionModulePath);\n rel = rel.split(pathApi.sep).join(\"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\nfunction renderWrapper(\n extensionModulePath: string,\n configPath: string,\n wrapperDir?: string,\n): string {\n // omp pre-bundles this entry with `bun build`, whose bundler cannot resolve\n // `file://` specifiers — it exits with \"Could not resolve: file://...\" on\n // Bun 1.2–1.3 (verified). When the wrapper will be bun-built, emit a relative\n // specifier resolved against the wrapper's directory; bun, tsx, and Node ESM\n // all resolve relative specifiers. For tsx-loaded wrappers (pi) the file://\n // URL is retained.\n let importSpecifier: string;\n if (wrapperDir) {\n importSpecifier = resolveOmpWrapperImportSpecifier(extensionModulePath, wrapperDir);\n } else {\n importSpecifier = pathToFileURL(extensionModulePath).href;\n }\n return [\n `import { createRemnicPiExtension } from ${JSON.stringify(importSpecifier)};`,\n \"\",\n `export default createRemnicPiExtension({ configPath: ${JSON.stringify(configPath)} });`,\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Generates the self-healing `loader.js` that omp loads via the package\n * manifest's `omp.extensions` entry. It mtime-compares the pre-bundled\n * `dist-bundle/index.js` against `index.ts` and the underlying @remnic/plugin-pi\n * dist, rebuilds via `bun build` when stale (e.g. after an `npm update`), then\n * imports the self-contained bundle so omp's embedded runtime never resolves\n * bare npm specifiers at load time.\n */\nfunction renderOmpLoader(pluginPiDistPath: string, bunBin: string): string {\n return [\n \"// Auto-generated by Remnic's OmpMemoryExtensionPublisher.\",\n \"// omp's embedded runtime cannot resolve bare npm specifiers from this\",\n \"// extension's node_modules, so we pre-bundle with `bun build` and import\",\n \"// the self-contained bundle here. Rebuilt automatically when index.ts or\",\n \"// the underlying @remnic/plugin-pi dist changes.\",\n \"\",\n 'import { existsSync, renameSync, rmSync, statSync } from \"node:fs\";',\n 'import { spawnSync } from \"node:child_process\";',\n 'import { dirname, join } from \"node:path\";',\n 'import { fileURLToPath, pathToFileURL } from \"node:url\";',\n \"\",\n 'const here = dirname(fileURLToPath(import.meta.url));',\n 'const bundleDir = join(here, \"dist-bundle\");',\n 'const bundleEntry = join(bundleDir, \"index.js\");',\n 'const sourceEntry = join(here, \"index.ts\");',\n `const pluginPiEntry = ${JSON.stringify(pluginPiDistPath)};`,\n // Reuse the bun path resolved at install time (REMNIC_OMP_BUN_BIN, PATH,\n // or a common absolute location). Fall back to \"bun\" on PATH if the\n // resolved path no longer exists (e.g. the extension tree was moved), so\n // self-healing still works when bun is reachable only via PATH.\n `const resolvedBunBin = ${JSON.stringify(bunBin)};`,\n 'const bunForRebuild = resolvedBunBin && existsSync(resolvedBunBin) ? resolvedBunBin : \"bun\";',\n \"\",\n \"function bundleIsStale() {\",\n \" if (!existsSync(bundleEntry)) return true;\",\n \" const bundleMtime = statSync(bundleEntry).mtimeMs;\",\n \" if (existsSync(sourceEntry) && bundleMtime < statSync(sourceEntry).mtimeMs) return true;\",\n \" if (pluginPiEntry && existsSync(pluginPiEntry) && bundleMtime < statSync(pluginPiEntry).mtimeMs) return true;\",\n \" return false;\",\n \"}\",\n \"\",\n \"function rebuildBundle() {\",\n \" // Build to a temp dir and swap, mirroring the install-time build, so a\",\n \" // failed self-heal rebuild never corrupts the working bundle.\",\n ' var tmp = join(here, \".dist-bundle.tmp-\" + process.pid + \"-\" + Date.now());',\n \" var result = spawnSync(bunForRebuild, [\",\n ' \"build\",',\n \" sourceEntry,\",\n ' \"--target=bun\",',\n ' \"--outdir=\" + tmp',\n \" ], {\",\n \" cwd: here,\",\n ' stdio: \"inherit\",',\n \" });\",\n \" if (result.status !== 0 || result.error) {\",\n \" try { rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\",\n \" throw new Error(\",\n ' \"Remnic omp extension: bundle is stale or missing and could not be rebuilt. \" +',\n ' \"Install bun (https://bun.sh), then run \" +',\n ' \"`bun build index.ts --target=bun --outdir=dist-bundle` inside \" + here',\n \" );\",\n \" }\",\n \" var backup = null;\",\n \" try {\",\n \" if (existsSync(bundleDir)) {\",\n ' backup = join(here, \".dist-bundle.bak-\" + process.pid + \"-\" + Date.now());',\n \" renameSync(bundleDir, backup);\",\n \" }\",\n \" renameSync(tmp, bundleDir);\",\n \" if (backup) { try { rmSync(backup, { recursive: true, force: true }); } catch (e) {} }\",\n \" } catch (err) {\",\n \" try { rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\",\n \" if (backup && existsSync(backup) && !existsSync(bundleDir)) { try { renameSync(backup, bundleDir); } catch (e) {} }\",\n \" throw new Error(\",\n ' \"Remnic omp extension: failed to finalize rebuilt bundle - \" + (err && err.message ? err.message : err)',\n \" );\",\n \" }\",\n \"}\",\n\n \"\",\n \"if (bundleIsStale()) rebuildBundle();\",\n \"\",\n \"// Cache-bust so a freshly rebuilt bundle is loaded instead of a stale cached copy.\",\n 'const bundle = await import(pathToFileURL(bundleEntry).href + \"?t=\" + Date.now());',\n \"export default bundle.default;\",\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Generates the `package.json` that tells omp to load `loader.js` (not\n * auto-discover `index.ts`) and re-bundles after `npm install` via postinstall.\n */\nfunction renderOmpPackageJson(): string {\n // Postinstall re-bundles after `npm install` (e.g. a plugin-pi upgrade moved\n // the dist mtime past the bundle). It delegates to postinstall-bundle.cjs — a\n // Node-only helper — so npm's default cmd.exe shell on Windows runs it just as\n // well as POSIX bash. The helper embeds the resolved bun path with a PATH\n // fallback and swaps the bundle atomically.\n const manifest = {\n name: \"remnic-omp-extension\",\n version: \"0.0.0\",\n private: true,\n type: \"module\",\n omp: { extensions: [\"./loader.js\"] },\n // Legacy key so older omp builds that only read `pi.extensions` also\n // resolve loader.js instead of falling through to index.ts.\n pi: { extensions: [\"./loader.js\"] },\n scripts: { postinstall: \"node postinstall-bundle.cjs\" },\n };\n return `${JSON.stringify(manifest, null, 2)}\\n`;\n}\n\n/**\n * Generates the cross-platform `postinstall-bundle.cjs` helper. Node-only, so\n * npm's default cmd.exe shell on Windows re-bundles after `npm install` just as\n * well as POSIX bash. Embeds the bun path resolved at install time with a PATH\n * fallback and writes the new bundle via a temp-dir swap so a failed rebuild\n * never corrupts the working bundle. The emitted script uses string\n * concatenation (no template literals) so it stays parseable everywhere.\n */\nfunction renderOmpPostinstall(bunBin: string): string {\n // Single template literal: the emitted .cjs uses string concatenation (no\n // template literals of its own), so this body has no backticks and the one\n // ${JSON.stringify(bunBin)} interpolation is unambiguous.\n return `// Auto-generated by Remnic's OmpMemoryExtensionPublisher.\n// Re-bundles the omp extension after npm install (e.g. a plugin-pi upgrade)\n// using the bun path resolved at install time, with a PATH fallback. Node-only\n// so it runs under npm's default cmd.exe shell on Windows as well as POSIX bash.\n\"use strict\";\nvar fs = require(\"node:fs\");\nvar cp = require(\"node:child_process\");\nvar path = require(\"node:path\");\n\nvar RESOLVED_BUN = ${JSON.stringify(bunBin)};\nvar dir = __dirname;\nvar entry = path.join(dir, \"index.ts\");\nvar out = path.join(dir, \"dist-bundle\");\n\nfunction pickBun() {\n var env = process.env.REMNIC_OMP_BUN_BIN;\n if (env && fs.existsSync(env)) return env;\n if (RESOLVED_BUN && fs.existsSync(RESOLVED_BUN)) return RESOLVED_BUN;\n return \"bun\";\n}\n\nfunction rebuild() {\n var bun = pickBun();\n var tmp = path.join(dir, \".dist-bundle.tmp-\" + process.pid + \"-\" + Date.now());\n var r = cp.spawnSync(bun, [\"build\", entry, \"--target=bun\", \"--outdir=\" + tmp], { cwd: dir, stdio: \"inherit\" });\n if (r.error || r.status !== 0) {\n try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\n throw new Error(\"Remnic omp extension: postinstall bun build failed (bun=\" + bun + \"). Run bun build index.ts --target=bun --outdir=dist-bundle manually inside \" + dir);\n }\n var backup = null;\n try {\n if (fs.existsSync(out)) {\n backup = path.join(dir, \".dist-bundle.bak-\" + process.pid + \"-\" + Date.now());\n fs.renameSync(out, backup);\n }\n fs.renameSync(tmp, out);\n if (backup) { try { fs.rmSync(backup, { recursive: true, force: true }); } catch (e) {} }\n } catch (err) {\n try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\n if (backup && fs.existsSync(backup) && !fs.existsSync(out)) { try { fs.renameSync(backup, out); } catch (e) {} }\n throw err;\n }\n}\n\ntry {\n rebuild();\n} catch (err) {\n console.error(err && err.message ? err.message : err);\n process.exit(1);\n}\n`;\n}\n\n/**\n * True when `candidate` is a regular file that the current process can\n * execute. Used by every `bun`-binary candidate selection site so a stale,\n * non-executable file named `bun` (or `bun.exe`) cannot win over a later\n * working binary — matching `which(1)` and the `spawnSync(\"bun\", [\"--version\"])`\n * version probe, which both skip non-executable files. On Windows\n * `fs.accessSync(X_OK)` verifies read access, which holds for real `.exe`\n * files, so the check is a harmless no-op there.\n */\nfunction isExecutableFile(candidate: string): boolean {\n try {\n const stat = fs.statSync(candidate);\n if (!stat.isFile()) return false;\n fs.accessSync(candidate, fs.constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Walks `PATH` the way a shell does and returns the first `bun` executable it\n * finds, as a realpath-resolved absolute path (or null when nothing on PATH\n * is an executable `bun`). Used so the install-time PATH probe can embed an\n * absolute bun path in the generated loader/postinstall instead of the bare\n * string `\"bun\"`, which would break self-heal rebuilds under a stripped\n * runtime PATH (GUI/service launches). Mirrors `which(1)`; no dependency.\n */\nexport function resolveBunOnPath(): string | null {\n const pathVar = process.env.PATH ?? process.env.Path ?? process.env.path ?? \"\";\n const separator = process.platform === \"win32\" ? \";\" : \":\";\n const candidateNames =\n process.platform === \"win32\" ? [\"bun.exe\", \"bun\"] : [\"bun\"];\n for (const dir of pathVar.split(separator)) {\n if (!dir) continue;\n for (const name of candidateNames) {\n const candidate = path.isAbsolute(dir)\n ? path.join(dir, name)\n : path.resolve(dir, name);\n if (isExecutableFile(candidate)) {\n return fs.realpathSync(candidate);\n }\n }\n }\n return null;\n}\n\n/**\n * Resolves the `bun` binary for the install-time pre-bundle. Honours\n * `REMNIC_OMP_BUN_BIN` (test/override seam), then PATH, then common locations.\n * Returns null when bun is unavailable so the caller can fail with guidance.\n */\nexport function resolveBunBinary(): string | null {\n const override = process.env.REMNIC_OMP_BUN_BIN;\n if (override !== undefined) {\n return fs.existsSync(override) ? override : null;\n }\n\n const pathProbe = spawnSync(\"bun\", [\"--version\"], { encoding: \"utf-8\" });\n if (!pathProbe.error && pathProbe.status === 0) {\n // Resolve the PATH-found bun to an absolute executable so the embedded\n // loader/postinstall don't depend on omp's runtime PATH — GUI/service\n // launches commonly inherit a stripped PATH, which would make a bare\n // \"bun\" self-heal spawn fail even though install found a working binary.\n // Fall back to \"bun\" only if the PATH walk can't locate it (e.g. a shell\n // function/alias that isn't an actual file on PATH).\n return resolveBunOnPath() ?? \"bun\";\n }\n\n // Mirror omp's path helpers, which resolve the agent home as\n // HOME ?? USERPROFILE ?? os.homedir(). Relying on HOME alone breaks the\n // ~/.bun/bin/bun fallback on Windows installs where HOME is unset.\n const home = process.env.HOME ?? process.env.USERPROFILE ?? os.homedir();\n // The official Bun installer writes ~/.bun/bin/bun on POSIX and\n // ~/.bun/bin/bun.exe on Windows. Select the first candidate that is an\n // executable regular file (not merely one that exists) so a stale,\n // non-executable ~/.bun/bin/bun cannot win over a later working binary\n // (e.g. /usr/local/bin/bun or /opt/homebrew/bin/bun) — same `which(1)`\n // semantics as the PATH walk above.\n const candidates = [\n path.join(home ?? \"\", \".bun\", \"bin\", \"bun\"),\n path.join(home ?? \"\", \".bun\", \"bin\", \"bun.exe\"),\n \"/usr/local/bin/bun\",\n \"/opt/homebrew/bin/bun\",\n ];\n for (const candidate of candidates) {\n if (isExecutableFile(candidate)) return candidate;\n }\n return null;\n}\n\nfunction atomicWriteFile(filePath: string, content: string, mode: number): void {\n rejectSymlinkPath(filePath);\n const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;\n try {\n fs.writeFileSync(tmpPath, content, { encoding: \"utf-8\", mode });\n fs.renameSync(tmpPath, filePath);\n try {\n fs.chmodSync(filePath, mode);\n } catch {\n // Best effort for platforms that do not support chmod.\n }\n } catch (err) {\n try {\n if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);\n } catch {\n // Best-effort cleanup only.\n }\n throw err;\n }\n}\n\nfunction snapshotFiles(paths: string[]): FileSnapshot[] {\n return paths.map((filePath) => {\n if (!fs.existsSync(filePath)) return { path: filePath, existed: false };\n const stat = fs.lstatSync(filePath);\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${filePath}`);\n }\n if (!stat.isFile()) return { path: filePath, existed: false };\n return {\n path: filePath,\n existed: true,\n content: fs.readFileSync(filePath),\n mode: stat.mode & 0o777,\n };\n });\n}\n\nfunction restorePublishSnapshot(extensionRoot: string, rootExisted: boolean, snapshots: FileSnapshot[]): void {\n if (!rootExisted && !canCleanNewExtensionRoot(extensionRoot)) return;\n\n for (const snapshot of snapshots) {\n restoreOwnedFile(snapshot);\n }\n\n if (!rootExisted) {\n removeEmptyDirectory(extensionRoot);\n }\n}\n\n/**\n * Restores a single owned file to its pre-publish state, atomically.\n *\n * Two cases:\n *\n * - The file did NOT exist before publish (publish created it): remove it to\n * undo the publish. {@link assertSafeExistingPath} re-checks it is not a\n * symlink swapped in after the snapshot; `rmSync` removes a symlink itself\n * rather than following it, but refusing surfaces tampering loudly.\n *\n * - The file DID exist before publish: restore its prior content using\n * \"write-new-before-delete-old\" (rules 42/54). We write the prior content to\n * a temp path in the same directory, then {@link fs.renameSync} it into\n * place. The live file is never truncated, so a mid-restore failure (disk\n * full, EACCES, …) leaves the current on-disk content intact rather than\n * half-written — the restore either fully lands or does nothing. The temp\n * path uses the `.tmp-<pid>-<ts>` suffix tracked by\n * {@link EXTENSION_OWNED_TEMP_FILE_SUFFIX}, so any lingering temp is swept\n * by unpublish. The final `renameSync` does NOT follow a symlink even if one\n * was swapped into the snapshot path after the snapshot (TOCTOU\n * defense-in-depth): `rename(2)` replaces the symlink itself, so no write\n * ever reaches an arbitrary target. We still re-check for a symlink right\n * before the rename so the rollback surfaces tampering instead of silently\n * replacing it.\n */\nfunction restoreOwnedFile(snapshot: FileSnapshot): void {\n if (!snapshot.existed) {\n assertSafeExistingPath(snapshot.path);\n fs.rmSync(snapshot.path, { force: true });\n return;\n }\n\n fs.mkdirSync(path.dirname(snapshot.path), { recursive: true });\n const tmpPath = `${snapshot.path}.tmp-${process.pid}-${Date.now()}`;\n try {\n fs.writeFileSync(tmpPath, snapshot.content ?? Buffer.alloc(0), {\n mode: snapshot.mode ?? 0o644,\n });\n if (snapshot.mode !== undefined) {\n try {\n fs.chmodSync(tmpPath, snapshot.mode);\n } catch {\n // Best effort for platforms that do not support chmod.\n }\n }\n rejectSymlinkPath(snapshot.path);\n fs.renameSync(tmpPath, snapshot.path);\n } catch (err) {\n try {\n if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);\n } catch {\n // Best-effort cleanup only.\n }\n throw err;\n }\n}\n\nfunction snapshotDirs(paths: string[]): DirSnapshot[] {\n return paths.map((dirPath) => {\n let existed = false;\n try {\n const stat = fs.lstatSync(dirPath);\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${dirPath}`);\n }\n existed = stat.isDirectory();\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") {\n existed = false;\n } else {\n throw err;\n }\n }\n return { path: dirPath, existed };\n });\n}\n\nfunction restoreDirSnapshots(snapshots: DirSnapshot[]): void {\n for (const snapshot of snapshots) {\n if (snapshot.existed) continue;\n try {\n fs.rmSync(snapshot.path, { recursive: true, force: true });\n } catch {\n // best-effort — the loader self-heals at runtime if the dir lingers\n }\n }\n}\n\n\nfunction canCleanNewExtensionRoot(extensionRoot: string): boolean {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(extensionRoot);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") return false;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${extensionRoot}`);\n }\n return stat.isDirectory();\n}\n\nfunction removeEmptyDirectory(dirPath: string): void {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(dirPath);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") return;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${dirPath}`);\n }\n if (!stat.isDirectory()) return;\n if (fs.readdirSync(dirPath).length > 0) return;\n fs.rmdirSync(dirPath);\n}\n\nfunction removableOwnedExtensionFiles(filePaths: string[]): string[] {\n const removableFiles: string[] = [];\n for (const filePath of filePaths) {\n const stat = statOwnedExtensionPath(filePath);\n if (stat === null) continue;\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${filePath}`);\n }\n if (stat.isFile()) removableFiles.push(filePath);\n }\n return removableFiles;\n}\n\nfunction statOwnedExtensionPath(filePath: string): fs.Stats | null {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(filePath);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") return null;\n throw err;\n }\n return stat;\n}\n\nfunction mkdirExtensionRoot(extensionRoot: string, agentHome: string): void {\n const extensionsDir = path.join(path.resolve(agentHome), \"extensions\");\n assertSafeExtensionRoot(extensionRoot, agentHome);\n fs.mkdirSync(extensionsDir, { recursive: true });\n rejectSymlinkPath(extensionsDir);\n fs.mkdirSync(extensionRoot, { recursive: true });\n rejectSymlinkPath(extensionRoot);\n}\n\nfunction assertSafeExtensionRoot(extensionRoot: string, agentHome: string): void {\n const resolvedAgentHome = path.resolve(agentHome);\n const expected = path.join(resolvedAgentHome, \"extensions\", \"remnic\");\n if (path.resolve(extensionRoot) !== path.resolve(expected)) {\n throw new Error(`Extension root is outside the configured extensions directory: ${extensionRoot}`);\n }\n const extensionsDir = path.join(resolvedAgentHome, \"extensions\");\n assertPathContained(resolvedAgentHome, extensionsDir);\n assertPathContained(extensionsDir, extensionRoot);\n rejectSymlinkPath(resolvedAgentHome);\n if (fs.existsSync(extensionsDir)) rejectSymlinkPath(extensionsDir);\n if (fs.existsSync(extensionRoot)) rejectSymlinkPath(extensionRoot);\n}\n\nfunction assertSafeExistingPath(filePath: string): void {\n if (fs.existsSync(filePath)) rejectSymlinkPath(filePath);\n}\n\nfunction rejectSymlinkPath(filePath: string): void {\n if (!fs.existsSync(filePath)) return;\n const stat = fs.lstatSync(filePath);\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${filePath}`);\n }\n}\n\nfunction assertPathContained(root: string, candidate: string): void {\n const rootResolved = path.resolve(root);\n const candidateResolved = path.resolve(candidate);\n const relative = path.relative(rootResolved, candidateResolved);\n if (relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative))) return;\n throw new Error(`Extension path escapes allowed root: ${candidate}`);\n}\n\nfunction readPriorConfig(configPath: string): Record<string, unknown> {\n if (!fs.existsSync(configPath)) return {};\n try {\n const parsed = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\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 existing Remnic Pi config at ${configPath}: ${reason}`);\n }\n}\n\nfunction snapshotTokenEntry(connectorId: string): TokenEntry | null {\n const entry = loadTokenStore().tokens.find((candidate) => candidate.connector === connectorId);\n return cloneTokenEntry(entry ?? null);\n}\n\nfunction cloneTokenEntry(entry: TokenEntry | null): TokenEntry | null {\n return entry ? { ...entry } : null;\n}\n\nfunction restoreTokenEntry(priorEntry: TokenEntry | null, connectorId: string): void {\n const store = loadTokenStore();\n store.tokens = store.tokens.filter((entry) => entry.connector !== connectorId);\n if (priorEntry) store.tokens.push(priorEntry);\n saveTokenStore(store);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/publisher.ts","../src/omp-loader-templates.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport { spawnSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport os from \"node:os\";\n\nimport {\n type MemoryExtensionPublisher,\n type PublishContext,\n type PublishResult,\n type PublisherCapabilities,\n type TokenEntry,\n getConnectorToken,\n loadTokenStore,\n saveTokenStore,\n} from \"@remnic/core\";\n\nimport {\n resolveOmpAgentHome,\n resolveOmpConfigRoot,\n resolveOmpExtensionRoot,\n resolvePiAgentHome,\n resolvePiExtensionRoot,\n resolvePrimeAgentAgentHome,\n resolvePrimeAgentExtensionRoot,\n} from \"./paths.js\";\nimport {\n renderOmpLoader,\n renderOmpPackageJson,\n renderOmpPostinstall,\n} from \"./omp-loader-templates.js\";\nimport { DEFAULT_CONFIG } from \"./config.js\";\n\nconst DEFAULT_DAEMON_PORT = 4318;\nconst BASE_OWNED_FILES = [\"remnic.config.json\", \"index.ts\", \"README.md\"] as const;\nconst EXTENSION_OWNED_TEMP_FILE_SUFFIX = /\\.tmp-\\d+-\\d+$/u;\n\ntype FileSnapshot = {\n path: string;\n existed: boolean;\n content?: Buffer;\n mode?: number;\n};\n\ntype DirSnapshot = {\n path: string;\n existed: boolean;\n};\n\n/**\n * Host-specific parameters for a Pi-family memory extension publisher.\n *\n * The Remnic runtime extension is host-neutral (it only uses Pi's extension\n * hooks, which omp preserves as a superset), so the only things that vary\n * between hosts are *where* the extension is installed, *which* connector\n * token it uses, and *how* it is labelled. Everything else — atomic writes,\n * rollback, symlink guards, config merge — is shared.\n */\nexport interface HostPublisherDescriptor {\n readonly hostId: string;\n readonly connectorId: string;\n readonly displayName: string;\n readonly tokenGenerateHint: string;\n resolveAgentHome(env: NodeJS.ProcessEnv): string;\n resolveExtensionRoot(env: NodeJS.ProcessEnv): string;\n /**\n * Optional: every agent home `unpublish` should sweep for a stale extension,\n * beyond the one resolved from the current env. Hosts with env-sensitive\n * install locations (e.g. omp profiles) provide this so `remnic connectors\n * remove` cleans up even when the remove-time env differs from install time.\n */\n listRemovalAgentHomes?(env: NodeJS.ProcessEnv): string[];\n}\n\nconst PI_HOST: HostPublisherDescriptor = {\n hostId: \"pi\",\n connectorId: \"pi\",\n displayName: \"Pi Coding Agent\",\n tokenGenerateHint: \"remnic token generate pi\",\n resolveAgentHome: resolvePiAgentHome,\n resolveExtensionRoot: resolvePiExtensionRoot,\n};\n\nconst OMP_HOST: HostPublisherDescriptor = {\n hostId: \"omp\",\n connectorId: \"omp\",\n displayName: \"Oh My Pi (omp)\",\n tokenGenerateHint: \"remnic token generate omp\",\n resolveAgentHome: resolveOmpAgentHome,\n resolveExtensionRoot: resolveOmpExtensionRoot,\n listRemovalAgentHomes: ompRemovalAgentHomes,\n};\n\nconst PRIME_AGENT_HOST: HostPublisherDescriptor = {\n hostId: \"prime-agent\",\n connectorId: \"prime-agent\",\n displayName: \"Prime Agent\",\n tokenGenerateHint: \"remnic token generate prime-agent\",\n resolveAgentHome: resolvePrimeAgentAgentHome,\n resolveExtensionRoot: resolvePrimeAgentExtensionRoot,\n listRemovalAgentHomes: primeAgentRemovalAgentHomes,\n};\n\nfunction primeAgentRemovalAgentHomes(env: NodeJS.ProcessEnv): string[] {\n const homes = new Set<string>([resolvePrimeAgentAgentHome(env)]);\n homes.add(resolvePrimeAgentAgentHome({ HOME: env.HOME, USERPROFILE: env.USERPROFILE }));\n return [...homes];\n}\n\n/**\n * Every omp agent home a stale extension might live under, so `unpublish` cleans\n * up regardless of the profile/env active at remove time: the env-resolved home,\n * the base `<configRoot>/agent`, an explicit `PI_CODING_AGENT_DIR`, and every\n * existing `<configRoot>/profiles/<name>/agent`. Symlinked profile dirs are\n * skipped defensively.\n */\nfunction ompRemovalAgentHomes(env: NodeJS.ProcessEnv): string[] {\n const homes = new Set<string>([resolveOmpAgentHome(env)]);\n const configRoot = resolveOmpConfigRoot(env);\n homes.add(path.join(configRoot, \"agent\"));\n\n const explicit = env.PI_CODING_AGENT_DIR?.trim();\n if (explicit) homes.add(path.resolve(explicit));\n\n const profilesDir = path.join(configRoot, \"profiles\");\n let entries: fs.Dirent[] = [];\n try {\n entries = fs.readdirSync(profilesDir, { withFileTypes: true });\n } catch {\n entries = [];\n }\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.isSymbolicLink()) {\n homes.add(path.join(profilesDir, entry.name, \"agent\"));\n }\n }\n return [...homes];\n}\n\n/**\n * Shared publisher for Pi-family hosts. Concrete hosts (Pi, omp, Prime Agent) subclass this\n * with a {@link HostPublisherDescriptor}; the install/rollback machinery is\n * identical across hosts.\n */\nexport class HostMemoryExtensionPublisher implements MemoryExtensionPublisher {\n static readonly capabilities: PublisherCapabilities = {\n // Real publisher: writes host config + wrapper + readme, just no\n // instructions.md/skills/citation/read-path-template artefacts. The\n // explicit flag prevents the parity gate from mis-inferring \"all flags\n // false ⇒ stub\" for this host (#1518).\n isStub: false,\n instructionsMd: false,\n skillsFolder: false,\n citationFormat: false,\n readPathTemplate: false,\n };\n\n protected constructor(private readonly host: HostPublisherDescriptor) {}\n\n /**\n * File basenames this publisher owns inside the extension root. The shared\n * set is config + wrapper + readme; subclasses add host-specific files\n * (e.g. omp's pre-bundle loader + package manifest). Used for snapshot,\n * atomic-write rollback, and unpublish cleanup.\n */\n protected get ownedFileNames(): readonly string[] {\n return BASE_OWNED_FILES;\n }\n\n /**\n * Directory names this publisher owns inside the extension root (build\n * outputs). Recursively removed on unpublish and on publish rollback when\n * newly created.\n */\n protected get ownedDirNames(): readonly string[] {\n return [];\n }\n\n /**\n * Whether the generated wrapper must use a bun-buildable import specifier\n * (relative path) instead of a file:// URL. omp pre-bundles the wrapper with\n * `bun build`, which cannot resolve file:// specifiers; pi loads the wrapper\n * directly via tsx and keeps the file:// URL.\n */\n protected get usesBundledWrapper(): boolean {\n return false;\n }\n\n /**\n * Hook for subclasses to write host-specific files and run install-time\n * build steps after the shared config/wrapper/readme are written. Runs\n * inside the publish try-block: a throw triggers full rollback.\n */\n protected finalizePublish(\n _ctx: PublishContext,\n _extensionRoot: string,\n _paths: { configPath: string; wrapperPath: string; pluginPiDistPath: string },\n ): void {\n // No-op by default; subclasses override.\n }\n\n get hostId(): string {\n return this.host.hostId;\n }\n\n async resolveExtensionRoot(env?: NodeJS.ProcessEnv): Promise<string> {\n return this.host.resolveExtensionRoot(env ?? process.env);\n }\n\n async isHostAvailable(): Promise<boolean> {\n // Pi-family agents auto-discover extensions from their agent extensions\n // directory. The directory can be created before the agent has been\n // launched, so availability should not block first-time installation.\n return true;\n }\n\n async renderInstructions(ctx: PublishContext): Promise<string> {\n const namespace = ctx.config.namespace ?? \"default\";\n const daemonUrl = resolveDaemonUrl(ctx);\n return [\n `# Remnic for ${this.host.displayName}`,\n \"\",\n `Remnic provides memory, retrieval, observation, MCP tools, and long-context compaction coordination for ${this.host.displayName}.`,\n \"\",\n \"## Installed Capabilities\",\n \"\",\n \"- Recall relevant Remnic context in the `before_agent_start` hook via system prompt injection.\",\n '- Observe user, assistant, and tool messages with `sourceFormat: \"pi\"`.',\n \"- Coordinate `session_before_compact` with Remnic LCM flush and checkpoint recording.\",\n \"- Register Remnic MCP tools as host tools when daemon authentication is configured.\",\n \"- Persist lightweight dedupe state in custom entries via `appendEntry`.\",\n \"\",\n \"## Runtime\",\n \"\",\n `- Remnic daemon: \\`${daemonUrl}\\``,\n `- Namespace: \\`${namespace}\\``,\n `- Memory directory: \\`${ctx.config.memoryDir}\\``,\n \"\",\n \"The private `remnic.config.json` file stores the daemon URL, namespace, and connector auth token with owner-only permissions.\",\n ].join(\"\\n\");\n }\n\n async publish(ctx: PublishContext): Promise<PublishResult> {\n const extensionRoot = await this.resolveExtensionRoot();\n const agentHome = this.host.resolveAgentHome(process.env);\n assertSafeExtensionRoot(extensionRoot, agentHome);\n const filesWritten: string[] = [];\n const skipped: string[] = [];\n\n ctx.log.info(`Publishing ${this.host.displayName} memory extension to ${extensionRoot}`);\n\n const ownedFilePaths = this.ownedFileNames.map((fileName) => path.join(extensionRoot, fileName));\n const configPath = ownedFilePaths[0];\n const wrapperPath = ownedFilePaths[1];\n const readmePath = ownedFilePaths[2];\n const pluginPiDistPath = resolveExtensionModulePath();\n const rootExisted = fs.existsSync(extensionRoot);\n const fileSnapshots = snapshotFiles(ownedFilePaths);\n const dirSnapshots = snapshotDirs(\n this.ownedDirNames.map((dirName) => path.join(extensionRoot, dirName)),\n );\n const priorTokenEntry =\n ctx.rollbackTokenEntry === undefined\n ? snapshotTokenEntry(this.host.connectorId)\n : cloneTokenEntry(ctx.rollbackTokenEntry);\n\n const token = getConnectorToken(this.host.connectorId);\n if (!token) {\n skipped.push(\n `auth token unavailable; run \\`${this.host.tokenGenerateHint}\\` and reinstall the connector`,\n );\n }\n\n try {\n const priorConfig = readPriorConfig(configPath);\n const config: Record<string, unknown> = {\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 recallTimeoutThreshold: DEFAULT_CONFIG.recallTimeoutThreshold,\n recallTimeoutWindow: DEFAULT_CONFIG.recallTimeoutWindow,\n ...priorConfig,\n remnicDaemonUrl: resolveDaemonUrl(ctx),\n };\n if (token) {\n config.authToken = token;\n }\n if (ctx.config.namespace) {\n config.namespace = ctx.config.namespace;\n }\n\n mkdirExtensionRoot(extensionRoot, agentHome);\n\n atomicWriteFile(configPath, `${JSON.stringify(config, null, 2)}\\n`, 0o600);\n filesWritten.push(configPath);\n\n atomicWriteFile(\n wrapperPath,\n renderWrapper(\n pluginPiDistPath,\n configPath,\n this.usesBundledWrapper ? extensionRoot : undefined,\n ),\n 0o644,\n );\n filesWritten.push(wrapperPath);\n\n atomicWriteFile(readmePath, `${await this.renderInstructions(ctx)}\\n`, 0o644);\n filesWritten.push(readmePath);\n\n this.finalizePublish(ctx, extensionRoot, { configPath, wrapperPath, pluginPiDistPath });\n for (let i = BASE_OWNED_FILES.length; i < ownedFilePaths.length; i++) {\n filesWritten.push(ownedFilePaths[i]);\n }\n } catch (err) {\n try {\n // Remove newly created owned dirs (e.g. dist-bundle) BEFORE\n // restorePublishSnapshot's removeEmptyDirectory check, otherwise a\n // first-time publish that created dist-bundle would leave an empty\n // extension root behind on rollback.\n restoreDirSnapshots(dirSnapshots);\n restorePublishSnapshot(extensionRoot, rootExisted, fileSnapshots);\n } catch (restoreErr) {\n ctx.log.warn(\n `${this.host.displayName} extension rollback failed: ${restoreErr instanceof Error ? restoreErr.message : String(restoreErr)}`,\n );\n }\n // A failed omp pre-bundle (bun missing or `bun build` failing) is\n // recoverable: the runtime loader self-heals dist-bundle on first load,\n // and the connector token is already committed by the CLI. Rolling it\n // back here would leave the connector registered with no credential and\n // block a non-`--force` reinstall (AGENTS.md #14 — don't destroy\n // committed state before the new state is confirmed). File/dir rollback\n // above still runs, so a failed first-time publish still cleans its root.\n if (!(err instanceof OmpPreBundleError)) {\n try {\n restoreTokenEntry(priorTokenEntry, this.host.connectorId);\n } catch (tokenErr) {\n ctx.log.warn(\n `${this.host.displayName} connector token rollback failed: ${tokenErr instanceof Error ? tokenErr.message : String(tokenErr)}`,\n );\n }\n }\n throw err;\n }\n\n return {\n hostId: this.host.hostId,\n extensionRoot,\n filesWritten,\n skipped,\n };\n }\n\n async unpublish(): Promise<void> {\n const agentHomes = this.host.listRemovalAgentHomes\n ? this.host.listRemovalAgentHomes(process.env)\n : [this.host.resolveAgentHome(process.env)];\n\n const ownedFileNames = this.ownedFileNames;\n const ownedDirNames = this.ownedDirNames;\n const seen = new Set<string>();\n for (const agentHome of agentHomes) {\n const extensionRoot = path.join(path.resolve(agentHome), \"extensions\", \"remnic\");\n if (seen.has(extensionRoot)) continue;\n seen.add(extensionRoot);\n if (!fs.existsSync(extensionRoot)) continue;\n\n assertSafeExtensionRoot(extensionRoot, agentHome);\n const removableFiles = removableOwnedExtensionFiles(\n extensionOwnedUnpublishPaths(extensionRoot, ownedFileNames),\n );\n for (const filePath of removableFiles) {\n fs.rmSync(filePath, { force: true });\n }\n for (const dirName of ownedDirNames) {\n const dirPath = path.join(extensionRoot, dirName);\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(dirPath);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") continue;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${dirPath}`);\n }\n if (stat.isDirectory()) {\n fs.rmSync(dirPath, { recursive: true, force: true });\n }\n }\n removeEmptyDirectory(extensionRoot);\n }\n }\n}\n\n/** Publisher for upstream Pi (`~/.pi/agent/extensions/remnic`). */\nexport class PiMemoryExtensionPublisher extends HostMemoryExtensionPublisher {\n constructor() {\n super(PI_HOST);\n }\n}\n\n/**\n * Publisher for Prime Agent (`~/.prime/agent/extensions/remnic`), a Pi-fork\n * coding agent. It loads the plain `index.ts` wrapper directly (no bun\n * pre-bundle, so no loader/dist-bundle machinery), but discovers extensions\n * through a package manifest — the install therefore also writes a\n * `package.json` depending on `@remnic/plugin-pi`.\n */\nexport class PrimeAgentMemoryExtensionPublisher extends HostMemoryExtensionPublisher {\n constructor() {\n super(PRIME_AGENT_HOST);\n }\n\n protected get ownedFileNames(): readonly string[] {\n return [...BASE_OWNED_FILES, \"package.json\"];\n }\n\n protected finalizePublish(\n _ctx: PublishContext,\n extensionRoot: string,\n ): void {\n atomicWriteFile(\n path.join(extensionRoot, \"package.json\"),\n renderPrimeAgentPackageJson(),\n 0o644,\n );\n }\n}\n\n/**\n * Renders the extension `package.json` for Prime Agent: a private module whose\n * only content is the `@remnic/plugin-pi` dependency, so a package install\n * inside the extension root makes the wrapper's imports resolvable.\n */\nfunction renderPrimeAgentPackageJson(): string {\n const manifest = {\n name: \"remnic-prime-agent-extension\",\n version: \"0.0.0\",\n private: true,\n type: \"module\",\n dependencies: {\n \"@remnic/plugin-pi\": `^${readPluginPiVersion()}`,\n },\n };\n return `${JSON.stringify(manifest, null, 2)}\\n`;\n}\n\n/**\n * Reads the running @remnic/plugin-pi package version so the generated\n * dependency range tracks the publisher that wrote it. Works from both the\n * compiled dist entry and the tsx-run source. Fails loudly: a package.json the\n * publisher cannot read is an install-environment bug, not something to paper\n * over with a wildcard.\n */\nfunction readPluginPiVersion(): string {\n const manifestPath = path.resolve(\n path.dirname(fileURLToPath(import.meta.url)),\n \"..\",\n \"package.json\",\n );\n try {\n const parsed = JSON.parse(fs.readFileSync(manifestPath, \"utf8\")) as { version?: unknown };\n if (typeof parsed.version === \"string\" && parsed.version.length > 0) {\n return parsed.version;\n }\n } catch {\n // fall through to the error below\n }\n throw new Error(\n `Remnic prime-agent extension: cannot read the @remnic/plugin-pi version from ${manifestPath}.`,\n );\n}\n\n/**\n * Marks a failure originating from the omp pre-bundle step (bun missing, or the\n * `bun build` itself failing). {@link HostMemoryExtensionPublisher.publish}\n * catches this and rolls back the written files but SKIPS the connector-token\n * rollback: the pre-bundle runs after the install (config + wrapper + token) is\n * already committed, and the runtime loader self-heals `dist-bundle` on first\n * load, so destroying the just-generated token would leave the connector\n * registered with no credential and block a non-`--force` reinstall\n * (AGENTS.md #14 — don't destroy committed state before the new state is\n * confirmed). The message is preserved verbatim so existing `/requires \\`bun\\`/`\n * and `/bun build failed/` assertions still match.\n */\nclass OmpPreBundleError extends Error {}\n\n/** Publisher for Oh My Pi / omp (`~/.omp/agent/extensions/remnic`). */\nexport class OmpMemoryExtensionPublisher extends HostMemoryExtensionPublisher {\n constructor() {\n super(OMP_HOST);\n }\n\n protected get ownedFileNames(): readonly string[] {\n return [...BASE_OWNED_FILES, \"loader.js\", \"package.json\", \"postinstall-bundle.cjs\"];\n }\n\n protected get ownedDirNames(): readonly string[] {\n return [\"dist-bundle\"];\n }\n\n // omp pre-bundles index.ts with `bun build`; the wrapper must use a relative\n // import specifier (bun's bundler cannot resolve file:// URLs).\n protected get usesBundledWrapper(): boolean {\n return true;\n }\n\n protected finalizePublish(\n ctx: PublishContext,\n extensionRoot: string,\n paths: { configPath: string; wrapperPath: string; pluginPiDistPath: string },\n ): void {\n // Resolve once so the install-time build and the generated loader share the\n // same bun path — a loader that hardcodes \"bun\" cannot self-heal when bun\n // is reachable only via REMNIC_OMP_BUN_BIN or a common absolute install\n // path that is not on omp's PATH at runtime.\n const bunBin = resolveBunBinary();\n if (!bunBin) {\n // OmpPreBundleError so publish() keeps the connector token intact (see\n // the class doc); the runtime loader self-heals the bundle once bun is\n // installed.\n throw new OmpPreBundleError(\n \"Remnic omp extension requires `bun` to pre-bundle the extension: omp's embedded \" +\n \"runtime cannot resolve bare npm specifiers from the extension's node_modules. \" +\n \"Install bun from https://bun.sh, then re-run `remnic connectors install omp`.\",\n );\n }\n\n const loaderPath = path.join(extensionRoot, \"loader.js\");\n const packageJsonPath = path.join(extensionRoot, \"package.json\");\n\n const postinstallPath = path.join(extensionRoot, \"postinstall-bundle.cjs\");\n\n atomicWriteFile(loaderPath, renderOmpLoader(paths.pluginPiDistPath, bunBin), 0o644);\n // Cross-platform postinstall helper (Node-only) so npm's default cmd.exe\n // shell on Windows re-bundles after `npm install`; the POSIX one-liner it\n // replaces only ran under bash.\n atomicWriteFile(postinstallPath, renderOmpPostinstall(bunBin), 0o644);\n atomicWriteFile(packageJsonPath, renderOmpPackageJson(), 0o644);\n\n try {\n this.runBundleBuild(ctx, extensionRoot, bunBin);\n } catch (err) {\n // OmpPreBundleError so publish() keeps the connector token intact (see\n // the class doc); the runtime loader self-heals the bundle on next load.\n const message = err instanceof Error ? err.message : String(err);\n throw new OmpPreBundleError(message);\n }\n }\n\n /**\n * Pre-bundles the omp extension with `bun build` so omp's embedded runtime\n * never resolves bare npm specifiers (e.g. @sinclair/typebox) from the\n * extension's node_modules at load time. The bundle is written to a temp\n * directory and swapped into dist-bundle/ on success. The pre-existing\n * dist-bundle is renamed aside (not removed) before the swap, so a failure\n * during the final rename restores the previously working bundle rather than\n * leaving the install with no bundle at all.\n *\n * Override in tests to skip the real bun invocation.\n */\n protected runBundleBuild(ctx: PublishContext, extensionRoot: string, bunBin: string): void {\n const sourceEntry = path.join(extensionRoot, \"index.ts\");\n const tmpOutDir = path.join(extensionRoot, `.dist-bundle.tmp-${process.pid}-${Date.now()}`);\n const finalOutDir = path.join(extensionRoot, \"dist-bundle\");\n\n const result = spawnSync(bunBin, [\"build\", sourceEntry, \"--target=bun\", `--outdir=${tmpOutDir}`], {\n cwd: extensionRoot,\n encoding: \"utf-8\",\n });\n\n if (result.error || result.status !== 0) {\n try {\n fs.rmSync(tmpOutDir, { recursive: true, force: true });\n } catch {\n // best-effort tmp cleanup\n }\n const detail =\n (typeof result.stderr === \"string\" ? result.stderr.trim() : \"\") ||\n (result.error instanceof Error ? result.error.message : \"\") ||\n `bun exited with status ${result.status ?? \"null\"}`;\n throw new Error(\n `Remnic omp extension: bun build failed (${detail}). Resolve the error and re-run ` +\n \"`remnic connectors install omp`, or build manually with \" +\n \"`bun build index.ts --target=bun --outdir=dist-bundle` inside \" +\n `${extensionRoot}.`,\n );\n }\n\n // Swap the freshly built bundle into place without ever leaving the install\n // bundle-less. Rename the existing dist-bundle aside, move the new one in,\n // and only then discard the backup. On any failure mid-swap, restore the\n // backup so the previously working bundle survives (the publish-level\n // rollback only removes newly created dirs — it never restores a removed\n // dist-bundle, so we must not remove it here).\n let backupDir: string | null = null;\n try {\n if (fs.existsSync(finalOutDir)) {\n backupDir = path.join(extensionRoot, `.dist-bundle.bak-${process.pid}-${Date.now()}`);\n fs.renameSync(finalOutDir, backupDir);\n }\n fs.renameSync(tmpOutDir, finalOutDir);\n if (backupDir) {\n try {\n fs.rmSync(backupDir, { recursive: true, force: true });\n } catch {\n // best-effort backup cleanup; leaving it does not break the install\n }\n }\n } catch (err) {\n try {\n if (fs.existsSync(tmpOutDir)) fs.rmSync(tmpOutDir, { recursive: true, force: true });\n } catch {\n // best-effort tmp cleanup\n }\n // Restore the previously working bundle if we moved it aside and the\n // final swap did not land.\n if (backupDir && fs.existsSync(backupDir) && !fs.existsSync(finalOutDir)) {\n try {\n fs.renameSync(backupDir, finalOutDir);\n } catch {\n // best-effort restore; the loader's self-heal rebuilds on next start\n }\n }\n throw new Error(\n `Remnic omp extension: failed to finalize bundle output — ${err instanceof Error ? err.message : String(err)}.`,\n );\n }\n\n ctx.log.info(`Pre-bundled omp extension into ${finalOutDir}`);\n }\n}\n\nfunction extensionOwnedPaths(extensionRoot: string, ownedFileNames: readonly string[]): string[] {\n return ownedFileNames.map((fileName) => path.join(extensionRoot, fileName));\n}\n\nfunction extensionOwnedUnpublishPaths(extensionRoot: string, ownedFileNames: readonly string[]): string[] {\n const ownedBaseNames = new Set(ownedFileNames);\n const ownedPaths = extensionOwnedPaths(extensionRoot, ownedFileNames);\n for (const fileName of fs.readdirSync(extensionRoot)) {\n const match = EXTENSION_OWNED_TEMP_FILE_SUFFIX.exec(fileName);\n if (match && ownedBaseNames.has(fileName.slice(0, match.index))) {\n ownedPaths.push(path.join(extensionRoot, fileName));\n }\n }\n return ownedPaths;\n}\n\nfunction resolveDaemonUrl(ctx: PublishContext): string {\n if (ctx.config.daemonUrl && ctx.config.daemonUrl.trim().length > 0) {\n return trimTrailingSlashes(ctx.config.daemonUrl.trim());\n }\n return `http://127.0.0.1:${ctx.config.daemonPort ?? DEFAULT_DAEMON_PORT}`;\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\nfunction resolveExtensionModulePath(): string {\n const moduleDir = path.dirname(fileURLToPath(import.meta.url));\n const built = path.join(moduleDir, \"index.js\");\n if (fs.existsSync(built)) return built;\n\n const source = path.join(moduleDir, \"index.ts\");\n if (fs.existsSync(source)) return source;\n\n return built;\n}\n\n/**\n * Resolves the import specifier the omp wrapper uses to reach the\n * `@remnic/plugin-pi` dist entry from the generated `index.ts`. omp pre-bundles\n * that wrapper with `bun build`, whose bundler cannot resolve `file://`\n * specifiers (\"Could not resolve: file://…\" on Bun 1.2–1.3, verified), so the\n * specifier must be a relative path. On Windows, when the extension directory\n * and the plugin-pi install sit on different drives, `path.relative` cannot\n * express a relative path and returns an absolute drive path (e.g. `D:\\…`);\n * prefixing `./` then yields an invalid module specifier that fails `bun build`\n * with a cryptic error. Detect that layout and fail fast with an actionable\n * message instead. (Cross-drive omp installs are unsupported because neither a\n * relative specifier nor a `file://` URL is acceptable to `bun build`.) Drive\n * roots are compared case-insensitively so a same-drive Windows install is not\n * falsely rejected when the agent home and the plugin-pi install report the\n * drive letter in different casing (`C:\\\\` vs `c:\\\\`).\n *\n * Exported so the cross-drive guard can be exercised on non-Windows hosts via\n * `path.win32`.\n */\nexport function resolveOmpWrapperImportSpecifier(\n extensionModulePath: string,\n wrapperDir: string,\n pathApi: typeof path = path,\n): string {\n // Windows drive roots are case-insensitive: `C:\\\\…` (e.g. from the omp agent\n // home) and `c:\\\\…` (e.g. from fileURLToPath(import.meta.url)) are the SAME\n // drive, and path.win32.relative yields a valid relative specifier between\n // them. Compare the parsed roots case-insensitively so a same-drive install\n // isn't falsely rejected as \"different drives\". posix roots (`/`) are\n // unaffected by toLowerCase().\n if (pathApi.parse(wrapperDir).root.toLowerCase() !== pathApi.parse(extensionModulePath).root.toLowerCase()) {\n throw new Error(\n \"Remnic omp extension cannot pre-bundle: the extension directory \" +\n `(${wrapperDir}) and the @remnic/plugin-pi install (${extensionModulePath}) ` +\n \"are on different drives, so no relative import specifier can be generated \" +\n \"for `bun build` (and `bun build` cannot resolve a `file://` specifier). \" +\n \"Move the omp agent home and the Remnic install onto the same drive.\",\n );\n }\n let rel = pathApi.relative(wrapperDir, extensionModulePath);\n rel = rel.split(pathApi.sep).join(\"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\nfunction renderWrapper(\n extensionModulePath: string,\n configPath: string,\n wrapperDir?: string,\n): string {\n // omp pre-bundles this entry with `bun build`, whose bundler cannot resolve\n // `file://` specifiers — it exits with \"Could not resolve: file://...\" on\n // Bun 1.2–1.3 (verified). When the wrapper will be bun-built, emit a relative\n // specifier resolved against the wrapper's directory; bun, tsx, and Node ESM\n // all resolve relative specifiers. For tsx-loaded wrappers (pi) the file://\n // URL is retained.\n let importSpecifier: string;\n if (wrapperDir) {\n importSpecifier = resolveOmpWrapperImportSpecifier(extensionModulePath, wrapperDir);\n } else {\n importSpecifier = pathToFileURL(extensionModulePath).href;\n }\n return [\n `import { createRemnicPiExtension } from ${JSON.stringify(importSpecifier)};`,\n \"\",\n `export default createRemnicPiExtension({ configPath: ${JSON.stringify(configPath)} });`,\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * True when `candidate` is a regular file that the current process can\n * execute. Used by every `bun`-binary candidate selection site so a stale,\n * non-executable file named `bun` (or `bun.exe`) cannot win over a later\n * working binary — matching `which(1)` and the `spawnSync(\"bun\", [\"--version\"])`\n * version probe, which both skip non-executable files. On Windows\n * `fs.accessSync(X_OK)` verifies read access, which holds for real `.exe`\n * files, so the check is a harmless no-op there.\n */\nfunction isExecutableFile(candidate: string): boolean {\n try {\n const stat = fs.statSync(candidate);\n if (!stat.isFile()) return false;\n fs.accessSync(candidate, fs.constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Walks `PATH` the way a shell does and returns the first `bun` executable it\n * finds, as a realpath-resolved absolute path (or null when nothing on PATH\n * is an executable `bun`). Used so the install-time PATH probe can embed an\n * absolute bun path in the generated loader/postinstall instead of the bare\n * string `\"bun\"`, which would break self-heal rebuilds under a stripped\n * runtime PATH (GUI/service launches). Mirrors `which(1)`; no dependency.\n */\nexport function resolveBunOnPath(): string | null {\n const pathVar = process.env.PATH ?? process.env.Path ?? process.env.path ?? \"\";\n const separator = process.platform === \"win32\" ? \";\" : \":\";\n const candidateNames =\n process.platform === \"win32\" ? [\"bun.exe\", \"bun\"] : [\"bun\"];\n for (const dir of pathVar.split(separator)) {\n if (!dir) continue;\n for (const name of candidateNames) {\n const candidate = path.isAbsolute(dir)\n ? path.join(dir, name)\n : path.resolve(dir, name);\n if (isExecutableFile(candidate)) {\n return fs.realpathSync(candidate);\n }\n }\n }\n return null;\n}\n\n/**\n * Resolves the `bun` binary for the install-time pre-bundle. Honours\n * `REMNIC_OMP_BUN_BIN` (test/override seam), then PATH, then common locations.\n * Returns null when bun is unavailable so the caller can fail with guidance.\n */\nexport function resolveBunBinary(): string | null {\n const override = process.env.REMNIC_OMP_BUN_BIN;\n if (override !== undefined) {\n return fs.existsSync(override) ? override : null;\n }\n\n const pathProbe = spawnSync(\"bun\", [\"--version\"], { encoding: \"utf-8\" });\n if (!pathProbe.error && pathProbe.status === 0) {\n // Resolve the PATH-found bun to an absolute executable so the embedded\n // loader/postinstall don't depend on omp's runtime PATH — GUI/service\n // launches commonly inherit a stripped PATH, which would make a bare\n // \"bun\" self-heal spawn fail even though install found a working binary.\n // Fall back to \"bun\" only if the PATH walk can't locate it (e.g. a shell\n // function/alias that isn't an actual file on PATH).\n return resolveBunOnPath() ?? \"bun\";\n }\n\n // Mirror omp's path helpers, which resolve the agent home as\n // HOME ?? USERPROFILE ?? os.homedir(). Relying on HOME alone breaks the\n // ~/.bun/bin/bun fallback on Windows installs where HOME is unset.\n const home = process.env.HOME ?? process.env.USERPROFILE ?? os.homedir();\n // The official Bun installer writes ~/.bun/bin/bun on POSIX and\n // ~/.bun/bin/bun.exe on Windows. Select the first candidate that is an\n // executable regular file (not merely one that exists) so a stale,\n // non-executable ~/.bun/bin/bun cannot win over a later working binary\n // (e.g. /usr/local/bin/bun or /opt/homebrew/bin/bun) — same `which(1)`\n // semantics as the PATH walk above.\n const candidates = [\n path.join(home ?? \"\", \".bun\", \"bin\", \"bun\"),\n path.join(home ?? \"\", \".bun\", \"bin\", \"bun.exe\"),\n \"/usr/local/bin/bun\",\n \"/opt/homebrew/bin/bun\",\n ];\n for (const candidate of candidates) {\n if (isExecutableFile(candidate)) return candidate;\n }\n return null;\n}\n\nfunction atomicWriteFile(filePath: string, content: string, mode: number): void {\n rejectSymlinkPath(filePath);\n const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;\n try {\n fs.writeFileSync(tmpPath, content, { encoding: \"utf-8\", mode });\n fs.renameSync(tmpPath, filePath);\n try {\n fs.chmodSync(filePath, mode);\n } catch {\n // Best effort for platforms that do not support chmod.\n }\n } catch (err) {\n try {\n if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);\n } catch {\n // Best-effort cleanup only.\n }\n throw err;\n }\n}\n\nfunction snapshotFiles(paths: string[]): FileSnapshot[] {\n return paths.map((filePath) => {\n if (!fs.existsSync(filePath)) return { path: filePath, existed: false };\n const stat = fs.lstatSync(filePath);\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${filePath}`);\n }\n if (!stat.isFile()) return { path: filePath, existed: false };\n return {\n path: filePath,\n existed: true,\n content: fs.readFileSync(filePath),\n mode: stat.mode & 0o777,\n };\n });\n}\n\nfunction restorePublishSnapshot(extensionRoot: string, rootExisted: boolean, snapshots: FileSnapshot[]): void {\n if (!rootExisted && !canCleanNewExtensionRoot(extensionRoot)) return;\n\n for (const snapshot of snapshots) {\n restoreOwnedFile(snapshot);\n }\n\n if (!rootExisted) {\n removeEmptyDirectory(extensionRoot);\n }\n}\n\n/**\n * Restores a single owned file to its pre-publish state, atomically.\n *\n * Two cases:\n *\n * - The file did NOT exist before publish (publish created it): remove it to\n * undo the publish. {@link assertSafeExistingPath} re-checks it is not a\n * symlink swapped in after the snapshot; `rmSync` removes a symlink itself\n * rather than following it, but refusing surfaces tampering loudly.\n *\n * - The file DID exist before publish: restore its prior content using\n * \"write-new-before-delete-old\" (rules 42/54). We write the prior content to\n * a temp path in the same directory, then {@link fs.renameSync} it into\n * place. The live file is never truncated, so a mid-restore failure (disk\n * full, EACCES, …) leaves the current on-disk content intact rather than\n * half-written — the restore either fully lands or does nothing. The temp\n * path uses the `.tmp-<pid>-<ts>` suffix tracked by\n * {@link EXTENSION_OWNED_TEMP_FILE_SUFFIX}, so any lingering temp is swept\n * by unpublish. The final `renameSync` does NOT follow a symlink even if one\n * was swapped into the snapshot path after the snapshot (TOCTOU\n * defense-in-depth): `rename(2)` replaces the symlink itself, so no write\n * ever reaches an arbitrary target. We still re-check for a symlink right\n * before the rename so the rollback surfaces tampering instead of silently\n * replacing it.\n */\nfunction restoreOwnedFile(snapshot: FileSnapshot): void {\n if (!snapshot.existed) {\n assertSafeExistingPath(snapshot.path);\n fs.rmSync(snapshot.path, { force: true });\n return;\n }\n\n fs.mkdirSync(path.dirname(snapshot.path), { recursive: true });\n const tmpPath = `${snapshot.path}.tmp-${process.pid}-${Date.now()}`;\n try {\n fs.writeFileSync(tmpPath, snapshot.content ?? Buffer.alloc(0), {\n mode: snapshot.mode ?? 0o644,\n });\n if (snapshot.mode !== undefined) {\n try {\n fs.chmodSync(tmpPath, snapshot.mode);\n } catch {\n // Best effort for platforms that do not support chmod.\n }\n }\n rejectSymlinkPath(snapshot.path);\n fs.renameSync(tmpPath, snapshot.path);\n } catch (err) {\n try {\n if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);\n } catch {\n // Best-effort cleanup only.\n }\n throw err;\n }\n}\n\nfunction snapshotDirs(paths: string[]): DirSnapshot[] {\n return paths.map((dirPath) => {\n let existed = false;\n try {\n const stat = fs.lstatSync(dirPath);\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${dirPath}`);\n }\n existed = stat.isDirectory();\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") {\n existed = false;\n } else {\n throw err;\n }\n }\n return { path: dirPath, existed };\n });\n}\n\nfunction restoreDirSnapshots(snapshots: DirSnapshot[]): void {\n for (const snapshot of snapshots) {\n if (snapshot.existed) continue;\n try {\n fs.rmSync(snapshot.path, { recursive: true, force: true });\n } catch {\n // best-effort — the loader self-heals at runtime if the dir lingers\n }\n }\n}\n\n\nfunction canCleanNewExtensionRoot(extensionRoot: string): boolean {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(extensionRoot);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") return false;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${extensionRoot}`);\n }\n return stat.isDirectory();\n}\n\nfunction removeEmptyDirectory(dirPath: string): void {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(dirPath);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") return;\n throw err;\n }\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${dirPath}`);\n }\n if (!stat.isDirectory()) return;\n if (fs.readdirSync(dirPath).length > 0) return;\n fs.rmdirSync(dirPath);\n}\n\nfunction removableOwnedExtensionFiles(filePaths: string[]): string[] {\n const removableFiles: string[] = [];\n for (const filePath of filePaths) {\n const stat = statOwnedExtensionPath(filePath);\n if (stat === null) continue;\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${filePath}`);\n }\n if (stat.isFile()) removableFiles.push(filePath);\n }\n return removableFiles;\n}\n\nfunction statOwnedExtensionPath(filePath: string): fs.Stats | null {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(filePath);\n } catch (err) {\n if (err && typeof err === \"object\" && \"code\" in err && err.code === \"ENOENT\") return null;\n throw err;\n }\n return stat;\n}\n\nfunction mkdirExtensionRoot(extensionRoot: string, agentHome: string): void {\n const extensionsDir = path.join(path.resolve(agentHome), \"extensions\");\n assertSafeExtensionRoot(extensionRoot, agentHome);\n fs.mkdirSync(extensionsDir, { recursive: true });\n rejectSymlinkPath(extensionsDir);\n fs.mkdirSync(extensionRoot, { recursive: true });\n rejectSymlinkPath(extensionRoot);\n}\n\nfunction assertSafeExtensionRoot(extensionRoot: string, agentHome: string): void {\n const resolvedAgentHome = path.resolve(agentHome);\n const expected = path.join(resolvedAgentHome, \"extensions\", \"remnic\");\n if (path.resolve(extensionRoot) !== path.resolve(expected)) {\n throw new Error(`Extension root is outside the configured extensions directory: ${extensionRoot}`);\n }\n const extensionsDir = path.join(resolvedAgentHome, \"extensions\");\n assertPathContained(resolvedAgentHome, extensionsDir);\n assertPathContained(extensionsDir, extensionRoot);\n rejectSymlinkPath(resolvedAgentHome);\n if (fs.existsSync(extensionsDir)) rejectSymlinkPath(extensionsDir);\n if (fs.existsSync(extensionRoot)) rejectSymlinkPath(extensionRoot);\n}\n\nfunction assertSafeExistingPath(filePath: string): void {\n if (fs.existsSync(filePath)) rejectSymlinkPath(filePath);\n}\n\nfunction rejectSymlinkPath(filePath: string): void {\n if (!fs.existsSync(filePath)) return;\n const stat = fs.lstatSync(filePath);\n if (stat.isSymbolicLink()) {\n throw new Error(`Extension path must not be a symlink: ${filePath}`);\n }\n}\n\nfunction assertPathContained(root: string, candidate: string): void {\n const rootResolved = path.resolve(root);\n const candidateResolved = path.resolve(candidate);\n const relative = path.relative(rootResolved, candidateResolved);\n if (relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative))) return;\n throw new Error(`Extension path escapes allowed root: ${candidate}`);\n}\n\nfunction readPriorConfig(configPath: string): Record<string, unknown> {\n if (!fs.existsSync(configPath)) return {};\n try {\n const parsed = JSON.parse(fs.readFileSync(configPath, \"utf8\"));\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 existing Remnic Pi config at ${configPath}: ${reason}`);\n }\n}\n\nfunction snapshotTokenEntry(connectorId: string): TokenEntry | null {\n const entry = loadTokenStore().tokens.find((candidate) => candidate.connector === connectorId);\n return cloneTokenEntry(entry ?? null);\n}\n\nfunction cloneTokenEntry(entry: TokenEntry | null): TokenEntry | null {\n return entry ? { ...entry } : null;\n}\n\nfunction restoreTokenEntry(priorEntry: TokenEntry | null, connectorId: string): void {\n const store = loadTokenStore();\n store.tokens = store.tokens.filter((entry) => entry.connector !== connectorId);\n if (priorEntry) store.tokens.push(priorEntry);\n saveTokenStore(store);\n}\n","/**\n * Pure template renderers for the omp extension's generated files\n * (loader.js, package.json, postinstall-bundle.cjs).\n *\n * Extracted from publisher.ts so that file stays under the file-size ratchet\n * cap; the functions are string builders only — no filesystem or env access —\n * which keeps them trivially testable in isolation.\n */\n\n/**\n * Generates the self-healing `loader.js` that omp loads via the package\n * manifest's `omp.extensions` entry. It mtime-compares the pre-bundled\n * `dist-bundle/index.js` against `index.ts` and the underlying @remnic/plugin-pi\n * dist, rebuilds via `bun build` when stale (e.g. after an `npm update`), then\n * imports the self-contained bundle so omp's embedded runtime never resolves\n * bare npm specifiers at load time.\n */\nexport function renderOmpLoader(pluginPiDistPath: string, bunBin: string): string {\n return [\n \"// Auto-generated by Remnic's OmpMemoryExtensionPublisher.\",\n \"// omp's embedded runtime cannot resolve bare npm specifiers from this\",\n \"// extension's node_modules, so we pre-bundle with `bun build` and import\",\n \"// the self-contained bundle here. Rebuilt automatically when index.ts or\",\n \"// the underlying @remnic/plugin-pi dist changes.\",\n \"\",\n 'import { existsSync, renameSync, rmSync, statSync } from \"node:fs\";',\n 'import { spawnSync } from \"node:child_process\";',\n 'import { dirname, join } from \"node:path\";',\n 'import { fileURLToPath, pathToFileURL } from \"node:url\";',\n \"\",\n \"const here = dirname(fileURLToPath(import.meta.url));\",\n 'const bundleDir = join(here, \"dist-bundle\");',\n 'const bundleEntry = join(bundleDir, \"index.js\");',\n 'const sourceEntry = join(here, \"index.ts\");',\n `const pluginPiEntry = ${JSON.stringify(pluginPiDistPath)};`,\n // Reuse the bun path resolved at install time (REMNIC_OMP_BUN_BIN, PATH,\n // or a common absolute location). Fall back to \"bun\" on PATH if the\n // resolved path no longer exists (e.g. the extension tree was moved), so\n // self-healing still works when bun is reachable only via PATH.\n `const resolvedBunBin = ${JSON.stringify(bunBin)};`,\n 'const bunForRebuild = resolvedBunBin && existsSync(resolvedBunBin) ? resolvedBunBin : \"bun\";',\n \"\",\n \"function bundleIsStale() {\",\n \" if (!existsSync(bundleEntry)) return true;\",\n \" const bundleMtime = statSync(bundleEntry).mtimeMs;\",\n \" if (existsSync(sourceEntry) && bundleMtime < statSync(sourceEntry).mtimeMs) return true;\",\n \" if (pluginPiEntry && existsSync(pluginPiEntry) && bundleMtime < statSync(pluginPiEntry).mtimeMs) return true;\",\n \" return false;\",\n \"}\",\n \"\",\n \"function rebuildBundle() {\",\n \" // Build to a temp dir and swap, mirroring the install-time build, so a\",\n \" // failed self-heal rebuild never corrupts the working bundle.\",\n ' var tmp = join(here, \".dist-bundle.tmp-\" + process.pid + \"-\" + Date.now());',\n \" var result = spawnSync(bunForRebuild, [\",\n ' \"build\",',\n \" sourceEntry,\",\n ' \"--target=bun\",',\n ' \"--outdir=\" + tmp',\n \" ], {\",\n \" cwd: here,\",\n ' stdio: \"inherit\",',\n \" });\",\n \" if (result.status !== 0 || result.error) {\",\n \" try { rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\",\n \" throw new Error(\",\n ' \"Remnic omp extension: bundle is stale or missing and could not be rebuilt. \" +',\n ' \"Install bun (https://bun.sh), then run \" +',\n ' \"`bun build index.ts --target=bun --outdir=dist-bundle` inside \" + here',\n \" );\",\n \" }\",\n \" var backup = null;\",\n \" try {\",\n \" if (existsSync(bundleDir)) {\",\n ' backup = join(here, \".dist-bundle.bak-\" + process.pid + \"-\" + Date.now());',\n \" renameSync(bundleDir, backup);\",\n \" }\",\n \" renameSync(tmp, bundleDir);\",\n \" if (backup) { try { rmSync(backup, { recursive: true, force: true }); } catch (e) {} }\",\n \" } catch (err) {\",\n \" try { rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\",\n \" if (backup && existsSync(backup) && !existsSync(bundleDir)) { try { renameSync(backup, bundleDir); } catch (e) {} }\",\n \" throw new Error(\",\n ' \"Remnic omp extension: failed to finalize rebuilt bundle - \" + (err && err.message ? err.message : err)',\n \" );\",\n \" }\",\n \"}\",\n\n \"\",\n \"if (bundleIsStale()) rebuildBundle();\",\n \"\",\n \"// Cache-bust so a freshly rebuilt bundle is loaded instead of a stale cached copy.\",\n 'const bundle = await import(pathToFileURL(bundleEntry).href + \"?t=\" + Date.now());',\n \"export default bundle.default;\",\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Generates the `package.json` that tells omp to load `loader.js` (not\n * auto-discover `index.ts`) and re-bundles after `npm install` via postinstall.\n */\nexport function renderOmpPackageJson(): string {\n // Postinstall re-bundles after `npm install` (e.g. a plugin-pi upgrade moved\n // the dist mtime past the bundle). It delegates to postinstall-bundle.cjs — a\n // Node-only helper — so npm's default cmd.exe shell on Windows runs it just as\n // well as POSIX bash. The helper embeds the resolved bun path with a PATH\n // fallback and swaps the bundle atomically.\n const manifest = {\n name: \"remnic-omp-extension\",\n version: \"0.0.0\",\n private: true,\n type: \"module\",\n omp: { extensions: [\"./loader.js\"] },\n // Legacy key so older omp builds that only read `pi.extensions` also\n // resolve loader.js instead of falling through to index.ts.\n pi: { extensions: [\"./loader.js\"] },\n scripts: { postinstall: \"node postinstall-bundle.cjs\" },\n };\n return `${JSON.stringify(manifest, null, 2)}\\n`;\n}\n\n/**\n * Generates the cross-platform `postinstall-bundle.cjs` helper. Node-only, so\n * npm's default cmd.exe shell on Windows re-bundles after `npm install` just as\n * well as POSIX bash. Embeds the bun path resolved at install time with a PATH\n * fallback and writes the new bundle via a temp-dir swap so a failed rebuild\n * never corrupts the working bundle. The emitted script uses string\n * concatenation (no template literals) so it stays parseable everywhere.\n */\nexport function renderOmpPostinstall(bunBin: string): string {\n // Single template literal: the emitted .cjs uses string concatenation (no\n // template literals of its own), so this body has no backticks and the one\n // ${JSON.stringify(bunBin)} interpolation is unambiguous.\n return `// Auto-generated by Remnic's OmpMemoryExtensionPublisher.\n// Re-bundles the omp extension after npm install (e.g. a plugin-pi upgrade)\n// using the bun path resolved at install time, with a PATH fallback. Node-only\n// so it runs under npm's default cmd.exe shell on Windows as well as POSIX bash.\n\"use strict\";\nvar fs = require(\"node:fs\");\nvar cp = require(\"node:child_process\");\nvar path = require(\"node:path\");\n\nvar RESOLVED_BUN = ${JSON.stringify(bunBin)};\nvar dir = __dirname;\nvar entry = path.join(dir, \"index.ts\");\nvar out = path.join(dir, \"dist-bundle\");\n\nfunction pickBun() {\n var env = process.env.REMNIC_OMP_BUN_BIN;\n if (env && fs.existsSync(env)) return env;\n if (RESOLVED_BUN && fs.existsSync(RESOLVED_BUN)) return RESOLVED_BUN;\n return \"bun\";\n}\n\nfunction rebuild() {\n var bun = pickBun();\n var tmp = path.join(dir, \".dist-bundle.tmp-\" + process.pid + \"-\" + Date.now());\n var r = cp.spawnSync(bun, [\"build\", entry, \"--target=bun\", \"--outdir=\" + tmp], { cwd: dir, stdio: \"inherit\" });\n if (r.error || r.status !== 0) {\n try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\n throw new Error(\"Remnic omp extension: postinstall bun build failed (bun=\" + bun + \"). Run bun build index.ts --target=bun --outdir=dist-bundle manually inside \" + dir);\n }\n var backup = null;\n try {\n if (fs.existsSync(out)) {\n backup = path.join(dir, \".dist-bundle.bak-\" + process.pid + \"-\" + Date.now());\n fs.renameSync(out, backup);\n }\n fs.renameSync(tmp, out);\n if (backup) { try { fs.rmSync(backup, { recursive: true, force: true }); } catch (e) {} }\n } catch (err) {\n try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (e) {}\n if (backup && fs.existsSync(backup) && !fs.existsSync(out)) { try { fs.renameSync(backup, out); } catch (e) {} }\n throw err;\n }\n}\n\ntry {\n rebuild();\n} catch (err) {\n console.error(err && err.message ? err.message : err);\n process.exit(1);\n}\n`;\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/plugin-pi",
3
- "version": "9.63.11",
3
+ "version": "9.64.0",
4
4
  "description": "Remnic memory extension for Pi Coding Agent",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@sinclair/typebox": "^0.34.0",
45
- "@remnic/core": "^9.63.11"
45
+ "@remnic/core": "^9.64.0"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "@earendil-works/pi-coding-agent": "*"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/paths.ts","../src/config.ts"],"sourcesContent":["import os from \"node:os\";\nimport path from \"node:path\";\n\n// Leaf submodule (not the `@remnic/core` barrel) so the omp pre-bundle's\n// `bun build` does not pull the rest of core into the extension bundle.\nimport { expandTildePath } from \"@remnic/core/utils/path\";\n\nexport const REMNIC_PI_EXTENSION_DIR_NAME = \"remnic\";\n\nexport function resolvePiAgentHome(env: NodeJS.ProcessEnv): string {\n const explicitCodingAgentDir = env.PI_CODING_AGENT_DIR?.trim();\n if (explicitCodingAgentDir) return path.resolve(expandTildePath(explicitCodingAgentDir));\n\n const explicitAgentHome = env.PI_AGENT_HOME?.trim();\n if (explicitAgentHome) return path.resolve(expandTildePath(explicitAgentHome));\n\n const explicitPiHome = env.PI_HOME?.trim();\n if (explicitPiHome) return path.join(path.resolve(expandTildePath(explicitPiHome)), \"agent\");\n\n return path.join(env.HOME ?? env.USERPROFILE ?? os.homedir(), \".pi\", \"agent\");\n}\n\nexport function resolvePiExtensionRoot(env: NodeJS.ProcessEnv): string {\n return path.join(resolvePiAgentHome(env), \"extensions\", REMNIC_PI_EXTENSION_DIR_NAME);\n}\n\n/**\n * Resolve the active omp profile from the environment, mirroring omp's\n * `resolveProfileEnv`: `OMP_PROFILE` is authoritative, and `PI_PROFILE` is a\n * compatibility fallback consulted **only** when `OMP_PROFILE` is undefined\n * (an explicitly-empty `OMP_PROFILE` therefore selects the default profile).\n * The reserved name \"default\" and blank values resolve to the base (no profile).\n */\nfunction resolveOmpProfile(env: NodeJS.ProcessEnv): string | undefined {\n const raw = env.OMP_PROFILE !== undefined ? env.OMP_PROFILE : env.PI_PROFILE;\n const trimmed = raw?.trim();\n if (!trimmed || trimmed === \"default\") return undefined;\n return trimmed;\n}\n\n/**\n * Resolve the omp (oh-my-pi) agent home directory that omp auto-discovers\n * extensions from. Mirrors omp's `DirResolver` (packages/utils/src/dirs.ts):\n *\n * - The config dir name is `PI_CONFIG_DIR` (default `.omp`).\n * - When a profile (`OMP_PROFILE`, falling back to `PI_PROFILE`) is active it\n * wins and resolves to `<configRoot>/profiles/<name>/agent`; omp discards\n * the `PI_CODING_AGENT_DIR` override while a profile is active.\n * - Otherwise `PI_CODING_AGENT_DIR` overrides the whole agent dir.\n * - Otherwise the base agent dir is `<configRoot>/agent`.\n *\n * Note: omp's XDG redirection (`XDG_DATA_HOME`, etc.) applies to the `data`,\n * `state`, and `cache` categories (sessions/state/cache) — NOT to the base\n * agent dir that extensions are discovered from — so it is intentionally not\n * consulted here.\n */\n/**\n * The omp config root (`~/<PI_CONFIG_DIR or .omp>`), which contains the base\n * `agent/` dir and any `profiles/<name>/agent/` dirs.\n */\nexport function resolveOmpConfigRoot(env: NodeJS.ProcessEnv): string {\n const home = env.HOME ?? env.USERPROFILE ?? os.homedir();\n const configDirName = env.PI_CONFIG_DIR?.trim() || \".omp\";\n return path.join(home, configDirName);\n}\n\nexport function resolveOmpAgentHome(env: NodeJS.ProcessEnv): string {\n const configRoot = resolveOmpConfigRoot(env);\n\n const profile = resolveOmpProfile(env);\n if (profile) {\n return path.join(configRoot, \"profiles\", profile, \"agent\");\n }\n\n const explicitCodingAgentDir = env.PI_CODING_AGENT_DIR?.trim();\n if (explicitCodingAgentDir) return path.resolve(expandTildePath(explicitCodingAgentDir));\n\n return path.join(configRoot, \"agent\");\n}\n\nexport function resolveOmpExtensionRoot(env: NodeJS.ProcessEnv): string {\n return path.join(resolveOmpAgentHome(env), \"extensions\", REMNIC_PI_EXTENSION_DIR_NAME);\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\n// Leaf submodule (not the `@remnic/core` barrel) so the omp pre-bundle's\n// `bun build` does not pull the rest of core — including the LanceDB native\n// asset — into the extension bundle. See PR #1641.\nimport { expandTildePath } from \"@remnic/core/utils/path\";\n\nimport { REMNIC_PI_EXTENSION_DIR_NAME, resolvePiAgentHome } from \"./paths.js\";\n\nexport interface RemnicPiConfig {\n remnicDaemonUrl: string;\n authToken?: string;\n namespace?: string;\n recallMode: \"auto\" | \"minimal\" | \"full\" | \"graph_mode\" | \"no_recall\";\n recallTopK: number;\n recallBudgetChars: number;\n recallEnabled: boolean;\n observeEnabled: boolean;\n observeSkipExtraction: boolean;\n compactionEnabled: boolean;\n mcpToolsEnabled: boolean;\n statusEnabled: boolean;\n requestTimeoutMs: number;\n startupRequestTimeoutMs: number;\n /**\n * Per-turn request budget for observe/recall. MUST stay below the host's\n * in-handler kill budget (Pi/omp kills handlers at 30 s). Defaults to 20 s,\n * capped at 25 s so a misconfiguration can never produce a structurally\n * unsatisfiable timeout (issue #1626).\n */\n turnRequestTimeoutMs: number;\n /**\n * Soft cap on a single observe POST body in bytes. The client chunks observe\n * batches to stay under this; individual oversized messages are truncated\n * with a marker. Defaults to 100 KiB, safely under the daemon's default\n * 128 KiB `maxBodyBytes` (issue #1600).\n */\n observeMaxBytes: number;\n /**\n * Maximum retry attempts for observe/recall on transient connection-level\n * failures (socket close, ECONNRESET, EPIPE). Observe is dedupe-safe so\n * retrying is harmless (issue #1602).\n */\n observeMaxRetries: number;\n /**\n * Cooldown base for the daemon-reachability circuit breaker. When observe/\n * recall fails on a timeout or connection error, subsequent turns skip fast\n * for an exponentially growing window starting at this value (issue #1626).\n */\n daemonCooldownMs: number;\n /**\n * Number of explicit recall timeout errors in the last {@link recallTimeoutWindow}\n * recall calls that permanently disables automatic recall for the process lifetime.\n */\n recallTimeoutThreshold: number;\n /**\n * Size of the rolling window of recent recall calls used by the recall-timeout\n * circuit breaker.\n */\n recallTimeoutWindow: number;\n}\n\nexport interface LoadConfigOptions {\n configPath?: string;\n env?: NodeJS.ProcessEnv;\n}\n\nexport const 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 // Recall-timeout circuit breaker: 7 timeouts in the last 10 recall calls trip permanently.\n recallTimeoutThreshold: 7,\n recallTimeoutWindow: 10,\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 const recallTimeoutThreshold = coercePositiveInt(\n fileConfig.recallTimeoutThreshold,\n DEFAULT_CONFIG.recallTimeoutThreshold,\n 1000,\n \"recallTimeoutThreshold\",\n );\n const recallTimeoutWindow = coercePositiveInt(\n fileConfig.recallTimeoutWindow,\n DEFAULT_CONFIG.recallTimeoutWindow,\n 1000,\n \"recallTimeoutWindow\",\n );\n if (recallTimeoutThreshold > recallTimeoutWindow) {\n throw new Error(\n `Invalid recall timeout circuit breaker config: threshold (${recallTimeoutThreshold}) cannot exceed window (${recallTimeoutWindow})`,\n );\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 recallTimeoutThreshold,\n recallTimeoutWindow,\n };\n}\n"],"mappings":"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","names":["path","expandTildePath","path","expandTildePath"]}