@getplumb/core 0.4.3 → 0.4.5

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 (44) hide show
  1. package/dist/chunker.d.ts +2 -27
  2. package/dist/chunker.d.ts.map +1 -1
  3. package/dist/chunker.js +2 -36
  4. package/dist/chunker.js.map +1 -1
  5. package/dist/context-builder.d.ts +18 -6
  6. package/dist/context-builder.d.ts.map +1 -1
  7. package/dist/context-builder.js +48 -24
  8. package/dist/context-builder.js.map +1 -1
  9. package/dist/embedder.d.ts.map +1 -1
  10. package/dist/embedder.js +6 -0
  11. package/dist/embedder.js.map +1 -1
  12. package/dist/index.d.ts +4 -8
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -3
  15. package/dist/index.js.map +1 -1
  16. package/dist/local-store.d.ts +24 -31
  17. package/dist/local-store.d.ts.map +1 -1
  18. package/dist/local-store.js +81 -331
  19. package/dist/local-store.js.map +1 -1
  20. package/dist/memory-facts-search.d.ts +33 -0
  21. package/dist/memory-facts-search.d.ts.map +1 -0
  22. package/dist/memory-facts-search.js +143 -0
  23. package/dist/memory-facts-search.js.map +1 -0
  24. package/dist/read-path.d.ts +22 -13
  25. package/dist/read-path.d.ts.map +1 -1
  26. package/dist/read-path.js +19 -15
  27. package/dist/read-path.js.map +1 -1
  28. package/dist/schema.d.ts +13 -8
  29. package/dist/schema.d.ts.map +1 -1
  30. package/dist/schema.js +63 -83
  31. package/dist/schema.js.map +1 -1
  32. package/dist/scorer.d.ts +12 -10
  33. package/dist/scorer.d.ts.map +1 -1
  34. package/dist/scorer.js +13 -10
  35. package/dist/scorer.js.map +1 -1
  36. package/dist/store.d.ts +1 -6
  37. package/dist/store.d.ts.map +1 -1
  38. package/dist/types.d.ts +27 -1
  39. package/dist/types.d.ts.map +1 -1
  40. package/dist/wasm-db.d.ts +17 -20
  41. package/dist/wasm-db.d.ts.map +1 -1
  42. package/dist/wasm-db.js +51 -56
  43. package/dist/wasm-db.js.map +1 -1
  44. package/package.json +5 -6
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Memory facts hybrid search (Layer 2 retrieval).
3
+ *
4
+ * Pipeline (same as raw-log-search, but for memory_facts table):
5
+ * 1. BM25 keyword search over memory_facts content
6
+ * 2. KNN vector search via JS cosine similarity
7
+ * 3. Reciprocal Rank Fusion (RRF, k=60) merges both ranked lists
8
+ * 4. Apply MEMORY_FACT_BOOST (2.0×) to RRF scores
9
+ * 5. Return top-k by boosted score
10
+ *
11
+ * Search is cross-session: no session filter is applied.
12
+ * The caller (LocalStore) passes its internal db handle — no separate DB connection.
13
+ */
14
+ import { Bm25 } from './bm25.js';
15
+ import { embedQuery } from './embedder.js';
16
+ import { knnSearch, deserializeEmbedding } from './vector-search.js';
17
+ import { scoreMemoryFact } from './scorer.js';
18
+ // RRF constant (standard k=60; higher = less weight on top-1 rank).
19
+ const RRF_K = 60;
20
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
21
+ /**
22
+ * Merge two ranked lists via Reciprocal Rank Fusion.
23
+ * Each list entry is [id, score] ordered by descending relevance (rank 1 = index 0).
24
+ * Returns a map of id → rrf_score.
25
+ */
26
+ function rrf(vecRanked, bm25Ranked) {
27
+ const scores = new Map();
28
+ for (let rank = 0; rank < vecRanked.length; rank++) {
29
+ const id = vecRanked[rank]?.[0];
30
+ if (id === undefined)
31
+ continue;
32
+ scores.set(id, (scores.get(id) ?? 0) + 1 / (RRF_K + rank + 1));
33
+ }
34
+ for (let rank = 0; rank < bm25Ranked.length; rank++) {
35
+ const id = bm25Ranked[rank]?.[0];
36
+ if (id === undefined)
37
+ continue;
38
+ scores.set(id, (scores.get(id) ?? 0) + 1 / (RRF_K + rank + 1));
39
+ }
40
+ return scores;
41
+ }
42
+ // ─── Public API ───────────────────────────────────────────────────────────────
43
+ /**
44
+ * Hybrid search over memory_facts.
45
+ *
46
+ * @param db The WASM SQLite Database instance
47
+ * @param userId Scopes the search to this user's data
48
+ * @param query Natural language query string
49
+ * @param limit Number of results to return (default 10)
50
+ */
51
+ export async function searchMemoryFacts(db, userId, query, limit = 10) {
52
+ // ── 1. Fetch all memory_facts rows (non-deleted, with embeddings) ────────
53
+ const stmt = db.prepare(`SELECT id, content, source_session_id, source_session_label, tags, created_at, vec_rowid
54
+ FROM memory_facts
55
+ WHERE user_id = ? AND deleted_at IS NULL AND embed_status = 'done'
56
+ ORDER BY created_at DESC`);
57
+ stmt.bind([userId]);
58
+ const allRows = [];
59
+ while (stmt.step()) {
60
+ allRows.push(stmt.get({}));
61
+ }
62
+ stmt.finalize();
63
+ if (allRows.length === 0)
64
+ return [];
65
+ const idToRow = new Map(allRows.map((r) => [r.id, r]));
66
+ // ── 2. BM25 search ───────────────────────────────────────────────────────
67
+ const corpus = allRows.map((r) => r.content);
68
+ const bm25 = new Bm25(corpus);
69
+ const bm25RawScores = bm25.scores(query);
70
+ const bm25Ranked = allRows
71
+ .map((r, i) => [r.id, bm25RawScores[i] ?? 0])
72
+ .sort((a, b) => b[1] - a[1]);
73
+ // ── 3. Vector search via JS cosine similarity ───────────────────────────
74
+ const queryVec = await embedQuery(query);
75
+ // Fetch all embeddings from vec_raw_log (memory_facts use same table)
76
+ const vecRowids = allRows
77
+ .filter((r) => r.vec_rowid !== null)
78
+ .map((r) => r.vec_rowid);
79
+ if (vecRowids.length === 0) {
80
+ // No embeddings available — fall back to BM25 only
81
+ const bm25Only = bm25Ranked
82
+ .slice(0, limit)
83
+ .map(([id, score]) => {
84
+ const row = idToRow.get(id);
85
+ const boostedScore = scoreMemoryFact(score);
86
+ return {
87
+ content: row.content,
88
+ source_session_id: row.source_session_id,
89
+ source_session_label: row.source_session_label,
90
+ created_at: row.created_at,
91
+ tags: row.tags ? JSON.parse(row.tags) : null,
92
+ final_score: boostedScore,
93
+ };
94
+ });
95
+ return bm25Only;
96
+ }
97
+ // Fetch embeddings for these vec_rowids
98
+ const placeholders = vecRowids.map(() => '?').join(',');
99
+ const vecStmt = db.prepare(`SELECT id, embedding FROM vec_raw_log WHERE id IN (${placeholders})`);
100
+ vecStmt.bind(vecRowids);
101
+ const vecCorpus = [];
102
+ while (vecStmt.step()) {
103
+ const row = vecStmt.get({});
104
+ vecCorpus.push({
105
+ id: row.id,
106
+ embedding: deserializeEmbedding(row.embedding),
107
+ });
108
+ }
109
+ vecStmt.finalize();
110
+ // Perform KNN search
111
+ const vecFetchLimit = Math.min(allRows.length, Math.max(limit * 3, 50));
112
+ const vecResults = knnSearch(queryVec, vecCorpus, vecFetchLimit);
113
+ // Map vec_raw_log ids back to memory_facts ids
114
+ const vecRanked = [];
115
+ for (const vecResult of vecResults) {
116
+ const factRow = allRows.find((r) => r.vec_rowid === vecResult.id);
117
+ if (factRow !== undefined) {
118
+ vecRanked.push([factRow.id, 1 - vecResult.distance]);
119
+ }
120
+ }
121
+ // ── 4. RRF merge ────────────────────────────────────────────────────────
122
+ const rrfScores = rrf(vecRanked, bm25Ranked);
123
+ // ── 5. Apply MEMORY_FACT_BOOST ──────────────────────────────────────────
124
+ const boostedScores = [];
125
+ for (const [id, rrfScore] of rrfScores) {
126
+ const boosted = scoreMemoryFact(rrfScore);
127
+ boostedScores.push([id, boosted]);
128
+ }
129
+ boostedScores.sort((a, b) => b[1] - a[1]);
130
+ // ── 6. Build output ─────────────────────────────────────────────────────
131
+ return boostedScores.slice(0, limit).map(([id, finalScore]) => {
132
+ const row = idToRow.get(id);
133
+ return {
134
+ content: row.content,
135
+ source_session_id: row.source_session_id,
136
+ source_session_label: row.source_session_label,
137
+ created_at: row.created_at,
138
+ tags: row.tags ? JSON.parse(row.tags) : null,
139
+ final_score: finalScore,
140
+ };
141
+ });
142
+ }
143
+ //# sourceMappingURL=memory-facts-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-facts-search.js","sourceRoot":"","sources":["../src/memory-facts-search.ts"],"names":[],"mappings":"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"}
@@ -1,39 +1,48 @@
1
1
  /**
2
- * Read path — Layer 1 retrieval (raw log search only).
2
+ * Read path — Layer 2 (memory facts) retrieval.
3
3
  *
4
4
  * buildMemoryContext() is called before every agent response. It queries
5
- * the raw_log table with hybrid search and returns a MemoryContext ready
5
+ * memory_facts, applies score boosting, and returns a MemoryContext ready
6
6
  * for formatContextBlock().
7
7
  *
8
8
  * Search is always cross-session — no session filter is applied.
9
9
  */
