@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.
- package/README.md +244 -0
- package/dist/consolidate/index.d.ts +7 -0
- package/dist/consolidate/index.d.ts.map +1 -0
- package/dist/consolidate/index.js +4 -0
- package/dist/consolidate/index.js.map +1 -0
- package/dist/consolidate/proposer.d.ts +43 -0
- package/dist/consolidate/proposer.d.ts.map +1 -0
- package/dist/consolidate/proposer.js +276 -0
- package/dist/consolidate/proposer.js.map +1 -0
- package/dist/consolidate/query.d.ts +32 -0
- package/dist/consolidate/query.d.ts.map +1 -0
- package/dist/consolidate/query.js +40 -0
- package/dist/consolidate/query.js.map +1 -0
- package/dist/consolidate/store.d.ts +21 -0
- package/dist/consolidate/store.d.ts.map +1 -0
- package/dist/consolidate/store.js +91 -0
- package/dist/consolidate/store.js.map +1 -0
- package/dist/consolidate/types.d.ts +68 -0
- package/dist/consolidate/types.d.ts.map +1 -0
- package/dist/consolidate/types.js +2 -0
- package/dist/consolidate/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/frontmatter.d.ts +6 -0
- package/dist/internal/frontmatter.d.ts.map +1 -0
- package/dist/internal/frontmatter.js +38 -0
- package/dist/internal/frontmatter.js.map +1 -0
- package/dist/internal/proactive-curator-helpers.d.ts +171 -0
- package/dist/internal/proactive-curator-helpers.d.ts.map +1 -0
- package/dist/internal/proactive-curator-helpers.js +162 -0
- package/dist/internal/proactive-curator-helpers.js.map +1 -0
- package/dist/mcp/document-tools.d.ts +144 -0
- package/dist/mcp/document-tools.d.ts.map +1 -0
- package/dist/mcp/document-tools.js +319 -0
- package/dist/mcp/document-tools.js.map +1 -0
- package/dist/mcp/index.d.ts +34 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +29 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/install.d.ts +72 -0
- package/dist/mcp/install.d.ts.map +1 -0
- package/dist/mcp/install.js +92 -0
- package/dist/mcp/install.js.map +1 -0
- package/dist/mcp/memory-tools.d.ts +101 -0
- package/dist/mcp/memory-tools.d.ts.map +1 -0
- package/dist/mcp/memory-tools.js +105 -0
- package/dist/mcp/memory-tools.js.map +1 -0
- package/dist/mcp/recall-tool.d.ts +52 -0
- package/dist/mcp/recall-tool.d.ts.map +1 -0
- package/dist/mcp/recall-tool.js +60 -0
- package/dist/mcp/recall-tool.js.map +1 -0
- package/dist/mcp/server.d.ts +32 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +113 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/recall/engine.d.ts +38 -0
- package/dist/recall/engine.d.ts.map +1 -0
- package/dist/recall/engine.js +113 -0
- package/dist/recall/engine.js.map +1 -0
- package/dist/recall/index.d.ts +22 -0
- package/dist/recall/index.d.ts.map +1 -0
- package/dist/recall/index.js +20 -0
- package/dist/recall/index.js.map +1 -0
- package/dist/recall/intent.d.ts +24 -0
- package/dist/recall/intent.d.ts.map +1 -0
- package/dist/recall/intent.js +95 -0
- package/dist/recall/intent.js.map +1 -0
- package/dist/recall/render.d.ts +11 -0
- package/dist/recall/render.d.ts.map +1 -0
- package/dist/recall/render.js +23 -0
- package/dist/recall/render.js.map +1 -0
- package/dist/recall/types.d.ts +72 -0
- package/dist/recall/types.d.ts.map +1 -0
- package/dist/recall/types.js +2 -0
- package/dist/recall/types.js.map +1 -0
- package/dist/sessionArchive/adapters/claude-code.d.ts +3 -0
- package/dist/sessionArchive/adapters/claude-code.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/claude-code.js +276 -0
- package/dist/sessionArchive/adapters/claude-code.js.map +1 -0
- package/dist/sessionArchive/adapters/claude-desktop.d.ts +3 -0
- package/dist/sessionArchive/adapters/claude-desktop.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/claude-desktop.js +234 -0
- package/dist/sessionArchive/adapters/claude-desktop.js.map +1 -0
- package/dist/sessionArchive/adapters/codex.d.ts +3 -0
- package/dist/sessionArchive/adapters/codex.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/codex.js +322 -0
- package/dist/sessionArchive/adapters/codex.js.map +1 -0
- package/dist/sessionArchive/adapters/gemini.d.ts +3 -0
- package/dist/sessionArchive/adapters/gemini.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/gemini.js +248 -0
- package/dist/sessionArchive/adapters/gemini.js.map +1 -0
- package/dist/sessionArchive/adapters/index.d.ts +5 -0
- package/dist/sessionArchive/adapters/index.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/index.js +5 -0
- package/dist/sessionArchive/adapters/index.js.map +1 -0
- package/dist/sessionArchive/index.d.ts +9 -0
- package/dist/sessionArchive/index.d.ts.map +1 -0
- package/dist/sessionArchive/index.js +6 -0
- package/dist/sessionArchive/index.js.map +1 -0
- package/dist/sessionArchive/ingest.d.ts +68 -0
- package/dist/sessionArchive/ingest.d.ts.map +1 -0
- package/dist/sessionArchive/ingest.js +134 -0
- package/dist/sessionArchive/ingest.js.map +1 -0
- package/dist/sessionArchive/normalize.d.ts +15 -0
- package/dist/sessionArchive/normalize.d.ts.map +1 -0
- package/dist/sessionArchive/normalize.js +40 -0
- package/dist/sessionArchive/normalize.js.map +1 -0
- package/dist/sessionArchive/store.d.ts +118 -0
- package/dist/sessionArchive/store.d.ts.map +1 -0
- package/dist/sessionArchive/store.js +491 -0
- package/dist/sessionArchive/store.js.map +1 -0
- package/dist/sessionArchive/types.d.ts +124 -0
- package/dist/sessionArchive/types.d.ts.map +1 -0
- package/dist/sessionArchive/types.js +24 -0
- package/dist/sessionArchive/types.js.map +1 -0
- package/dist/sqlite/drift.d.ts +22 -0
- package/dist/sqlite/drift.d.ts.map +1 -0
- package/dist/sqlite/drift.js +69 -0
- package/dist/sqlite/drift.js.map +1 -0
- package/dist/sqlite/index.d.ts +22 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +4 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/rebuild.d.ts +32 -0
- package/dist/sqlite/rebuild.d.ts.map +1 -0
- package/dist/sqlite/rebuild.js +80 -0
- package/dist/sqlite/rebuild.js.map +1 -0
- package/dist/sqlite/schema.d.ts +15 -0
- package/dist/sqlite/schema.d.ts.map +1 -0
- package/dist/sqlite/schema.js +41 -0
- package/dist/sqlite/schema.js.map +1 -0
- package/dist/sqlite/store.d.ts +49 -0
- package/dist/sqlite/store.d.ts.map +1 -0
- package/dist/sqlite/store.js +179 -0
- package/dist/sqlite/store.js.map +1 -0
- package/dist/sqlite/types.d.ts +57 -0
- package/dist/sqlite/types.d.ts.map +1 -0
- package/dist/sqlite/types.js +2 -0
- package/dist/sqlite/types.js.map +1 -0
- package/dist/vector/backend-brute.d.ts +38 -0
- package/dist/vector/backend-brute.d.ts.map +1 -0
- package/dist/vector/backend-brute.js +112 -0
- package/dist/vector/backend-brute.js.map +1 -0
- package/dist/vector/embedder.d.ts +59 -0
- package/dist/vector/embedder.d.ts.map +1 -0
- package/dist/vector/embedder.js +47 -0
- package/dist/vector/embedder.js.map +1 -0
- package/dist/vector/index.d.ts +24 -0
- package/dist/vector/index.d.ts.map +1 -0
- package/dist/vector/index.js +7 -0
- package/dist/vector/index.js.map +1 -0
- package/dist/vector/math.d.ts +21 -0
- package/dist/vector/math.d.ts.map +1 -0
- package/dist/vector/math.js +34 -0
- package/dist/vector/math.js.map +1 -0
- package/dist/vector/schema.d.ts +14 -0
- package/dist/vector/schema.d.ts.map +1 -0
- package/dist/vector/schema.js +24 -0
- package/dist/vector/schema.js.map +1 -0
- package/dist/vector/segment.d.ts +65 -0
- package/dist/vector/segment.d.ts.map +1 -0
- package/dist/vector/segment.js +72 -0
- package/dist/vector/segment.js.map +1 -0
- package/dist/vector/session.d.ts +90 -0
- package/dist/vector/session.d.ts.map +1 -0
- package/dist/vector/session.js +242 -0
- package/dist/vector/session.js.map +1 -0
- package/dist/vector/store.d.ts +69 -0
- package/dist/vector/store.d.ts.map +1 -0
- package/dist/vector/store.js +131 -0
- package/dist/vector/store.js.map +1 -0
- package/dist/vector/types.d.ts +109 -0
- package/dist/vector/types.d.ts.map +1 -0
- package/dist/vector/types.js +24 -0
- package/dist/vector/types.js.map +1 -0
- package/package.json +96 -0
- package/scripts/mcp-stdio.mjs +143 -0
- package/scripts/rebuild-memory-sqlite.mjs +39 -0
- 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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"}
|