@theokit/sdk 4.57.0 → 4.58.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.
Files changed (98) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/dist/{agent-ZIYCVEZL.js → agent-6WM4QOWS.js} +10 -9
  3. package/dist/{agent-ZIYCVEZL.js.map → agent-6WM4QOWS.js.map} +1 -1
  4. package/dist/{agent-2T2EJIP6.cjs → agent-MIYY7TXV.cjs} +11 -10
  5. package/dist/{agent-2T2EJIP6.cjs.map → agent-MIYY7TXV.cjs.map} +1 -1
  6. package/dist/{chunk-E7JHD6QJ.cjs → chunk-27AXGJMV.cjs} +25 -15
  7. package/dist/chunk-27AXGJMV.cjs.map +1 -0
  8. package/dist/{chunk-LEM2GOMI.js → chunk-5QOIQJ5J.js} +3 -3
  9. package/dist/{chunk-LEM2GOMI.js.map → chunk-5QOIQJ5J.js.map} +1 -1
  10. package/dist/{chunk-E3WZ6Y4H.cjs → chunk-7BG5UH3T.cjs} +8 -8
  11. package/dist/{chunk-E3WZ6Y4H.cjs.map → chunk-7BG5UH3T.cjs.map} +1 -1
  12. package/dist/chunk-AQO3NSRG.cjs +41 -0
  13. package/dist/chunk-AQO3NSRG.cjs.map +1 -0
  14. package/dist/{chunk-7TCRNXNK.js → chunk-CF5OMCZG.js} +40 -16
  15. package/dist/chunk-CF5OMCZG.js.map +1 -0
  16. package/dist/{chunk-U2AC6JUP.cjs → chunk-J2UROOIG.cjs} +2 -27
  17. package/dist/chunk-J2UROOIG.cjs.map +1 -0
  18. package/dist/{chunk-ZNW6V4Y6.cjs → chunk-LKFET5A3.cjs} +23 -2
  19. package/dist/chunk-LKFET5A3.cjs.map +1 -0
  20. package/dist/{chunk-YMA4S2WO.js → chunk-MZ2FGBLZ.js} +33 -11
  21. package/dist/chunk-MZ2FGBLZ.js.map +1 -0
  22. package/dist/{chunk-M2JWHKO6.cjs → chunk-N47KLTKD.cjs} +76 -52
  23. package/dist/chunk-N47KLTKD.cjs.map +1 -0
  24. package/dist/{chunk-53CBTBWO.cjs → chunk-P6H23T43.cjs} +33 -11
  25. package/dist/chunk-P6H23T43.cjs.map +1 -0
  26. package/dist/{chunk-X7O4255I.cjs → chunk-QIRDQZYL.cjs} +4 -4
  27. package/dist/{chunk-X7O4255I.cjs.map → chunk-QIRDQZYL.cjs.map} +1 -1
  28. package/dist/{chunk-7FHZ4VQX.js → chunk-SAVIWMZB.js} +3 -3
  29. package/dist/{chunk-7FHZ4VQX.js.map → chunk-SAVIWMZB.js.map} +1 -1
  30. package/dist/{chunk-UPRJR6IP.js → chunk-UCBJBJ27.js} +3 -25
  31. package/dist/chunk-UCBJBJ27.js.map +1 -0
  32. package/dist/chunk-UFLD2HEV.js +35 -0
  33. package/dist/chunk-UFLD2HEV.js.map +1 -0
  34. package/dist/{chunk-GTKFV7O5.js → chunk-WFC26L6Y.js} +23 -2
  35. package/dist/chunk-WFC26L6Y.js.map +1 -0
  36. package/dist/{chunk-G6EEYRDD.js → chunk-WLZ7XRWH.js} +24 -14
  37. package/dist/chunk-WLZ7XRWH.js.map +1 -0
  38. package/dist/{chunk-HJBMA5MB.cjs → chunk-WNTAPVU5.cjs} +47 -6
  39. package/dist/chunk-WNTAPVU5.cjs.map +1 -0
  40. package/dist/{chunk-2QKTVKH3.js → chunk-ZHPWGIUX.js} +48 -8
  41. package/dist/chunk-ZHPWGIUX.js.map +1 -0
  42. package/dist/context/index.cjs +7 -7
  43. package/dist/context/index.js +3 -3
  44. package/dist/{context-VMIE4BMD.cjs → context-LCKNH2XI.cjs} +7 -6
  45. package/dist/{context-VMIE4BMD.cjs.map → context-LCKNH2XI.cjs.map} +1 -1
  46. package/dist/context-MZSKGKYR.js +7 -0
  47. package/dist/{context-FDOON2DB.js.map → context-MZSKGKYR.js.map} +1 -1
  48. package/dist/cron.cjs +10 -9
  49. package/dist/cron.js +9 -8
  50. package/dist/eval.cjs +9 -8
  51. package/dist/eval.cjs.map +1 -1
  52. package/dist/eval.js +8 -7
  53. package/dist/eval.js.map +1 -1
  54. package/dist/{index-manager-AO4LJQZ4.cjs → index-manager-J2QIDI6I.cjs} +5 -4
  55. package/dist/{index-manager-AO4LJQZ4.cjs.map → index-manager-J2QIDI6I.cjs.map} +1 -1
  56. package/dist/{index-manager-QDPIYIGV.js → index-manager-WYESAVTX.js} +4 -3
  57. package/dist/{index-manager-QDPIYIGV.js.map → index-manager-WYESAVTX.js.map} +1 -1
  58. package/dist/index.cjs +32 -33
  59. package/dist/index.cjs.map +1 -1
  60. package/dist/index.d.cts +1 -1
  61. package/dist/index.d.ts +1 -1
  62. package/dist/index.js +12 -13
  63. package/dist/index.js.map +1 -1
  64. package/dist/internal/memory/storage/markdown-store.d.ts +13 -0
  65. package/dist/internal/persistence/index.cjs +17 -16
  66. package/dist/internal/persistence/index.cjs.map +1 -1
  67. package/dist/internal/persistence/index.js +2 -1
  68. package/dist/internal/persistence/index.js.map +1 -1
  69. package/dist/internal/persistence/paths.d.cts +42 -0
  70. package/dist/internal/persistence/paths.d.ts +42 -0
  71. package/dist/internal/runtime/plugins/plugin-bundles.d.ts +19 -0
  72. package/dist/project.cjs +3 -3
  73. package/dist/project.js +1 -1
  74. package/dist/subagents-loader-6ATCDEKN.js +8 -0
  75. package/dist/{subagents-loader-J54ESLDV.js.map → subagents-loader-6ATCDEKN.js.map} +1 -1
  76. package/dist/subagents-loader-X5QKODCL.cjs +17 -0
  77. package/dist/{subagents-loader-AZIXJ7D3.cjs.map → subagents-loader-X5QKODCL.cjs.map} +1 -1
  78. package/dist/subagents-loader.cjs +3 -2
  79. package/dist/subagents-loader.cjs.map +1 -1
  80. package/dist/subagents-loader.js +2 -1
  81. package/dist/subagents-loader.js.map +1 -1
  82. package/docs/error-codes.md +15 -15
  83. package/package.json +1 -1
  84. package/dist/chunk-2QKTVKH3.js.map +0 -1
  85. package/dist/chunk-53CBTBWO.cjs.map +0 -1
  86. package/dist/chunk-7TCRNXNK.js.map +0 -1
  87. package/dist/chunk-E7JHD6QJ.cjs.map +0 -1
  88. package/dist/chunk-G6EEYRDD.js.map +0 -1
  89. package/dist/chunk-GTKFV7O5.js.map +0 -1
  90. package/dist/chunk-HJBMA5MB.cjs.map +0 -1
  91. package/dist/chunk-M2JWHKO6.cjs.map +0 -1
  92. package/dist/chunk-U2AC6JUP.cjs.map +0 -1
  93. package/dist/chunk-UPRJR6IP.js.map +0 -1
  94. package/dist/chunk-YMA4S2WO.js.map +0 -1
  95. package/dist/chunk-ZNW6V4Y6.cjs.map +0 -1
  96. package/dist/context-FDOON2DB.js +0 -6
  97. package/dist/subagents-loader-AZIXJ7D3.cjs +0 -16
  98. package/dist/subagents-loader-J54ESLDV.js +0 -7
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/persistence/schema-version.ts"],"names":[],"mappings":"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","file":"chunk-UCBJBJ27.js","sourcesContent":["/**\n * Schema versioning helpers (ADR D62).\n *\n * Two parallel APIs:\n * - `migrateSchema` — SQLite via `PRAGMA user_version` + ordered forward-only migrations.\n * - `readVersionedJson` / `writeVersionedJson` — JSON files with `_schemaVersion` field.\n *\n * Forward-only: never deletes data, never downgrades. Migration callbacks\n * own the transformation; this module owns the bookkeeping.\n *\n * @internal\n */\n\nimport { readFile, rename } from \"node:fs/promises\";\nimport { diag } from \"../diagnostics.js\";\nimport { atomicWriteJson } from \"./atomic-write.js\";\n\n// ────────────────────── SQLite migrations ──────────────────────\n\n/**\n * The three `better-sqlite3` methods this module actually uses, declared structurally so nothing\n * here imports the driver.\n *\n * A real `better-sqlite3` `Database` satisfies it. `pragma` is called both to read\n * (`pragma(\"user_version\", { simple: true })`, which must return the number itself rather than a\n * row array) and to write (`pragma(\"user_version = N\")`). `transaction` must return a function\n * that, when called, runs the wrapped body inside a real SQLite transaction and rolls it back if\n * the body throws — a stub that merely calls through gives `migrateSchema` no atomicity, and a\n * half-applied migration is exactly what it exists to prevent.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface SqliteLike {\n pragma: (statement: string, options?: { simple?: boolean }) => unknown;\n exec: (sql: string) => void;\n transaction: <Args extends unknown[], R>(fn: (...args: Args) => R) => (...args: Args) => R;\n}\n\n/**\n * One forward migration step.\n *\n * `up` runs inside the shared transaction and must be self-contained: it may not commit, and any\n * error it throws aborts the whole run and rolls back every step in it, including the ones that\n * already succeeded. `user_version` is set to `toVersion` immediately after `up` returns, inside\n * the same transaction, so version and schema move together or not at all.\n *\n * `toVersion` is the version the database reaches, not the one it starts from. Steps are ordered\n * by it, gaps are allowed, and two steps sharing a `toVersion` both run — nothing deduplicates\n * them.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface Migration {\n toVersion: number;\n up: (db: SqliteLike) => void;\n}\n\n/**\n * Arguments to `migrateSchema`.\n *\n * `currentVersion` is the version the code expects, and it is the ceiling: a migration whose\n * `toVersion` exceeds it is left unapplied for a later release. `migrations` may be given in any\n * order and may contain steps already applied — both are filtered and sorted here. `label` only\n * ever appears in the downgrade error message.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface MigrateSchemaOptions {\n db: SqliteLike;\n currentVersion: number;\n migrations: ReadonlyArray<Migration>;\n /** For log/error context (e.g., \"memory-index\", \"registry\"). */\n label?: string;\n}\n\n/**\n * What `migrateSchema` did.\n *\n * `from` is the `user_version` found on the database, `to` is the version it holds afterwards,\n * and `ran` is how many steps executed. When nothing was pending, `to === from` and `ran === 0`.\n *\n * `to` is the `toVersion` of the last step applied, NOT `currentVersion`. A database at version 3\n * against `currentVersion: 5` with no migration in that range comes back as\n * `{ from: 3, to: 3, ran: 0 }` — the gap is reported, not closed. Comparing `to` with\n * `currentVersion` is how a caller notices that the migration list is missing a step.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface MigrateSchemaResult {\n from: number;\n to: number;\n ran: number;\n}\n\n/**\n * Bring a SQLite database's `user_version` up to `currentVersion` by running the steps that sit\n * between the two.\n *\n * Steps are sorted ascending by `toVersion` and filtered to `> stored && <= currentVersion`, then\n * run in that order inside ONE transaction: each `up` is followed immediately by the matching\n * `user_version` write. Either every step in the run commits or none does — a step that throws\n * aborts the transaction, propagates the error, and leaves the database at the version it started\n * from. There is no partially-migrated state and no resume point.\n *\n * Forward-only. When the stored version is greater than `currentVersion` — an older build opening\n * a database a newer one already migrated — this throws immediately, before touching anything,\n * with a message naming both versions and `label`. Nothing downgrades, and no data is deleted on\n * any path here; what a step does is the step's own business.\n *\n * A stored version equal to `currentVersion` returns without opening a transaction. A `user_version`\n * that is absent or not a number is read as 0, so a fresh database runs every step.\n *\n * Concurrency: the SQLite transaction is the only serialization. This takes no file lock, so two\n * processes migrating the same file at once are arbitrated by SQLite's own write lock — the loser\n * gets a busy or locked error from the driver, not a queued turn. Wrap the call in `withFileLock`\n * if that matters.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function migrateSchema(opts: MigrateSchemaOptions): MigrateSchemaResult {\n const { db, currentVersion, migrations, label = \"db\" } = opts;\n const storedRaw = db.pragma(\"user_version\", { simple: true });\n const stored = typeof storedRaw === \"number\" ? storedRaw : 0;\n\n if (stored > currentVersion) {\n throw new Error(\n `[${label}] schema version ${stored} > current ${currentVersion}; ` +\n \"did you downgrade the SDK? Forward-only migrations only.\",\n );\n }\n\n if (stored === currentVersion) {\n return { from: stored, to: stored, ran: 0 };\n }\n\n const pending = [...migrations]\n .sort((a, b) => a.toVersion - b.toVersion)\n .filter((m) => m.toVersion > stored && m.toVersion <= currentVersion);\n\n let ran = 0;\n let lastApplied = stored;\n\n db.transaction(() => {\n for (const m of pending) {\n m.up(db);\n db.pragma(`user_version = ${m.toVersion}`);\n lastApplied = m.toVersion;\n ran += 1;\n }\n })();\n\n return { from: stored, to: lastApplied, ran };\n}\n\n// ────────────────────── JSON versioned files ──────────────────────\n\n/**\n * The on-disk envelope: the payload under `data`, its schema version alongside it.\n *\n * `writeVersionedJson` always produces this shape. `readVersionedJson` reads it, but tolerates a\n * file that is not in it — a legacy object with no `_schemaVersion` is treated as version 0 and\n * handed to the migrate callback whole, so this type describes what is written, not everything\n * that can be read.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface VersionedJsonFile<T> {\n _schemaVersion: number;\n data: T;\n}\n\n/**\n * Upgrade callback for `readVersionedJson`, invoked only when the stored version is BELOW the\n * current one.\n *\n * `parsed` is the whole parsed JSON value, not `parsed.data`. That is deliberate: a legacy file\n * predating the envelope has its fields at the top level, and a callback handed only `.data`\n * would receive `undefined` for exactly the files that need migrating. It is typed `unknown`\n * because nothing has validated it — narrow before reading.\n *\n * `fromVersion` is the `_schemaVersion` found on disk, or 0 when the field is missing or not a\n * number.\n *\n * The return value is used as-is and is NOT written back; the file on disk still holds the old\n * shape until someone calls `writeVersionedJson`. Throwing here propagates out of\n * `readVersionedJson`, which otherwise never throws — return the default value instead if an\n * unmigratable file should be survivable.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport type VersionedJsonMigrate<T> = (parsed: unknown, fromVersion: number) => T;\n\n/**\n * Arguments to `readVersionedJson`.\n *\n * `defaultValue` is a factory rather than a value because it is called on several paths and each\n * caller must get its own object — returning a shared mutable default would let one caller's\n * edits appear in another's. It is called for a missing file, an unreadable one, a corrupt one,\n * a non-object payload, and a file written by a newer version.\n *\n * `migrate` is called only for a stored version strictly below `currentVersion`.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport interface ReadVersionedJsonOptions<T> {\n path: string;\n currentVersion: number;\n migrate: VersionedJsonMigrate<T>;\n defaultValue: () => T;\n}\n\n/**\n * Read a versioned JSON file, migrating or falling back rather than failing.\n *\n * What comes back, by case:\n *\n * - version matches `currentVersion` — the file's `data` field, returned as `T` WITHOUT\n * validation. A file whose envelope is right but whose payload is not gets through.\n * - version below `currentVersion` — whatever `migrate(parsed, stored)` returns. The migrated\n * value is not persisted; the file is untouched.\n * - version above `currentVersion` — `defaultValue()`, plus a warning. Forward-only: a file\n * written by a newer build is left alone rather than downgraded.\n * - file missing, or unreadable for any other reason (permissions, a directory in its place) —\n * `defaultValue()`, silently. Absence and denial are not distinguished.\n * - file present but not valid JSON — `defaultValue()`, and the file is RENAMED to\n * `<path>.corrupt.<epoch-ms>` so the next run starts clean and the bad bytes stay available.\n * A rename that itself fails is warned about and the read still returns the default, which\n * means the same corrupt file will be met again next time.\n * - JSON that parses to a non-object (a number, a string, `null`) — `defaultValue()`, silently\n * and without moving the file aside.\n *\n * The only ways this throws are through the callbacks you supply: `migrate` or `defaultValue`\n * raising. Nothing else here rejects.\n *\n * There is no locking. A concurrent `writeVersionedJson` on the same path is atomic at the rename,\n * so a reader sees either the old file or the new one, never a half-written one — but a\n * read-modify-write built from this pair is NOT atomic across the two calls, and needs\n * `withFileLock` around both.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport async function readVersionedJson<T>(opts: ReadVersionedJsonOptions<T>): Promise<T> {\n const { path, currentVersion, migrate, defaultValue } = opts;\n\n let raw: string;\n try {\n raw = await readFile(path, \"utf-8\");\n } catch {\n return defaultValue();\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n // T5.10 — move corrupt file aside so (a) user can investigate and\n // (b) next run starts fresh instead of hitting the same warning loop.\n const asidePath = `${path}.corrupt.${Date.now()}`;\n try {\n await rename(path, asidePath);\n diag(`[theokit-sdk] ${path} is corrupt; moved to ${asidePath}. Using default value.\\n`);\n } catch {\n diag(`[theokit-sdk] ${path} is corrupt; using default value.\\n`);\n }\n return defaultValue();\n }\n\n if (typeof parsed !== \"object\" || parsed === null) {\n return defaultValue();\n }\n\n const file = parsed as { _schemaVersion?: unknown; data?: unknown };\n const storedRaw = file._schemaVersion;\n const stored = typeof storedRaw === \"number\" ? storedRaw : 0;\n\n if (stored === currentVersion) {\n return file.data as T;\n }\n\n if (stored > currentVersion) {\n diag(\n `[theokit-sdk] ${path} schema version ${stored} > current ${currentVersion}; ` +\n \"using default value (forward-only).\\n\",\n );\n return defaultValue();\n }\n\n // EC-2 fix: pass the FULL parsed object to migrate, not just `file.data`.\n // Legacy shapes (e.g., `{ schemaVersion: \"1.0\", agents: {...} }` without\n // a `data` field) need to inspect the whole thing.\n return migrate(parsed, stored);\n}\n\n/**\n * Write `data` wrapped in the version envelope, replacing the file atomically.\n *\n * The bytes go to a temp file in the same directory and are moved into place with `rename`, so a\n * crash or a concurrent reader never observes a partial file: the path holds either the previous\n * content or the complete new content. Parent directories are created as needed, and the file is\n * created with mode 0600 (subject to the process umask), formatted with two-space indentation and\n * a trailing newline.\n *\n * `currentVersion` is stamped verbatim as `_schemaVersion`; nothing checks it against what was\n * already on disk, so this will happily overwrite a newer file with an older version. Read first\n * if that matters.\n *\n * Rejects when `data` cannot be serialized — a circular reference, or a `toJSON` that throws.\n * Serialization happens before any filesystem call, so on that path no directory is created, no\n * temp file exists, and the file already on disk is untouched.\n *\n * Atomicity is the rename's, so it is as strong as the filesystem's: on NFS, SMB or FUSE the\n * underlying writer emits a one-shot warning that the guarantee is weaker there.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport async function writeVersionedJson<T>(\n path: string,\n data: T,\n currentVersion: number,\n): Promise<void> {\n const file: VersionedJsonFile<T> = {\n _schemaVersion: currentVersion,\n data,\n };\n await atomicWriteJson(path, file);\n}\n"]}
@@ -0,0 +1,35 @@
1
+ import { homedir } from 'os';
2
+ import { join } from 'path';
3
+
4
+ // src/internal/persistence/paths.ts
5
+ var THEOKIT_DIR_LITERAL = ".theokit";
6
+ var CLAUDE_DIR_NAME = ".claude";
7
+ function getTheokitHome(cwd) {
8
+ const override = process.env.THEOKIT_HOME?.trim();
9
+ if (override !== void 0 && override.length > 0) {
10
+ return override;
11
+ }
12
+ return join(cwd, THEOKIT_DIR_LITERAL);
13
+ }
14
+ function projectConfigRoots(cwd) {
15
+ return [join(cwd, THEOKIT_DIR_LITERAL), join(cwd, CLAUDE_DIR_NAME)];
16
+ }
17
+ function pluginBundleRoots(cwd) {
18
+ return projectConfigRoots(cwd).map((root) => join(root, "plugins"));
19
+ }
20
+ function getProfilesRoot() {
21
+ return join(homedir(), THEOKIT_DIR_LITERAL, "profiles");
22
+ }
23
+ function displayTheokitHome(cwd) {
24
+ const resolved = getTheokitHome(cwd);
25
+ const home = homedir();
26
+ if (resolved === home) return "~";
27
+ if (resolved.startsWith(`${home}/`)) {
28
+ return `~${resolved.slice(home.length)}`;
29
+ }
30
+ return resolved;
31
+ }
32
+
33
+ export { displayTheokitHome, getProfilesRoot, getTheokitHome, pluginBundleRoots, projectConfigRoots };
34
+ //# sourceMappingURL=chunk-UFLD2HEV.js.map
35
+ //# sourceMappingURL=chunk-UFLD2HEV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/persistence/paths.ts"],"names":[],"mappings":";;;;AAuCA,IAAM,mBAAA,GAAsB,UAAA;AAG5B,IAAM,eAAA,GAAkB,SAAA;AAuBjB,SAAS,eAAe,GAAA,EAAqB;AAClD,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,YAAA,EAAc,IAAA,EAAK;AAChD,EAAA,IAAI,QAAA,KAAa,MAAA,IAAa,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG;AACjD,IAAA,OAAO,QAAA;AAAA,EACT;AACA,EAAA,OAAO,IAAA,CAAK,KAAK,mBAAmB,CAAA;AACtC;AA6BO,SAAS,mBAAmB,GAAA,EAAuB;AACxD,EAAA,OAAO,CAAC,KAAK,GAAA,EAAK,mBAAmB,GAAG,IAAA,CAAK,GAAA,EAAK,eAAe,CAAC,CAAA;AACpE;AAeO,SAAS,kBAAkB,GAAA,EAAuB;AACvD,EAAA,OAAO,kBAAA,CAAmB,GAAG,CAAA,CAAE,GAAA,CAAI,CAAC,IAAA,KAAS,IAAA,CAAK,IAAA,EAAM,SAAS,CAAC,CAAA;AACpE;AAeO,SAAS,eAAA,GAA0B;AACxC,EAAA,OAAO,IAAA,CAAK,OAAA,EAAQ,EAAG,mBAAA,EAAqB,UAAU,CAAA;AACxD;AAmBO,SAAS,mBAAmB,GAAA,EAAqB;AACtD,EAAA,MAAM,QAAA,GAAW,eAAe,GAAG,CAAA;AACnC,EAAA,MAAM,OAAO,OAAA,EAAQ;AACrB,EAAA,IAAI,QAAA,KAAa,MAAM,OAAO,GAAA;AAC9B,EAAA,IAAI,QAAA,CAAS,UAAA,CAAW,CAAA,EAAG,IAAI,GAAG,CAAA,EAAG;AACnC,IAAA,OAAO,CAAA,CAAA,EAAI,QAAA,CAAS,KAAA,CAAM,IAAA,CAAK,MAAM,CAAC,CAAA,CAAA;AAAA,EACxC;AACA,EAAA,OAAO,QAAA;AACT","file":"chunk-UFLD2HEV.js","sourcesContent":["/**\n * Path resolution for SDK state files (ADR D60).\n *\n * Theokit anchors state at `<cwd>/.theokit/` by default (per-cwd). An\n * optional `THEOKIT_HOME` environment variable overrides this, enabling\n * test isolation, profile switching, and multi-tenant deployments.\n *\n * Rules:\n * - `getTheokitHome(cwd)` is the canonical resolver **for cwd-anchored state**. Never hardcode\n * `path.join(cwd, \".theokit\")` in callers — use this function so tests\n * and overrides stay consistent.\n *\n * M94 — this comment said \"the ONLY canonical resolver\", and stopped being true: the\n * transcript gained `transcriptRoot()`, which is **home-anchored** (`~/.theokit`) with the same\n * `THEOKIT_HOME` override. The two defaults differ on purpose — unifying would move the\n * transcript of everyone who does NOT set the variable, which is a data migration and not a\n * re-export.\n *\n * A consequence worth writing down: **without `THEOKIT_HOME` the state stays split in two**\n * — registry in `<cwd>/.theokit`, transcript in `~/.theokit`. M94 unifies only for those who set\n * the variable. Unifying both defaults is another milestone's work.\n * - `getProfilesRoot()` is intentionally home-anchored (not affected by\n * `THEOKIT_HOME`) so `theokit profile list` discovers all profiles\n * regardless of which is active.\n * - `displayTheokitHome(cwd)` returns a human-readable path for logs.\n *\n * @internal\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * The project config directory literal.\n *\n * Renamed from `THEOKIT_DIR_NAME` in #410. Sharing a name with the (now removed) sovereign env var\n * was the MECHANISM of that defect, not scenery: every grep for the variable landed on this const\n * and looked answered, so \"is it read?\" returned five hits and nobody checked what they were.\n */\nconst THEOKIT_DIR_LITERAL = \".theokit\";\n\n/** The Claude Code CLI's project configuration directory. */\nconst CLAUDE_DIR_NAME = \".claude\";\n\n/**\n * Resolve the directory cwd-anchored SDK state lives in.\n *\n * `THEOKIT_HOME` wins when it is set and not blank after trimming; the trimmed value is used, and\n * it is used VERBATIM — it is not resolved against `cwd`, so a relative value stays relative and\n * `.theokit` is not appended to it. Otherwise the answer is `<cwd>/.theokit`.\n *\n * The environment is read on every call, so a change to the variable takes effect immediately\n * rather than being frozen at import.\n *\n * This creates nothing and checks nothing: the returned path may not exist, and the caller owns\n * the `mkdir`. Call it instead of writing `join(cwd, \".theokit\")` by hand, or the override stops\n * working for that one call site and tests silently touch the real home.\n *\n * Not the whole story about where state lives — the transcript is home-anchored via\n * `transcriptRoot()`, honoring the same variable but defaulting to `~/.theokit`. With\n * `THEOKIT_HOME` unset, state is genuinely split between two roots.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function getTheokitHome(cwd: string): string {\n const override = process.env.THEOKIT_HOME?.trim();\n if (override !== undefined && override.length > 0) {\n return override;\n }\n return join(cwd, THEOKIT_DIR_LITERAL);\n}\n\n/**\n * Every directory a project's configuration may be read from, in precedence order.\n *\n * `.theokit` first, then `.claude`. The order is the whole contract: a project that declares a\n * skill, agent or rule in both means the explicit namespace to win, and a caller merging these\n * roots must therefore keep the FIRST occurrence of a name rather than the last.\n *\n * `.claude` is read because the formats already agree and only the location did not. Measured\n * 2026-08-26: the SKILL.md frontmatter this SDK requires (`name` + `description`) is exactly what\n * the CLI writes, its hook config is the same JSON shape, and 59 of the CLI's agent declarations\n * parse here unchanged. A repository set up for the CLI was failing on the directory name alone.\n *\n * NOT a rename of `.theokit`, and not a migration. Both are read, so nothing that works today stops\n * working — which is why this returns a LIST and not a single resolved answer.\n *\n * Deliberately NOT affected by `THEOKIT_HOME`, and this is the one thing to remember about it.\n * That variable relocates cwd-anchored SDK *state* — sessions, the credential store. A project's\n * *configuration* is a property of the repository, not of where this SDK keeps its state, and the\n * loaders that read these directories have always anchored on `cwd` directly. Honouring the\n * override here would silently move where a project's agents and skills come from, which is a\n * behaviour change wearing the costume of a refactor.\n *\n * Creates nothing and checks nothing; either path may not exist, and the caller owns that.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function projectConfigRoots(cwd: string): string[] {\n return [join(cwd, THEOKIT_DIR_LITERAL), join(cwd, CLAUDE_DIR_NAME)];\n}\n\n/**\n * Every directory that may hold a plugin BUNDLE contributed by the Claude Code CLI.\n *\n * A CLI plugin is not a JS entry point — it is a folder whose `skills/` and `agents/` are what it\n * exists to provide. Measured 2026-08-26 on an installed one: seven agents and three skills beside\n * a manifest in `.claude-plugin/plugin.json`. Parsing that manifest and stopping there produced a\n * plugin that loaded and did nothing.\n *\n * Project-scoped deliberately. The CLI also keeps plugins under `~/.claude/plugins/cache`, behind\n * its own installer and enable/disable state — reproducing that is an installation system, not\n * reading a project's configuration, and guessing at someone's enablement would run code they\n * turned off.\n */\nexport function pluginBundleRoots(cwd: string): string[] {\n return projectConfigRoots(cwd).map((root) => join(root, \"plugins\"));\n}\n\n/**\n * The directory holding every profile: always `~/.theokit/profiles`, from `os.homedir()`.\n *\n * Deliberately NOT affected by `THEOKIT_HOME`, which is the one thing to remember about it. If it\n * followed the override, a session pointed at one profile would only be able to see that profile,\n * and `theokit profile list` could never enumerate the rest. Profiles are the thing the override\n * switches between, so their index cannot live behind it.\n *\n * Takes no `cwd` for the same reason. Creates nothing; the path may not exist.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function getProfilesRoot(): string {\n return join(homedir(), THEOKIT_DIR_LITERAL, \"profiles\");\n}\n\n/**\n * The same path `getTheokitHome(cwd)` returns, shortened for display: the home directory prefix\n * collapses to `~`, so `/home/ada/.theokit` prints as `~/.theokit`.\n *\n * For humans only — log lines, CLI output, error messages. The result is NOT a usable path: `~`\n * is a shell convention that `fs` does not expand, so passing this to a filesystem call resolves\n * a literal directory named `~` relative to the process cwd. Use `getTheokitHome` for anything\n * that touches disk.\n *\n * Collapsing is a prefix match on the home directory followed by a literal `/`, so a sibling like\n * `/home/adalovelace` is left alone even though `/home/ada` is a string prefix of it. A path\n * outside the home directory comes back unchanged — and so does a Windows path, where the\n * separator is a backslash and the prefix test therefore never matches.\n *\n * Semver-exempt: reachable via the `@theokit/sdk/internal/persistence` sub-path, which the package\n * declares in `exports` but does NOT cover with its semver contract.\n */\nexport function displayTheokitHome(cwd: string): string {\n const resolved = getTheokitHome(cwd);\n const home = homedir();\n if (resolved === home) return \"~\";\n if (resolved.startsWith(`${home}/`)) {\n return `~${resolved.slice(home.length)}`;\n }\n return resolved;\n}\n"]}
@@ -44,6 +44,27 @@ var DEFAULT_DISCOVERY_SPECS = [
44
44
  followImports: false,
45
45
  priority: 45
46
46
  },
