@vortex-os/memory-extended 0.5.1

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 (181) hide show
  1. package/README.md +244 -0
  2. package/dist/consolidate/index.d.ts +7 -0
  3. package/dist/consolidate/index.d.ts.map +1 -0
  4. package/dist/consolidate/index.js +4 -0
  5. package/dist/consolidate/index.js.map +1 -0
  6. package/dist/consolidate/proposer.d.ts +43 -0
  7. package/dist/consolidate/proposer.d.ts.map +1 -0
  8. package/dist/consolidate/proposer.js +276 -0
  9. package/dist/consolidate/proposer.js.map +1 -0
  10. package/dist/consolidate/query.d.ts +32 -0
  11. package/dist/consolidate/query.d.ts.map +1 -0
  12. package/dist/consolidate/query.js +40 -0
  13. package/dist/consolidate/query.js.map +1 -0
  14. package/dist/consolidate/store.d.ts +21 -0
  15. package/dist/consolidate/store.d.ts.map +1 -0
  16. package/dist/consolidate/store.js +91 -0
  17. package/dist/consolidate/store.js.map +1 -0
  18. package/dist/consolidate/types.d.ts +68 -0
  19. package/dist/consolidate/types.d.ts.map +1 -0
  20. package/dist/consolidate/types.js +2 -0
  21. package/dist/consolidate/types.js.map +1 -0
  22. package/dist/index.d.ts +7 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +7 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/internal/frontmatter.d.ts +6 -0
  27. package/dist/internal/frontmatter.d.ts.map +1 -0
  28. package/dist/internal/frontmatter.js +38 -0
  29. package/dist/internal/frontmatter.js.map +1 -0
  30. package/dist/internal/proactive-curator-helpers.d.ts +171 -0
  31. package/dist/internal/proactive-curator-helpers.d.ts.map +1 -0
  32. package/dist/internal/proactive-curator-helpers.js +162 -0
  33. package/dist/internal/proactive-curator-helpers.js.map +1 -0
  34. package/dist/mcp/document-tools.d.ts +144 -0
  35. package/dist/mcp/document-tools.d.ts.map +1 -0
  36. package/dist/mcp/document-tools.js +319 -0
  37. package/dist/mcp/document-tools.js.map +1 -0
  38. package/dist/mcp/index.d.ts +34 -0
  39. package/dist/mcp/index.d.ts.map +1 -0
  40. package/dist/mcp/index.js +29 -0
  41. package/dist/mcp/index.js.map +1 -0
  42. package/dist/mcp/install.d.ts +72 -0
  43. package/dist/mcp/install.d.ts.map +1 -0
  44. package/dist/mcp/install.js +92 -0
  45. package/dist/mcp/install.js.map +1 -0
  46. package/dist/mcp/memory-tools.d.ts +101 -0
  47. package/dist/mcp/memory-tools.d.ts.map +1 -0
  48. package/dist/mcp/memory-tools.js +105 -0
  49. package/dist/mcp/memory-tools.js.map +1 -0
  50. package/dist/mcp/recall-tool.d.ts +52 -0
  51. package/dist/mcp/recall-tool.d.ts.map +1 -0
  52. package/dist/mcp/recall-tool.js +60 -0
  53. package/dist/mcp/recall-tool.js.map +1 -0
  54. package/dist/mcp/server.d.ts +32 -0
  55. package/dist/mcp/server.d.ts.map +1 -0
  56. package/dist/mcp/server.js +113 -0
  57. package/dist/mcp/server.js.map +1 -0
  58. package/dist/recall/engine.d.ts +38 -0
  59. package/dist/recall/engine.d.ts.map +1 -0
  60. package/dist/recall/engine.js +113 -0
  61. package/dist/recall/engine.js.map +1 -0
  62. package/dist/recall/index.d.ts +22 -0
  63. package/dist/recall/index.d.ts.map +1 -0
  64. package/dist/recall/index.js +20 -0
  65. package/dist/recall/index.js.map +1 -0
  66. package/dist/recall/intent.d.ts +24 -0
  67. package/dist/recall/intent.d.ts.map +1 -0
  68. package/dist/recall/intent.js +95 -0
  69. package/dist/recall/intent.js.map +1 -0
  70. package/dist/recall/render.d.ts +11 -0
  71. package/dist/recall/render.d.ts.map +1 -0
  72. package/dist/recall/render.js +23 -0
  73. package/dist/recall/render.js.map +1 -0
  74. package/dist/recall/types.d.ts +72 -0
  75. package/dist/recall/types.d.ts.map +1 -0
  76. package/dist/recall/types.js +2 -0
  77. package/dist/recall/types.js.map +1 -0
  78. package/dist/sessionArchive/adapters/claude-code.d.ts +3 -0
  79. package/dist/sessionArchive/adapters/claude-code.d.ts.map +1 -0
  80. package/dist/sessionArchive/adapters/claude-code.js +276 -0
  81. package/dist/sessionArchive/adapters/claude-code.js.map +1 -0
  82. package/dist/sessionArchive/adapters/claude-desktop.d.ts +3 -0
  83. package/dist/sessionArchive/adapters/claude-desktop.d.ts.map +1 -0
  84. package/dist/sessionArchive/adapters/claude-desktop.js +234 -0
  85. package/dist/sessionArchive/adapters/claude-desktop.js.map +1 -0
  86. package/dist/sessionArchive/adapters/codex.d.ts +3 -0
  87. package/dist/sessionArchive/adapters/codex.d.ts.map +1 -0
  88. package/dist/sessionArchive/adapters/codex.js +322 -0
  89. package/dist/sessionArchive/adapters/codex.js.map +1 -0
  90. package/dist/sessionArchive/adapters/gemini.d.ts +3 -0
  91. package/dist/sessionArchive/adapters/gemini.d.ts.map +1 -0
  92. package/dist/sessionArchive/adapters/gemini.js +248 -0
  93. package/dist/sessionArchive/adapters/gemini.js.map +1 -0
  94. package/dist/sessionArchive/adapters/index.d.ts +5 -0
  95. package/dist/sessionArchive/adapters/index.d.ts.map +1 -0
  96. package/dist/sessionArchive/adapters/index.js +5 -0
  97. package/dist/sessionArchive/adapters/index.js.map +1 -0
  98. package/dist/sessionArchive/index.d.ts +9 -0
  99. package/dist/sessionArchive/index.d.ts.map +1 -0
  100. package/dist/sessionArchive/index.js +6 -0
  101. package/dist/sessionArchive/index.js.map +1 -0
  102. package/dist/sessionArchive/ingest.d.ts +68 -0
  103. package/dist/sessionArchive/ingest.d.ts.map +1 -0
  104. package/dist/sessionArchive/ingest.js +134 -0
  105. package/dist/sessionArchive/ingest.js.map +1 -0
  106. package/dist/sessionArchive/normalize.d.ts +15 -0
  107. package/dist/sessionArchive/normalize.d.ts.map +1 -0
  108. package/dist/sessionArchive/normalize.js +40 -0
  109. package/dist/sessionArchive/normalize.js.map +1 -0
  110. package/dist/sessionArchive/store.d.ts +118 -0
  111. package/dist/sessionArchive/store.d.ts.map +1 -0
  112. package/dist/sessionArchive/store.js +491 -0
  113. package/dist/sessionArchive/store.js.map +1 -0
  114. package/dist/sessionArchive/types.d.ts +124 -0
  115. package/dist/sessionArchive/types.d.ts.map +1 -0
  116. package/dist/sessionArchive/types.js +24 -0
  117. package/dist/sessionArchive/types.js.map +1 -0
  118. package/dist/sqlite/drift.d.ts +22 -0
  119. package/dist/sqlite/drift.d.ts.map +1 -0
  120. package/dist/sqlite/drift.js +69 -0
  121. package/dist/sqlite/drift.js.map +1 -0
  122. package/dist/sqlite/index.d.ts +22 -0
  123. package/dist/sqlite/index.d.ts.map +1 -0
  124. package/dist/sqlite/index.js +4 -0
  125. package/dist/sqlite/index.js.map +1 -0
  126. package/dist/sqlite/rebuild.d.ts +32 -0
  127. package/dist/sqlite/rebuild.d.ts.map +1 -0
  128. package/dist/sqlite/rebuild.js +80 -0
  129. package/dist/sqlite/rebuild.js.map +1 -0
  130. package/dist/sqlite/schema.d.ts +15 -0
  131. package/dist/sqlite/schema.d.ts.map +1 -0
  132. package/dist/sqlite/schema.js +41 -0
  133. package/dist/sqlite/schema.js.map +1 -0
  134. package/dist/sqlite/store.d.ts +49 -0
  135. package/dist/sqlite/store.d.ts.map +1 -0
  136. package/dist/sqlite/store.js +179 -0
  137. package/dist/sqlite/store.js.map +1 -0
  138. package/dist/sqlite/types.d.ts +57 -0
  139. package/dist/sqlite/types.d.ts.map +1 -0
  140. package/dist/sqlite/types.js +2 -0
  141. package/dist/sqlite/types.js.map +1 -0
  142. package/dist/vector/backend-brute.d.ts +38 -0
  143. package/dist/vector/backend-brute.d.ts.map +1 -0
  144. package/dist/vector/backend-brute.js +112 -0
  145. package/dist/vector/backend-brute.js.map +1 -0
  146. package/dist/vector/embedder.d.ts +59 -0
  147. package/dist/vector/embedder.d.ts.map +1 -0
  148. package/dist/vector/embedder.js +47 -0
  149. package/dist/vector/embedder.js.map +1 -0
  150. package/dist/vector/index.d.ts +24 -0
  151. package/dist/vector/index.d.ts.map +1 -0
  152. package/dist/vector/index.js +7 -0
  153. package/dist/vector/index.js.map +1 -0
  154. package/dist/vector/math.d.ts +21 -0
  155. package/dist/vector/math.d.ts.map +1 -0
  156. package/dist/vector/math.js +34 -0
  157. package/dist/vector/math.js.map +1 -0
  158. package/dist/vector/schema.d.ts +14 -0
  159. package/dist/vector/schema.d.ts.map +1 -0
  160. package/dist/vector/schema.js +24 -0
  161. package/dist/vector/schema.js.map +1 -0
  162. package/dist/vector/segment.d.ts +65 -0
  163. package/dist/vector/segment.d.ts.map +1 -0
  164. package/dist/vector/segment.js +72 -0
  165. package/dist/vector/segment.js.map +1 -0
  166. package/dist/vector/session.d.ts +90 -0
  167. package/dist/vector/session.d.ts.map +1 -0
  168. package/dist/vector/session.js +242 -0
  169. package/dist/vector/session.js.map +1 -0
  170. package/dist/vector/store.d.ts +69 -0
  171. package/dist/vector/store.d.ts.map +1 -0
  172. package/dist/vector/store.js +131 -0
  173. package/dist/vector/store.js.map +1 -0
  174. package/dist/vector/types.d.ts +109 -0
  175. package/dist/vector/types.d.ts.map +1 -0
  176. package/dist/vector/types.js +24 -0
  177. package/dist/vector/types.js.map +1 -0
  178. package/package.json +96 -0
  179. package/scripts/mcp-stdio.mjs +143 -0
  180. package/scripts/rebuild-memory-sqlite.mjs +39 -0
  181. package/scripts/rebuild-memory-vector.mjs +64 -0