10
- import type { RawLogSearchResult } from './local-store.js';
11
- export interface RawChunk {
12
- readonly chunkText: string;
13
- readonly sessionId: string;
14
- readonly sessionLabel: string | null;
10
+ import type { MemoryFactSearchResult } from './local-store.js';
11
+ /** A memory fact chunk with metadata and boosted score. */
12
+ export interface MemoryFactChunk {
13
+ readonly content: string;
14
+ readonly sourceSessionId: string;
15
+ readonly sourceSessionLabel: string | null;
15
16
  readonly timestamp: Date;
16
- /** Final score from hybrid search (RRF × decay × reranker). */
17
+ readonly tags: readonly string[] | null;
18
+ /** Final score from hybrid search with MEMORY_FACT_BOOST applied. */
17
19
  readonly score: number;
18
20
  }
21
+ /**
22
+ * Memory context returned by buildMemoryContext().
23
+ *
24
+ * relatedMemories holds curated memory facts (high-signal).
25
+ */
19
26
  export interface MemoryContext {
20
- readonly relatedConversations: RawChunk[];
27
+ readonly relatedMemories: MemoryFactChunk[];
21
28
  }
22
29
  export interface ReadPathOptions {
23
- /** Max raw log chunks returned. Default: 3. */
24
- maxRawChunks?: number;
30
+ /** Max memory facts returned. Default: 5. */
31
+ maxMemoryFacts?: number;
25
32
  }
26
33
  /**
27
34
  * Minimal store interface required by the read path.
28
35
  * LocalStore satisfies this; tests can pass a mock.
29
36
  */
30
37
  export interface ReadPathStore {
31
- searchRawLog(query: string, limit?: number): Promise<readonly RawLogSearchResult[]>;
38
+ /** Layer 2: hybrid search over curated memory facts. */
39
+ searchMemoryFacts(query: string, limit?: number): Promise<readonly MemoryFactSearchResult[]>;
32
40
  }
33
41
  /**
34
42
  * Build a structured MemoryContext for a given query.
35
43
  *
36
- * Queries Layer 1 (raw log hybrid search) and returns ranked chunks.
44
+ * Queries Layer 2 (memory facts hybrid search).
45
+ * Memory facts get MEMORY_FACT_BOOST (2.0×) applied to their scores.
37
46
  *
38
47
  * @param query Natural language query (the incoming user message or context).
39
48
  * @param store Any store implementing ReadPathStore (typically LocalStore).
@@ -1 +1 @@
1
- {"version":3,"file":"read-path.d.ts","sourceRoot":"","sources":["../src/read-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAI3D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,oBAAoB,EAAE,QAAQ,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;CACrF;AAID;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,aAAa,EACpB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,aAAa,CAAC,CAmBxB"}
1
+ {"version":3,"file":"read-path.d.ts","sourceRoot":"","sources":["../src/read-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAI/D,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IACxC,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,eAAe,EAAE,eAAe,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,sBAAsB,EAAE,CAAC,CAAC;CAC9F;AAID;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,aAAa,EACpB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,aAAa,CAAC,CAuBxB"}
package/dist/read-path.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Read path — Layer 1 retrieval (raw log search only).
2
+ * Read path — Layer 2 (memory facts) retrieval.
3
3
  *
4
4
  * buildMemoryContext() is called before every agent response. It queries
5
- * the raw_log table with hybrid search and returns a MemoryContext ready
5
+ * memory_facts, applies score boosting, and returns a MemoryContext ready
6
6
  * for formatContextBlock().
7
7
  *
8
8
  * Search is always cross-session — no session filter is applied.
@@ -11,27 +11,31 @@
11
11
  /**
12
12
  * Build a structured MemoryContext for a given query.
13
13
  *
14
- * Queries Layer 1 (raw log hybrid search) and returns ranked chunks.
14
+ * Queries Layer 2 (memory facts hybrid search).
15
+ * Memory facts get MEMORY_FACT_BOOST (2.0×) applied to their scores.
15
16
  *
16
17
  * @param query Natural language query (the incoming user message or context).
17
18
  * @param store Any store implementing ReadPathStore (typically LocalStore).
18
19
  * @param options Optional limits.
19
20
  */
20
21
  export async function buildMemoryContext(query, store, options) {
21
- const maxRawChunks = options?.maxRawChunks ?? 3;
22
- const rawCandidateLimit = maxRawChunks * 3;
23
- // ── Query Layer 1 (raw log) ───────────────────────────────────────────────
24
- const rawLogResults = await store.searchRawLog(query, rawCandidateLimit);
25
- // ── Build raw chunks (already ranked by hybrid search score) ──────────────
26
- const relatedConversations = rawLogResults
27
- .slice(0, maxRawChunks)
22
+ const maxMemoryFacts = options?.maxMemoryFacts ?? 5;
23
+ const memoryCandidateLimit = maxMemoryFacts * 2;
24
+ // ── Query Layer 2 (memory facts) ─────────────────────────────────────────
25
+ const memoryFactResults = await store.searchMemoryFacts(query, memoryCandidateLimit);
26
+ // ── Build memory fact chunks (scores already have MEMORY_FACT_BOOST applied)
27
+ const relatedMemories = memoryFactResults
28
+ .slice(0, maxMemoryFacts)
28
29
  .map((r) => ({
29
- chunkText: r.chunk_text,
30
- sessionId: r.session_id,
31
- sessionLabel: r.session_label,
32
- timestamp: new Date(r.timestamp),
30
+ content: r.content,
31
+ sourceSessionId: r.source_session_id,
32
+ sourceSessionLabel: r.source_session_label,
33
+ timestamp: new Date(r.created_at),
34
+ tags: r.tags,
33
35
  score: r.final_score,
34
36
  }));
35
- return { relatedConversations };
37
+ return {
38
+ relatedMemories,
39
+ };
36
40
  }
37
41
  //# sourceMappingURL=read-path.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"read-path.js","sourceRoot":"","sources":["../src/read-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgCH,iFAAiF;AAEjF;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAa,EACb,KAAoB,EACpB,OAAyB;IAEzB,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,CAAC,CAAC;IAChD,MAAM,iBAAiB,GAAG,YAAY,GAAG,CAAC,CAAC;IAE3C,6EAA6E;IAC7E,MAAM,aAAa,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;IAEzE,6EAA6E;IAC7E,MAAM,oBAAoB,GAAe,aAAa;SACnD,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC;SACtB,GAAG,CAAC,CAAC,CAAqB,EAAE,EAAE,CAAC,CAAC;QAC/B,SAAS,EAAE,CAAC,CAAC,UAAU;QACvB,SAAS,EAAE,CAAC,CAAC,UAAU;QACvB,YAAY,EAAE,CAAC,CAAC,aAAa;QAC7B,SAAS,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QAChC,KAAK,EAAE,CAAC,CAAC,WAAW;KACrB,CAAC,CAAC,CAAC;IAEN,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"read-path.js","sourceRoot":"","sources":["../src/read-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAwCH,iFAAiF;AAEjF;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAa,EACb,KAAoB,EACpB,OAAyB;IAEzB,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,IAAI,CAAC,CAAC;IAEpD,MAAM,oBAAoB,GAAG,cAAc,GAAG,CAAC,CAAC;IAEhD,4EAA4E;IAC5E,MAAM,iBAAiB,GAAG,MAAM,KAAK,CAAC,iBAAiB,CAAC,KAAK,EAAE,oBAAoB,CAAC,CAAC;IAErF,gFAAgF;IAChF,MAAM,eAAe,GAAsB,iBAAiB;SACzD,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC;SACxB,GAAG,CAAC,CAAC,CAAyB,EAAE,EAAE,CAAC,CAAC;QACnC,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,eAAe,EAAE,CAAC,CAAC,iBAAiB;QACpC,kBAAkB,EAAE,CAAC,CAAC,oBAAoB;QAC1C,SAAS,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC;QACjC,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,KAAK,EAAE,CAAC,CAAC,WAAW;KACrB,CAAC,CAAC,CAAC;IAEN,OAAO;QACL,eAAe;KAChB,CAAC;AACJ,CAAC"}
package/dist/schema.d.ts CHANGED
@@ -1,20 +1,25 @@
1
1
  /**
2
2
  * SQLite schema for Plumb LocalStore.
3
3
  *
4
- * Single table per user (scoped by user_id column):
5
- * - raw_log: Lossless conversation chunks with vector embeddings
4
+ * Tables:
5
+ * - memory_facts: Curated facts written by the agent via plumb_remember
6
+ * - vec_raw_log: Embedding vectors for memory_facts (keyed by vec_rowid)
7
+ * - nudge_log: One-time upgrade prompt tracking
6
8
  *
7
9
  * Design principles:
8
10
  * - Cross-session by design: session info stored as metadata, not as a boundary
9
- * - Indexes on high-cardinality query axes: user_id, session_id, timestamp
11
+ * - Indexes on high-cardinality query axes: user_id, embed_status
10
12
  */
11
- export declare const CREATE_RAW_LOG_TABLE = "\n CREATE TABLE IF NOT EXISTS raw_log (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL,\n session_id TEXT NOT NULL,\n session_label TEXT,\n user_message TEXT NOT NULL,\n agent_response TEXT NOT NULL,\n timestamp TEXT NOT NULL,\n source TEXT NOT NULL,\n chunk_text TEXT NOT NULL,\n chunk_index INTEGER NOT NULL,\n vec_rowid INTEGER,\n content_hash TEXT,\n embed_status TEXT NOT NULL DEFAULT 'pending',\n embed_error TEXT,\n embed_model TEXT,\n parent_id TEXT REFERENCES raw_log(id),\n UNIQUE(user_id, content_hash)\n )\n";
12
- export declare const CREATE_RAW_LOG_INDEXES: string[];
13
13
  /**
14
- * Embeddings table for vector search over raw_log chunks.
15
- * Stores embeddings as JSON arrays (WASM-compatible, no native extension needed).
14
+ * Memory facts table for curated, high-signal facts written by Terra.
15
+ * Each fact is a short, dense piece of information stored as a single chunk.
16
+ *
17
+ * confidence (0–1) and decay_rate ('slow'|'medium'|'fast') drive scoring.
18
+ * source_session_label is optional human-readable session name for provenance.
19
+ * vec_rowid links to vec_raw_log for vector similarity search.
16
20
  */
17
- export declare const CREATE_VEC_RAW_LOG = "\n CREATE TABLE IF NOT EXISTS vec_raw_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n embedding TEXT NOT NULL\n )\n";
21
+ export declare const CREATE_MEMORY_FACTS_TABLE = "\n CREATE TABLE IF NOT EXISTS memory_facts (\n id TEXT PRIMARY KEY,\n user_id TEXT NOT NULL,\n content TEXT NOT NULL,\n confidence REAL NOT NULL DEFAULT 0.9,\n decay_rate TEXT NOT NULL DEFAULT 'slow',\n source_session_id TEXT NOT NULL,\n source_session_label TEXT,\n tags TEXT,\n created_at TEXT NOT NULL,\n deleted_at TEXT,\n embed_status TEXT NOT NULL DEFAULT 'pending',\n embed_error TEXT,\n embed_model TEXT,\n vec_rowid INTEGER\n )\n";
22
+ export declare const CREATE_MEMORY_FACTS_INDEXES: string[];
18
23
  /**
19
24
  * Nudge log table for tracking one-time upgrade prompts.
20
25
  * Each trigger type fires exactly once per install.
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,eAAO,MAAM,oBAAoB,onBAoBhC,CAAC;AAEF,eAAO,MAAM,sBAAsB,UAKlC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,gIAK9B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,8JAMlC,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAAE,EAAE,OAAO,cAAc,EAAE,MAAM,GAAG,IAAI,CAoEnE"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,mnBAiBrC,CAAC;AAEF,eAAO,MAAM,2BAA2B,UAGvC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,8JAMlC,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAAE,EAAE,OAAO,cAAc,EAAE,MAAM,GAAG,IAAI,CA6CnE"}
package/dist/schema.js CHANGED
@@ -1,50 +1,45 @@
1
1
  /**
2
2
  * SQLite schema for Plumb LocalStore.
3
3
  *
4
- * Single table per user (scoped by user_id column):
5
- * - raw_log: Lossless conversation chunks with vector embeddings
4
+ * Tables:
5
+ * - memory_facts: Curated facts written by the agent via plumb_remember
6
+ * - vec_raw_log: Embedding vectors for memory_facts (keyed by vec_rowid)
7
+ * - nudge_log: One-time upgrade prompt tracking
6
8
  *
7
9
  * Design principles:
8
10
  * - Cross-session by design: session info stored as metadata, not as a boundary
9
- * - Indexes on high-cardinality query axes: user_id, session_id, timestamp
11
+ * - Indexes on high-cardinality query axes: user_id, embed_status
10
12
  */
11
- export const CREATE_RAW_LOG_TABLE = `
12
- CREATE TABLE IF NOT EXISTS raw_log (
13
- id TEXT PRIMARY KEY,
14
- user_id TEXT NOT NULL,
15
- session_id TEXT NOT NULL,
16
- session_label TEXT,
17
- user_message TEXT NOT NULL,
18
- agent_response TEXT NOT NULL,
19
- timestamp TEXT NOT NULL,
20
- source TEXT NOT NULL,
21
- chunk_text TEXT NOT NULL,
22
- chunk_index INTEGER NOT NULL,
23
- vec_rowid INTEGER,
24
- content_hash TEXT,
25
- embed_status TEXT NOT NULL DEFAULT 'pending',
26
- embed_error TEXT,
27
- embed_model TEXT,
28
- parent_id TEXT REFERENCES raw_log(id),
29
- UNIQUE(user_id, content_hash)
30
- )
31
- `;
32
- export const CREATE_RAW_LOG_INDEXES = [
33
- `CREATE INDEX IF NOT EXISTS idx_raw_log_user_id ON raw_log (user_id)`,
34
- `CREATE INDEX IF NOT EXISTS idx_raw_log_session_id ON raw_log (session_id)`,
35
- `CREATE INDEX IF NOT EXISTS idx_raw_log_timestamp ON raw_log (timestamp)`,
36
- `CREATE INDEX IF NOT EXISTS idx_raw_log_parent_id ON raw_log (parent_id)`,
37
- ];
38
13
  /**
39
- * Embeddings table for vector search over raw_log chunks.
40
- * Stores embeddings as JSON arrays (WASM-compatible, no native extension needed).
14
+ * Memory facts table for curated, high-signal facts written by Terra.
15
+ * Each fact is a short, dense piece of information stored as a single chunk.
16
+ *
17
+ * confidence (0–1) and decay_rate ('slow'|'medium'|'fast') drive scoring.
18
+ * source_session_label is optional human-readable session name for provenance.
19
+ * vec_rowid links to vec_raw_log for vector similarity search.
41
20
  */
42
- export const CREATE_VEC_RAW_LOG = `
43
- CREATE TABLE IF NOT EXISTS vec_raw_log (
44
- id INTEGER PRIMARY KEY AUTOINCREMENT,
45
- embedding TEXT NOT NULL
21
+ export const CREATE_MEMORY_FACTS_TABLE = `
22
+ CREATE TABLE IF NOT EXISTS memory_facts (
23
+ id TEXT PRIMARY KEY,
24
+ user_id TEXT NOT NULL,
25
+ content TEXT NOT NULL,
26
+ confidence REAL NOT NULL DEFAULT 0.9,
27
+ decay_rate TEXT NOT NULL DEFAULT 'slow',
28
+ source_session_id TEXT NOT NULL,
29
+ source_session_label TEXT,
30
+ tags TEXT,
31
+ created_at TEXT NOT NULL,
32
+ deleted_at TEXT,
33
+ embed_status TEXT NOT NULL DEFAULT 'pending',
34
+ embed_error TEXT,
35
+ embed_model TEXT,
36
+ vec_rowid INTEGER
46
37
  )
47
38
  `;
39
+ export const CREATE_MEMORY_FACTS_INDEXES = [
40
+ `CREATE INDEX IF NOT EXISTS idx_memory_facts_user_id ON memory_facts (user_id)`,
41
+ `CREATE INDEX IF NOT EXISTS idx_memory_facts_embed_status ON memory_facts (embed_status)`,
42
+ ];
48
43
  /**
49
44
  * Nudge log table for tracking one-time upgrade prompts.
50
45
  * Each trigger type fires exactly once per install.
@@ -57,24 +52,6 @@ export const CREATE_NUDGE_LOG_TABLE = `
57
52
  )