47
+ {
48
+ // Rules written for the Claude Code CLI. Measured 2026-08-26 over this repository's 32 rule
49
+ // files: none carries frontmatter, and `rules-frontmatter` already reads a file without it as
50
+ // `alwaysApply: true` — the format needed nothing, only a spec pointing at the directory.
51
+ //
52
+ // 47, not 46. Specs sort ascending and a context budget drops the tail first, so it must land
53
+ // AFTER `.theokit/rules` (45) — the explicit namespace should survive a squeeze the borrowed one
54
+ // does not. It must also leave a slot on BOTH sides: B-127 makes these numbers a public contract
55
+ // precisely so a consumer can place its own source between two defaults, and 46 would have left
56
+ // no room between 45 and itself. 47 keeps 46 free below and 48–49 free above.
57
+ //
58
+ // The reckoning B-127's docblock asks for: no published priority MOVES, so a consumer that chose
59
+ // 46, 48 or 49 is unaffected. A consumer that had chosen 47 now collides — that is the cost of
60
+ // an eighth default, paid once and recorded here rather than discovered later.
61
+ id: "claude-rules",
62
+ pattern: ".claude/rules/*.md",
63
+ scope: "globbed",
64
+ parser: "rules-frontmatter",
65
+ followImports: false,
66
+ priority: 47
67
+ },
47
68
  {
48
69
  id: "theokit-context",
49
70
  pattern: ".theokit/context/*.md",
@@ -124,5 +145,5 @@ async function walkUpForGlob(cwd, pattern) {
124
145
  }
125
146
 
126
147
  export { DEFAULT_DISCOVERY_SPECS, findGitRoot, isSafePattern, walkUpForFile, walkUpForGlob };
127
- //# sourceMappingURL=chunk-GTKFV7O5.js.map
128
- //# sourceMappingURL=chunk-GTKFV7O5.js.map
148
+ //# sourceMappingURL=chunk-WFC26L6Y.js.map
149
+ //# sourceMappingURL=chunk-WFC26L6Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/runtime/context/context-discovery.ts"],"names":[],"mappings":"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","file":"chunk-WFC26L6Y.js","sourcesContent":["/**\n * Context file discovery (T1.1, ADRs D150 / D151).\n *\n * Discovers context files via three scopes:\n * - `cwd-only` — single dir, single path lookup\n * - `git-root-walk` — walk cwd → git-root, collect every directory's match\n * (nearest-first ordering)\n * - `globbed` — glob pattern relative to cwd (e.g. `.cursor/rules/*.mdc`)\n *\n * Pure `existsSync` checks — **no `.gitignore` parsing** (EC-A, KISS) and\n * **no invented `.theokitignore`** (EC-B). Paths normalized via\n * `realpath` to dedup symlink chains pointing to the same physical file\n * (EC-F). Git worktrees work transparently because `.git` exists as a\n * file in that case (EC-N).\n *\n * @internal\n */\n\nimport { existsSync, realpathSync } from \"node:fs\";\nimport { glob } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\n\n/** Single filename (\"AGENTS.md\") or relative glob (\".cursor/rules/*.mdc\"). */\nexport type DiscoveryScope = \"cwd-only\" | \"git-root-walk\" | \"globbed\";\n\n/** Parser to apply once file is read. */\nexport type DiscoveryParser = \"plain-markdown\" | \"mdc\" | \"frontmatter-zod\" | \"rules-frontmatter\";\n\n/**\n * One kind of context file the runner knows how to find and read. The shipped registry is\n * `DEFAULT_DISCOVERY_SPECS`; a caller supplies its own array to change the set.\n *\n * `scope` decides how `pattern` is used and how many files a single spec can yield:\n * `cwd-only` looks for one path and stops, `git-root-walk` collects a match in every directory\n * from `cwd` up to the git root (nearest first), and `globbed` expands `pattern` as a glob\n * relative to `cwd`. So `pattern` is a filename for the first two and a glob for the third —\n * putting a glob on a walk scope silently finds nothing.\n *\n * `priority` orders the merged prompt, ascending, and is a plain number rather than an index, so\n * a new spec can be slotted between two existing ones. Lower means earlier and therefore more\n * general; later content wins on conflict.\n *\n * `parser` must match the file format — `plain-markdown` reads the whole file, `mdc` and\n * `rules-frontmatter` parse frontmatter and can DECLINE the file when its activation conditions\n * do not hold, and `frontmatter-zod` is the legacy path the runner currently skips entirely.\n *\n * `followImports` is honored only by `plain-markdown`, and turns `@path` directives in the body\n * into inlined content bounded by the import root. Setting it on a frontmatter parser does\n * nothing.\n *\n * `id` names the source in `<source name=\"\">` and in telemetry. When one spec matches files in\n * several directories, the runner suffixes it with the path relative to the git root to keep them\n * apart.\n *\n * @public — re-exported from `@theokit/sdk/context`, and therefore under semver.\n */\nexport interface DiscoverySpec {\n /** Stable identifier — used as `<source name=\"\">` and telemetry key. */\n readonly id: string;\n /** Priority for merge (lower = earlier in prompt). */\n readonly priority: number;\n /** Filename (cwd-only/git-root-walk) or glob (globbed). */\n readonly pattern: string;\n readonly scope: DiscoveryScope;\n readonly parser: DiscoveryParser;\n /** Whether to follow `@path` import directives (CLAUDE.md / GEMINI.md). */\n readonly followImports: boolean;\n}\n\n/**\n * The context files theokit looks for out of the box, in the order they are concatenated.\n *\n * Two things follow from the ordering. `AGENTS.md` comes first at priority 10 and `THEO.md` last,\n * so theokit-specific instruction wins over the vendor-neutral file on conflict. And the array is\n * consumed in the order written — the runner does not re-sort it — so a caller passing its own\n * array is responsible for keeping `priority` and array position consistent.\n *\n * `CLAUDE.md` and `GEMINI.md` are the only two entries with `followImports: true`, which means\n * they are the only files whose `@path` directives pull other files into the prompt. Those\n * imports cannot escape the import root.\n *\n * Frozen only by type: `ReadonlyArray` is a compile-time constraint, and the array and its\n * elements are not deep-frozen at runtime. Build a new array rather than mutating this one.\n *\n * @public — re-exported from `@theokit/sdk/context`, and therefore under semver.\n */\nexport const DEFAULT_DISCOVERY_SPECS: ReadonlyArray<DiscoverySpec> = [\n {\n id: \"AGENTS.md\",\n pattern: \"AGENTS.md\",\n scope: \"git-root-walk\",\n parser: \"plain-markdown\",\n followImports: false,\n priority: 10,\n },\n {\n id: \"GEMINI.md\",\n pattern: \"GEMINI.md\",\n scope: \"git-root-walk\",\n parser: \"plain-markdown\",\n followImports: true,\n priority: 20,\n },\n {\n id: \"CLAUDE.md\",\n pattern: \"CLAUDE.md\",\n scope: \"git-root-walk\",\n parser: \"plain-markdown\",\n followImports: true,\n priority: 30,\n },\n {\n id: \"cursor-rules\",\n pattern: \".cursor/rules/*.mdc\",\n scope: \"globbed\",\n parser: \"mdc\",\n followImports: false,\n priority: 40,\n },\n {\n id: \"theokit-rules\",\n pattern: \".theokit/rules/*.md\",\n scope: \"globbed\",\n parser: \"rules-frontmatter\",\n followImports: false,\n priority: 45,\n },\n {\n // Rules written for the Claude Code CLI. Measured 2026-08-26 over this repository's 32 rule\n // files: none carries frontmatter, and `rules-frontmatter` already reads a file without it as\n // `alwaysApply: true` — the format needed nothing, only a spec pointing at the directory.\n //\n // 47, not 46. Specs sort ascending and a context budget drops the tail first, so it must land\n // AFTER `.theokit/rules` (45) — the explicit namespace should survive a squeeze the borrowed one\n // does not. It must also leave a slot on BOTH sides: B-127 makes these numbers a public contract\n // precisely so a consumer can place its own source between two defaults, and 46 would have left\n // no room between 45 and itself. 47 keeps 46 free below and 48–49 free above.\n //\n // The reckoning B-127's docblock asks for: no published priority MOVES, so a consumer that chose\n // 46, 48 or 49 is unaffected. A consumer that had chosen 47 now collides — that is the cost of\n // an eighth default, paid once and recorded here rather than discovered later.\n id: \"claude-rules\",\n pattern: \".claude/rules/*.md\",\n scope: \"globbed\",\n parser: \"rules-frontmatter\",\n followImports: false,\n priority: 47,\n },\n {\n id: \"theokit-context\",\n pattern: \".theokit/context/*.md\",\n scope: \"globbed\",\n parser: \"frontmatter-zod\",\n followImports: false,\n priority: 50,\n },\n {\n id: \"THEO.md\",\n pattern: \".theokit/THEO.md\",\n scope: \"cwd-only\",\n parser: \"plain-markdown\",\n followImports: false,\n priority: 60,\n },\n];\n\nconst SAFE_FILENAME = /^[a-zA-Z0-9_.\\-/*]+$/;\nconst TRAVERSAL_RE = /(^|\\/)\\.\\.(\\/|$)/;\n\n/**\n * Reject patterns that contain path traversal (`..`) or non-allowed\n * characters (D81 parity, EC-4).\n *\n * @internal\n */\nexport function isSafePattern(pattern: string): boolean {\n if (typeof pattern !== \"string\" || pattern.length === 0) return false;\n if (TRAVERSAL_RE.test(pattern)) return false;\n if (isAbsolute(pattern)) return false;\n return SAFE_FILENAME.test(pattern);\n}\n\n/**\n * Walk upward from `cwd` looking for the closest directory containing\n * a `.git` entry (file OR directory — worktrees use a `.git` FILE,\n * EC-N). Returns the absolute path of that directory, or `undefined`\n * when no git root exists at or above `cwd`.\n *\n * @internal\n */\nexport function findGitRoot(cwd: string): string | undefined {\n if (typeof cwd !== \"string\" || cwd.length === 0) return undefined;\n let current = resolve(cwd);\n // Guard against infinite loops on weird filesystems.\n for (let i = 0; i < 64; i += 1) {\n if (existsSync(join(current, \".git\"))) return current;\n const parent = dirname(current);\n if (parent === current) return undefined;\n current = parent;\n }\n return undefined;\n}\n\n/**\n * Walk `cwd` upward to `stopDir` (inclusive) collecting every existing\n * occurrence of `filename`. Returns absolute, realpath-deduped paths in\n * nearest-first order (innermost dir first).\n *\n * No `.gitignore` parsing (EC-A). Realpath collapses symlink chains\n * pointing to the same physical file (EC-F). Filesystem races (file\n * deleted mid-walk) are skipped silently (EC-5).\n *\n * @internal\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: walk-up loop combines validation + realpath dedup + FS-race handling + stopDir guard in a single bounded loop; splitting fragments the dedup invariant.\nexport function walkUpForFile(\n cwd: string,\n filename: string,\n stopDir: string | undefined,\n): string[] {\n if (!isSafePattern(filename)) {\n return [];\n }\n const start = resolve(cwd);\n const stop = stopDir !== undefined ? resolve(stopDir) : undefined;\n const found: string[] = [];\n const seenReal = new Set<string>();\n let current = start;\n // 64-level depth cap.\n for (let i = 0; i < 64; i += 1) {\n const candidate = join(current, filename);\n if (existsSync(candidate)) {\n let real: string;\n try {\n real = realpathSync(candidate);\n } catch {\n // FS race (deleted mid-walk) — skip.\n real = candidate;\n }\n if (!seenReal.has(real)) {\n seenReal.add(real);\n found.push(real);\n }\n }\n if (stop !== undefined && current === stop) break;\n const parent = dirname(current);\n if (parent === current) break;\n current = parent;\n }\n return found;\n}\n\n/**\n * Glob-style discovery under `cwd` (e.g. `.cursor/rules/*.mdc`, `.theokit/rules/**\\/*.md`).\n * Returns absolute, lex-sorted paths.\n *\n * `*` matches within one path segment and `**` spans any depth, including zero — so\n * `.theokit/rules/**\\/*.md` finds `rules/top.md` as well as `rules/deep/nested/inner.md`, while\n * `.theokit/rules/*.md` keeps its flat meaning and finds only the first. That distinction is the\n * compatibility contract: every existing spec uses a single `*`, and widening it would silently\n * start absorbing nested files nobody chose to expose.\n *\n * ## Why this used to be flat, and what changed (B-119)\n *\n * The previous implementation split the pattern at its LAST `/`, treated the prefix as a literal\n * directory and did one `readdir` — documented as \"nested directories deferred to v2\" (EC-R). The\n * deferral was deliberate; what made it a defect was measured from a consumer. TheoCode's own rule\n * loader descends recursively, so migrating it onto the `theokit-rules` spec would have silently\n * dropped every nested rule — on the path that decides whether a repository's hooks execute. And a\n * pattern written to say so, `.theokit/rules/**\\/*.md`, resolved its directory part to a literal\n * `**` and matched NOTHING, not even the top-level file it matched before the globstar was added.\n *\n * ## Why the stdlib rather than a walker\n *\n * `fs.promises.glob` (Node ≥ 22, and this package requires ≥ 22.12) implements exactly these\n * semantics, verified against a fixture before adoption: `**\\/*.md` returns all three depths,\n * `*.md` returns one, and it emits no experimental warning. Writing a recursive walker here would\n * have been a third implementation of matching inside one package — the same duplication that let\n * the enumerator and the compiler in `context-glob.ts` disagree in the first place. `globToRegex`\n * stays where it belongs: deciding whether a rule APPLIES to a set of paths, which is a different\n * question from which files exist.\n *\n * `isSafePattern` still runs first and is unchanged, so `..` is refused before any I/O.\n *\n * @internal\n */\nexport async function walkUpForGlob(cwd: string, pattern: string): Promise<string[]> {\n if (!isSafePattern(pattern)) return [];\n const found: string[] = [];\n try {\n for await (const entry of glob(pattern, { cwd })) {\n found.push(resolve(cwd, entry));\n }\n } catch {\n // A pattern whose directory does not exist is the ordinary case — most projects have no\n // `.cursor/rules/`. Same outcome as matching nothing.\n return [];\n }\n // Sorted, because discovery order becomes prompt order and must not vary with the filesystem.\n //\n // The comparator is explicit and deliberately NOT `localeCompare`, which is the usual suggestion\n // for a bare `.sort()`. `localeCompare` orders by the machine's locale, so the same tree would\n // assemble a different prompt on a differently-configured machine — trading one source of\n // non-determinism for a subtler one. Code-unit ordering is what a bare `.sort()` already does for\n // strings; writing it out states the intent and keeps the result machine-independent.\n return found.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { LruEmbeddingCache } from './chunk-H73MEMQB.js';
2
2
  import { splitFrontmatter, parseSimpleYaml } from './chunk-KVNWIAO4.js';
3
3
  import { sanitizeIdentifier, safePathJoin, safeFilenameForId } from './chunk-QARJGQSA.js';
4
+ import { encodeProjectDir } from './chunk-FKMUFNQE.js';
4
5
  import { openSqliteResilient, sanitizeFts5Query } from './chunk-CQ2TQ32Y.js';
5
6
  import { withCwdMutex } from './chunk-Q5EWJPRY.js';
6
7
  import { replaceFileAtomic } from './chunk-VF7EWVDG.js';
@@ -11,6 +12,7 @@ import { readdir, readFile, mkdir, stat } from 'fs/promises';
11
12
  import { join, resolve, relative } from 'path';
12
13
  import { mkdirSync } from 'fs';
13
14
  import { createRequire } from 'module';
15
+ import { homedir } from 'os';
14
16
 
15
17
  // src/internal/memory/escape-like-pattern.ts
16
18
  function escapeLikePattern(query) {
@@ -363,6 +365,11 @@ var FACTS_HEADING = "## Facts";
363
365
  function memoryDir(cwd) {
364
366
  return join(cwd, ".theokit", "memory");
365
367
  }
368
+ function claudeProjectMemoryDir(cwd) {
369
+ const home = process.env.CLAUDE_CONFIG_DIR?.trim();
370
+ const root = home !== void 0 && home.length > 0 ? home : join(homedir(), ".claude");
371
+ return join(root, "projects", encodeProjectDir(cwd), "memory");
372
+ }
366
373
  function memoryMdPath(cwd) {
367
374
  return join(memoryDir(cwd), "MEMORY.md");
368
375
  }
@@ -370,16 +377,18 @@ function notesDir(cwd) {
370
377
  return join(memoryDir(cwd), "notes");
371
378
  }
372
379
  async function readFactsFromMarkdown(cwd) {
373
- let entries;
374
- try {
375
- entries = await readdir(memoryDir(cwd));
376
- } catch {
377
- return [];
378
- }
379
380
  const facts = [];
380
- for (const entry of entries.sort()) {
381
- const fact = await readMemoryFileAt(cwd, entry);
382
- if (fact !== void 0) facts.push(fact);
381
+ for (const dir of [memoryDir(cwd), claudeProjectMemoryDir(cwd)]) {
382
+ let entries;
383
+ try {
384
+ entries = await readdir(dir);
385
+ } catch {
386
+ continue;
387
+ }
388
+ for (const entry of entries.sort()) {
389
+ const fact = await readMemoryFileIn(dir, entry);
390
+ if (fact !== void 0) facts.push(fact);
391
+ }
383
392
  }
384
393
  try {
385
394
  facts.push(...parseFactsSection(await readFile(memoryMdPath(cwd), "utf8")));
@@ -387,18 +396,19 @@ async function readFactsFromMarkdown(cwd) {
387
396
  }
388
397
  return facts;
389
398
  }
390
- async function readMemoryFileAt(cwd, entry) {
399
+ async function readMemoryFileIn(dir, entry) {
391
400
  if (!entry.endsWith(".md") || entry === "MEMORY.md") return void 0;
392
401
  let raw;
393
402
  try {
394
- raw = await readFile(join(memoryDir(cwd), entry), "utf8");
403
+ raw = await readFile(join(dir, entry), "utf8");
395
404
  } catch {
396
405
  return void 0;
397
406
  }
398
407
  const parsed = parseMemoryFile(raw);
399
408
  if (parsed === void 0) return void 0;
409
+ const body = parsed.body.trim();
400
410
  return {
401
- text: parsed.description,
411
+ text: body.length > 0 ? body : parsed.description,
402
412
  ...parsed.kind !== void 0 ? { kind: parsed.kind } : {},
403
413
  ...parsed.modified !== void 0 ? { modified: parsed.modified } : {}
404
414
  };
@@ -1045,5 +1055,5 @@ var IndexManager = class _IndexManager {
1045
1055
  };
1046
1056
 
1047
1057
  export { IndexManager, LanceIndex, MEMORY_KINDS, appendFact, appendFactToMarkdown, defaultIndexPath, lanceStoragePath, legacyMemoryJsonPath, memoryDir, memoryMdPath, openMemoryDb, readFacts, readFactsFromMarkdown, writeSessionSummary };
1048
- //# sourceMappingURL=chunk-G6EEYRDD.js.map
1049
- //# sourceMappingURL=chunk-G6EEYRDD.js.map
1058
+ //# sourceMappingURL=chunk-WLZ7XRWH.js.map
1059
+ //# sourceMappingURL=chunk-WLZ7XRWH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/memory/escape-like-pattern.ts","../src/internal/memory/index-schema.ts","../src/internal/memory/index-db.ts","../src/internal/memory/lance-index.ts","../src/internal/memory/memory-index.ts","../src/internal/memory/lance-memory-adapter.ts","../src/internal/memory/index-manager-dispatch.ts","../src/internal/memory/types.ts","../src/internal/memory/storage/memory-file.ts","../src/internal/memory/storage/markdown-store.ts","../src/internal/memory/storage/session-summary-writer.ts","../src/internal/memory/storage/session-loader.ts","../src/internal/memory/storage/wiki-loader.ts","../src/internal/memory/index-manager-helpers.ts","../src/internal/memory/sqlite-vec-loader.ts","../src/internal/memory/storage/chunk-markdown.ts","../src/internal/memory/vec-index.ts","../src/internal/memory/index-manager.ts"],"names":["join","resolvePath","mkdir","readdir","relativeToRoot","createHash","readFile","stat"],"mappings":"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","file":"chunk-WLZ7XRWH.js","sourcesContent":["/**\n * Escapes a user query for a SQL `LIKE ... ESCAPE '\\'` clause.\n *\n * The order of the three replacements is the whole correctness argument, so it is its own\n * function rather than a chain inline at the call site.\n *\n * The previous version escaped `%` and `_` but not the escape character itself, which is the\n * defect CodeQL reports as `js/incomplete-sanitization` (alerts #14, #15). Two things went wrong,\n * and the second is the one that matters:\n *\n * \"back\\slash\" -> \"%back\\slash%\" the `\\s` is an escape of `s`, which LIKE does not define\n * \"x\\%y\" -> \"%x\\\\%y%\" the inserted `\\` is consumed escaping the user's `\\`,\n * leaving `%` UNESCAPED — a wildcard the caller did not\n * write, matching anything between `x\\` and `y`\n *\n * The second is wildcard injection: a search for a literal string silently becomes a scan. In a\n * memory index that is both a wrong answer and a way to pull back rows the query never asked for.\n *\n * Escaping the backslash FIRST is what fixes it — afterwards every `\\` in the string is one this\n * function put there, so the later replacements cannot collide with a user-supplied one.\n */\nexport function escapeLikePattern(query: string): string {\n return `%${query.replace(/\\\\/g, \"\\\\\\\\\").replace(/%/g, \"\\\\%\").replace(/_/g, \"\\\\_\")}%`;\n}\n","/**\n * SQLite schema for the memory index.\n *\n * Tables:\n * - files — one row per indexed markdown file (path, mtime, hash).\n * - chunks — one row per chunk produced by `chunkMarkdown`.\n * - chunks_fts — FTS5 virtual table mirroring chunks.text for BM25 search.\n * - meta — key/value store; persists the active embedding identity\n * (provider id, model, dimension) so we detect when the\n * index was built against a different embedding setup.\n * - embeddings — sqlite-vec virtual table holding the chunk vectors\n * (created on demand by `vec-index.ts`).\n *\n * @internal\n */\n\nexport const SCHEMA_STATEMENTS: ReadonlyArray<string> = [\n `CREATE TABLE IF NOT EXISTS files (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n path TEXT UNIQUE NOT NULL,\n rel_path TEXT NOT NULL,\n mtime INTEGER NOT NULL,\n hash TEXT NOT NULL,\n source TEXT NOT NULL DEFAULT 'memory'\n )`,\n `CREATE TABLE IF NOT EXISTS chunks (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,\n start_line INTEGER NOT NULL,\n end_line INTEGER NOT NULL,\n text TEXT NOT NULL,\n hash TEXT NOT NULL,\n session_key TEXT\n )`,\n `CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(\n text,\n content='chunks',\n content_rowid='id'\n )`,\n `CREATE TRIGGER IF NOT EXISTS chunks_fts_insert AFTER INSERT ON chunks BEGIN\n INSERT INTO chunks_fts(rowid, text) VALUES (new.id, new.text);\n END`,\n `CREATE TRIGGER IF NOT EXISTS chunks_fts_delete AFTER DELETE ON chunks BEGIN\n INSERT INTO chunks_fts(chunks_fts, rowid, text) VALUES ('delete', old.id, old.text);\n END`,\n `CREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT\n )`,\n `CREATE INDEX IF NOT EXISTS idx_chunks_file_id ON chunks(file_id)`,\n];\n\n/**\n * Non-WAL pragmas. WAL is applied separately via\n * `applyWalWithFallback` (T4.2, ADR D63) so NFS/SMB users get a graceful\n * DELETE fallback instead of crashing.\n */\nexport const PRAGMA_STATEMENTS: ReadonlyArray<string> = [\n \"PRAGMA synchronous=NORMAL\",\n \"PRAGMA foreign_keys=ON\",\n];\n","import { join } from \"node:path\";\n\nimport { openSqliteResilient } from \"../persistence/sqlite-open.js\";\nimport { PRAGMA_STATEMENTS, SCHEMA_STATEMENTS } from \"./index-schema.js\";\n\n/**\n * Memory index DB handle + opener.\n *\n * M0-5 (plan m0-foundation-expose-primitives): the driver-load + WAL +\n * corruption-recovery (EC-7) plumbing is now the shared `openSqliteResilient`\n * primitive in `internal/persistence`; this module only declares the memory\n * schema application (PRAGMA + SCHEMA) via its `onOpen` callback.\n *\n * @internal\n */\n\nexport interface MemoryDb {\n exec(sql: string): void;\n prepare(sql: string): {\n run(...args: unknown[]): { changes: number; lastInsertRowid: number | bigint };\n get(...args: unknown[]): Record<string, unknown> | undefined;\n all(...args: unknown[]): Array<Record<string, unknown>>;\n };\n /** SQLite `pragma()` access (used by `applyWalWithFallback`). */\n pragma(statement: string, options?: { simple?: boolean }): unknown;\n close(): void;\n /** Load a SQLite loadable extension at the given path (used by sqlite-vec). */\n loadExtension(path: string): void;\n}\n\nexport interface OpenDbOptions {\n filePath: string;\n /**\n * When true and opening fails with a corruption error, rename the file aside\n * and create a fresh DB. Default true.\n */\n recoverCorrupt?: boolean;\n}\n\nexport async function openMemoryDb(opts: OpenDbOptions): Promise<MemoryDb> {\n return openSqliteResilient<MemoryDb>({\n filePath: opts.filePath,\n label: \"memory-index\",\n recoverCorrupt: opts.recoverCorrupt,\n onOpen: (db) => {\n // PRAGMA + SCHEMA run after WAL is applied (handled by openSqliteResilient).\n for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);\n for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);\n },\n });\n}\n\nexport function defaultIndexPath(cwd: string): string {\n return join(cwd, \".theokit\", \"memory\", \".index\", \"memory.sqlite\");\n}\n","import { existsSync, mkdirSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\n\nimport { ConfigurationError } from \"../../errors.js\";\nimport type { EmbeddingRuntime } from \"./embedding-adapter.js\";\n\n/**\n * LanceDB-backed memory index (ADR D43). Implements the same logical\n * interface as `IndexManager` but stores embeddings in Lance's columnar\n * vector-aware file format — scalable to 100k+ facts.\n *\n * Opt-in via `Memory.create({ index: { backend: \"lance\" } })`. SQLite\n * remains the default; this code path only activates when explicitly\n * requested AND `@lancedb/lancedb` is installed.\n *\n * EC-1 MUST FIX: filters use Lance structured filter API (object form) —\n * NEVER string interpolation. SQL injection via namespace is impossible.\n *\n * EC-8: embedding dimension is validated when opening an existing table.\n *\n * @internal\n */\n\ninterface LanceModule {\n connect: (uri: string) => Promise<LanceConnection>;\n}\n\ninterface LanceConnection {\n openTable: (name: string) => Promise<LanceTable>;\n createTable: (name: string, data: ReadonlyArray<LanceFactRecord>) => Promise<LanceTable>;\n tableNames: () => Promise<string[]>;\n}\n\ninterface LanceTable {\n add: (rows: ReadonlyArray<LanceFactRecord>) => Promise<void>;\n search: (vector: ReadonlyArray<number>) => LanceQuery;\n delete: (predicate: string) => Promise<void>;\n countRows: () => Promise<number>;\n // EC-8: schema introspection for dimension check.\n schema: () => Promise<{\n fields: ReadonlyArray<{\n name: string;\n // Lance 0.30 returns Apache Arrow Field. FixedSizeList type has\n // `listSize`, not `fixedSize`. typeId=16 = FixedSizeList in Arrow.\n type: { listSize?: number; fixedSize?: number };\n }>;\n }>;\n}\n\ninterface LanceQuery {\n where: (filter: string | Record<string, unknown>) => LanceQuery;\n limit: (n: number) => LanceQuery;\n toArray: () => Promise<ReadonlyArray<LanceFactRecord & { _distance?: number }>>;\n}\n\nexport interface LanceFactRecord {\n id: string;\n text: string;\n source: \"memory\" | \"sessions\" | \"wiki\";\n embedding: ReadonlyArray<number>;\n namespace: string;\n scope: string;\n user_id: string;\n timestamp: number;\n}\n\nexport interface OpenLanceOptions {\n cwd: string;\n embedding: EmbeddingRuntime;\n /** Override storage location. Default: `<cwd>/.theokit/memory/lance/`. */\n storagePath?: string;\n}\n\nexport interface LanceSearchOptions {\n namespace: string;\n scope?: string;\n limit?: number;\n sources?: ReadonlyArray<\"memory\" | \"sessions\" | \"wiki\">;\n}\n\nexport interface LanceSearchHit {\n id: string;\n text: string;\n source: \"memory\" | \"sessions\" | \"wiki\";\n namespace: string;\n scope: string;\n userId: string;\n score: number;\n}\n\n/**\n * Lazily load `@lancedb/lancedb`. Throws typed error when absent.\n *\n * @internal\n */\nfunction requireLance(): LanceModule {\n try {\n const r = createRequire(import.meta.url);\n return r(\"@lancedb/lancedb\") as LanceModule;\n } catch (cause) {\n throw new ConfigurationError(\n \"Lance backend selected but `@lancedb/lancedb` is not installed. \" +\n 'Install with: `pnpm add @lancedb/lancedb`. SQLite remains available as the default (omit `backend: \"lance\"`).',\n { code: \"lance_backend_unavailable\", cause },\n );\n }\n}\n\n/**\n * Lance-backed memory index. Public API mirrors the SQLite one logically:\n * `addFacts`, `search`, `countFacts`, `removeFacts`, `close`.\n *\n * @internal\n */\nexport class LanceIndex {\n private constructor(\n private readonly table: LanceTable,\n private readonly embedding: EmbeddingRuntime,\n private readonly embeddingDim: number,\n ) {}\n\n static async open(opts: OpenLanceOptions): Promise<LanceIndex> {\n const lance = requireLance();\n const storagePath = opts.storagePath ?? join(opts.cwd, \".theokit\", \"memory\", \"lance\");\n mkdirSync(storagePath, { recursive: true });\n const conn = await lance.connect(storagePath);\n const dim = opts.embedding.dimension;\n const tableName = \"facts\";\n const existing = await conn.tableNames();\n let table: LanceTable;\n if (existing.includes(tableName)) {\n table = await conn.openTable(tableName);\n // EC-8: validate embedding dimension matches what's in storage.\n // Lance 0.30 uses Apache Arrow FixedSizeList type — the size lives\n // in `type.listSize` (older API used `fixedSize`; we read both for\n // forward/backward safety).\n const schema = await table.schema();\n const embField = schema.fields.find((f) => f.name === \"embedding\");\n const existingDim = embField?.type?.listSize ?? embField?.type?.fixedSize;\n if (typeof existingDim === \"number\" && existingDim !== dim) {\n throw new ConfigurationError(\n `Embedding dimension mismatch in Lance index: storage has ${existingDim}-dim vectors, current provider yields ${dim}-dim. Run \\`theokit-migrate-memory\\` after switching providers, or use a different storagePath.`,\n { code: \"embedding_dimension_mismatch\" },\n );\n }\n } else {\n // Create with one bootstrap record. Lance requires an initial row to\n // infer schema; we delete the bootstrap immediately.\n const bootstrap: LanceFactRecord = {\n id: \"__bootstrap__\",\n text: \"\",\n source: \"memory\",\n embedding: new Array(dim).fill(0),\n namespace: \"__bootstrap__\",\n scope: \"__bootstrap__\",\n user_id: \"__bootstrap__\",\n timestamp: 0,\n };\n table = await conn.createTable(tableName, [bootstrap]);\n await table.delete(\"namespace = '__bootstrap__'\");\n }\n return new LanceIndex(table, opts.embedding, dim);\n }\n\n async addFacts(facts: ReadonlyArray<Omit<LanceFactRecord, \"embedding\">>): Promise<void> {\n if (facts.length === 0) return;\n const texts = facts.map((f) => f.text);\n const embeddings = await this.embedding.embed(texts);\n const rows: LanceFactRecord[] = facts.map((f, i) => ({\n ...f,\n embedding: embeddings[i] ?? new Array(this.embeddingDim).fill(0),\n }));\n await this.table.add(rows);\n }\n\n /**\n * Search facts by semantic similarity. EC-1: filters use Lance's\n * structured object filter — NEVER string interpolation.\n */\n async search(query: string, opts: LanceSearchOptions): Promise<LanceSearchHit[]> {\n const [embedding] = await this.embedding.embed([query]);\n if (embedding === undefined) return [];\n // EC-1 (lancedb-backend-ship-v1-1 integration test caught this 2026-05-31):\n // Lance 0.30.0's `.where()` accepts SQL STRING only, NOT object filter —\n // contrary to D43's original assumption. We build the SQL string with\n // single-quote escaping (`'` → `''`) to neutralize injection. This is\n // the standard SQL string-literal escape; bind parameters are not\n // supported in Lance's predicate API. Backed by integration test\n // `injection attempt in namespace does not break filter` (test 7).\n const predicates: string[] = [`namespace = '${escapeSqlValue(opts.namespace)}'`];\n if (opts.scope !== undefined) {\n predicates.push(`scope = '${escapeSqlValue(opts.scope)}'`);\n }\n let q = this.table.search(embedding).where(predicates.join(\" AND \"));\n if (opts.limit !== undefined) q = q.limit(opts.limit);\n const results = await q.toArray();\n return results\n .filter((r) => opts.sources === undefined || opts.sources.includes(r.source))\n .map((r) => ({\n id: r.id,\n text: r.text,\n source: r.source,\n namespace: r.namespace,\n scope: r.scope,\n userId: r.user_id,\n // Lower _distance = better match; normalize to 0..1 (1=best).\n score: 1 / (1 + (r._distance ?? 1)),\n }));\n }\n\n async countFacts(namespace: string): Promise<number> {\n // Total count, regardless of namespace, used by migration validation.\n // For per-namespace count, callers can do a where() + countRows() if\n // Lance supports it; v1.2 ships with global count only.\n void namespace;\n return this.table.countRows();\n }\n\n async removeFacts(ids: ReadonlyArray<string>): Promise<void> {\n if (ids.length === 0) return;\n // Build a parameterized-style delete predicate. Lance's delete still\n // takes a string predicate, so we explicitly quote-escape each id —\n // ids in our system are content-hashed (alphanumeric), so the risk\n // is low, but we belt-and-suspenders.\n const escaped = ids.map((id) => `'${id.replace(/'/g, \"''\")}'`).join(\", \");\n await this.table.delete(`id IN (${escaped})`);\n }\n\n async close(): Promise<void> {\n // Lance auto-closes on GC; no explicit shutdown needed.\n }\n}\n\n/**\n * T5.2 — Harden SQL value escaping for Lance `.where()` predicates.\n *\n * Pre-T5.2 this only escaped single quotes (`'` → `''`). T5.2 adds:\n * - NUL byte rejection (NUL truncates the string in some SQL engines)\n * - C0 control char rejection (prevents invisible payload injection)\n * - Backslash escaping (`\\` → `\\\\` — some engines interpret `\\'` as\n * a single-quote escape, bypassing the `''` defense)\n *\n * Throws `ConfigurationError` on NUL/control to surface the attack\n * honestly rather than silently corrupting the query.\n */\nfunction escapeSqlValue(value: string): string {\n // Reject NUL + C0/DEL control chars (T5.5 pattern adapted for SQL).\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i);\n if (code === 0x00 || (code >= 0x01 && code <= 0x1f) || code === 0x7f) {\n throw new ConfigurationError(\n `SQL filter value contains control character at position ${i} (0x${code.toString(16)}). ` +\n \"This may indicate an injection attempt.\",\n { code: \"sql_injection_blocked\" },\n );\n }\n }\n // Escape both single-quote and backslash.\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/'/g, \"''\");\n}\n\n/**\n * Test helper for {@link LanceIndex}: indicates whether the Lance module\n * is loadable in the current environment. Allows tests to gracefully skip\n * when the optional dep is absent.\n *\n * @internal\n */\nexport function isLanceAvailable(): boolean {\n try {\n const r = createRequire(import.meta.url);\n r(\"@lancedb/lancedb\");\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Test helper: re-export the storage path computation.\n *\n * @internal\n */\nexport function lanceStoragePath(cwd: string): string {\n return join(cwd, \".theokit\", \"memory\", \"lance\");\n}\n\nvoid existsSync; // imported but only used conditionally via mkdirSync\n","/**\n * Common `MemoryIndex` interface shared between the SQLite-backed\n * `IndexManager` (default) and the Lance-backed adapter (ADR D43, shipped\n * v1.4.0 via lancedb-backend-ship-v1-1 plan).\n *\n * Consumers (`local-agent-memory.ts`, `active-memory.ts`, `tools.ts`,\n * `runActiveMemory`) only ever call `sync()`, `search()`, `status()`,\n * `close()` — the union of those four methods is the portable surface.\n *\n * Why a separate file (vs declaring inline in `index-manager.ts`)? D43\n * promised \"extract an abstract `MemoryIndex` interface in\n * `internal/memory/index-interface.ts`\". Keeping the contract physically\n * separated from one of its implementations preserves OCP — future\n * backends (Qdrant, PGVector) can land without touching IndexManager.\n *\n * @internal\n */\n\nimport type { IndexStatus, MemorySearchHit, SearchOptions } from \"./index-manager-contract.js\";\n\n// Re-export consumer-facing shapes for stable internal import paths.\nexport type { IndexStatus, MemorySearchHit, SearchOptions };\n\nexport interface SyncResult {\n filesScanned: number;\n filesUpdated: number;\n chunksWritten: number;\n chunksEmbedded: number;\n}\n\n/**\n * Common search-options parser used by both `IndexManager.search` and\n * `LanceMemoryAdapter.search`. Returns the normalized cap + minimum-score\n * floor — eliminates jscpd duplication while keeping per-backend search\n * logic distinct.\n */\nexport function parseSearchOptions(options: SearchOptions = {}): {\n maxResults: number;\n minScore: number;\n} {\n return {\n maxResults: Math.max(1, options.maxResults ?? 10),\n minScore: options.minScore ?? 0,\n };\n}\n\nexport interface MemoryIndex {\n /**\n * Walk the memory corpus + reindex changed files. Lance backend has no\n * markdown corpus to walk — its adapter returns zero counts (no-op) and\n * relies on explicit `addFact` calls instead.\n */\n sync(): Promise<SyncResult>;\n\n /**\n * Semantic + textual search over the indexed corpus. Both backends\n * return the same `MemorySearchHit[]` shape; Lance leaves `textScore`\n * undefined (vector-only — no FTS5 layer).\n */\n search(query: string, options?: SearchOptions): Promise<MemorySearchHit[]>;\n\n /** Snapshot of the index health (backend type + counts). */\n status(): IndexStatus;\n\n /** Release native handles. Idempotent. */\n close(): Promise<void> | void;\n}\n","/**\n * `LanceMemoryAdapter` — wraps `LanceIndex` to expose the common\n * `MemoryIndex` interface (sync/search/status/close) so consumers of\n * `IndexManager.open({ backend: \"lance\" })` get a drop-in replacement\n * for the SQLite-backed `IndexManager`.\n *\n * Semantic deltas vs SQLite (documented so callers know what to expect):\n *\n * - `sync()` is a NO-OP. Lance is a pure vector store — there is no\n * markdown corpus to crawl. Returns zero counts. Consumers writing\n * facts use `LanceIndex.addFacts` directly (exposed via the index\n * object returned to advanced callers).\n * - `search()` performs vector-only retrieval. `MemorySearchHit.textScore`\n * is undefined (no FTS5 layer); `vectorScore === score`.\n * - `status()` reports `backend: \"hybrid\"` only when an embedding runtime\n * is wired (always the case for Lance — embedding is required at open).\n * `chunksIndexed` reflects total Lance row count; `filesIndexed` is 0\n * because Lance does not track file provenance per-row.\n *\n * Ships with the lancedb-backend-ship-v1-1 plan (close D12, supersede via\n * D43). v1.4.0 of `@theokit/sdk`.\n *\n * @internal\n */\n\nimport type { IndexStatus, MemorySearchHit, SearchOptions } from \"./index-manager-contract.js\";\nimport type { LanceIndex } from \"./lance-index.js\";\nimport { type MemoryIndex, parseSearchOptions, type SyncResult } from \"./memory-index.js\";\n\n/**\n * Default namespace used when consumers call `search()` without specifying\n * one. The SQLite `IndexManager` does not surface namespace in its public\n * `search()` signature either — both backends therefore behave consistently\n * under the \"single-workspace, single-namespace\" assumption that v1.4 ships.\n *\n * v1.5 candidate: surface `namespace` in `SearchOptions` (additive — no\n * breaking change) so multi-tenant Lance consumers can scope queries.\n */\nconst DEFAULT_NAMESPACE = \"default\";\n\n/** Empty sync result — Lance has no corpus to walk. */\nconst EMPTY_SYNC_RESULT: SyncResult = Object.freeze({\n filesScanned: 0,\n filesUpdated: 0,\n chunksWritten: 0,\n chunksEmbedded: 0,\n});\n\nexport class LanceMemoryAdapter implements MemoryIndex {\n constructor(private readonly inner: LanceIndex) {}\n\n /**\n * No-op for Lance — see file header. Returns zero counts so callers'\n * existing logging (`filesScanned: X`) does not break.\n */\n async sync(): Promise<SyncResult> {\n return EMPTY_SYNC_RESULT;\n }\n\n // jscpd:ignore-start — search signature + early-out is idiomatic\n // overlap with IndexManager.search; further factoring would hurt\n // readability (the bodies diverge sharply right after).\n async search(query: string, options: SearchOptions = {}): Promise<MemorySearchHit[]> {\n if (query.trim().length === 0) return [];\n const { maxResults, minScore } = parseSearchOptions(options);\n // jscpd:ignore-end\n const lanceHits = await this.inner.search(query, {\n namespace: DEFAULT_NAMESPACE,\n limit: maxResults * 2,\n ...(options.sources !== undefined ? { sources: options.sources } : {}),\n });\n return lanceHits\n .filter((h) => h.score >= minScore)\n .slice(0, maxResults)\n .map((h) => translateLanceHit(h, query));\n }\n\n status(): IndexStatus {\n // chunksIndexed via a synchronous best-effort — Lance API is async so\n // we surface zero here and document that consumers needing exact counts\n // should call `inner.countFacts()` directly.\n return {\n backend: \"hybrid\",\n filesIndexed: 0,\n chunksIndexed: 0,\n };\n }\n\n async close(): Promise<void> {\n await this.inner.close();\n }\n\n /**\n * Escape hatch for advanced callers (migration tool, benchmark script)\n * that need direct access to addFacts/countFacts/removeFacts.\n */\n unwrap(): LanceIndex {\n return this.inner;\n }\n}\n\n/**\n * Translate `LanceSearchHit` to `MemorySearchHit`. Field mapping:\n *\n * - `id` → `path` (Lance has no file-system provenance; use opaque id)\n * - `text` → `snippet` (truncated to 200 chars matching IndexManager convention)\n * - `score` → combined (0.7 × vectorScore + 0.3 × textScore)\n * - `vectorScore` → from Lance's distance metric\n * - `textScore` → T4.5 client-side term-overlap ratio (removes ADR D43 vector-only caveat)\n * - `source` → `source`\n * - synthetic `startLine: 0, endLine: 0` (Lance has no line info)\n * - synthetic `citation: id` (no path:line citation available)\n */\nfunction translateLanceHit(\n hit: { id: string; text: string; source: \"memory\" | \"sessions\" | \"wiki\"; score: number },\n query?: string,\n): MemorySearchHit {\n const textScore = query !== undefined ? computeTermOverlapScore(query, hit.text) : 0;\n const vectorScore = hit.score;\n // Hybrid combination: 70% vector (semantic) + 30% text (lexical).\n const combined = 0.7 * vectorScore + 0.3 * textScore;\n return {\n path: hit.id,\n startLine: 0,\n endLine: 0,\n score: combined,\n textScore,\n vectorScore,\n snippet: hit.text.slice(0, 200),\n source: hit.source,\n citation: hit.id,\n };\n}\n\n/**\n * T4.5 — Client-side term-overlap score for Lance hybrid search.\n * Counts what fraction of query terms appear in the text (case-insensitive).\n * Simple but effective for re-ranking vector hits by lexical relevance.\n * Returns 0..1 where 1 = all query terms present.\n */\nfunction computeTermOverlapScore(query: string, text: string): number {\n const queryTerms = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((t) => t.length > 1);\n if (queryTerms.length === 0) return 0;\n const textLower = text.toLowerCase();\n const matched = queryTerms.filter((term) => textLower.includes(term)).length;\n return matched / queryTerms.length;\n}\n","/**\n * Dispatch helpers for `IndexManager.open`. Extracted to keep\n * `index-manager.ts` under the G8 400-LoC budget (lancedb-backend-ship-v1-1).\n *\n * @internal\n */\n\nimport { ConfigurationError } from \"../../errors.js\";\nimport type { MemoryBackend, OpenIndexOptions } from \"./index-manager-contract.js\";\nimport { LanceIndex } from \"./lance-index.js\";\nimport { LanceMemoryAdapter } from \"./lance-memory-adapter.js\";\nimport type { MemoryIndex } from \"./memory-index.js\";\n\n/** Valid backend identifiers — runtime guard against TS-narrowing escapes (EC-1). */\nexport const VALID_BACKENDS: readonly MemoryBackend[] = [\"sqlite-vec\", \"lance\"];\n\n/**\n * EC-1: runtime guard for `opts.backend`. TS union is compile-time only;\n * consumers passing JS `as any` typos must hit a typed error, not silent\n * SQLite fallback.\n */\nexport function assertValidBackend(backend: string): asserts backend is MemoryBackend {\n if (!VALID_BACKENDS.includes(backend as MemoryBackend)) {\n throw new ConfigurationError(\n `Invalid memory backend \"${backend}\". Valid values: ${VALID_BACKENDS.join(\", \")}.`,\n { code: \"invalid_memory_backend\" },\n );\n }\n}\n\n/**\n * Lance-path open. Throws `lance_requires_embedding` when no embedding\n * runtime is provided (Lance is vector-only — no FTS fallback).\n * Surfaces `lance_backend_unavailable` from LanceIndex.open when peer\n * dep is absent.\n */\nexport async function openLanceIndex(opts: OpenIndexOptions): Promise<MemoryIndex> {\n if (opts.embedding === undefined) {\n throw new ConfigurationError(\n \"Lance backend requires an embedding runtime. Pass `embedding: { provider, model, apiKey }` in Memory.create options (or omit `backend` to use SQLite default).\",\n { code: \"lance_requires_embedding\" },\n );\n }\n const lance = await LanceIndex.open({\n cwd: opts.cwd,\n embedding: opts.embedding,\n ...(opts.filePath !== undefined ? { storagePath: opts.filePath } : {}),\n });\n return new LanceMemoryAdapter(lance);\n}\n","/**\n * Public memory types (used by runtime + storage + migration + future index).\n *\n * Leaf module — only depends on `node:path` — so neither `runtime/memory-store.ts`\n * nor `internal/memory/markdown-store.ts` introduces a dependency cycle.\n *\n * @internal\n */\nimport { resolve as resolvePath } from \"node:path\";\n\nimport { safePathJoin, sanitizeIdentifier } from \"../security/path-guard.js\";\n\nexport interface MemoryConfig {\n enabled: boolean;\n namespace?: string;\n userId?: string;\n scope?: \"agent\" | \"user\" | \"team\";\n storePath?: string;\n}\n\n/**\n * What a fact IS, which is what decides whether it ages (#389).\n *\n * The four are the distinctions that matter for retention and recall: a preference stays true until\n * the user changes their mind, a project fact goes stale when the code moves, a reference either\n * resolves or 404s, and feedback records a correction that was given. Without them, recall ranks one\n * undifferentiated corpus and pruning has no basis to prefer a durable fact over a transient one.\n *\n * A kind is never INFERRED. A wrong kind is worse than none, because it makes retention and recall\n * confident about the wrong thing — so a fact whose author did not say stays untyped.\n */\nexport type MemoryKind = \"user\" | \"feedback\" | \"project\" | \"reference\";\n\n/** The four values {@link MemoryKind} admits, for runtime validation at the storage boundary. */\nexport const MEMORY_KINDS: readonly MemoryKind[] = [\"user\", \"feedback\", \"project\", \"reference\"];\n\nexport interface MemoryFact {\n text: string;\n /**\n * What this fact is (#389). Absent means untyped, which is what a hand-written bullet under\n * `## Facts` stays — those files are already on disk in consumers' repositories and the store's\n * own header invites editing them.\n */\n kind?: MemoryKind;\n /**\n * ISO 8601 instant this fact was last written, stamped BY THE SDK.\n *\n * Supplying it does nothing: a timestamp a caller can set is a timestamp that can lie about when\n * something was learned, and the whole point is to weigh a note from this morning against one\n * from four months ago. Absent on a fact written before this existed, or hand-added.\n */\n modified?: string;\n}\n\n// `redactSecrets` is now re-exported from the canonical security module\n// (ADR D68). Pre-T0.2 it was a 3-pattern local fn; consolidated to avoid\n// drift with the central 12-pattern list.\nexport { redactSecrets } from \"../security/index.js\";\n\n/**\n * Resolve the legacy JSON memory path used pre-ADR-D8 (kept for migration\n * helpers + tests). Centralized here so `migration.ts` and the legacy-aware\n * `runtime/memory-store.ts` don't duplicate the path logic (jscpd cleanup).\n */\nexport function legacyMemoryJsonPath(cwd: string, config: MemoryConfig): string {\n // ADRs D79-D81: storePath is programmatic (trusted); namespace/scope/userId\n // are user-shaped and pass sanitizeIdentifier. EC-7 (edge-case review):\n // realistic userIds (UUIDs, hash IDs, \"default\") pass; \"user@example.com\"\n // and similar need to be normalized by the caller before passing.\n if (config.storePath !== undefined) {\n return resolvePath(cwd, config.storePath);\n }\n const namespace = sanitizeIdentifier(config.namespace ?? \"default\");\n const scope = sanitizeIdentifier(config.scope ?? \"agent\", { maxLen: 16 });\n const userId = sanitizeIdentifier(config.userId ?? \"default\");\n return safePathJoin(cwd, \".theokit\", \"memory\", namespace, `${scope}-${userId}.json`);\n}\n\n/**\n * A semantically meaningful slice of a markdown memory file, produced by\n * `chunkMarkdown`. Each chunk carries stable line numbers + a content hash\n * used downstream by the embedding cache.\n *\n * Mirrors peer-project's `MemoryChunk` shape\n * (`reference/peer-project/packages/memory-host-sdk/src/host/engine-storage.ts`).\n *\n * @internal\n */\nexport interface MemoryChunk {\n /** 1-indexed starting line in the source file. */\n startLine: number;\n /** 1-indexed ending line (inclusive). */\n endLine: number;\n /** Slice of markdown source text. */\n text: string;\n /** sha256 of `text`; stable across runs for identical inputs. */\n hash: string;\n /** Optional nearest heading text (without the `#` markers). */\n heading?: string;\n}\n\n/**\n * Result of `reader.readFile`. Contains the bounded slice plus truncation\n * + provenance info.\n *\n * Mirrors peer-project's `MemoryReadResult` shape.\n *\n * @internal\n */\nexport interface MemoryReadResult {\n path: string;\n /** Requested starting line (1-indexed, defaults to 1). */\n from: number;\n /** Number of lines actually returned (may be less than `lines` near EOF). */\n linesReturned: number;\n /** Total lines in the file (after the read). */\n totalLines: number;\n /** True when fewer lines were returned than the requested `lines` AND EOF was hit. */\n truncated: boolean;\n /** Lines past the returned slice that remain in the file. */\n remainingLines: number;\n /** Slice text (joined with `\\n`). */\n text: string;\n}\n","/**\n * One memory as a file, in the shape the Claude Code CLI reads.\n *\n * `@theokit/sdk` already writes native Claude Code `.jsonl` sessions — the README's differentiator\n * is \"point `local.sessionDir` at `~/.claude` and the Claude Code CLI can `--continue` a session\n * your agent wrote\". Memory had no such convergence: a fact was a bullet under `## Facts` with its\n * kind in an HTML comment (#389), which that CLI reads as prose. Pointing a memory directory at\n * `~/.claude/projects/<project>/memory/` produced nothing it could open.\n *\n * The contract here was measured against a real store rather than inferred from documentation. Of\n * nine files, all nine carry `name`, `description` and `metadata.type`; six also carry\n * `node_type`, `originSessionId` and `modified`, which the runtime stamps on write. So the minimum\n * a reader must accept is the first three — refusing the rest would refuse memories the CLI itself\n * accepts.\n *\n * `originSessionId` is deliberately not written. It identifies the session that learned the fact,\n * and the append path has no session in scope; inventing one would be worse than omitting a field\n * the format already treats as optional.\n *\n * @internal\n */\n\nimport { parseSimpleYaml, splitFrontmatter } from \"../../runtime/context/context-yaml-lite.js\";\nimport { safeFilenameForId } from \"../../security/path-guard.js\";\nimport { MEMORY_KINDS, type MemoryKind } from \"../types.js\";\n\n/** The fields one memory file carries. */\nexport interface MemoryFileFields {\n /** Slug, and the file's basename. */\n readonly name: string;\n /** One-line summary — what the index shows and what recall ranks. */\n readonly description: string;\n /** `metadata.type`, absent when the file does not declare one this contract admits. */\n readonly kind?: MemoryKind;\n /** `metadata.modified`, an ISO 8601 instant stamped by whoever wrote the file. */\n readonly modified?: string;\n /** The markdown after the frontmatter. */\n readonly body: string;\n}\n\n/** The safe filename grammar a slug must satisfy before it is used as one. */\nconst SAFE_SLUG = /^[a-z0-9][a-z0-9_-]*$/;\n/** Long enough to stay readable, short enough to survive every filesystem's component limit. */\nconst MAX_SLUG_LENGTH = 64;\n\n/**\n * A readable, filesystem-safe slug for `text`.\n *\n * Readability is the goal — a directory of `h-3f2a…` files is a directory nobody browses — but it\n * is not the floor. The text comes from whatever a caller learned, so anything that fails the safe\n * grammar falls back to {@link safeFilenameForId}, which is total and always yields a valid\n * component.\n */\nexport function slugForFact(text: string): string {\n const slug = text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, MAX_SLUG_LENGTH)\n .replace(/-+$/, \"\");\n return SAFE_SLUG.test(slug) ? slug : safeFilenameForId(text);\n}\n\n/** Render one memory file. `description` is quoted so a colon in the text cannot break the block. */\nexport function renderMemoryFile(fields: MemoryFileFields): string {\n const metadata = [\" node_type: memory\"];\n if (fields.kind !== undefined) metadata.push(` type: ${fields.kind}`);\n if (fields.modified !== undefined) metadata.push(` modified: ${fields.modified}`);\n return [\n \"---\",\n `name: ${fields.name}`,\n `description: ${JSON.stringify(fields.description)}`,\n \"metadata:\",\n ...metadata,\n \"---\",\n \"\",\n fields.body,\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Read one memory file, or `undefined` when the content is not one.\n *\n * `undefined` rather than a throw, and rather than a best-effort object: the directory holds\n * hand-written notes and a `MEMORY.md` index alongside the memories, and turning any of those into\n * a fact would put text into recall that nobody recorded as one.\n */\nexport function parseMemoryFile(raw: string): MemoryFileFields | undefined {\n const { yaml, body } = splitFrontmatter(raw);\n if (yaml === undefined) return undefined;\n\n let fields: Record<string, unknown>;\n try {\n fields = parseSimpleYaml(yaml);\n } catch {\n return undefined; // malformed frontmatter is not a memory\n }\n\n const name = fields.name;\n const description = fields.description;\n if (typeof name !== \"string\" || typeof description !== \"string\") return undefined;\n\n const metadata = (fields.metadata ?? {}) as Record<string, unknown>;\n const rawKind = metadata.type;\n // An unknown `type` reads as untyped rather than as a fifth kind: the file is hand-editable and\n // recall must not act on a value this contract does not admit.\n const kind =\n typeof rawKind === \"string\" && MEMORY_KINDS.includes(rawKind as MemoryKind)\n ? (rawKind as MemoryKind)\n : undefined;\n const modified = typeof metadata.modified === \"string\" ? metadata.modified : undefined;\n\n return {\n name,\n description,\n ...(kind !== undefined ? { kind } : {}),\n ...(modified !== undefined ? { modified } : {}),\n body: body.trim(),\n };\n}\n","import { mkdir, readdir, readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { ConfigurationError } from \"../../../errors.js\";\nimport { replaceFileAtomic } from \"../../persistence/atomic-write.js\";\nimport { withCwdMutex } from \"../../persistence/cwd-mutex.js\";\nimport { encodeProjectDir } from \"../../persistence/session-transcript.js\";\nimport { MEMORY_KINDS, type MemoryConfig, type MemoryFact, redactSecrets } from \"../types.js\";\nimport { parseMemoryFile, renderMemoryFile, slugForFact } from \"./memory-file.js\";\n\n/**\n * Markdown-first memory storage (ADR D1 of memory-system-peer-project-parity).\n *\n * Layout, converged with the one the Claude Code CLI reads:\n * <memoryDir>/\n * ├── MEMORY.md # INDEX — one `- [text](slug.md)` line per memory\n * ├── <slug>.md # one memory, frontmatter carrying `metadata.type` + `modified`\n * └── notes/\n * └── <slug>.md # hand-written per-topic notes (read by the indexer, never written here)\n *\n * The SDK already emits native Claude Code `.jsonl` sessions, so a consumer can point\n * `local.sessionDir` at `~/.claude` and `--continue` a session its agent wrote. Memory did not hold\n * that line: a fact was a bullet under `## Facts` with its kind in an HTML comment (#389), which\n * that CLI reads as prose. Pointing a memory directory at `~/.claude/projects/<project>/memory/`\n * produced nothing it could open.\n *\n * `## Facts` bullets are still READ. They are already on disk in consumers' repositories, and a\n * format change that stopped reading them would delete what someone recorded — worse than the\n * format it replaces.\n *\n * All writes go through `replaceFileAtomic` + a per-cwd mutex (EC-4 of the\n * edge-case review) so concurrent `appendFact` calls within the same process\n * serialize. Multi-process safety is NOT provided.\n *\n * @internal\n */\n\nconst MEMORY_MD_HEADER =\n \"# Memory\\n\\n> Auto-managed by @theokit/sdk. Edit freely — the SDK reads from here.\\n\";\nconst FACTS_HEADING = \"## Facts\";\n\nexport function memoryDir(cwd: string): string {\n return join(cwd, \".theokit\", \"memory\");\n}\n\n/**\n * Where the Claude Code CLI keeps THIS project's memories.\n *\n * `<claudeHome>/projects/<encoded-cwd>/memory` — the same `encodeProjectDir` scheme the transcripts\n * already use, which is why no new encoding is invented here. `CLAUDE_CONFIG_DIR` names the home\n * when set (the CLI's own variable); `~/.claude` otherwise.\n *\n * Read, never written. Writing here by default would relocate every existing consumer's memories,\n * and an additive change must not move what is already on disk — so this is the direction that\n * costs nothing: a memory the CLI wrote becomes visible, and a memory the SDK wrote stays where the\n * SDK put it.\n */\nexport function claudeProjectMemoryDir(cwd: string): string {\n const home = process.env.CLAUDE_CONFIG_DIR?.trim();\n const root = home !== undefined && home.length > 0 ? home : join(homedir(), \".claude\");\n return join(root, \"projects\", encodeProjectDir(cwd), \"memory\");\n}\n\nexport function memoryMdPath(cwd: string): string {\n return join(memoryDir(cwd), \"MEMORY.md\");\n}\n\nexport function notesDir(cwd: string): string {\n return join(memoryDir(cwd), \"notes\");\n}\n\n/**\n * Every memory in the store: the per-memory files, plus any legacy `## Facts` bullets still in\n * `MEMORY.md`. Returns `[]` when the directory does not exist.\n *\n * Reading both is not transitional politeness. Those bullets are already on disk in consumers'\n * repositories, and the store's own header invites editing them by hand — a converged writer that\n * stopped reading them would delete what someone recorded, which is worse than the format it fixes.\n */\nexport async function readFactsFromMarkdown(cwd: string): Promise<MemoryFact[]> {\n const facts: MemoryFact[] = [];\n // Both stores: this SDK's, then the one the Claude Code CLI keeps for the same project. The\n // format has been shared since #389; only the directory was not, so a memory the CLI recorded was\n // invisible to an agent working in the same repository.\n for (const dir of [memoryDir(cwd), claudeProjectMemoryDir(cwd)]) {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n continue;\n }\n for (const entry of entries.sort()) {\n const fact = await readMemoryFileIn(dir, entry);\n if (fact !== undefined) facts.push(fact);\n }\n }\n\n try {\n facts.push(...parseFactsSection(await readFile(memoryMdPath(cwd), \"utf8\")));\n } catch {\n // no index yet — the per-memory files above are the whole store\n }\n return facts;\n}\n\n/**\n * One directory entry as a fact, or `undefined` when it is not one.\n *\n * `MEMORY.md` is the index, not a memory; a non-markdown entry is not a memory; and a markdown file\n * without the frontmatter is a note somebody wrote by hand. Turning any of those into a fact would\n * put text into recall that nobody recorded as one.\n *\n * The DIRECTORY is a parameter rather than derived from `cwd`, because both stores — this SDK's and\n * the one the Claude Code CLI keeps for the same project — read entries the same way, and a second\n * copy of this function is a second place for those three rules to drift.\n */\nasync function readMemoryFileIn(dir: string, entry: string): Promise<MemoryFact | undefined> {\n if (!entry.endsWith(\".md\") || entry === \"MEMORY.md\") return undefined;\n let raw: string;\n try {\n raw = await readFile(join(dir, entry), \"utf8\");\n } catch {\n return undefined; // vanished between readdir and read\n }\n const parsed = parseMemoryFile(raw);\n if (parsed === undefined) return undefined;\n // The BODY is the memory; `description` is the one-line recall aid. This SDK writes both the same,\n // so nothing it wrote changes — but the Claude Code CLI writes a summary in `description` and the\n // substance below it, and reading only the summary silently dropped the fact itself.\n const body = parsed.body.trim();\n return {\n text: body.length > 0 ? body : parsed.description,\n ...(parsed.kind !== undefined ? { kind: parsed.kind } : {}),\n ...(parsed.modified !== undefined ? { modified: parsed.modified } : {}),\n };\n}\n\n/**\n * Write a fact as its own memory file and point the `MEMORY.md` index at it. Atomic + serialized.\n *\n * `modified` is stamped HERE and never read from `fact`: a timestamp a caller can set is a\n * timestamp that can lie about when something was learned, and weighing recency is the point.\n */\nexport function appendFactToMarkdown(cwd: string, fact: MemoryFact): Promise<void> {\n return withCwdMutex(memoryDir(cwd), async () => {\n // Validate at the boundary (`error-handling.md` § 2): a kind outside the four would be written\n // to a file recall later trusts, so it is refused here rather than stored and believed.\n if (fact.kind !== undefined && !MEMORY_KINDS.includes(fact.kind)) {\n throw new ConfigurationError(\n `Unknown memory fact kind \"${fact.kind}\". Expected one of: ${MEMORY_KINDS.join(\", \")}.`,\n { code: \"invalid_memory_kind\" },\n );\n }\n const text = redactSecrets(fact.text);\n const name = slugForFact(text);\n await mkdir(memoryDir(cwd), { recursive: true });\n await replaceFileAtomic(\n join(memoryDir(cwd), `${name}.md`),\n renderMemoryFile({\n name,\n description: text,\n ...(fact.kind !== undefined ? { kind: fact.kind } : {}),\n modified: new Date().toISOString(),\n body: text,\n }),\n );\n await replaceFileAtomic(memoryMdPath(cwd), await nextIndex(cwd, text, name));\n });\n}\n\n/**\n * The `MEMORY.md` index with `name` present exactly once.\n *\n * Rewriting the same memory replaces its line rather than adding a second: the index is a map from\n * memory to file, and two lines for one file is a map that disagrees with itself.\n */\nasync function nextIndex(cwd: string, text: string, name: string): Promise<string> {\n let existing = \"\";\n try {\n existing = await readFile(memoryMdPath(cwd), \"utf8\");\n } catch {\n existing = \"\";\n }\n const entry = `- [${text}](${name}.md)`;\n const kept = existing\n .split(\"\\n\")\n .filter((line) => !line.startsWith(`- [`) || !line.includes(`](${name}.md)`));\n const body = kept.join(\"\\n\").trimEnd();\n const head = body.length > 0 ? body : MEMORY_MD_HEADER.trimEnd();\n return `${head}\\n${entry}\\n`;\n}\n\n/** A `MEMORY.md` index entry: a link whose target is a sibling `.md` file. */\nconst INDEX_ENTRY = /^\\[[^\\]]*\\]\\([^)]+\\.md\\)$/;\n\n/**\n * Facts from a legacy `## Facts` section — the shape every released version wrote.\n *\n * Kept for reading only. The converged writer puts each memory in its own file, and this parser\n * exists so a store written by an earlier version keeps answering.\n *\n * There is no metadata to recover from a bullet: `kind` and `modified` are absent, which is exactly\n * right, since nothing recorded them. A brief encoding that carried them in a trailing HTML comment\n * (#389) never reached a published version — 4.55.0 was published at 17:23Z and that commit landed\n * at 20:06Z — so there is no store in the wild to migrate from it.\n */\nfunction parseFactsSection(raw: string): MemoryFact[] {\n const idx = raw.indexOf(FACTS_HEADING);\n if (idx === -1) return [];\n const tail = raw.slice(idx + FACTS_HEADING.length);\n // Stop at the next top-level or h2 heading.\n const nextHeading = tail.search(/\\n#{1,2}\\s/);\n const block = nextHeading === -1 ? tail : tail.slice(0, nextHeading);\n return (\n block\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.startsWith(\"- \"))\n .map((line) => line.slice(2).trim())\n // An index entry is a POINTER to a memory, not a memory. Both are `- ` bullets, and when a\n // legacy `## Facts` heading is present the appended index lines land under it — counting them\n // here would report every converged memory twice, once from its file and once from its link.\n .filter((body) => !INDEX_ENTRY.test(body))\n .map((body) => ({ text: body }))\n );\n}\n\n/** Configuration-aware accessors honoring the existing MemoryConfig contract. */\nexport async function readFacts(cwd: string, config: MemoryConfig): Promise<MemoryFact[]> {\n if (!config.enabled) return [];\n return readFactsFromMarkdown(cwd);\n}\n\nexport async function appendFact(\n cwd: string,\n config: MemoryConfig,\n fact: MemoryFact,\n): Promise<void> {\n if (!config.enabled) return;\n await appendFactToMarkdown(cwd, fact);\n}\n","import { mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { replaceFileAtomic } from \"../../persistence/atomic-write.js\";\nimport { safeFilenameForId } from \"../../security/path-guard.js\";\nimport { redactSecrets } from \"../types.js\";\nimport { memoryDir } from \"./markdown-store.js\";\n\n/**\n * Per-run session summary writer (ADR D20).\n *\n * After every finished run, write a markdown summary to\n * `.theokit/memory/sessions/<runId>.md`. IndexManager picks these up with\n * `source=\"sessions\"` so `memory_search({ corpus: \"sessions\" })` can recall\n * past conversations.\n *\n * EC-9: only `status === \"finished\"` runs trigger a write. Cancelled/errored\n * runs would otherwise pollute the recall corpus with partial transcripts.\n *\n * @internal\n */\n\nexport interface SessionSummaryInput {\n cwd: string;\n runId: string;\n agentId: string;\n userText: string;\n assistantText: string;\n status: \"finished\" | \"running\" | \"error\" | \"cancelled\";\n at: number;\n}\n\nconst MAX_TURN_CHARS = 2000;\n\nexport function sessionsDir(cwd: string): string {\n return join(memoryDir(cwd), \"sessions\");\n}\n\nexport function sessionSummaryPath(cwd: string, runId: string): string {\n // M0-4: total id->filename helper. UUID runIds (the production case, from\n // randomUUID) pass through to the identical name; non-conforming ids get a\n // deterministic sha256 token instead of the old lossy replace-collapse.\n return join(sessionsDir(cwd), `${safeFilenameForId(runId, { maxLen: 128 })}.md`);\n}\n\nfunction truncate(text: string): string {\n if (text.length <= MAX_TURN_CHARS) return text;\n return `${text.slice(0, MAX_TURN_CHARS)}…`;\n}\n\n/**\n * Write a session summary file. EC-9: a non-finished status returns early\n * without touching disk. Secrets in both user and assistant text are\n * redacted via the shared `redactSecrets` pattern.\n *\n * @internal\n */\nexport async function writeSessionSummary(input: SessionSummaryInput): Promise<void> {\n if (input.status !== \"finished\") return;\n const path = sessionSummaryPath(input.cwd, input.runId);\n await mkdir(sessionsDir(input.cwd), { recursive: true });\n\n const safeUser = redactSecrets(truncate(input.userText));\n const safeAssistant = redactSecrets(truncate(input.assistantText));\n const iso = new Date(input.at).toISOString();\n\n const body = [\n \"---\",\n `runId: ${input.runId}`,\n `agentId: ${input.agentId}`,\n `at: ${iso}`,\n `status: ${input.status}`,\n \"---\",\n \"\",\n \"## User\",\n \"\",\n safeUser,\n \"\",\n \"## Assistant\",\n \"\",\n safeAssistant,\n \"\",\n ].join(\"\\n\");\n\n await replaceFileAtomic(path, body);\n}\n","import { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { memoryDir } from \"./markdown-store.js\";\nimport { sessionsDir } from \"./session-summary-writer.js\";\n\n/**\n * Session summary discovery (ADR D20).\n *\n * Mirrors `wiki-loader.ts:discoverWikiFiles`: scans\n * `.theokit/memory/sessions/*.md` and returns `SessionFile` records —\n * `{ absolutePath, relPath }`. IndexManager tags each chunk with\n * `source=\"sessions\"` so `memory_search({ corpus: \"sessions\" })` filters\n * them in.\n *\n * B-140: this said \"returns `MemoryFileEntry`-shaped records\" and that was\n * not true — `MemoryFileEntry` carried four fields (`path`, `relPath`,\n * `mtime`, `hash`) against this function's two, and even the path field was\n * named differently. Nothing consumed the interface, so it was removed and\n * the sentence now names the type the function actually returns. A docblock\n * asserting agreement between two shapes is worth no more than the agreement.\n *\n * @internal\n */\n\nexport interface SessionFile {\n absolutePath: string;\n relPath: string;\n}\n\nexport async function discoverSessionFiles(cwd: string): Promise<SessionFile[]> {\n let entries: string[];\n try {\n entries = await readdir(sessionsDir(cwd));\n } catch {\n return [];\n }\n const root = memoryDir(cwd);\n return entries\n .filter((entry) => entry.endsWith(\".md\"))\n .map((entry) => {\n const absolutePath = join(sessionsDir(cwd), entry);\n return {\n absolutePath,\n relPath: relativeToRoot(root, absolutePath),\n };\n });\n}\n\nfunction relativeToRoot(root: string, absolutePath: string): string {\n if (absolutePath.startsWith(`${root}/`)) return absolutePath.slice(root.length + 1);\n return absolutePath;\n}\n","import { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { memoryDir } from \"./markdown-store.js\";\n\n/**\n * Wiki supplement discovery (ADR Phase 10 of memory-system-peer-project-parity).\n *\n * Wiki files live under `.theokit/memory/wiki/*.md`. They are READ-ONLY —\n * the SDK never writes here. Each indexed chunk carries `source=\"wiki\"` so\n * `memory_search { corpus: \"wiki\" }` and `corpus: \"all\"` can scope hits.\n *\n * @internal\n */\n\nexport interface WikiFile {\n absolutePath: string;\n relPath: string;\n}\n\nexport function wikiDir(cwd: string): string {\n return join(memoryDir(cwd), \"wiki\");\n}\n\nexport async function discoverWikiFiles(cwd: string): Promise<WikiFile[]> {\n let entries: string[];\n try {\n entries = await readdir(wikiDir(cwd));\n } catch {\n return [];\n }\n const root = memoryDir(cwd);\n return entries\n .filter((entry) => entry.endsWith(\".md\"))\n .map((entry) => ({\n absolutePath: join(wikiDir(cwd), entry),\n relPath: join(\"wiki\", entry),\n }))\n .map((file) => ({\n absolutePath: file.absolutePath,\n relPath: relativeToRoot(root, file.absolutePath),\n }));\n}\n\nfunction relativeToRoot(root: string, absolutePath: string): string {\n // memory root is e.g. /tmp/x/.theokit/memory; wiki file is /tmp/x/.theokit/memory/wiki/foo.md\n // Strip the root + \"/\" prefix to get \"wiki/foo.md\".\n if (absolutePath.startsWith(`${root}/`)) return absolutePath.slice(root.length + 1);\n return absolutePath;\n}\n","import { createHash } from \"node:crypto\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\n\nimport type { MemorySearchHit, SearchOptions } from \"./index-manager-contract.js\";\nimport { memoryDir, memoryMdPath, notesDir } from \"./storage/markdown-store.js\";\nimport { discoverSessionFiles } from \"./storage/session-loader.js\";\nimport { discoverWikiFiles } from \"./storage/wiki-loader.js\";\n\n// ───── hybrid scoring helpers ─────────────────────────────────────────\n\n/** @internal */\nexport interface HybridWeights {\n vectorWeight: number;\n textWeight: number;\n total: number;\n}\n\n/** @internal */\nexport function resolveWeights(options: SearchOptions): HybridWeights {\n const vectorWeight = options.vectorWeight ?? 0.6;\n const textWeight = options.textWeight ?? 0.4;\n const total = vectorWeight + textWeight || 1;\n return { vectorWeight, textWeight, total };\n}\n\n/** @internal */\nexport function blendScores(\n hit: MemorySearchHit & { chunkId: number },\n vectorScore: number,\n weights: HybridWeights,\n): MemorySearchHit {\n const score =\n (vectorScore * weights.vectorWeight + hit.textScore * weights.textWeight) / weights.total;\n return {\n path: hit.path,\n startLine: hit.startLine,\n endLine: hit.endLine,\n score,\n textScore: hit.textScore,\n snippet: hit.snippet,\n source: hit.source,\n citation: hit.citation,\n ...(vectorScore > 0 ? { vectorScore } : {}),\n };\n}\n\n// ───── file discovery ─────────────────────────────────────────────────\n\n/** @internal */\nexport interface DiscoveredFile {\n absolutePath: string;\n relPath: string;\n source: \"memory\" | \"wiki\" | \"sessions\";\n}\n\n/**\n * Every `*.md` in `dir` as a discovered memory file, minus `skip`. A directory that does not exist\n * yet contributes nothing — the store is created lazily, and its absence is not an error.\n */\nasync function markdownFilesIn(\n dir: string,\n root: string,\n skip: readonly string[] = [],\n): Promise<DiscoveredFile[]> {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n return [];\n }\n return entries\n .filter((entry) => entry.endsWith(\".md\") && !skip.includes(entry))\n .map((entry) => ({\n absolutePath: join(dir, entry),\n relPath: relative(root, join(dir, entry)),\n source: \"memory\" as const,\n }));\n}\n\n/** @internal */\nexport async function collectMarkdownFiles(cwd: string): Promise<DiscoveredFile[]> {\n const root = memoryDir(cwd);\n const results: DiscoveredFile[] = [];\n // MEMORY.md\n try {\n await stat(memoryMdPath(cwd));\n results.push({\n absolutePath: memoryMdPath(cwd),\n relPath: relative(root, memoryMdPath(cwd)),\n source: \"memory\",\n });\n } catch {\n // skip\n }\n // <memoryDir>/*.md — one file per memory, the converged layout. MEMORY.md is the index and is\n // discovered above; picking it up twice would index every memory's link alongside its text.\n results.push(...(await markdownFilesIn(memoryDir(cwd), root, [\"MEMORY.md\"])));\n results.push(...(await markdownFilesIn(notesDir(cwd), root)));\n // wiki/*.md (Phase 10 — read-only supplements)\n const wikiFiles = await discoverWikiFiles(cwd);\n for (const wiki of wikiFiles) {\n results.push({\n absolutePath: wiki.absolutePath,\n relPath: wiki.relPath,\n source: \"wiki\",\n });\n }\n // sessions/*.md (ADR D20 — per-run summaries for corpus=\"sessions\" recall)\n const sessionFiles = await discoverSessionFiles(cwd);\n for (const session of sessionFiles) {\n results.push({\n absolutePath: session.absolutePath,\n relPath: session.relPath,\n source: \"sessions\",\n });\n }\n return results;\n}\n\n// ───── misc helpers ───────────────────────────────────────────────────\n\n/** @internal */\nexport function sha256(text: string): string {\n return createHash(\"sha256\").update(text).digest(\"hex\");\n}\n\n/** @internal */\nexport function truncateSnippet(text: string): string {\n const max = 500;\n return text.length <= max ? text : `${text.slice(0, max)}…`;\n}\n","import { ConfigurationError } from \"../../errors.js\";\nimport type { MemoryDb } from \"./index-db.js\";\n\n/**\n * Load the `sqlite-vec` extension into an opened SQLite connection.\n *\n * Wraps the native `load(db)` call from the `sqlite-vec` npm package with a\n * typed error path (EC-8 of the edge-case review) so callers see a\n * `sqlite_vec_unavailable` ConfigurationError instead of a raw native error.\n *\n * @internal\n */\nexport async function loadSqliteVecExtension(db: MemoryDb): Promise<void> {\n try {\n const mod = await import(\"sqlite-vec\");\n const loadFn = (mod as { load?: (db: unknown) => void }).load;\n if (typeof loadFn !== \"function\") {\n throw new Error(\"sqlite-vec module exposes no `load` export\");\n }\n loadFn(db);\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n throw new ConfigurationError(\n `sqlite-vec extension unavailable. Install \\`sqlite-vec\\` and ensure the native binary matches your Node + OS. Cause: ${message}`,\n { code: \"sqlite_vec_unavailable\", cause },\n );\n }\n}\n","import { createHash } from \"node:crypto\";\n\nimport type { MemoryChunk } from \"../types.js\";\n\n/**\n * Split a markdown document into semantically meaningful chunks (ADR D1 of\n * memory-system-peer-project-parity).\n *\n * Algorithm:\n * 1. Walk lines tracking the current heading (latest `^#+ ` line).\n * 2. Split chunks at the next heading boundary AND on blank-line paragraph\n * boundaries.\n * 3. If a single paragraph exceeds `maxChars`, split it at the nearest\n * whitespace ≤ maxChars (word-aligned per edge-case review EC-6) —\n * never mid-word.\n *\n * Mirrors peer-project's `chunkMarkdown` from\n * `packages/memory-host-sdk/src/host/chunk-markdown.ts`.\n *\n * @internal\n */\n\nexport interface ChunkMarkdownOptions {\n /** Maximum chars per chunk. Default 800. */\n maxChars?: number;\n /** Minimum chars per chunk (avoids splintering tiny lines). Default 80. */\n minChars?: number;\n}\n\nconst HEADING_RE = /^(#{1,6})\\s+(.+?)\\s*$/;\n\ninterface ChunkAccumulator {\n chunks: MemoryChunk[];\n lines: string[];\n start: number;\n heading: string | undefined;\n}\n\nexport function chunkMarkdown(text: string, options: ChunkMarkdownOptions = {}): MemoryChunk[] {\n const maxChars = options.maxChars ?? 800;\n if (text.length === 0) return [];\n const lines = text.split(\"\\n\");\n const acc: ChunkAccumulator = { chunks: [], lines: [], start: 1, heading: undefined };\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? \"\";\n const lineNumber = i + 1;\n processLine(acc, line, lineNumber, maxChars);\n }\n flushAccumulator(acc, lines.length, maxChars);\n return acc.chunks;\n}\n\nfunction processLine(\n acc: ChunkAccumulator,\n line: string,\n lineNumber: number,\n maxChars: number,\n): void {\n const headingMatch = HEADING_RE.exec(line);\n const isBlank = line.trim().length === 0;\n if (headingMatch !== null && acc.lines.length > 0) {\n flushAccumulator(acc, lineNumber - 1, maxChars);\n }\n if (headingMatch !== null) acc.heading = headingMatch[2];\n acc.lines.push(line);\n if (isBlank && acc.lines.length > 1) {\n const joined = acc.lines.join(\"\\n\");\n if (joined.trim().length > 0) flushAccumulator(acc, lineNumber, maxChars);\n }\n}\n\nfunction flushAccumulator(acc: ChunkAccumulator, endLineExclusive: number, maxChars: number): void {\n if (acc.lines.length === 0) return;\n const chunkText = acc.lines.join(\"\\n\");\n if (chunkText.trim().length === 0) {\n acc.lines = [];\n acc.start = endLineExclusive + 1;\n return;\n }\n if (chunkText.length > maxChars) {\n pushOversizedSlices(acc.chunks, chunkText, acc.start, acc.heading, maxChars);\n } else {\n acc.chunks.push(buildChunk(chunkText, acc.start, endLineExclusive, acc.heading));\n }\n acc.lines = [];\n acc.start = endLineExclusive + 1;\n}\n\nfunction buildChunk(\n text: string,\n startLine: number,\n endLine: number,\n heading: string | undefined,\n): MemoryChunk {\n return {\n startLine,\n endLine,\n text,\n hash: createHash(\"sha256\").update(text).digest(\"hex\"),\n ...(heading !== undefined ? { heading } : {}),\n };\n}\n\nfunction pushOversizedSlices(\n chunks: MemoryChunk[],\n text: string,\n startLine: number,\n heading: string | undefined,\n maxChars: number,\n): void {\n let remaining = text;\n let lineCursor = startLine;\n while (remaining.length > maxChars) {\n const splitAt = findWordBoundarySplit(remaining, maxChars);\n const slice = remaining.slice(0, splitAt);\n const sliceLines = slice.split(\"\\n\").length;\n chunks.push(buildChunk(slice, lineCursor, lineCursor + sliceLines - 1, heading));\n remaining = remaining.slice(splitAt).replace(/^\\s+/, \"\");\n lineCursor += sliceLines - 1;\n }\n if (remaining.length > 0) {\n const sliceLines = remaining.split(\"\\n\").length;\n chunks.push(buildChunk(remaining, lineCursor, lineCursor + sliceLines - 1, heading));\n }\n}\n\n/**\n * Find a split point ≤ maxChars that lands on a whitespace boundary so we\n * don't slice a word in half. Falls back to maxChars if no whitespace is\n * within range (very rare — a single token > maxChars).\n */\nfunction findWordBoundarySplit(text: string, maxChars: number): number {\n if (text.length <= maxChars) return text.length;\n // Walk backward from maxChars looking for whitespace.\n for (let i = maxChars; i > Math.max(0, maxChars - 200); i--) {\n if (/\\s/.test(text[i] ?? \"x\")) return i;\n }\n // No whitespace within 200 chars of the cap — accept a hard split.\n return maxChars;\n}\n","import type { EmbeddingRuntime } from \"./embedding-adapter.js\";\nimport type { MemoryDb } from \"./index-db.js\";\n\n/**\n * Vector index helpers (ADR D2 + D4 of memory-system-peer-project-parity).\n *\n * Embeddings live in `embeddings(chunk_id, vec)` — a `vec0` virtual table\n * provided by the sqlite-vec extension. Embedding identity (providerId +\n * model + dimension) lives in the `meta` table; mismatches force a full\n * re-embed sweep (EC-1).\n *\n * @internal\n */\n\nexport const META_KEY_PROVIDER_ID = \"embedding.providerId\";\nexport const META_KEY_MODEL = \"embedding.model\";\nexport const META_KEY_DIMENSION = \"embedding.dimension\";\n\nexport interface EmbeddingIdentity {\n providerId: string;\n model: string;\n dimension: number;\n}\n\nexport function readEmbeddingIdentity(db: MemoryDb): EmbeddingIdentity | undefined {\n const get = (key: string): string | undefined => {\n const row = db.prepare(\"SELECT value FROM meta WHERE key = ?\").get(key);\n return row !== undefined ? String(row.value) : undefined;\n };\n const providerId = get(META_KEY_PROVIDER_ID);\n const model = get(META_KEY_MODEL);\n const dimRaw = get(META_KEY_DIMENSION);\n if (providerId === undefined || model === undefined || dimRaw === undefined) return undefined;\n const dimension = Number(dimRaw);\n if (!Number.isFinite(dimension) || dimension <= 0) return undefined;\n return { providerId, model, dimension };\n}\n\nexport function writeEmbeddingIdentity(db: MemoryDb, identity: EmbeddingIdentity): void {\n const stmt = db.prepare(\n \"INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value\",\n );\n stmt.run(META_KEY_PROVIDER_ID, identity.providerId);\n stmt.run(META_KEY_MODEL, identity.model);\n stmt.run(META_KEY_DIMENSION, String(identity.dimension));\n}\n\nexport function identityMatches(a: EmbeddingIdentity, b: EmbeddingIdentity): boolean {\n return a.providerId === b.providerId && a.model === b.model && a.dimension === b.dimension;\n}\n\nexport function dropVectorIndex(db: MemoryDb): void {\n db.exec(\"DROP TABLE IF EXISTS embeddings\");\n}\n\nexport function createVectorIndex(db: MemoryDb, dimension: number): void {\n db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(\n chunk_id INTEGER PRIMARY KEY,\n vec FLOAT[${dimension}]\n )`);\n}\n\n/** Pack a Float32Array into a Buffer suitable for sqlite-vec BLOB binding. */\nexport function packVector(vec: ReadonlyArray<number>): Buffer {\n const f32 = new Float32Array(vec.length);\n for (let i = 0; i < vec.length; i++) f32[i] = vec[i] ?? 0;\n return Buffer.from(f32.buffer);\n}\n\nexport function upsertEmbedding(db: MemoryDb, chunkId: number, vec: ReadonlyArray<number>): void {\n // sqlite-vec vec0 virtual tables don't support UPSERT — emulate via\n // DELETE-then-INSERT. vec0 REJECTS JS Number for chunk_id columns; it\n // requires BigInt binding. Verified via repro against vec0 v0.1.9.\n const id = BigInt(chunkId);\n db.prepare(\"DELETE FROM embeddings WHERE chunk_id = ?\").run(id);\n db.prepare(\"INSERT INTO embeddings (chunk_id, vec) VALUES (?, ?)\").run(id, packVector(vec));\n}\n\nexport interface VectorHitRow {\n chunk_id: number;\n distance: number;\n}\n\nexport function vectorSearch(\n db: MemoryDb,\n query: ReadonlyArray<number>,\n k: number,\n): VectorHitRow[] {\n // sqlite-vec returns distance (lower = closer). Use KNN syntax. k needs\n // a BigInt-typed binding too.\n const rows = db\n .prepare(\n `SELECT chunk_id, distance FROM embeddings\n WHERE vec MATCH ? AND k = ?\n ORDER BY distance`,\n )\n .all(packVector(query), BigInt(k));\n return rows.map((row) => ({ chunk_id: Number(row.chunk_id), distance: Number(row.distance) }));\n}\n\nexport interface EmbedAllArgs {\n db: MemoryDb;\n runtime: EmbeddingRuntime;\n}\n\n/** Embed every chunk that doesn't yet have a vector. */\nexport async function embedMissingChunks(args: EmbedAllArgs): Promise<number> {\n const rows = args.db\n .prepare(\n `SELECT chunks.id as id, chunks.text as text\n FROM chunks\n LEFT JOIN embeddings ON embeddings.chunk_id = chunks.id\n WHERE embeddings.chunk_id IS NULL`,\n )\n .all();\n if (rows.length === 0) return 0;\n const normalized = rows.map((row) => ({ id: Number(row.id), text: String(row.text ?? \"\") }));\n const vectors = await args.runtime.embed(normalized.map((r) => r.text));\n for (let i = 0; i < normalized.length; i++) {\n const row = normalized[i];\n const vector = vectors[i];\n if (row === undefined || vector === undefined) continue;\n upsertEmbedding(args.db, row.id, vector);\n }\n return normalized.length;\n}\n","import { createHash } from \"node:crypto\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport { sanitizeFts5Query } from \"../persistence/fts5-sanitize.js\";\nimport type { EmbeddingRuntime } from \"./embedding-adapter.js\";\nimport { LruEmbeddingCache } from \"./embedding-cache.js\";\nimport { escapeLikePattern } from \"./escape-like-pattern.js\";\nimport { defaultIndexPath, type MemoryDb, openMemoryDb } from \"./index-db.js\";\nimport { assertValidBackend, openLanceIndex } from \"./index-manager-dispatch.js\";\nimport {\n blendScores,\n collectMarkdownFiles,\n resolveWeights,\n sha256,\n truncateSnippet,\n} from \"./index-manager-helpers.js\";\nimport { type MemoryIndex, parseSearchOptions } from \"./memory-index.js\";\nimport { loadSqliteVecExtension } from \"./sqlite-vec-loader.js\";\nimport { chunkMarkdown } from \"./storage/chunk-markdown.js\";\n\n// T4.1 — query-vector LRU cache (DR4 finding #1). Keyed by\n// sha256(query); caches the embedding vector so repeated search\n// queries skip the HTTP round-trip (p99 1.5-3s → ~0ms on hit).\n// Process-wide singleton with bounded capacity (default 2000 queries).\nconst queryVectorCache = new LruEmbeddingCache(2000);\n\nimport {\n createVectorIndex,\n dropVectorIndex,\n embedMissingChunks,\n identityMatches,\n readEmbeddingIdentity,\n vectorSearch,\n writeEmbeddingIdentity,\n} from \"./vec-index.js\";\n\n/**\n * Memory index manager (ADR D2). FTS5-only at Phase 3; vector index lands in\n * Phase 5.\n *\n * Lifecycle:\n * const idx = await IndexManager.open({ cwd });\n * await idx.sync();\n * const hits = await idx.search(\"query\");\n * idx.close();\n *\n * @internal\n */\n\n// T2.1 / ADR D433 — these types now live in `index-manager-contract.ts`\n// to break the 3-cycle memory cluster. Re-exported here for back-compat\n// with downstream importers that historically pulled from this file.\nexport type {\n IndexStatus,\n MemoryBackend,\n MemorySearchHit,\n OpenIndexOptions,\n SearchOptions,\n} from \"./index-manager-contract.js\";\n\nimport type {\n IndexStatus,\n MemorySearchHit,\n OpenIndexOptions,\n SearchOptions,\n} from \"./index-manager-contract.js\";\n\nexport class IndexManager implements MemoryIndex {\n private lastSyncMs: number | undefined;\n private vectorReady = false;\n\n private constructor(\n private readonly cwd: string,\n private readonly db: MemoryDb,\n private readonly embedding: EmbeddingRuntime | undefined,\n ) {}\n\n /**\n * Open a memory index. Dispatches to SQLite (default) or Lance (opt-in\n * via `backend: \"lance\"`). See `openLanceInternal` for Lance-specific\n * preconditions + typed errors.\n */\n static async open(opts: OpenIndexOptions & { backend: \"lance\" }): Promise<MemoryIndex>;\n static async open(\n opts: Omit<OpenIndexOptions, \"backend\"> | (OpenIndexOptions & { backend?: \"sqlite-vec\" }),\n ): Promise<IndexManager>;\n static async open(opts: OpenIndexOptions): Promise<MemoryIndex> {\n const backend = opts.backend ?? \"sqlite-vec\";\n assertValidBackend(backend);\n if (backend === \"lance\") return await openLanceIndex(opts);\n return await IndexManager.openSqliteInternal(opts);\n }\n\n /** Internal SQLite-path open. Renamed from previous public `open`. */\n private static async openSqliteInternal(opts: OpenIndexOptions): Promise<IndexManager> {\n const filePath = opts.filePath ?? defaultIndexPath(opts.cwd);\n const db = await openMemoryDb({ filePath });\n const manager = new IndexManager(opts.cwd, db, opts.embedding);\n if (opts.embedding !== undefined) await manager.initVectorBackend(opts.embedding);\n return manager;\n }\n\n private async initVectorBackend(runtime: EmbeddingRuntime): Promise<void> {\n await loadSqliteVecExtension(this.db);\n const currentIdentity = {\n providerId: runtime.id,\n model: runtime.model,\n dimension: runtime.dimension,\n };\n const persisted = readEmbeddingIdentity(this.db);\n if (persisted !== undefined && !identityMatches(persisted, currentIdentity)) {\n // EC-1: dimension/model/provider changed — drop the vector index and\n // force a full re-embed on next sync.\n dropVectorIndex(this.db);\n }\n createVectorIndex(this.db, runtime.dimension);\n writeEmbeddingIdentity(this.db, currentIdentity);\n this.vectorReady = true;\n }\n\n /** Walk the memory corpus + (re)index changed files. */\n async sync(): Promise<{\n filesScanned: number;\n filesUpdated: number;\n chunksWritten: number;\n chunksEmbedded: number;\n }> {\n const files = await collectMarkdownFiles(this.cwd);\n let filesUpdated = 0;\n let chunksWritten = 0;\n const existingByPath = this.loadFilesIndex();\n for (const entry of files) {\n const raw = await readFile(entry.absolutePath, \"utf8\");\n const hash = sha256(raw);\n const existing = existingByPath.get(entry.absolutePath);\n if (existing !== undefined && existing.hash === hash) continue;\n const stats = await stat(entry.absolutePath);\n const fileId = this.upsertFile(\n entry.absolutePath,\n entry.relPath,\n hash,\n stats.mtimeMs,\n entry.source,\n );\n this.deleteChunksForFile(fileId);\n const chunks = chunkMarkdown(raw);\n for (const chunk of chunks) {\n this.insertChunk(fileId, chunk.startLine, chunk.endLine, chunk.text, chunk.hash);\n }\n filesUpdated += 1;\n chunksWritten += chunks.length;\n }\n let chunksEmbedded = 0;\n if (this.vectorReady && this.embedding !== undefined) {\n chunksEmbedded = await embedMissingChunks({ db: this.db, runtime: this.embedding });\n }\n this.lastSyncMs = Date.now();\n return { filesScanned: files.length, filesUpdated, chunksWritten, chunksEmbedded };\n }\n\n async search(query: string, options: SearchOptions = {}): Promise<MemorySearchHit[]> {\n if (query.trim().length === 0) return [];\n const { maxResults, minScore } = parseSearchOptions(options);\n const textHits = this.ftsSearch(query, maxResults * 2);\n const vectorHitsById = await this.vectorSearchById(query, maxResults * 2);\n const combined = this.combineHybridScores(textHits, vectorHitsById, options);\n return combined\n .filter((h) => h.score >= minScore)\n .filter((h) => options.sources === undefined || options.sources.includes(h.source))\n .slice(0, maxResults);\n }\n\n status(): IndexStatus {\n const files = this.db.prepare(\"SELECT COUNT(*) as n FROM files\").get() ?? { n: 0 };\n const chunks = this.db.prepare(\"SELECT COUNT(*) as n FROM chunks\").get() ?? { n: 0 };\n const status: IndexStatus = {\n backend: this.vectorReady ? \"hybrid\" : \"fts-only\",\n filesIndexed: Number(files.n ?? 0),\n chunksIndexed: Number(chunks.n ?? 0),\n };\n if (this.lastSyncMs !== undefined) status.lastSyncMs = this.lastSyncMs;\n return status;\n }\n\n // ───── search internals ──────────────────────────────────────────────\n\n private ftsSearch(query: string, limit: number): Array<MemorySearchHit & { chunkId: number }> {\n // EC-3: short-circuit when sanitizer reduces input to empty string.\n // Calling `MATCH ''` would error inside FTS5 on some SQLite versions.\n const sanitized = sanitizeFts5Query(query);\n if (sanitized.length === 0) return [];\n const stmt = this.db.prepare(\n `SELECT chunks.id as id, files.rel_path as rel_path, files.source as source,\n chunks.start_line as start_line, chunks.end_line as end_line,\n chunks.text as text, bm25(chunks_fts) as bm25_score\n FROM chunks_fts\n JOIN chunks ON chunks_fts.rowid = chunks.id\n JOIN files ON chunks.file_id = files.id\n WHERE chunks_fts MATCH ?\n ORDER BY bm25_score\n LIMIT ?`,\n );\n let rows: Array<Record<string, unknown>> = [];\n try {\n rows = stmt.all(sanitized, limit);\n } catch {\n // T4.8 — CJK fallback: FTS5's default tokenizer chokes on CJK\n // characters (no word boundaries). Instead of returning empty,\n // fall back to a LIKE search which handles CJK correctly (slower\n // but correct). ADR D64 documents the trigram-routing deferral.\n return this.likeSearchFallback(query, limit);\n }\n return rows.map((row) => {\n const bm25 = Number(row.bm25_score ?? 0);\n const textScore = bm25 < 0 ? 1 / (1 + Math.abs(bm25)) : 1 / (1 + bm25);\n const startLine = Number(row.start_line ?? 0);\n const endLine = Number(row.end_line ?? 0);\n const path = String(row.rel_path);\n return {\n path,\n startLine,\n endLine,\n score: textScore,\n textScore,\n snippet: truncateSnippet(String(row.text ?? \"\")),\n source: String(row.source) as \"memory\" | \"sessions\" | \"wiki\",\n citation: `${path}:${startLine}-${endLine}`,\n // sneak the chunk id through for the hybrid join below\n // (cast away later)\n chunkId: Number(row.id),\n } as MemorySearchHit & { chunkId: number };\n });\n }\n\n /**\n * T4.8 — LIKE-based fallback for CJK and other scripts that FTS5's\n * default tokenizer cannot segment. Slower than FTS5 (full table scan)\n * but correct for any Unicode input. Returns the same shape as\n * `ftsSearch` so callers are unaware of the fallback.\n */\n private likeSearchFallback(\n query: string,\n limit: number,\n ): Array<MemorySearchHit & { chunkId: number }> {\n const pattern = escapeLikePattern(query);\n const stmt = this.db.prepare(\n `SELECT chunks.id as id, files.rel_path as rel_path, files.source as source,\n chunks.start_line as start_line, chunks.end_line as end_line,\n chunks.text as text\n FROM chunks\n JOIN files ON chunks.file_id = files.id\n WHERE chunks.text LIKE ? ESCAPE '\\\\'\n LIMIT ?`,\n );\n const rows = stmt.all(pattern, limit) as Array<Record<string, unknown>>;\n return rows.map((row) => {\n const path = String(row.rel_path ?? \"\");\n const startLine = Number(row.start_line ?? 0);\n const endLine = Number(row.end_line ?? 0);\n return {\n path,\n startLine,\n endLine,\n score: 0.5, // LIKE has no relevance score — use a neutral midpoint\n textScore: 0.5,\n snippet: truncateSnippet(String(row.text ?? \"\")),\n source: String(row.source) as \"memory\" | \"sessions\" | \"wiki\",\n citation: `${path}:${startLine}-${endLine}`,\n chunkId: Number(row.id),\n } as MemorySearchHit & { chunkId: number };\n });\n }\n\n private async vectorSearchById(\n query: string,\n limit: number,\n ): Promise<Map<number, { vectorScore: number; snippet?: string }>> {\n if (!this.vectorReady || this.embedding === undefined) return new Map();\n // T4.1 — query-vector LRU cache. The embed call is the p99 hottest\n // path (1.5-3s per hit). Cache by sha256(query) so repeated queries\n // skip the HTTP round-trip entirely. The embedding cache (T4.4\n // globalEmbeddingCache) catches text-level dedup; this catches the\n // query-level search-result dedup on top.\n const cacheKey = createHash(\"sha256\").update(query).digest(\"hex\");\n const cached = queryVectorCache.get(cacheKey);\n const queryVec = cached ?? (await this.embedding.embed([query]))[0];\n if (queryVec === undefined) return new Map();\n if (cached === undefined) queryVectorCache.set(cacheKey, queryVec);\n const rows = vectorSearch(this.db, queryVec, limit);\n // sqlite-vec distance: lower = closer. Normalize to 0..1 with higher = better.\n const out = new Map<number, { vectorScore: number }>();\n for (const row of rows) {\n const score = 1 / (1 + Math.max(0, row.distance));\n out.set(row.chunk_id, { vectorScore: score });\n }\n return out;\n }\n\n private combineHybridScores(\n textHits: Array<MemorySearchHit & { chunkId: number }>,\n vectorHitsById: Map<number, { vectorScore: number }>,\n options: SearchOptions,\n ): MemorySearchHit[] {\n const weights = resolveWeights(options);\n const merged = this.mergeHits(textHits, vectorHitsById);\n const combined = [...merged.values()].map((hit) =>\n blendScores(hit, vectorHitsById.get(hit.chunkId)?.vectorScore ?? 0, weights),\n );\n return combined.sort((a, b) => b.score - a.score);\n }\n\n private mergeHits(\n textHits: Array<MemorySearchHit & { chunkId: number }>,\n vectorHitsById: Map<number, { vectorScore: number }>,\n ): Map<number, MemorySearchHit & { chunkId: number }> {\n const merged = new Map<number, MemorySearchHit & { chunkId: number }>();\n for (const hit of textHits) merged.set(hit.chunkId, hit);\n const missingIds = [...vectorHitsById.keys()].filter((id) => !merged.has(id));\n if (missingIds.length > 0) {\n for (const hit of this.fetchChunksByIds(missingIds)) {\n merged.set(hit.chunkId, { ...hit, score: 0, textScore: 0 });\n }\n }\n return merged;\n }\n\n private fetchChunksByIds(\n ids: ReadonlyArray<number>,\n ): Array<MemorySearchHit & { chunkId: number }> {\n if (ids.length === 0) return [];\n const placeholders = ids.map(() => \"?\").join(\",\");\n const rows = this.db\n .prepare(\n `SELECT chunks.id as id, files.rel_path as rel_path, files.source as source,\n chunks.start_line as start_line, chunks.end_line as end_line,\n chunks.text as text\n FROM chunks JOIN files ON chunks.file_id = files.id\n WHERE chunks.id IN (${placeholders})`,\n )\n .all(...ids);\n return rows.map((row) => {\n const startLine = Number(row.start_line ?? 0);\n const endLine = Number(row.end_line ?? 0);\n const path = String(row.rel_path);\n return {\n chunkId: Number(row.id),\n path,\n startLine,\n endLine,\n score: 0,\n textScore: 0,\n snippet: truncateSnippet(String(row.text ?? \"\")),\n source: String(row.source) as \"memory\" | \"sessions\" | \"wiki\",\n citation: `${path}:${startLine}-${endLine}`,\n };\n });\n }\n\n // ───── persistence helpers ─────────────────────────────────────────\n\n private loadFilesIndex(): Map<string, { id: number; hash: string }> {\n const rows = this.db.prepare(\"SELECT id, path, hash FROM files\").all() as Array<{\n id: number;\n path: string;\n hash: string;\n }>;\n return new Map(rows.map((row) => [row.path, { id: row.id, hash: row.hash }]));\n }\n\n private upsertFile(\n absPath: string,\n relPath: string,\n hash: string,\n mtimeMs: number,\n source: \"memory\" | \"wiki\" | \"sessions\" = \"memory\",\n ): number {\n const stmt = this.db.prepare(\n `INSERT INTO files (path, rel_path, mtime, hash, source) VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(path) DO UPDATE SET hash = excluded.hash, mtime = excluded.mtime, source = excluded.source\n RETURNING id`,\n );\n const row = stmt.get(absPath, relPath, Math.floor(mtimeMs), hash, source) as { id: number };\n return row.id;\n }\n\n private deleteChunksForFile(fileId: number): void {\n this.db.prepare(\"DELETE FROM chunks WHERE file_id = ?\").run(fileId);\n }\n\n private insertChunk(\n fileId: number,\n startLine: number,\n endLine: number,\n text: string,\n hash: string,\n ): void {\n this.db\n .prepare(\n \"INSERT INTO chunks (file_id, start_line, end_line, text, hash) VALUES (?, ?, ?, ?, ?)\",\n )\n .run(fileId, startLine, endLine, text, hash);\n }\n\n close(): void {\n this.db.close();\n }\n}\n\n// Replaced by `sanitizeFts5Query` from `internal/persistence/fts5-sanitize.ts`\n// (T5.2, ADR D64). The new sanitizer is the 6-step port of Hermes'\n// `_sanitize_fts5_query` and handles hyphens/dots/underscores correctly\n// without quoting every token.\n"]}