beercan 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +546 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +8 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +29 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +49 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +61 -0
- package/dist/config.js.map +1 -0
- package/dist/core/gatekeeper.d.ts +163 -0
- package/dist/core/gatekeeper.d.ts.map +1 -0
- package/dist/core/gatekeeper.js +247 -0
- package/dist/core/gatekeeper.js.map +1 -0
- package/dist/core/job-queue.d.ts +61 -0
- package/dist/core/job-queue.d.ts.map +1 -0
- package/dist/core/job-queue.js +123 -0
- package/dist/core/job-queue.js.map +1 -0
- package/dist/core/logger.d.ts +22 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +65 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/role-templates.d.ts +3 -0
- package/dist/core/role-templates.d.ts.map +1 -0
- package/dist/core/role-templates.js +179 -0
- package/dist/core/role-templates.js.map +1 -0
- package/dist/core/roles.d.ts +94 -0
- package/dist/core/roles.d.ts.map +1 -0
- package/dist/core/roles.js +206 -0
- package/dist/core/roles.js.map +1 -0
- package/dist/core/runner.d.ts +76 -0
- package/dist/core/runner.d.ts.map +1 -0
- package/dist/core/runner.js +307 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/events/daemon.d.ts +9 -0
- package/dist/events/daemon.d.ts.map +1 -0
- package/dist/events/daemon.js +29 -0
- package/dist/events/daemon.js.map +1 -0
- package/dist/events/event-bus.d.ts +35 -0
- package/dist/events/event-bus.d.ts.map +1 -0
- package/dist/events/event-bus.js +41 -0
- package/dist/events/event-bus.js.map +1 -0
- package/dist/events/index.d.ts +41 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +57 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/sources/filesystem-source.d.ts +23 -0
- package/dist/events/sources/filesystem-source.d.ts.map +1 -0
- package/dist/events/sources/filesystem-source.js +95 -0
- package/dist/events/sources/filesystem-source.js.map +1 -0
- package/dist/events/sources/macos-source.d.ts +23 -0
- package/dist/events/sources/macos-source.d.ts.map +1 -0
- package/dist/events/sources/macos-source.js +123 -0
- package/dist/events/sources/macos-source.js.map +1 -0
- package/dist/events/sources/polling-source.d.ts +23 -0
- package/dist/events/sources/polling-source.d.ts.map +1 -0
- package/dist/events/sources/polling-source.js +47 -0
- package/dist/events/sources/polling-source.js.map +1 -0
- package/dist/events/sources/webhook-source.d.ts +23 -0
- package/dist/events/sources/webhook-source.d.ts.map +1 -0
- package/dist/events/sources/webhook-source.js +132 -0
- package/dist/events/sources/webhook-source.js.map +1 -0
- package/dist/events/trigger-manager.d.ts +78 -0
- package/dist/events/trigger-manager.d.ts.map +1 -0
- package/dist/events/trigger-manager.js +130 -0
- package/dist/events/trigger-manager.js.map +1 -0
- package/dist/index.d.ts +123 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +225 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +3 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/manager.d.ts +99 -0
- package/dist/mcp/manager.d.ts.map +1 -0
- package/dist/mcp/manager.js +143 -0
- package/dist/mcp/manager.js.map +1 -0
- package/dist/mcp/tool-adapter.d.ts +20 -0
- package/dist/mcp/tool-adapter.d.ts.map +1 -0
- package/dist/mcp/tool-adapter.js +29 -0
- package/dist/mcp/tool-adapter.js.map +1 -0
- package/dist/memory/embeddings.d.ts +28 -0
- package/dist/memory/embeddings.d.ts.map +1 -0
- package/dist/memory/embeddings.js +90 -0
- package/dist/memory/embeddings.js.map +1 -0
- package/dist/memory/hybrid-search.d.ts +31 -0
- package/dist/memory/hybrid-search.d.ts.map +1 -0
- package/dist/memory/hybrid-search.js +114 -0
- package/dist/memory/hybrid-search.js.map +1 -0
- package/dist/memory/index.d.ts +55 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +175 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/knowledge-graph.d.ts +21 -0
- package/dist/memory/knowledge-graph.d.ts.map +1 -0
- package/dist/memory/knowledge-graph.js +118 -0
- package/dist/memory/knowledge-graph.js.map +1 -0
- package/dist/memory/schemas.d.ts +187 -0
- package/dist/memory/schemas.d.ts.map +1 -0
- package/dist/memory/schemas.js +75 -0
- package/dist/memory/schemas.js.map +1 -0
- package/dist/memory/sqlite-vec-store.d.ts +22 -0
- package/dist/memory/sqlite-vec-store.d.ts.map +1 -0
- package/dist/memory/sqlite-vec-store.js +37 -0
- package/dist/memory/sqlite-vec-store.js.map +1 -0
- package/dist/memory/working-memory.d.ts +22 -0
- package/dist/memory/working-memory.d.ts.map +1 -0
- package/dist/memory/working-memory.js +53 -0
- package/dist/memory/working-memory.js.map +1 -0
- package/dist/scheduler/index.d.ts +3 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/index.js +2 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +82 -0
- package/dist/scheduler/scheduler.d.ts.map +1 -0
- package/dist/scheduler/scheduler.js +127 -0
- package/dist/scheduler/scheduler.js.map +1 -0
- package/dist/schemas.d.ts +328 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +77 -0
- package/dist/schemas.js.map +1 -0
- package/dist/storage/database.d.ts +97 -0
- package/dist/storage/database.d.ts.map +1 -0
- package/dist/storage/database.js +685 -0
- package/dist/storage/database.js.map +1 -0
- package/dist/tools/builtin/filesystem.d.ts +11 -0
- package/dist/tools/builtin/filesystem.d.ts.map +1 -0
- package/dist/tools/builtin/filesystem.js +137 -0
- package/dist/tools/builtin/filesystem.js.map +1 -0
- package/dist/tools/builtin/memory.d.ts +13 -0
- package/dist/tools/builtin/memory.d.ts.map +1 -0
- package/dist/tools/builtin/memory.js +299 -0
- package/dist/tools/builtin/memory.js.map +1 -0
- package/dist/tools/builtin/notification.d.ts +5 -0
- package/dist/tools/builtin/notification.d.ts.map +1 -0
- package/dist/tools/builtin/notification.js +36 -0
- package/dist/tools/builtin/notification.js.map +1 -0
- package/dist/tools/builtin/web.d.ts +7 -0
- package/dist/tools/builtin/web.d.ts.map +1 -0
- package/dist/tools/builtin/web.js +191 -0
- package/dist/tools/builtin/web.js.map +1 -0
- package/dist/tools/registry.d.ts +36 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +49 -0
- package/dist/tools/registry.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding providers for vector memory.
|
|
3
|
+
* Default: TF-IDF based local embeddings (no API calls needed).
|
|
4
|
+
* Pluggable: swap in Voyage AI or OpenAI for neural embeddings.
|
|
5
|
+
*/
|
|
6
|
+
// ── Local TF-IDF Embedder (default, no API needed) ─────────
|
|
7
|
+
const VOCAB_SIZE = 512; // Fixed-dimension output
|
|
8
|
+
/**
|
|
9
|
+
* Simple but effective local embedder using hashed TF-IDF.
|
|
10
|
+
* No external API calls. Deterministic. Fast.
|
|
11
|
+
* Good enough for finding similar goals/results in a personal system.
|
|
12
|
+
*/
|
|
13
|
+
export class LocalEmbedder {
|
|
14
|
+
dimensions = VOCAB_SIZE;
|
|
15
|
+
async embed(text) {
|
|
16
|
+
return hashTfIdf(text, VOCAB_SIZE);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Hash-based TF-IDF: maps tokens to fixed-size vector via hashing trick */
|
|
20
|
+
function hashTfIdf(text, dims) {
|
|
21
|
+
const vector = new Float64Array(dims);
|
|
22
|
+
const tokens = tokenize(text);
|
|
23
|
+
const tf = new Map();
|
|
24
|
+
for (const token of tokens) {
|
|
25
|
+
tf.set(token, (tf.get(token) || 0) + 1);
|
|
26
|
+
}
|
|
27
|
+
for (const [token, count] of tf) {
|
|
28
|
+
// Hashing trick: map token to bucket
|
|
29
|
+
const hash = fnv1a(token);
|
|
30
|
+
const bucket = ((hash % dims) + dims) % dims;
|
|
31
|
+
// Sign from second hash for variance reduction
|
|
32
|
+
const sign = fnv1a(token + "_sign") % 2 === 0 ? 1 : -1;
|
|
33
|
+
// TF weight with sublinear scaling
|
|
34
|
+
vector[bucket] += sign * (1 + Math.log(count));
|
|
35
|
+
}
|
|
36
|
+
// L2 normalize
|
|
37
|
+
let norm = 0;
|
|
38
|
+
for (let i = 0; i < dims; i++)
|
|
39
|
+
norm += vector[i] * vector[i];
|
|
40
|
+
norm = Math.sqrt(norm);
|
|
41
|
+
if (norm > 0) {
|
|
42
|
+
for (let i = 0; i < dims; i++)
|
|
43
|
+
vector[i] /= norm;
|
|
44
|
+
}
|
|
45
|
+
return Array.from(vector);
|
|
46
|
+
}
|
|
47
|
+
/** Simple tokenizer: lowercase, split on non-alphanumeric, filter short */
|
|
48
|
+
function tokenize(text) {
|
|
49
|
+
return text
|
|
50
|
+
.toLowerCase()
|
|
51
|
+
.replace(/[^a-z0-9\s]/g, " ")
|
|
52
|
+
.split(/\s+/)
|
|
53
|
+
.filter((t) => t.length > 2);
|
|
54
|
+
}
|
|
55
|
+
/** FNV-1a hash (32-bit) */
|
|
56
|
+
function fnv1a(str) {
|
|
57
|
+
let hash = 0x811c9dc5;
|
|
58
|
+
for (let i = 0; i < str.length; i++) {
|
|
59
|
+
hash ^= str.charCodeAt(i);
|
|
60
|
+
hash = (hash * 0x01000193) >>> 0;
|
|
61
|
+
}
|
|
62
|
+
return hash;
|
|
63
|
+
}
|
|
64
|
+
// ── LRU Cache for embeddings ────────────────────────────────
|
|
65
|
+
export class EmbeddingCache {
|
|
66
|
+
cache = new Map();
|
|
67
|
+
maxSize;
|
|
68
|
+
constructor(maxSize = 1000) {
|
|
69
|
+
this.maxSize = maxSize;
|
|
70
|
+
}
|
|
71
|
+
get(key) {
|
|
72
|
+
const val = this.cache.get(key);
|
|
73
|
+
if (val) {
|
|
74
|
+
// Move to end (most recently used)
|
|
75
|
+
this.cache.delete(key);
|
|
76
|
+
this.cache.set(key, val);
|
|
77
|
+
}
|
|
78
|
+
return val;
|
|
79
|
+
}
|
|
80
|
+
set(key, value) {
|
|
81
|
+
if (this.cache.size >= this.maxSize) {
|
|
82
|
+
// Evict oldest
|
|
83
|
+
const oldest = this.cache.keys().next().value;
|
|
84
|
+
if (oldest !== undefined)
|
|
85
|
+
this.cache.delete(oldest);
|
|
86
|
+
}
|
|
87
|
+
this.cache.set(key, value);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=embeddings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../../src/memory/embeddings.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { BeerCanDB } from "../storage/database.js";
|
|
2
|
+
import type { SqliteVecStore } from "./sqlite-vec-store.js";
|
|
3
|
+
import type { KnowledgeGraph } from "./knowledge-graph.js";
|
|
4
|
+
import type { MemoryEntry, MemoryType } from "./schemas.js";
|
|
5
|
+
export interface HybridSearchOptions {
|
|
6
|
+
query: string;
|
|
7
|
+
projectId: string;
|
|
8
|
+
projectSlug: string;
|
|
9
|
+
memoryType?: MemoryType;
|
|
10
|
+
limit?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface HybridSearchResult {
|
|
13
|
+
entry: MemoryEntry;
|
|
14
|
+
score: number;
|
|
15
|
+
sources: Array<{
|
|
16
|
+
type: "fts" | "vector" | "graph";
|
|
17
|
+
rank: number;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
export declare class HybridSearch {
|
|
21
|
+
private db;
|
|
22
|
+
private vecStore;
|
|
23
|
+
private kg;
|
|
24
|
+
constructor(db: BeerCanDB, vecStore: SqliteVecStore, kg: KnowledgeGraph);
|
|
25
|
+
search(options: HybridSearchOptions): Promise<HybridSearchResult[]>;
|
|
26
|
+
private searchFTS;
|
|
27
|
+
private searchVector;
|
|
28
|
+
private graphExpand;
|
|
29
|
+
private addScore;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=hybrid-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hybrid-search.d.ts","sourceRoot":"","sources":["../../src/memory/hybrid-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAmB,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM5D,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpE;AAID,qBAAa,YAAY;IAErB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,EAAE;gBAFF,EAAE,EAAE,SAAS,EACb,QAAQ,EAAE,cAAc,EACxB,EAAE,EAAE,cAAc;IAGtB,MAAM,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IA8CzE,OAAO,CAAC,SAAS;YASH,YAAY;IAQ1B,OAAO,CAAC,WAAW;IA2BnB,OAAO,CAAC,QAAQ;CAoBjB"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const RRF_K = 60; // Standard RRF constant
|
|
2
|
+
export class HybridSearch {
|
|
3
|
+
db;
|
|
4
|
+
vecStore;
|
|
5
|
+
kg;
|
|
6
|
+
constructor(db, vecStore, kg) {
|
|
7
|
+
this.db = db;
|
|
8
|
+
this.vecStore = vecStore;
|
|
9
|
+
this.kg = kg;
|
|
10
|
+
}
|
|
11
|
+
async search(options) {
|
|
12
|
+
const { query, projectId, memoryType, limit = 10 } = options;
|
|
13
|
+
const fetchLimit = limit * 2; // Over-fetch for better RRF merging
|
|
14
|
+
// Run FTS5 and vector search in parallel
|
|
15
|
+
const [ftsResults, vectorResults] = await Promise.all([
|
|
16
|
+
this.searchFTS(projectId, query, fetchLimit),
|
|
17
|
+
this.searchVector(query, fetchLimit),
|
|
18
|
+
]);
|
|
19
|
+
// Graph expansion: find entities matching query, get their linked memory IDs
|
|
20
|
+
const graphMemoryIds = this.graphExpand(projectId, query);
|
|
21
|
+
// Build RRF score map
|
|
22
|
+
const scoreMap = new Map();
|
|
23
|
+
// FTS contributions
|
|
24
|
+
for (let rank = 0; rank < ftsResults.length; rank++) {
|
|
25
|
+
const entry = ftsResults[rank];
|
|
26
|
+
if (memoryType && entry.memoryType !== memoryType)
|
|
27
|
+
continue;
|
|
28
|
+
this.addScore(scoreMap, entry, rank, "fts");
|
|
29
|
+
}
|
|
30
|
+
// Vector contributions (look up the MemoryEntry for each vector result)
|
|
31
|
+
for (let rank = 0; rank < vectorResults.length; rank++) {
|
|
32
|
+
const result = vectorResults[rank];
|
|
33
|
+
const entry = this.db.getMemoryEntry(result.memoryId);
|
|
34
|
+
if (!entry)
|
|
35
|
+
continue;
|
|
36
|
+
if (memoryType && entry.memoryType !== memoryType)
|
|
37
|
+
continue;
|
|
38
|
+
this.addScore(scoreMap, entry, rank, "vector");
|
|
39
|
+
}
|
|
40
|
+
// Graph contributions (boost)
|
|
41
|
+
for (let rank = 0; rank < graphMemoryIds.length; rank++) {
|
|
42
|
+
const entry = this.db.getMemoryEntry(graphMemoryIds[rank]);
|
|
43
|
+
if (!entry)
|
|
44
|
+
continue;
|
|
45
|
+
if (memoryType && entry.memoryType !== memoryType)
|
|
46
|
+
continue;
|
|
47
|
+
this.addScore(scoreMap, entry, rank, "graph");
|
|
48
|
+
}
|
|
49
|
+
// Sort by combined RRF score, return top results
|
|
50
|
+
return Array.from(scoreMap.values())
|
|
51
|
+
.sort((a, b) => b.score - a.score)
|
|
52
|
+
.slice(0, limit);
|
|
53
|
+
}
|
|
54
|
+
searchFTS(projectId, query, limit) {
|
|
55
|
+
try {
|
|
56
|
+
return this.db.searchMemoryFTS(projectId, query, limit);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// FTS query might fail on syntax errors (e.g., special characters)
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async searchVector(query, limit) {
|
|
64
|
+
try {
|
|
65
|
+
return this.vecStore.query(query, limit);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
graphExpand(projectId, query) {
|
|
72
|
+
try {
|
|
73
|
+
// Find entities whose names match the query terms
|
|
74
|
+
const entities = this.kg.searchEntities(projectId, query);
|
|
75
|
+
if (entities.length === 0)
|
|
76
|
+
return [];
|
|
77
|
+
// Collect memory IDs linked to matching entities and their neighbors
|
|
78
|
+
const memoryIds = new Set();
|
|
79
|
+
for (const entity of entities.slice(0, 5)) {
|
|
80
|
+
// Direct entity memories
|
|
81
|
+
const directIds = this.db.getKGEntityMemoryIds(entity.id);
|
|
82
|
+
for (const id of directIds)
|
|
83
|
+
memoryIds.add(id);
|
|
84
|
+
// Neighbor entity memories (1 hop)
|
|
85
|
+
const neighbors = this.kg.getNeighbors(entity.id, 1);
|
|
86
|
+
for (const neighbor of neighbors.slice(0, 10)) {
|
|
87
|
+
const neighborIds = this.db.getKGEntityMemoryIds(neighbor.id);
|
|
88
|
+
for (const id of neighborIds)
|
|
89
|
+
memoryIds.add(id);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return Array.from(memoryIds);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
addScore(scoreMap, entry, rank, source) {
|
|
99
|
+
const rrfScore = 1 / (RRF_K + rank + 1);
|
|
100
|
+
const existing = scoreMap.get(entry.id);
|
|
101
|
+
if (existing) {
|
|
102
|
+
existing.score += rrfScore;
|
|
103
|
+
existing.sources.push({ type: source, rank });
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
scoreMap.set(entry.id, {
|
|
107
|
+
entry,
|
|
108
|
+
score: rrfScore,
|
|
109
|
+
sources: [{ type: source, rank }],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=hybrid-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hybrid-search.js","sourceRoot":"","sources":["../../src/memory/hybrid-search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type EmbeddingProvider } from "./embeddings.js";
|
|
2
|
+
import { SqliteVecStore } from "./sqlite-vec-store.js";
|
|
3
|
+
import { KnowledgeGraph } from "./knowledge-graph.js";
|
|
4
|
+
import { WorkingMemory } from "./working-memory.js";
|
|
5
|
+
import { type HybridSearchResult } from "./hybrid-search.js";
|
|
6
|
+
import type { BeerCanDB } from "../storage/database.js";
|
|
7
|
+
import type { MemoryEntry, MemoryType } from "./schemas.js";
|
|
8
|
+
import type { Bloop } from "../schemas.js";
|
|
9
|
+
export declare class MemoryManager {
|
|
10
|
+
private embedder;
|
|
11
|
+
private cache;
|
|
12
|
+
private db;
|
|
13
|
+
private vecStore;
|
|
14
|
+
private kg;
|
|
15
|
+
private wm;
|
|
16
|
+
private hybridSearch;
|
|
17
|
+
constructor(db: BeerCanDB, embedder?: EmbeddingProvider);
|
|
18
|
+
getKnowledgeGraph(): KnowledgeGraph;
|
|
19
|
+
getWorkingMemory(): WorkingMemory;
|
|
20
|
+
getVectorStore(): SqliteVecStore;
|
|
21
|
+
/** Store a completed bloop's result in both FTS5 and sqlite-vec */
|
|
22
|
+
storeBloopResult(bloop: Bloop, projectSlug: string): Promise<void>;
|
|
23
|
+
/** Store a new memory (fact, insight, decision, note) */
|
|
24
|
+
storeMemory(projectSlug: string, opts: {
|
|
25
|
+
projectId: string;
|
|
26
|
+
title: string;
|
|
27
|
+
content: string;
|
|
28
|
+
memoryType: MemoryType;
|
|
29
|
+
tags?: string[];
|
|
30
|
+
confidence?: number;
|
|
31
|
+
sourceBloopId?: string;
|
|
32
|
+
}): Promise<MemoryEntry>;
|
|
33
|
+
/** Update a memory by creating a new version that supersedes it */
|
|
34
|
+
updateMemory(projectSlug: string, memoryId: string, updates: {
|
|
35
|
+
title?: string;
|
|
36
|
+
content: string;
|
|
37
|
+
confidence?: number;
|
|
38
|
+
}): Promise<MemoryEntry | null>;
|
|
39
|
+
/** Search across all memory layers using RRF-fused hybrid search */
|
|
40
|
+
search(projectSlug: string, query: string, opts?: {
|
|
41
|
+
projectId?: string;
|
|
42
|
+
memoryType?: MemoryType;
|
|
43
|
+
limit?: number;
|
|
44
|
+
}): Promise<HybridSearchResult[]>;
|
|
45
|
+
/** Retrieve relevant past context for a new goal */
|
|
46
|
+
retrieveContext(projectSlug: string, goal: string, count?: number): Promise<string>;
|
|
47
|
+
}
|
|
48
|
+
export { SqliteVecStore } from "./sqlite-vec-store.js";
|
|
49
|
+
export { LocalEmbedder, type EmbeddingProvider } from "./embeddings.js";
|
|
50
|
+
export { KnowledgeGraph } from "./knowledge-graph.js";
|
|
51
|
+
export { WorkingMemory } from "./working-memory.js";
|
|
52
|
+
export { HybridSearch } from "./hybrid-search.js";
|
|
53
|
+
export type { HybridSearchResult, HybridSearchOptions } from "./hybrid-search.js";
|
|
54
|
+
export type { MemoryEntry, MemoryType, KGEntity, KGEdge, EntityType, EdgeType } from "./schemas.js";
|
|
55
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/memory/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAiC,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAgB,KAAK,kBAAkB,EAA4B,MAAM,oBAAoB,CAAC;AACrG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAU3C,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,EAAE,CAAY;IACtB,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,EAAE,CAAgB;IAC1B,OAAO,CAAC,YAAY,CAAe;gBAEvB,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,iBAAiB;IAgBvD,iBAAiB,IAAI,cAAc;IAInC,gBAAgB,IAAI,aAAa;IAIjC,cAAc,IAAI,cAAc;IAMhC,mEAAmE;IAC7D,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqCxE,yDAAyD;IACnD,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;QACJ,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,UAAU,CAAC;QACvB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,GACA,OAAO,CAAC,WAAW,CAAC;IAiCvB,mEAAmE;IAC7D,YAAY,CAChB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAChE,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAkC9B,oEAAoE;IAC9D,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACrE,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAmBhC,oDAAoD;IAC9C,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,OAAO,CAAC,MAAM,CAAC;CAiBrF;AAED,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAClF,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { v4 as uuid } from "uuid";
|
|
2
|
+
import { LocalEmbedder, EmbeddingCache } from "./embeddings.js";
|
|
3
|
+
import { SqliteVecStore } from "./sqlite-vec-store.js";
|
|
4
|
+
import { KnowledgeGraph } from "./knowledge-graph.js";
|
|
5
|
+
import { WorkingMemory } from "./working-memory.js";
|
|
6
|
+
import { HybridSearch } from "./hybrid-search.js";
|
|
7
|
+
// ── Memory Manager ──────────────────────────────────────────
|
|
8
|
+
// Central facade over all memory subsystems:
|
|
9
|
+
// - FTS5 full-text search (BM25 keyword matching)
|
|
10
|
+
// - sqlite-vec vector store (semantic similarity)
|
|
11
|
+
// - Knowledge graph (entity relationships, multi-hop)
|
|
12
|
+
// - Working memory (per-bloop scratchpad)
|
|
13
|
+
// - Hybrid search (RRF-fused results)
|
|
14
|
+
export class MemoryManager {
|
|
15
|
+
embedder;
|
|
16
|
+
cache;
|
|
17
|
+
db;
|
|
18
|
+
vecStore;
|
|
19
|
+
kg;
|
|
20
|
+
wm;
|
|
21
|
+
hybridSearch;
|
|
22
|
+
constructor(db, embedder) {
|
|
23
|
+
this.db = db;
|
|
24
|
+
this.embedder = embedder ?? new LocalEmbedder();
|
|
25
|
+
this.cache = new EmbeddingCache(500);
|
|
26
|
+
this.vecStore = new SqliteVecStore(db, this.embedder);
|
|
27
|
+
this.kg = new KnowledgeGraph(db);
|
|
28
|
+
this.wm = new WorkingMemory(db);
|
|
29
|
+
this.hybridSearch = new HybridSearch(db, this.vecStore, this.kg);
|
|
30
|
+
}
|
|
31
|
+
// ── Subsystem Access ──────────────────────────────────
|
|
32
|
+
getKnowledgeGraph() {
|
|
33
|
+
return this.kg;
|
|
34
|
+
}
|
|
35
|
+
getWorkingMemory() {
|
|
36
|
+
return this.wm;
|
|
37
|
+
}
|
|
38
|
+
getVectorStore() {
|
|
39
|
+
return this.vecStore;
|
|
40
|
+
}
|
|
41
|
+
// ── Store Bloop Result ─────────────────────────────────
|
|
42
|
+
/** Store a completed bloop's result in both FTS5 and sqlite-vec */
|
|
43
|
+
async storeBloopResult(bloop, projectSlug) {
|
|
44
|
+
if (bloop.status !== "completed" || !bloop.result)
|
|
45
|
+
return;
|
|
46
|
+
try {
|
|
47
|
+
const summary = typeof bloop.result === "string"
|
|
48
|
+
? bloop.result
|
|
49
|
+
: JSON.stringify(bloop.result).slice(0, 2000);
|
|
50
|
+
const now = new Date().toISOString();
|
|
51
|
+
const memoryId = uuid();
|
|
52
|
+
// Create structured memory entry (FTS5-indexed)
|
|
53
|
+
const entry = {
|
|
54
|
+
id: memoryId,
|
|
55
|
+
projectId: bloop.projectId,
|
|
56
|
+
memoryType: "loop_result",
|
|
57
|
+
title: bloop.goal,
|
|
58
|
+
content: summary,
|
|
59
|
+
sourceBloopId: bloop.id,
|
|
60
|
+
supersededBy: null,
|
|
61
|
+
confidence: 1.0,
|
|
62
|
+
tags: [],
|
|
63
|
+
createdAt: now,
|
|
64
|
+
updatedAt: now,
|
|
65
|
+
};
|
|
66
|
+
this.db.createMemoryEntry(entry);
|
|
67
|
+
// Store vector embedding in sqlite-vec
|
|
68
|
+
const text = `${bloop.goal}\n${summary}`;
|
|
69
|
+
await this.vecStore.store(memoryId, text);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
console.warn(`[memory] Failed to store bloop result: ${err.message}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// ── Store Arbitrary Memory ────────────────────────────
|
|
76
|
+
/** Store a new memory (fact, insight, decision, note) */
|
|
77
|
+
async storeMemory(projectSlug, opts) {
|
|
78
|
+
const now = new Date().toISOString();
|
|
79
|
+
const memoryId = uuid();
|
|
80
|
+
const entry = {
|
|
81
|
+
id: memoryId,
|
|
82
|
+
projectId: opts.projectId,
|
|
83
|
+
memoryType: opts.memoryType,
|
|
84
|
+
title: opts.title,
|
|
85
|
+
content: opts.content,
|
|
86
|
+
sourceBloopId: opts.sourceBloopId ?? null,
|
|
87
|
+
supersededBy: null,
|
|
88
|
+
confidence: opts.confidence ?? 1.0,
|
|
89
|
+
tags: opts.tags ?? [],
|
|
90
|
+
createdAt: now,
|
|
91
|
+
updatedAt: now,
|
|
92
|
+
};
|
|
93
|
+
this.db.createMemoryEntry(entry);
|
|
94
|
+
// Store vector embedding
|
|
95
|
+
try {
|
|
96
|
+
const text = `${opts.title}\n${opts.content}`;
|
|
97
|
+
await this.vecStore.store(memoryId, text);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
console.warn(`[memory] Failed to store vector: ${err.message}`);
|
|
101
|
+
}
|
|
102
|
+
return entry;
|
|
103
|
+
}
|
|
104
|
+
// ── Update Memory (supersede model) ───────────────────
|
|
105
|
+
/** Update a memory by creating a new version that supersedes it */
|
|
106
|
+
async updateMemory(projectSlug, memoryId, updates) {
|
|
107
|
+
const old = this.db.getMemoryEntry(memoryId);
|
|
108
|
+
if (!old)
|
|
109
|
+
return null;
|
|
110
|
+
const now = new Date().toISOString();
|
|
111
|
+
const newEntry = {
|
|
112
|
+
id: uuid(),
|
|
113
|
+
projectId: old.projectId,
|
|
114
|
+
memoryType: old.memoryType,
|
|
115
|
+
title: updates.title ?? old.title,
|
|
116
|
+
content: updates.content,
|
|
117
|
+
sourceBloopId: old.sourceBloopId,
|
|
118
|
+
supersededBy: null,
|
|
119
|
+
confidence: updates.confidence ?? old.confidence,
|
|
120
|
+
tags: old.tags,
|
|
121
|
+
createdAt: now,
|
|
122
|
+
updatedAt: now,
|
|
123
|
+
};
|
|
124
|
+
this.db.supersedeMemoryEntry(memoryId, newEntry);
|
|
125
|
+
// Store new vector
|
|
126
|
+
try {
|
|
127
|
+
const text = `${newEntry.title}\n${newEntry.content}`;
|
|
128
|
+
await this.vecStore.store(newEntry.id, text);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
console.warn(`[memory] Failed to store updated vector: ${err.message}`);
|
|
132
|
+
}
|
|
133
|
+
return newEntry;
|
|
134
|
+
}
|
|
135
|
+
// ── Hybrid Search ─────────────────────────────────────
|
|
136
|
+
/** Search across all memory layers using RRF-fused hybrid search */
|
|
137
|
+
async search(projectSlug, query, opts) {
|
|
138
|
+
let projectId = opts?.projectId;
|
|
139
|
+
if (!projectId) {
|
|
140
|
+
const project = this.db.getProjectBySlug(projectSlug);
|
|
141
|
+
if (!project)
|
|
142
|
+
return [];
|
|
143
|
+
projectId = project.id;
|
|
144
|
+
}
|
|
145
|
+
return this.hybridSearch.search({
|
|
146
|
+
query,
|
|
147
|
+
projectId,
|
|
148
|
+
projectSlug,
|
|
149
|
+
memoryType: opts?.memoryType,
|
|
150
|
+
limit: opts?.limit,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// ── Retrieve Context ──────────────────────────────────
|
|
154
|
+
/** Retrieve relevant past context for a new goal */
|
|
155
|
+
async retrieveContext(projectSlug, goal, count = 5) {
|
|
156
|
+
try {
|
|
157
|
+
const results = await this.search(projectSlug, goal, { limit: count });
|
|
158
|
+
if (results.length > 0) {
|
|
159
|
+
const lines = results.map((r, i) => `${i + 1}. [${r.entry.memoryType}] ${r.entry.title}\n ${r.entry.content.slice(0, 300)}`);
|
|
160
|
+
return `\n--- Relevant Memories ---\n${lines.join("\n\n")}`;
|
|
161
|
+
}
|
|
162
|
+
return "";
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
console.warn(`[memory] Failed to retrieve context: ${err.message}`);
|
|
166
|
+
return "";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
export { SqliteVecStore } from "./sqlite-vec-store.js";
|
|
171
|
+
export { LocalEmbedder } from "./embeddings.js";
|
|
172
|
+
export { KnowledgeGraph } from "./knowledge-graph.js";
|
|
173
|
+
export { WorkingMemory } from "./working-memory.js";
|
|
174
|
+
export { HybridSearch } from "./hybrid-search.js";
|
|
175
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/memory/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BeerCanDB } from "../storage/database.js";
|
|
2
|
+
import type { KGEntity, KGEdge, EntityType, EdgeType, MemoryEntry } from "./schemas.js";
|
|
3
|
+
export declare class KnowledgeGraph {
|
|
4
|
+
private db;
|
|
5
|
+
constructor(db: BeerCanDB);
|
|
6
|
+
/** Find or create an entity by name within a project (idempotent) */
|
|
7
|
+
getOrCreateEntity(projectId: string, name: string, entityType: EntityType, description?: string, bloopId?: string): KGEntity;
|
|
8
|
+
getEntity(id: string): KGEntity | null;
|
|
9
|
+
findByName(projectId: string, name: string): KGEntity | null;
|
|
10
|
+
listEntities(projectId: string, type?: EntityType): KGEntity[];
|
|
11
|
+
searchEntities(projectId: string, query: string): KGEntity[];
|
|
12
|
+
createEdge(projectId: string, sourceId: string, targetId: string, edgeType: EdgeType, weight?: number, bloopId?: string, properties?: Record<string, unknown>): KGEdge;
|
|
13
|
+
getEdgesFrom(entityId: string): KGEdge[];
|
|
14
|
+
getEdgesTo(entityId: string): KGEdge[];
|
|
15
|
+
/** BFS traversal to find neighbors up to a given depth */
|
|
16
|
+
getNeighbors(entityId: string, depth?: number, edgeTypes?: EdgeType[]): KGEntity[];
|
|
17
|
+
linkEntityToMemory(entityId: string, memoryId: string): void;
|
|
18
|
+
getMemoriesForEntity(entityId: string): MemoryEntry[];
|
|
19
|
+
getEntitiesForMemory(memoryId: string): KGEntity[];
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=knowledge-graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge-graph.d.ts","sourceRoot":"","sources":["../../src/memory/knowledge-graph.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAOxF,qBAAa,cAAc;IACb,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,SAAS;IAIjC,qEAAqE;IACrE,iBAAiB,CACf,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,UAAU,EACtB,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,GACf,QAAQ;IA6BX,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAItC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAI5D,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,QAAQ,EAAE;IAI9D,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE;IAM5D,UAAU,CACR,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,QAAQ,EAClB,MAAM,SAAM,EACZ,OAAO,CAAC,EAAE,MAAM,EAChB,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACvC,MAAM;IAiBT,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE;IAIxC,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE;IAMtC,0DAA0D;IAC1D,YAAY,CACV,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAI,EACT,SAAS,CAAC,EAAE,QAAQ,EAAE,GACrB,QAAQ,EAAE;IAkCb,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI5D,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE;IAOrD,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE;CAMnD"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { v4 as uuid } from "uuid";
|
|
2
|
+
// ── Knowledge Graph ─────────────────────────────────────────
|
|
3
|
+
// Entity-relationship graph for multi-hop reasoning.
|
|
4
|
+
// Entities are unique per (name, project_id).
|
|
5
|
+
// Agents create entities and edges via memory tools.
|
|
6
|
+
export class KnowledgeGraph {
|
|
7
|
+
db;
|
|
8
|
+
constructor(db) {
|
|
9
|
+
this.db = db;
|
|
10
|
+
}
|
|
11
|
+
// ── Entity Operations ──────────────────────────────────
|
|
12
|
+
/** Find or create an entity by name within a project (idempotent) */
|
|
13
|
+
getOrCreateEntity(projectId, name, entityType, description, bloopId) {
|
|
14
|
+
const existing = this.db.findKGEntityByName(projectId, name);
|
|
15
|
+
if (existing) {
|
|
16
|
+
// Update description if provided and entity had none
|
|
17
|
+
if (description && !existing.description) {
|
|
18
|
+
this.db.updateKGEntity(existing.id, { description });
|
|
19
|
+
return { ...existing, description };
|
|
20
|
+
}
|
|
21
|
+
return existing;
|
|
22
|
+
}
|
|
23
|
+
const now = new Date().toISOString();
|
|
24
|
+
const entity = {
|
|
25
|
+
id: uuid(),
|
|
26
|
+
projectId,
|
|
27
|
+
name,
|
|
28
|
+
entityType,
|
|
29
|
+
description: description ?? null,
|
|
30
|
+
properties: {},
|
|
31
|
+
sourceBloopId: bloopId ?? null,
|
|
32
|
+
sourceMemoryId: null,
|
|
33
|
+
createdAt: now,
|
|
34
|
+
updatedAt: now,
|
|
35
|
+
};
|
|
36
|
+
this.db.createKGEntity(entity);
|
|
37
|
+
return entity;
|
|
38
|
+
}
|
|
39
|
+
getEntity(id) {
|
|
40
|
+
return this.db.getKGEntity(id);
|
|
41
|
+
}
|
|
42
|
+
findByName(projectId, name) {
|
|
43
|
+
return this.db.findKGEntityByName(projectId, name);
|
|
44
|
+
}
|
|
45
|
+
listEntities(projectId, type) {
|
|
46
|
+
return this.db.listKGEntities(projectId, type);
|
|
47
|
+
}
|
|
48
|
+
searchEntities(projectId, query) {
|
|
49
|
+
return this.db.searchKGEntities(projectId, query);
|
|
50
|
+
}
|
|
51
|
+
// ── Edge Operations ────────────────────────────────────
|
|
52
|
+
createEdge(projectId, sourceId, targetId, edgeType, weight = 1.0, bloopId, properties = {}) {
|
|
53
|
+
const edge = {
|
|
54
|
+
id: uuid(),
|
|
55
|
+
projectId,
|
|
56
|
+
sourceId,
|
|
57
|
+
targetId,
|
|
58
|
+
edgeType,
|
|
59
|
+
weight,
|
|
60
|
+
properties,
|
|
61
|
+
sourceBloopId: bloopId ?? null,
|
|
62
|
+
createdAt: new Date().toISOString(),
|
|
63
|
+
};
|
|
64
|
+
this.db.createKGEdge(edge);
|
|
65
|
+
return edge;
|
|
66
|
+
}
|
|
67
|
+
getEdgesFrom(entityId) {
|
|
68
|
+
return this.db.getKGEdgesFrom(entityId);
|
|
69
|
+
}
|
|
70
|
+
getEdgesTo(entityId) {
|
|
71
|
+
return this.db.getKGEdgesTo(entityId);
|
|
72
|
+
}
|
|
73
|
+
// ── Graph Traversal ────────────────────────────────────
|
|
74
|
+
/** BFS traversal to find neighbors up to a given depth */
|
|
75
|
+
getNeighbors(entityId, depth = 2, edgeTypes) {
|
|
76
|
+
const maxDepth = Math.min(depth, 4); // Cap to prevent runaway queries
|
|
77
|
+
const visited = new Set([entityId]);
|
|
78
|
+
let frontier = [entityId];
|
|
79
|
+
for (let d = 0; d < maxDepth && frontier.length > 0; d++) {
|
|
80
|
+
const nextFrontier = [];
|
|
81
|
+
for (const nodeId of frontier) {
|
|
82
|
+
const edges = this.db.getKGEdgesBoth(nodeId);
|
|
83
|
+
for (const edge of edges) {
|
|
84
|
+
if (edgeTypes && !edgeTypes.includes(edge.edgeType))
|
|
85
|
+
continue;
|
|
86
|
+
const neighborId = edge.sourceId === nodeId ? edge.targetId : edge.sourceId;
|
|
87
|
+
if (!visited.has(neighborId)) {
|
|
88
|
+
visited.add(neighborId);
|
|
89
|
+
nextFrontier.push(neighborId);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
frontier = nextFrontier;
|
|
94
|
+
}
|
|
95
|
+
// Remove the starting entity from results
|
|
96
|
+
visited.delete(entityId);
|
|
97
|
+
return Array.from(visited)
|
|
98
|
+
.map((id) => this.db.getKGEntity(id))
|
|
99
|
+
.filter((e) => e !== null);
|
|
100
|
+
}
|
|
101
|
+
// ── Entity-Memory Linking ──────────────────────────────
|
|
102
|
+
linkEntityToMemory(entityId, memoryId) {
|
|
103
|
+
this.db.createKGEntityMemoryLink(entityId, memoryId);
|
|
104
|
+
}
|
|
105
|
+
getMemoriesForEntity(entityId) {
|
|
106
|
+
const memoryIds = this.db.getKGEntityMemoryIds(entityId);
|
|
107
|
+
return memoryIds
|
|
108
|
+
.map((id) => this.db.getMemoryEntry(id))
|
|
109
|
+
.filter((m) => m !== null);
|
|
110
|
+
}
|
|
111
|
+
getEntitiesForMemory(memoryId) {
|
|
112
|
+
const entityIds = this.db.getKGMemoryEntityIds(memoryId);
|
|
113
|
+
return entityIds
|
|
114
|
+
.map((id) => this.db.getKGEntity(id))
|
|
115
|
+
.filter((e) => e !== null);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=knowledge-graph.js.map
|