58
53
  `;
59
54
  export function applySchema(db) {
60
- db.exec(CREATE_RAW_LOG_TABLE);
61
- for (const idx of CREATE_RAW_LOG_INDEXES) {
62
- db.exec(idx);
63
- }
64
- db.exec(CREATE_VEC_RAW_LOG);
65
- // Conditional migration: add content_hash column to raw_log if it doesn't exist yet.
66
- const rawLogColumns = db.exec({
67
- sql: 'PRAGMA table_info(raw_log)',
68
- rowMode: 'object',
69
- returnValue: 'resultRows',
70
- });
71
- const hasContentHash = rawLogColumns.some((c) => c.name === 'content_hash');
72
- if (!hasContentHash) {
73
- db.exec('ALTER TABLE raw_log ADD COLUMN content_hash TEXT');
74
- // Create unique constraint on (user_id, content_hash).
75
- // SQLite UNIQUE constraints ignore NULL values, so existing rows with NULL won't conflict.
76
- db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_raw_log_content_hash ON raw_log(user_id, content_hash)');
77
- }
78
55
  // Conditional migration: create nudge_log table if it doesn't exist yet.
79
56
  const tables = db.exec({
80
57
  sql: `SELECT name FROM sqlite_master WHERE type='table' AND name='nudge_log'`,
@@ -84,39 +61,42 @@ export function applySchema(db) {
84
61
  if (tables.length === 0) {
85
62
  db.exec(CREATE_NUDGE_LOG_TABLE);
86
63
  }
87
- // T-079: Conditional migration for processing state machine columns (embed only).
88
- const rawLogColumns2 = db.exec({
89
- sql: 'PRAGMA table_info(raw_log)',
64
+ // T-118: Create memory_facts table if it doesn't exist (for curated facts from agent).
65
+ const memoryFactsTables = db.exec({
66
+ sql: `SELECT name FROM sqlite_master WHERE type='table' AND name='memory_facts'`,
90
67
  rowMode: 'object',
91
68
  returnValue: 'resultRows',
92
69
  });
93
- // Add embed columns to raw_log if they don't exist.
94
- const hasEmbedStatus = rawLogColumns2.some((c) => c.name === 'embed_status');
95
- if (!hasEmbedStatus) {
96
- // Add embed columns with defaults.
97
- db.exec('ALTER TABLE raw_log ADD COLUMN embed_status TEXT NOT NULL DEFAULT \'pending\'');
98
- db.exec('ALTER TABLE raw_log ADD COLUMN embed_error TEXT');
99
- db.exec('ALTER TABLE raw_log ADD COLUMN embed_model TEXT');
100
- // Backfill embed_status for existing rows based on vec_rowid.
101
- // Rows with vec_rowid already set -> embed_status='done', embed_model='Xenova/bge-small-en-v1.5'
102
- db.exec(`
103
- UPDATE raw_log
104
- SET embed_status = 'done', embed_model = 'Xenova/bge-small-en-v1.5'
105
- WHERE vec_rowid IS NOT NULL
106
- `);
107
- // Rows with vec_rowid=NULL remain embed_status='pending' (from DEFAULT).
70
+ if (memoryFactsTables.length === 0) {
71
+ db.exec(CREATE_MEMORY_FACTS_TABLE);
72
+ for (const idx of CREATE_MEMORY_FACTS_INDEXES) {
73
+ db.exec(idx);
74
+ }
108
75
  }
109
- // T-108: Add parent_id column to raw_log if it doesn't exist (for parent-child chunking).
110
- const rawLogColumns3 = db.exec({
111
- sql: 'PRAGMA table_info(raw_log)',
112
- rowMode: 'object',
113
- returnValue: 'resultRows',
114
- });
115
- const hasParentId = rawLogColumns3.some((c) => c.name === 'parent_id');
116
- if (!hasParentId) {
117
- db.exec('ALTER TABLE raw_log ADD COLUMN parent_id TEXT REFERENCES raw_log(id)');
118
- db.exec('CREATE INDEX IF NOT EXISTS idx_raw_log_parent_id ON raw_log (parent_id)');
119
- // Existing rows have parent_id=NULL (they are parent-only rows, treated as searchable fallback).
76
+ else {
77
+ // Conditional migration: add structured fact columns to existing memory_facts table.
78
+ const memFactColumns = db.exec({
79
+ sql: 'PRAGMA table_info(memory_facts)',
80
+ rowMode: 'object',
81
+ returnValue: 'resultRows',
82
+ });
83
+ const memFactColNames = new Set(memFactColumns.map((c) => c.name));
84
+ if (!memFactColNames.has('confidence'))
85
+ db.exec(`ALTER TABLE memory_facts ADD COLUMN confidence REAL NOT NULL DEFAULT 0.9`);
86
+ if (!memFactColNames.has('decay_rate'))
87
+ db.exec(`ALTER TABLE memory_facts ADD COLUMN decay_rate TEXT NOT NULL DEFAULT 'slow'`);
88
+ if (!memFactColNames.has('source_session_label'))
89
+ db.exec(`ALTER TABLE memory_facts ADD COLUMN source_session_label TEXT`);
90
+ if (!memFactColNames.has('deleted_at'))
91
+ db.exec(`ALTER TABLE memory_facts ADD COLUMN deleted_at TEXT`);
120
92
  }
93
+ // T-128: Share vec_raw_log for memory_facts embeddings (table may exist from old raw_log usage)
94
+ // Create only if it doesn't exist - this allows existing DBs to continue working
95
+ db.exec(`
96
+ CREATE TABLE IF NOT EXISTS vec_raw_log (
97
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
98
+ embedding TEXT NOT NULL
99
+ )
100
+ `);
121
101
  }
122
102
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG;;;;;;;;;;;;;;;;;CAiBxC,CAAC;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,+EAA+E;IAC/E,yFAAyF;CAC1F,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;CAMrC,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,EAAiC;IAC3D,yEAAyE;IACzE,MAAM,MAAM,GAAG,EAAE,CAAC,IAAI,CAAC;QACrB,GAAG,EAAE,wEAAwE;QAC7E,OAAO,EAAE,QAAQ;QACjB,WAAW,EAAE,YAAY;KAC1B,CAA4B,CAAC;IAC9B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAClC,CAAC;IAED,uFAAuF;IACvF,MAAM,iBAAiB,GAAG,EAAE,CAAC,IAAI,CAAC;QAChC,GAAG,EAAE,2EAA2E;QAChF,OAAO,EAAE,QAAQ;QACjB,WAAW,EAAE,YAAY;KAC1B,CAA4B,CAAC;IAC9B,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,EAAE,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACnC,KAAK,MAAM,GAAG,IAAI,2BAA2B,EAAE,CAAC;YAC9C,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,CAAC;IACH,CAAC;SAAM,CAAC;QACN,qFAAqF;QACrF,MAAM,cAAc,GAAG,EAAE,CAAC,IAAI,CAAC;YAC7B,GAAG,EAAE,iCAAiC;YACtC,OAAO,EAAE,QAAQ;YACjB,WAAW,EAAE,YAAY;SAC1B,CAA4B,CAAC;QAC9B,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAEnE,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,YAAY,CAAC;YAAE,EAAE,CAAC,IAAI,CAAC,0EAA0E,CAAC,CAAC;QAC5H,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,YAAY,CAAC;YAAE,EAAE,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;QAC/H,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,sBAAsB,CAAC;YAAE,EAAE,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;QAC3H,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,YAAY,CAAC;YAAE,EAAE,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;IACzG,CAAC;IAED,gGAAgG;IAChG,iFAAiF;IACjF,EAAE,CAAC,IAAI,CAAC;;;;;GAKP,CAAC,CAAC;AACL,CAAC"}
package/dist/scorer.d.ts CHANGED
@@ -1,21 +1,23 @@
1
+ /** Memory fact score boost multiplier (applied after RRF fusion). */
2
+ export declare const MEMORY_FACT_BOOST = 2;
3
+ /** Memory fact minimum score threshold (after boost) for fallback logic.
4
+ * Calibrated for RRF hybrid scores: top ~25% of boosted fact scores qualify.
5
+ */
6
+ export declare const MEMORY_FACT_MIN_SCORE = 0.054;
1
7
  export interface ScoreResult {
2
8
  readonly score: number;
3
9
  readonly isCold: boolean;
4
10
  }
5
- /**
6
- * Minimal shape required by scoreRawLog().
7
- * raw_log rows carry a timestamp; other fields are not needed for scoring.
8
- */
9
- export interface RawLogChunk {
10
- readonly timestamp: Date;
11
- }
12
11
  /**
13
12
  * Computes the exponential decay multiplier: e^(-lambda × age_in_days).
14
13
  */
15
14
  export declare function computeDecay(lambda: number, ageInDays: number): number;
16
15
  /**
17
- * Scores a raw log chunk using medium decay (lambda = 0.012).
18
- * Raw chunks have no confidence field; decay is applied to a base of 1.0.
16
+ * Scores a memory fact by applying MEMORY_FACT_BOOST to its hybrid search score.
17
+ * Memory facts are high-signal curated content, so they get a 2.0× boost.
18
+ *
19
+ * @param hybridScore The RRF-fused score from BM25 + vector search.
20
+ * @returns The boosted score.
19
21
  */
20
- export declare function scoreRawLog(chunk: RawLogChunk, now?: Date): ScoreResult;
22
+ export declare function scoreMemoryFact(hybridScore: number): number;
21
23
  //# sourceMappingURL=scorer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scorer.d.ts","sourceRoot":"","sources":["../src/scorer.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,WAAW,EAClB,GAAG,GAAE,IAAiB,GACrB,WAAW,CAKb"}
1
+ {"version":3,"file":"scorer.d.ts","sourceRoot":"","sources":["../src/scorer.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,IAAM,CAAC;AAErC;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAE3C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3D"}
package/dist/scorer.js CHANGED
@@ -1,7 +1,9 @@
1
- /** Lambda applied to raw log chunks (medium decay). */
2
- const RAW_LOG_LAMBDA = 0.012;
3
- /** Cold score threshold chunks below this are flagged. */
4
- const COLD_THRESHOLD = 0.01;
1
+ /** Memory fact score boost multiplier (applied after RRF fusion). */
2
+ export const MEMORY_FACT_BOOST = 2.0;
3
+ /** Memory fact minimum score threshold (after boost) for fallback logic.
4
+ * Calibrated for RRF hybrid scores: top ~25% of boosted fact scores qualify.
5
+ */
6
+ export const MEMORY_FACT_MIN_SCORE = 0.054;
5
7
  /**
6
8
  * Computes the exponential decay multiplier: e^(-lambda × age_in_days).
7
9
  */
@@ -9,12 +11,13 @@ export function computeDecay(lambda, ageInDays) {
9
11
  return Math.exp(-lambda * ageInDays);
10
12
  }
11
13
  /**
12
- * Scores a raw log chunk using medium decay (lambda = 0.012).
13
- * Raw chunks have no confidence field; decay is applied to a base of 1.0.
14
+ * Scores a memory fact by applying MEMORY_FACT_BOOST to its hybrid search score.
15
+ * Memory facts are high-signal curated content, so they get a 2.0× boost.
16
+ *
17
+ * @param hybridScore The RRF-fused score from BM25 + vector search.
18
+ * @returns The boosted score.
14
19
  */
15
- export function scoreRawLog(chunk, now = new Date()) {
16
- const ageInDays = (now.getTime() - chunk.timestamp.getTime()) / (1_000 * 60 * 60 * 24);
17
- const score = computeDecay(RAW_LOG_LAMBDA, ageInDays);
18
- return { score, isCold: score < COLD_THRESHOLD };
20
+ export function scoreMemoryFact(hybridScore) {
21
+ return hybridScore * MEMORY_FACT_BOOST;
19
22
  }
20
23
  //# sourceMappingURL=scorer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scorer.js","sourceRoot":"","sources":["../src/scorer.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,MAAM,cAAc,GAAG,KAAK,CAAC;AAE7B,4DAA4D;AAC5D,MAAM,cAAc,GAAG,IAAI,CAAC;AAe5B;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,SAAiB;IAC5D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,KAAkB,EAClB,MAAY,IAAI,IAAI,EAAE;IAEtB,MAAM,SAAS,GACb,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG,YAAY,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;IACtD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,GAAG,cAAc,EAAE,CAAC;AACnD,CAAC"}
1
+ {"version":3,"file":"scorer.js","sourceRoot":"","sources":["../src/scorer.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAErC;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAO3C;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,SAAiB;IAC5D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB;IACjD,OAAO,WAAW,GAAG,iBAAiB,CAAC;AACzC,CAAC"}
package/dist/store.d.ts CHANGED
@@ -1,13 +1,8 @@
1
- import type { IngestResult, MessageExchange, StoreStatus } from './types.js';
1
+ import type { StoreStatus } from './types.js';
2
2
  export interface MemoryStore {
3
3
  /**
4
4
  * Return current store statistics.
5
5
  */
6
6
  status(): Promise<StoreStatus>;
7
- /**
8
- * High-level entry point for ingesting a conversation exchange.
9
- * Writes to raw_log table and enqueues embedding.
10
- */
11
- ingest(exchange: MessageExchange): Promise<IngestResult>;
12
7
  }
13
8
  //# sourceMappingURL=store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE7E,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAE/B;;;OAGG;IACH,MAAM,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1D"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;CAChC"}