@@ -0,0 +1,90 @@
1
+ import Database from "better-sqlite3";
2
+ import type { EventRow, SessionArchiveStore } from "../sessionArchive/index.js";
3
+ import { segmentByEmbedding, type SegmentUnit } from "./segment.js";
4
+ import type { EmbedFn, VectorBackend } from "./types.js";
5
+ /**
6
+ * Session-chunk metadata + rebuild (Phase 11c follow-up, 2026-05-29).
7
+ *
8
+ * Conversation sessions are vectorized at *chunk* granularity — topic-
9
+ * coherent slices found by embedding-similarity segmentation (see
10
+ * `segment.ts`). Each chunk produces one vector (stored in the shared
11
+ * `memory_vectors` table with `source = "session-archive"`) plus a metadata
12
+ * row here (`session_chunks`) so `recall` can hydrate a session hit with a
13
+ * timestamp + excerpt — the parallel to how memory hits hydrate from the
14
+ * `memories` table.
15
+ *
16
+ * Both tables live in the derived-index DB (`memory.sqlite`); the
17
+ * sessionArchive `metadata.sqlite` remains the source of truth and is only
18
+ * read here.
19
+ */
20
+ export declare const SESSION_CHUNK_SCHEMA = "\nCREATE TABLE IF NOT EXISTS session_chunks (\n id TEXT PRIMARY KEY,\n host TEXT NOT NULL,\n session_id TEXT NOT NULL,\n chunk_idx INTEGER NOT NULL,\n started_at TEXT,\n ended_at TEXT,\n excerpt TEXT NOT NULL DEFAULT ''\n);\nCREATE INDEX IF NOT EXISTS idx_session_chunks_session ON session_chunks(host, session_id);\n";
21
+ export interface SessionChunkRow {
22
+ readonly id: string;
23
+ readonly host: string;
24
+ readonly sessionId: string;
25
+ readonly chunkIdx: number;
26
+ readonly startedAt: string | null;
27
+ readonly endedAt: string | null;
28
+ readonly excerpt: string;
29
+ }
30
+ /** Metadata store for session chunks — lives alongside the vectors in memory.sqlite. */
31
+ export declare class SessionChunkStore {
32
+ readonly dbPath: string;
33
+ private db;
34
+ private readonly ownsHandle;
35
+ constructor(db: string | Database.Database);
36
+ upsert(row: SessionChunkRow): void;
37
+ getById(id: string): SessionChunkRow | null;
38
+ /** Distinct (host, sessionId) keys currently indexed. */
39
+ sessionKeys(): readonly {
40
+ host: string;
41
+ sessionId: string;
42
+ }[];
43
+ /** Chunk ids belonging to one session. */
44
+ chunkIds(host: string, sessionId: string): readonly string[];
45
+ deleteSession(host: string, sessionId: string): number;
46
+ close(): void;
47
+ }
48
+ /** Build the vector id for a session chunk. host/sessionId carry no `/` or `#`. */
49
+ export declare function sessionChunkId(host: string, sessionId: string, chunkIdx: number): string;
50
+ export declare function buildTurnUnits(events: readonly EventRow[]): SegmentUnit[];
51
+ export interface SessionRebuildResult {
52
+ readonly sessions: number;
53
+ readonly chunks: number;
54
+ readonly prunedSessions: number;
55
+ }
56
+ export interface SessionRebuildOptions {
57
+ /**
58
+ * Chunking granularity:
59
+ * - `"turn"` (**default**) — one chunk per turn (user+assistant exchange).
60
+ * Sidesteps segmentation: no merge → no within-chunk truncation/dilution.
61
+ * A 2026-05-29 real-data tuning run found this equals or beats `"segment"`
62
+ * while being simpler; combined with the noise filter in `buildTurnUnits`
63
+ * it is the recommended default. Short turns are filtered by `minTurnChars`.
64
+ * - `"segment"` — embedding-similarity topic chunks. Retained as an option
65
+ * for corpora where coarser, topic-level units are preferred.
66
+ */
67
+ readonly granularity?: "segment" | "turn";
68
+ /** Turn mode: skip turns shorter than this many characters. Default 24. */
69
+ readonly minTurnChars?: number;
70
+ /** Passed through to the segmenter (threshold / min / max). */
71
+ readonly segment?: Parameters<typeof segmentByEmbedding>[2];
72
+ /** Skip sessions whose started_at is older than this ISO date. */
73
+ readonly sinceMs?: number;
74
+ /**
75
+ * Only (re)vectorize sessions that have no chunks yet — the lazy-recall
76
+ * backlog. Already-chunked sessions are skipped, so a run with nothing new
77
+ * costs one cheap `chunkIds` lookup each and prunes nothing. (It will not
78
+ * re-vectorize a session whose content changed after it was chunked; a full
79
+ * rebuild — `onlyMissing` off — does that.)
80
+ */
81
+ readonly onlyMissing?: boolean;
82
+ }
83
+ /**
84
+ * Rebuild session-archive vectors: segment every archived session into topic
85
+ * chunks, embed each chunk (centroid of its turn vectors), and write the
86
+ * vector + a metadata row. Sessions no longer present in the archive are
87
+ * pruned. Idempotent — a session is fully replaced on each run.
88
+ */
89
+ export declare function rebuildSessionVectors(backend: VectorBackend, chunks: SessionChunkStore, sessions: SessionArchiveStore, embed: EmbedFn, options?: SessionRebuildOptions): Promise<SessionRebuildResult>;
90
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/vector/session.ts"],"names":[],"mappings":"AAEA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAChF,OAAO,EAAE,kBAAkB,EAAgB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAElF,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEzD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,oWAWhC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,wFAAwF;AACxF,qBAAa,iBAAiB;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;gBAEzB,EAAE,EAAE,MAAM,GAAG,QAAQ,CAAC,QAAQ;IAe1C,MAAM,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAUlC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAU3C,yDAAyD;IACzD,WAAW,IAAI,SAAS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,EAAE;IAO7D,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE;IAO5D,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAMtD,KAAK,IAAI,IAAI;CAGd;AAwBD,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExF;AAiBD,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,GAAG,WAAW,EAAE,CA0BzE;AAkBD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;OASG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1C,2EAA2E;IAC3E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,kEAAkE;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;CAChC;AAwBD;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,CAAC,CA2D/B"}
@@ -0,0 +1,242 @@
1
+ import { mkdirSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import Database from "better-sqlite3";
4
+ import { segmentByEmbedding } from "./segment.js";
5
+ import { normalize } from "./math.js";
6
+ /**
7
+ * Session-chunk metadata + rebuild (Phase 11c follow-up, 2026-05-29).
8
+ *
9
+ * Conversation sessions are vectorized at *chunk* granularity — topic-
10
+ * coherent slices found by embedding-similarity segmentation (see
11
+ * `segment.ts`). Each chunk produces one vector (stored in the shared
12
+ * `memory_vectors` table with `source = "session-archive"`) plus a metadata
13
+ * row here (`session_chunks`) so `recall` can hydrate a session hit with a
14
+ * timestamp + excerpt — the parallel to how memory hits hydrate from the
15
+ * `memories` table.
16
+ *
17
+ * Both tables live in the derived-index DB (`memory.sqlite`); the
18
+ * sessionArchive `metadata.sqlite` remains the source of truth and is only
19
+ * read here.
20
+ */
21
+ export const SESSION_CHUNK_SCHEMA = `
22
+ CREATE TABLE IF NOT EXISTS session_chunks (
23
+ id TEXT PRIMARY KEY,
24
+ host TEXT NOT NULL,
25
+ session_id TEXT NOT NULL,
26
+ chunk_idx INTEGER NOT NULL,
27
+ started_at TEXT,
28
+ ended_at TEXT,
29
+ excerpt TEXT NOT NULL DEFAULT ''
30
+ );
31
+ CREATE INDEX IF NOT EXISTS idx_session_chunks_session ON session_chunks(host, session_id);
32
+ `;
33
+ /** Metadata store for session chunks — lives alongside the vectors in memory.sqlite. */
34
+ export class SessionChunkStore {
35
+ dbPath;
36
+ db;
37
+ ownsHandle;
38
+ constructor(db) {
39
+ if (typeof db === "string") {
40
+ mkdirSync(dirname(db), { recursive: true });
41
+ this.dbPath = db;
42
+ this.db = new Database(db);
43
+ this.db.pragma("journal_mode = WAL");
44
+ this.ownsHandle = true;
45
+ }
46
+ else {
47
+ this.dbPath = db.name;
48
+ this.db = db;
49
+ this.ownsHandle = false;
50
+ }
51
+ this.db.exec(SESSION_CHUNK_SCHEMA);
52
+ }
53
+ upsert(row) {
54
+ this.db
55
+ .prepare(`INSERT OR REPLACE INTO session_chunks
56
+ (id, host, session_id, chunk_idx, started_at, ended_at, excerpt)
57
+ VALUES (@id, @host, @sessionId, @chunkIdx, @startedAt, @endedAt, @excerpt)`)
58
+ .run(row);
59
+ }
60
+ getById(id) {
61
+ const r = this.db
62
+ .prepare(`SELECT id, host, session_id, chunk_idx, started_at, ended_at, excerpt
63
+ FROM session_chunks WHERE id = ?`)
64
+ .get(id);
65
+ return r ? hydrate(r) : null;
66
+ }
67
+ /** Distinct (host, sessionId) keys currently indexed. */
68
+ sessionKeys() {
69
+ const rows = this.db
70
+ .prepare(`SELECT DISTINCT host, session_id FROM session_chunks`)
71
+ .all();
72
+ return rows.map((r) => ({ host: r.host, sessionId: r.session_id }));
73
+ }
74
+ /** Chunk ids belonging to one session. */
75
+ chunkIds(host, sessionId) {
76
+ const rows = this.db
77
+ .prepare(`SELECT id FROM session_chunks WHERE host = ? AND session_id = ? ORDER BY chunk_idx`)
78
+ .all(host, sessionId);
79
+ return rows.map((r) => r.id);
80
+ }
81
+ deleteSession(host, sessionId) {
82
+ return this.db
83
+ .prepare(`DELETE FROM session_chunks WHERE host = ? AND session_id = ?`)
84
+ .run(host, sessionId).changes;
85
+ }
86
+ close() {
87
+ if (this.ownsHandle)
88
+ this.db.close();
89
+ }
90
+ }
91
+ function hydrate(r) {
92
+ return {
93
+ id: r.id,
94
+ host: r.host,
95
+ sessionId: r.session_id,
96
+ chunkIdx: r.chunk_idx,
97
+ startedAt: r.started_at,
98
+ endedAt: r.ended_at,
99
+ excerpt: r.excerpt,
100
+ };
101
+ }
102
+ /** Build the vector id for a session chunk. host/sessionId carry no `/` or `#`. */
103
+ export function sessionChunkId(host, sessionId, chunkIdx) {
104
+ return `${host}/${sessionId}#${chunkIdx}`;
105
+ }
106
+ /**
107
+ * Group a session's events into turn units for vectorization. Events sharing
108
+ * a `turnId` are concatenated into one unit (a user turn + its assistant
109
+ * reply); events with no turnId become standalone units. Order is preserved.
110
+ *
111
+ * **Content filter (2026-05-29 tuning):** only `user_message` and
112
+ * `assistant_message` text is kept. Tool calls/results (git/grep/ls output,
113
+ * file dumps, npm logs) are dropped — a real-data tuning run showed they
114
+ * dominate the index with noise and drown the substantive discussion. The
115
+ * host's system-reminder blocks injected into user turns are stripped for the
116
+ * same reason. The result is "what was actually discussed," which is what
117
+ * recall should match.
118
+ */
119
+ const SUBSTANTIVE_KINDS = new Set(["user_message", "assistant_message"]);
120
+ export function buildTurnUnits(events) {
121
+ const units = [];
122
+ let curKey = null;
123
+ let curTexts = [];
124
+ let curAt = null;
125
+ const flush = () => {
126
+ const text = curTexts.join("\n").trim();
127
+ if (text.length > 0)
128
+ units.push({ text, at: curAt });
129
+ curTexts = [];
130
+ curAt = null;
131
+ };
132
+ for (const ev of events) {
133
+ const key = ev.turnId ?? `__solo_${ev.idx}`;
134
+ if (key !== curKey) {
135
+ flush();
136
+ curKey = key;
137
+ curAt = ev.at;
138
+ }
139
+ if (!SUBSTANTIVE_KINDS.has(ev.kind))
140
+ continue;
141
+ const t = cleanTurnText(ev.text);
142
+ if (t)
143
+ curTexts.push(t);
144
+ }
145
+ flush();
146
+ return units;
147
+ }
148
+ /** Strip host-injected noise (system-reminder blocks) and collapse whitespace. */
149
+ function cleanTurnText(raw) {
150
+ if (!raw)
151
+ return "";
152
+ return raw
153
+ .replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi, " ")
154
+ .replace(/\s+/g, " ")
155
+ .trim();
156
+ }
157
+ const EXCERPT_CHARS = 280;
158
+ function excerpt(text) {
159
+ const c = text.replace(/\s+/g, " ").trim();
160
+ return c.length <= EXCERPT_CHARS ? c : `${c.slice(0, EXCERPT_CHARS).trimEnd()}…`;
161
+ }
162
+ /** Turn-granularity chunks: one chunk per (long-enough) turn unit. */
163
+ async function turnSegments(units, embed, minChars) {
164
+ const out = [];
165
+ for (let u = 0; u < units.length; u++) {
166
+ const text = units[u].text.trim();
167
+ if (text.length < minChars)
168
+ continue;
169
+ out.push({
170
+ unitStart: u,
171
+ unitEnd: u,
172
+ text,
173
+ vector: normalize(await embed(text)),
174
+ startedAt: units[u].at,
175
+ endedAt: units[u].at,
176
+ });
177
+ }
178
+ return out;
179
+ }
180
+ /**
181
+ * Rebuild session-archive vectors: segment every archived session into topic
182
+ * chunks, embed each chunk (centroid of its turn vectors), and write the
183
+ * vector + a metadata row. Sessions no longer present in the archive are
184
+ * pruned. Idempotent — a session is fully replaced on each run.
185
+ */
186
+ export async function rebuildSessionVectors(backend, chunks, sessions, embed, options) {
187
+ const metas = sessions.listSessions(options?.sinceMs !== undefined ? { sinceMs: options.sinceMs } : undefined);
188
+ const live = new Set();
189
+ let sessionCount = 0;
190
+ let chunkCount = 0;
191
+ for (const m of metas) {
192
+ // Lazy backlog: leave already-vectorized sessions untouched.
193
+ if (options?.onlyMissing && chunks.chunkIds(m.host, m.sessionId).length > 0)
194
+ continue;
195
+ const units = buildTurnUnits(sessions.readSessionEvents(m.host, m.sessionId));
196
+ if (units.length === 0)
197
+ continue;
198
+ const segs = (options?.granularity ?? "turn") === "turn"
199
+ ? await turnSegments(units, embed, options?.minTurnChars ?? 24)
200
+ : await segmentByEmbedding(units, embed, options?.segment);
201
+ if (segs.length === 0)
202
+ continue;
203
+ // Replace any existing chunks for this session.
204
+ for (const oldId of chunks.chunkIds(m.host, m.sessionId)) {
205
+ backend.delete(oldId, "session-archive");
206
+ }
207
+ chunks.deleteSession(m.host, m.sessionId);
208
+ segs.forEach((seg, idx) => {
209
+ const id = sessionChunkId(m.host, m.sessionId, idx);
210
+ backend.upsert({ id, source: "session-archive", dim: seg.vector.length, vector: seg.vector });
211
+ chunks.upsert({
212
+ id,
213
+ host: m.host,
214
+ sessionId: m.sessionId,
215
+ chunkIdx: idx,
216
+ startedAt: seg.startedAt ?? m.startedAt,
217
+ endedAt: seg.endedAt ?? m.endedAt,
218
+ excerpt: excerpt(seg.text),
219
+ });
220
+ chunkCount++;
221
+ });
222
+ live.add(`${m.host}/${m.sessionId}`);
223
+ sessionCount++;
224
+ }
225
+ // Prune sessions that vanished from the archive. Skipped for an onlyMissing
226
+ // (lazy backlog) run — it visits only un-chunked sessions, so `live` is
227
+ // partial and cannot tell "vanished" from "intentionally skipped".
228
+ let pruned = 0;
229
+ if (!options?.onlyMissing) {
230
+ for (const key of chunks.sessionKeys()) {
231
+ if (!live.has(`${key.host}/${key.sessionId}`)) {
232
+ for (const id of chunks.chunkIds(key.host, key.sessionId)) {
233
+ backend.delete(id, "session-archive");
234
+ }
235
+ chunks.deleteSession(key.host, key.sessionId);
236
+ pruned++;
237
+ }
238
+ }
239
+ }
240
+ return { sessions: sessionCount, chunks: chunkCount, prunedSessions: pruned };
241
+ }
242
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/vector/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ import type { MemorySqliteStore } from "../sqlite/store.js";
2
+ import type { MemoryRow } from "../sqlite/types.js";
3
+ import type { SessionArchiveStore } from "../sessionArchive/index.js";
4
+ import { type SessionRebuildOptions, type SessionRebuildResult } from "./session.js";
5
+ import type { EmbedFn, VectorBackend, VectorRebuildResult, VectorSearchHit, VectorSearchOptions, VectorSource } from "./types.js";
6
+ /** Options for {@link MemoryVectorStore}. */
7
+ export interface MemoryVectorStoreOptions {
8
+ /**
9
+ * Either a DB path (shares `memory.sqlite` with the sqlite namespace —
10
+ * the recommended default) or a pre-built {@link VectorBackend}. A path
11
+ * uses the default {@link BruteForceBackend}.
12
+ */
13
+ readonly db?: string;
14
+ readonly backend?: VectorBackend;
15
+ }
16
+ /**
17
+ * Semantic index over markdown memories. Backend-agnostic: the default is
18
+ * brute-force cosine in a shared SQLite table, but any {@link VectorBackend}
19
+ * works.
20
+ *
21
+ * The embedding text for a memory is `name + description + body` — the
22
+ * fields a human would skim to judge relevance. The text is embedded, the
23
+ * vector L2-normalized, and stored under `source = "memory"`.
24
+ *
25
+ * Source of truth remains the markdown (see design §Non-goal). This index
26
+ * is derived and fully rebuildable from `MemorySqliteStore`.
27
+ */
28
+ export declare class MemoryVectorStore {
29
+ private readonly backend;
30
+ /** Shared DB path, when known — enables session-chunk metadata storage. */
31
+ private readonly dbPath?;
32
+ constructor(options?: MemoryVectorStoreOptions);
33
+ /** Underlying backend (escape hatch for advanced callers). */
34
+ get vectorBackend(): VectorBackend;
35
+ /**
36
+ * Embed + upsert a single memory. Skips (returns false) when the memory
37
+ * has no embeddable text after trimming.
38
+ */
39
+ upsert(row: MemoryRow, embed: EmbedFn): Promise<boolean>;
40
+ /**
41
+ * Rebuild the `memory` source from the sqlite store: embed every memory,
42
+ * upsert it, and prune vectors whose memory no longer exists. Idempotent.
43
+ *
44
+ * `embed` is called once per memory; pass a memoized embedder (the
45
+ * default `createLocalEmbedder()` memoizes its pipeline) so the model
46
+ * loads once.
47
+ */
48
+ rebuild(sqlite: MemorySqliteStore, embed: EmbedFn): Promise<VectorRebuildResult>;
49
+ /**
50
+ * Rebuild the `session-archive` source: segment each archived session into
51
+ * topic chunks (embedding-similarity), embed each chunk, store the vector
52
+ * + a metadata row for hydration. Requires a db-backed store (construct
53
+ * with `{ db }`, or a backend exposing `dbPath`) so the `session_chunks`
54
+ * metadata table can live alongside the vectors.
55
+ */
56
+ rebuildSessions(sessions: SessionArchiveStore, embed: EmbedFn, options?: SessionRebuildOptions): Promise<SessionRebuildResult>;
57
+ /**
58
+ * Semantic search. Embeds `queryText`, normalizes, and asks the backend
59
+ * for the top-k closest vectors. `options.ids` restricts the candidate
60
+ * set (the `recall` engine uses this to apply a SQLite hard-filter
61
+ * first). `options.source` restricts by corpus.
62
+ */
63
+ search(queryText: string, embed: EmbedFn, options?: VectorSearchOptions): Promise<readonly VectorSearchHit[]>;
64
+ /** Synchronous search when the caller already holds a query vector. */
65
+ searchVector(queryVector: Float32Array, options?: VectorSearchOptions): readonly VectorSearchHit[];
66
+ count(source?: VectorSource): number;
67
+ close(): void;
68
+ }
69
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/vector/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGtE,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EACV,OAAO,EACP,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,mBAAmB,EACnB,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;CAClC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,2EAA2E;IAC3E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;gBAErB,OAAO,GAAE,wBAA6B;IAclD,8DAA8D;IAC9D,IAAI,aAAa,IAAI,aAAa,CAEjC;IAED;;;OAGG;IACG,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAQ9D;;;;;;;OAOG;IACG,OAAO,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAuBtF;;;;;;OAMG;IACG,eAAe,CACnB,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,CAAC;IAchC;;;;;OAKG;IACG,MAAM,CACV,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC;IAKtC,uEAAuE;IACvE,YAAY,CACV,WAAW,EAAE,YAAY,EACzB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,SAAS,eAAe,EAAE;IAI7B,KAAK,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM;IAIpC,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,131 @@
1
+ import { BruteForceBackend } from "./backend-brute.js";
2
+ import { normalize } from "./math.js";
3
+ import { SessionChunkStore, rebuildSessionVectors, } from "./session.js";
4
+ /**
5
+ * Semantic index over markdown memories. Backend-agnostic: the default is
6
+ * brute-force cosine in a shared SQLite table, but any {@link VectorBackend}
7
+ * works.
8
+ *
9
+ * The embedding text for a memory is `name + description + body` — the
10
+ * fields a human would skim to judge relevance. The text is embedded, the
11
+ * vector L2-normalized, and stored under `source = "memory"`.
12
+ *
13
+ * Source of truth remains the markdown (see design §Non-goal). This index
14
+ * is derived and fully rebuildable from `MemorySqliteStore`.
15
+ */
16
+ export class MemoryVectorStore {
17
+ backend;
18
+ /** Shared DB path, when known — enables session-chunk metadata storage. */
19
+ dbPath;
20
+ constructor(options = {}) {
21
+ if (options.backend) {
22
+ this.backend = options.backend;
23
+ if ("dbPath" in options.backend && typeof options.backend.dbPath === "string") {
24
+ this.dbPath = options.backend.dbPath;
25
+ }
26
+ }
27
+ else if (options.db) {
28
+ this.backend = new BruteForceBackend(options.db);
29
+ this.dbPath = options.db;
30
+ }
31
+ else {
32
+ throw new Error("MemoryVectorStore requires either { db } or { backend }.");
33
+ }
34
+ }
35
+ /** Underlying backend (escape hatch for advanced callers). */
36
+ get vectorBackend() {
37
+ return this.backend;
38
+ }
39
+ /**
40
+ * Embed + upsert a single memory. Skips (returns false) when the memory
41
+ * has no embeddable text after trimming.
42
+ */
43
+ async upsert(row, embed) {
44
+ const text = embeddableText(row);
45
+ if (text.length === 0)
46
+ return false;
47
+ const vec = normalize(await embed(text));
48
+ this.backend.upsert({ id: row.id, source: "memory", dim: vec.length, vector: vec });
49
+ return true;
50
+ }
51
+ /**
52
+ * Rebuild the `memory` source from the sqlite store: embed every memory,
53
+ * upsert it, and prune vectors whose memory no longer exists. Idempotent.
54
+ *
55
+ * `embed` is called once per memory; pass a memoized embedder (the
56
+ * default `createLocalEmbedder()` memoizes its pipeline) so the model
57
+ * loads once.
58
+ */
59
+ async rebuild(sqlite, embed) {
60
+ const memories = sqlite.listAll();
61
+ const liveIds = new Set();
62
+ let indexed = 0;
63
+ let skipped = 0;
64
+ for (const m of memories) {
65
+ const ok = await this.upsert(m, embed);
66
+ if (ok) {
67
+ liveIds.add(m.id);
68
+ indexed++;
69
+ }
70
+ else {
71
+ skipped++;
72
+ }
73
+ }
74
+ let pruned = 0;
75
+ for (const id of this.backend.listIds("memory")) {
76
+ if (!liveIds.has(id)) {
77
+ if (this.backend.delete(id, "memory"))
78
+ pruned++;
79
+ }
80
+ }
81
+ return { indexed, skipped, pruned };
82
+ }
83
+ /**
84
+ * Rebuild the `session-archive` source: segment each archived session into
85
+ * topic chunks (embedding-similarity), embed each chunk, store the vector
86
+ * + a metadata row for hydration. Requires a db-backed store (construct
87
+ * with `{ db }`, or a backend exposing `dbPath`) so the `session_chunks`
88
+ * metadata table can live alongside the vectors.
89
+ */
90
+ async rebuildSessions(sessions, embed, options) {
91
+ if (!this.dbPath) {
92
+ throw new Error("rebuildSessions requires a db-backed MemoryVectorStore (construct with { db }) so session-chunk metadata can be stored.");
93
+ }
94
+ const chunks = new SessionChunkStore(this.dbPath);
95
+ try {
96
+ return await rebuildSessionVectors(this.backend, chunks, sessions, embed, options);
97
+ }
98
+ finally {
99
+ chunks.close();
100
+ }
101
+ }
102
+ /**
103
+ * Semantic search. Embeds `queryText`, normalizes, and asks the backend
104
+ * for the top-k closest vectors. `options.ids` restricts the candidate
105
+ * set (the `recall` engine uses this to apply a SQLite hard-filter
106
+ * first). `options.source` restricts by corpus.
107
+ */
108
+ async search(queryText, embed, options) {
109
+ const q = normalize(await embed(queryText, "query"));
110
+ return this.backend.search(q, options);
111
+ }
112
+ /** Synchronous search when the caller already holds a query vector. */
113
+ searchVector(queryVector, options) {
114
+ return this.backend.search(normalize(queryVector), options);
115
+ }
116
+ count(source) {
117
+ return this.backend.count(source);
118
+ }
119
+ close() {
120
+ this.backend.close();
121
+ }
122
+ }
123
+ /** Build the text embedded for a memory: name + description + body. */
124
+ function embeddableText(row) {
125
+ return [row.name, row.description, row.body]
126
+ .map((s) => (s ?? "").trim())
127
+ .filter((s) => s.length > 0)
128
+ .join("\n\n")
129
+ .trim();
130
+ }
131
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/vector/store.ts"],"names":[],"mappings":"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"}