the-campfire 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/bin/cli.ts +182 -0
- package/dist/apple-touch-icon.png +0 -0
- package/dist/assets/MermaidDiagram-By1J3Whj.js +2 -0
- package/dist/assets/_basePickBy-Dq6gnD-p.js +1 -0
- package/dist/assets/_baseUniq-SpgN6cGc.js +1 -0
- package/dist/assets/arc-D9SEA8dX.js +1 -0
- package/dist/assets/architectureDiagram-VXUJARFQ-CoTcwLeR.js +36 -0
- package/dist/assets/blockDiagram-VD42YOAC-Dvc63voV.js +122 -0
- package/dist/assets/c4Diagram-YG6GDRKO-tuDcG4_4.js +10 -0
- package/dist/assets/channel-CxhcHn7S.js +1 -0
- package/dist/assets/chunk-4BX2VUAB-x1hChYu9.js +1 -0
- package/dist/assets/chunk-55IACEB6-BX6HWnWP.js +1 -0
- package/dist/assets/chunk-B4BG7PRW-B4IlF03G.js +165 -0
- package/dist/assets/chunk-DI55MBZ5-TpYb5rLg.js +220 -0
- package/dist/assets/chunk-FMBD7UC4-DZX4LUPx.js +15 -0
- package/dist/assets/chunk-QN33PNHL-CuQs0NDY.js +1 -0
- package/dist/assets/chunk-QZHKN3VN-CygWwVx8.js +1 -0
- package/dist/assets/chunk-TZMSLE5B-CtI-IdKV.js +1 -0
- package/dist/assets/classDiagram-2ON5EDUG-BM0LptJF.js +1 -0
- package/dist/assets/classDiagram-v2-WZHVMYZB-BM0LptJF.js +1 -0
- package/dist/assets/clone-C6kt1rE7.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-PdySs991.js +1 -0
- package/dist/assets/cytoscape.esm-BQaXIfA_.js +331 -0
- package/dist/assets/dagre-6UL2VRFP-J0SxEiEu.js +4 -0
- package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/assets/diagram-PSM6KHXK-CW0HLXaH.js +24 -0
- package/dist/assets/diagram-QEK2KX5R-DADHF-k4.js +43 -0
- package/dist/assets/diagram-S2PKOQOG-9S0-PC3S.js +24 -0
- package/dist/assets/erDiagram-Q2GNP2WA-BEHAiY_q.js +60 -0
- package/dist/assets/flowDiagram-NV44I4VS-Cu6n9Xnd.js +162 -0
- package/dist/assets/ganttDiagram-JELNMOA3-Cr_BHryu.js +267 -0
- package/dist/assets/gitGraphDiagram-V2S2FVAM-D5pT35v5.js +65 -0
- package/dist/assets/graph-DKRbrxcs.js +1 -0
- package/dist/assets/index-Byjg9zgI.css +32 -0
- package/dist/assets/index-Cwj3m0hT.js +284 -0
- package/dist/assets/infoDiagram-HS3SLOUP-BaJlrgKV.js +2 -0
- package/dist/assets/init-Gi6I4Gst.js +1 -0
- package/dist/assets/journeyDiagram-XKPGCS4Q-CXC4-DJv.js +139 -0
- package/dist/assets/kanban-definition-3W4ZIXB7-BtHBu9cx.js +89 -0
- package/dist/assets/katex-BlHpptmG.js +261 -0
- package/dist/assets/layout-DnxSwLDJ.js +1 -0
- package/dist/assets/linear-CWbbIT8l.js +1 -0
- package/dist/assets/mermaid.core-DXn1cFAK.js +256 -0
- package/dist/assets/mindmap-definition-VGOIOE7T-BR8br1hs.js +68 -0
- package/dist/assets/ordinal-BENe2yWM.js +1 -0
- package/dist/assets/pieDiagram-ADFJNKIX-B7RmXV7I.js +30 -0
- package/dist/assets/quadrantDiagram-AYHSOK5B-DK0FhyV_.js +7 -0
- package/dist/assets/requirementDiagram-UZGBJVZJ-3zAGxSpL.js +64 -0
- package/dist/assets/sankeyDiagram-TZEHDZUN-DZppKtjc.js +10 -0
- package/dist/assets/sequenceDiagram-WL72ISMW-D1x3r3wV.js +145 -0
- package/dist/assets/stateDiagram-FKZM4ZOC-2ByD08Vq.js +1 -0
- package/dist/assets/stateDiagram-v2-4FDKWEC3-Vm1cYjgV.js +1 -0
- package/dist/assets/timeline-definition-IT6M3QCI-l5S35biR.js +61 -0
- package/dist/assets/treemap-GDKQZRPO-DIPadPiF.js +162 -0
- package/dist/assets/xychartDiagram-PRI3JC2R-9GUvwS2b.js +7 -0
- package/dist/favicon.svg +3 -0
- package/dist/fonts/Geist-Variable.woff2 +0 -0
- package/dist/fonts/GeistMono-Variable.woff2 +0 -0
- package/dist/icon-192.png +0 -0
- package/dist/icon-512.png +0 -0
- package/dist/index.html +33 -0
- package/dist/logo-codex.svg +14 -0
- package/dist/logo.svg +3 -0
- package/dist/manifest.json +26 -0
- package/dist/og-image.png +0 -0
- package/dist/sw.js +130 -0
- package/package.json +92 -0
- package/server/ADAPTERS.md +121 -0
- package/server/adapter-registry-types.ts +28 -0
- package/server/adapter-registry.ts +293 -0
- package/server/adapter-types.ts +51 -0
- package/server/agent-executor.ts +249 -0
- package/server/agent-mcp-bridge.ts +148 -0
- package/server/agent-mcp-stdio.ts +171 -0
- package/server/agent-mcp-tools.ts +76 -0
- package/server/agent-store.ts +183 -0
- package/server/agent-types.ts +79 -0
- package/server/aider-adapter.ts +525 -0
- package/server/auth.ts +153 -0
- package/server/auto-namer.ts +132 -0
- package/server/capability-discovery.ts +431 -0
- package/server/claude-container-auth.ts +40 -0
- package/server/claude-stdio-adapter.ts +452 -0
- package/server/clawhub-export.ts +218 -0
- package/server/cli-launcher.ts +1613 -0
- package/server/codex-adapter.ts +1987 -0
- package/server/codex-container-auth.ts +40 -0
- package/server/codex-home.ts +26 -0
- package/server/collective-intelligence.ts +401 -0
- package/server/commands-discovery.ts +160 -0
- package/server/constants.ts +5 -0
- package/server/container-manager.ts +328 -0
- package/server/cron-scheduler.ts +243 -0
- package/server/cron-store.ts +149 -0
- package/server/cron-types.ts +63 -0
- package/server/deliberation-engine.ts +323 -0
- package/server/embedding.ts +89 -0
- package/server/env-manager.ts +146 -0
- package/server/environment-detector.ts +106 -0
- package/server/environment-rules.ts +106 -0
- package/server/gallery-store.ts +216 -0
- package/server/gallery-types.ts +52 -0
- package/server/gallery-votes.ts +101 -0
- package/server/git-utils.ts +384 -0
- package/server/github-pr.ts +379 -0
- package/server/goose-adapter.ts +929 -0
- package/server/image-pull-manager.ts +144 -0
- package/server/index.ts +363 -0
- package/server/linear-project-manager.ts +78 -0
- package/server/moltbook-client.ts +160 -0
- package/server/openclaw-adapter.ts +916 -0
- package/server/openclaw-channel/channel.ts +188 -0
- package/server/openclaw-channel/index.ts +68 -0
- package/server/openclaw-channel/package.json +19 -0
- package/server/openclaw-channel/types.ts +93 -0
- package/server/opencode-adapter.ts +951 -0
- package/server/openhands-adapter.ts +775 -0
- package/server/orchestrator-executor.ts +184 -0
- package/server/orchestrator-store.ts +106 -0
- package/server/orchestrator-types.ts +86 -0
- package/server/path-resolver.ts +168 -0
- package/server/pr-poller.ts +162 -0
- package/server/proactive-keepalive.ts +121 -0
- package/server/prompt-manager.ts +103 -0
- package/server/protocol-monitor.ts +197 -0
- package/server/race-controller.ts +295 -0
- package/server/race-store.ts +82 -0
- package/server/recorder.ts +359 -0
- package/server/recording-hub/compat-validator.ts +122 -0
- package/server/recording-hub/diagnostics.ts +289 -0
- package/server/recording-hub/hub-routes.ts +146 -0
- package/server/recording-hub/hub-store.ts +224 -0
- package/server/replay.ts +78 -0
- package/server/routes/adapter-routes.ts +31 -0
- package/server/routes/agent-mcp-routes.ts +35 -0
- package/server/routes/agent-routes.ts +185 -0
- package/server/routes/auth-routes.ts +101 -0
- package/server/routes/ci-routes.ts +165 -0
- package/server/routes/commands-routes.ts +94 -0
- package/server/routes/cron-routes.ts +100 -0
- package/server/routes/env-routes.ts +50 -0
- package/server/routes/folder-routes.ts +56 -0
- package/server/routes/fs-routes.ts +403 -0
- package/server/routes/gallery-routes.ts +410 -0
- package/server/routes/git-routes.ts +108 -0
- package/server/routes/index.ts +62 -0
- package/server/routes/linear-routes.ts +254 -0
- package/server/routes/monitor-routes.ts +11 -0
- package/server/routes/orchestrator-routes.ts +210 -0
- package/server/routes/prompt-routes.ts +47 -0
- package/server/routes/race-routes.ts +103 -0
- package/server/routes/recording-routes.ts +101 -0
- package/server/routes/route-deps.ts +32 -0
- package/server/routes/session-routes.ts +560 -0
- package/server/routes/settings-routes.ts +108 -0
- package/server/routes/skills-routes.ts +52 -0
- package/server/routes/system-routes.ts +243 -0
- package/server/routes/webhook-routes.ts +135 -0
- package/server/routes.ts +44 -0
- package/server/security-middleware.ts +100 -0
- package/server/semantic-memory.ts +389 -0
- package/server/service.ts +733 -0
- package/server/session-folders.ts +107 -0
- package/server/session-git-info.ts +89 -0
- package/server/session-linear-issues.ts +94 -0
- package/server/session-names.ts +67 -0
- package/server/session-store.ts +169 -0
- package/server/session-types.ts +415 -0
- package/server/settings-manager.ts +120 -0
- package/server/shared-context.ts +401 -0
- package/server/skills-manager.ts +250 -0
- package/server/sub-session-manager.ts +263 -0
- package/server/terminal-manager.ts +185 -0
- package/server/update-checker.ts +114 -0
- package/server/usage-limits.ts +192 -0
- package/server/webhook-manager.ts +228 -0
- package/server/webhook-store.ts +168 -0
- package/server/webhook-types.ts +66 -0
- package/server/worktree-tracker.ts +84 -0
- package/server/ws-bridge.ts +2322 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 1: Semantic Memory
|
|
3
|
+
*
|
|
4
|
+
* Persistent, shared knowledge base anchored to a git repository.
|
|
5
|
+
* Backed by LanceDB (embedded, TypeScript-native vector database — same stack as Continue.dev).
|
|
6
|
+
*
|
|
7
|
+
* Two tables:
|
|
8
|
+
* - fragments: episodic/semantic MemoryFragments with vector embeddings
|
|
9
|
+
* - consolidated: distilled ConsolidatedKnowledge per semantic tag
|
|
10
|
+
*
|
|
11
|
+
* Data is stored at ~/.campfire/memory/lancedb/ (one DB directory, two tables).
|
|
12
|
+
*
|
|
13
|
+
* Retrieval is vector similarity search via LanceDB's built-in ANN/flat-scan.
|
|
14
|
+
* Embeddings are generated by the configured provider (OpenAI or Ollama) at write time.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { mkdirSync, appendFileSync } from "node:fs";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import { homedir } from "node:os";
|
|
20
|
+
import { randomUUID } from "node:crypto";
|
|
21
|
+
import type { BackendType } from "./session-types.js";
|
|
22
|
+
import { embed, getEmbeddingDim } from "./embedding.js";
|
|
23
|
+
|
|
24
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
export type MemoryType = "observation" | "hypothesis" | "decision" | "pattern";
|
|
27
|
+
|
|
28
|
+
export interface GitContext {
|
|
29
|
+
commitHash?: string;
|
|
30
|
+
branch: string;
|
|
31
|
+
files: string[];
|
|
32
|
+
repoRoot: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface MemoryFragment {
|
|
36
|
+
id: string;
|
|
37
|
+
sessionId: string;
|
|
38
|
+
agentId: string;
|
|
39
|
+
backendType: BackendType;
|
|
40
|
+
timestamp: number;
|
|
41
|
+
type: MemoryType;
|
|
42
|
+
content: string;
|
|
43
|
+
gitContext: GitContext;
|
|
44
|
+
references: string[];
|
|
45
|
+
confidence: number;
|
|
46
|
+
tags: string[];
|
|
47
|
+
consolidatedInto?: string;
|
|
48
|
+
isConsolidated: boolean;
|
|
49
|
+
// Stored in LanceDB as a Float32 vector column — null when provider = "none"
|
|
50
|
+
embedding?: number[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ConsolidatedKnowledge {
|
|
54
|
+
id: string;
|
|
55
|
+
tag: string;
|
|
56
|
+
summary: string;
|
|
57
|
+
sourceFragments: string[];
|
|
58
|
+
lastUpdated: number;
|
|
59
|
+
confidence: number;
|
|
60
|
+
repoRoot: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface MemoryQueryOptions {
|
|
64
|
+
limit?: number;
|
|
65
|
+
repoRoot?: string;
|
|
66
|
+
tags?: string[];
|
|
67
|
+
type?: MemoryType;
|
|
68
|
+
sessionId?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ─── LanceDB integration ─────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
// Lazily import to avoid startup cost when memory is not used
|
|
74
|
+
type LanceDB = typeof import("@lancedb/lancedb");
|
|
75
|
+
type LanceTable = Awaited<ReturnType<Awaited<ReturnType<LanceDB["connect"]>>["openTable"]>>;
|
|
76
|
+
|
|
77
|
+
let _db: Awaited<ReturnType<LanceDB["connect"]>> | null = null;
|
|
78
|
+
let _fragmentsTable: LanceTable | null = null;
|
|
79
|
+
let _consolidatedTable: LanceTable | null = null;
|
|
80
|
+
let _memoryDir = join(homedir(), ".campfire", "memory", "lancedb");
|
|
81
|
+
|
|
82
|
+
async function getDB() {
|
|
83
|
+
if (_db) return _db;
|
|
84
|
+
const lancedb = await import("@lancedb/lancedb");
|
|
85
|
+
mkdirSync(_memoryDir, { recursive: true });
|
|
86
|
+
_db = await lancedb.connect(_memoryDir);
|
|
87
|
+
return _db;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function getFragmentsTable(): Promise<LanceTable> {
|
|
91
|
+
if (_fragmentsTable) return _fragmentsTable;
|
|
92
|
+
const db = await getDB();
|
|
93
|
+
const tableNames = await db.tableNames();
|
|
94
|
+
if (tableNames.includes("fragments")) {
|
|
95
|
+
_fragmentsTable = await db.openTable("fragments");
|
|
96
|
+
} else {
|
|
97
|
+
// Create with a seed row so the schema is established, then delete seed
|
|
98
|
+
const dim = getEmbeddingDim();
|
|
99
|
+
const seed = [{
|
|
100
|
+
id: "__seed__",
|
|
101
|
+
sessionId: "",
|
|
102
|
+
agentId: "",
|
|
103
|
+
backendType: "claude" as BackendType,
|
|
104
|
+
timestamp: 0,
|
|
105
|
+
type: "observation" as MemoryType,
|
|
106
|
+
content: "",
|
|
107
|
+
gitContextJson: "{}",
|
|
108
|
+
referencesJson: "[]",
|
|
109
|
+
tagsJson: "[]",
|
|
110
|
+
confidence: 0,
|
|
111
|
+
consolidatedInto: "",
|
|
112
|
+
isConsolidated: false,
|
|
113
|
+
vector: Array(dim).fill(0) as number[],
|
|
114
|
+
}];
|
|
115
|
+
_fragmentsTable = await db.createTable("fragments", seed);
|
|
116
|
+
await _fragmentsTable.delete('id = "__seed__"');
|
|
117
|
+
}
|
|
118
|
+
return _fragmentsTable;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function getConsolidatedTable(): Promise<LanceTable> {
|
|
122
|
+
if (_consolidatedTable) return _consolidatedTable;
|
|
123
|
+
const db = await getDB();
|
|
124
|
+
const tableNames = await db.tableNames();
|
|
125
|
+
if (tableNames.includes("consolidated")) {
|
|
126
|
+
_consolidatedTable = await db.openTable("consolidated");
|
|
127
|
+
} else {
|
|
128
|
+
const seed = [{
|
|
129
|
+
id: "__seed__",
|
|
130
|
+
tag: "",
|
|
131
|
+
summary: "",
|
|
132
|
+
sourceFragmentsJson: "[]",
|
|
133
|
+
lastUpdated: 0,
|
|
134
|
+
confidence: 0,
|
|
135
|
+
repoRoot: "",
|
|
136
|
+
}];
|
|
137
|
+
_consolidatedTable = await db.createTable("consolidated", seed);
|
|
138
|
+
await _consolidatedTable.delete('id = "__seed__"');
|
|
139
|
+
}
|
|
140
|
+
return _consolidatedTable;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── Internal row <-> domain object mapping ──────────────────────────────────
|
|
144
|
+
|
|
145
|
+
function fragmentToRow(fragment: MemoryFragment, embedding: number[] | null) {
|
|
146
|
+
const dim = getEmbeddingDim();
|
|
147
|
+
return {
|
|
148
|
+
id: fragment.id,
|
|
149
|
+
sessionId: fragment.sessionId,
|
|
150
|
+
agentId: fragment.agentId,
|
|
151
|
+
backendType: fragment.backendType,
|
|
152
|
+
timestamp: fragment.timestamp,
|
|
153
|
+
type: fragment.type,
|
|
154
|
+
content: fragment.content,
|
|
155
|
+
gitContextJson: JSON.stringify(fragment.gitContext),
|
|
156
|
+
referencesJson: JSON.stringify(fragment.references),
|
|
157
|
+
tagsJson: JSON.stringify(fragment.tags),
|
|
158
|
+
confidence: fragment.confidence,
|
|
159
|
+
consolidatedInto: fragment.consolidatedInto ?? "",
|
|
160
|
+
isConsolidated: fragment.isConsolidated,
|
|
161
|
+
vector: embedding ?? Array(dim).fill(0),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function rowToFragment(row: Record<string, unknown>): MemoryFragment {
|
|
166
|
+
return {
|
|
167
|
+
id: row.id as string,
|
|
168
|
+
sessionId: row.sessionId as string,
|
|
169
|
+
agentId: row.agentId as string,
|
|
170
|
+
backendType: row.backendType as BackendType,
|
|
171
|
+
timestamp: row.timestamp as number,
|
|
172
|
+
type: row.type as MemoryType,
|
|
173
|
+
content: row.content as string,
|
|
174
|
+
gitContext: JSON.parse(row.gitContextJson as string || "{}") as GitContext,
|
|
175
|
+
references: JSON.parse(row.referencesJson as string || "[]") as string[],
|
|
176
|
+
tags: JSON.parse(row.tagsJson as string || "[]") as string[],
|
|
177
|
+
confidence: row.confidence as number,
|
|
178
|
+
consolidatedInto: (row.consolidatedInto as string) || undefined,
|
|
179
|
+
isConsolidated: row.isConsolidated as boolean,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function rowToConsolidated(row: Record<string, unknown>): ConsolidatedKnowledge {
|
|
184
|
+
return {
|
|
185
|
+
id: row.id as string,
|
|
186
|
+
tag: row.tag as string,
|
|
187
|
+
summary: row.summary as string,
|
|
188
|
+
sourceFragments: JSON.parse(row.sourceFragmentsJson as string || "[]") as string[],
|
|
189
|
+
lastUpdated: row.lastUpdated as number,
|
|
190
|
+
confidence: row.confidence as number,
|
|
191
|
+
repoRoot: row.repoRoot as string,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
export interface StoreFragmentOptions {
|
|
198
|
+
sessionId: string;
|
|
199
|
+
agentId: string;
|
|
200
|
+
backendType: BackendType;
|
|
201
|
+
type: MemoryType;
|
|
202
|
+
content: string;
|
|
203
|
+
gitContext: GitContext;
|
|
204
|
+
tags?: string[];
|
|
205
|
+
confidence?: number;
|
|
206
|
+
references?: string[];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Store a new memory fragment. Generates an embedding at write time.
|
|
211
|
+
* Non-blocking: embedding generation failures are logged and the fragment
|
|
212
|
+
* is stored with a zero vector (still retrievable by metadata filters).
|
|
213
|
+
*/
|
|
214
|
+
export async function storeFragment(opts: StoreFragmentOptions): Promise<MemoryFragment> {
|
|
215
|
+
const fragment: MemoryFragment = {
|
|
216
|
+
id: randomUUID(),
|
|
217
|
+
sessionId: opts.sessionId,
|
|
218
|
+
agentId: opts.agentId,
|
|
219
|
+
backendType: opts.backendType,
|
|
220
|
+
timestamp: Date.now(),
|
|
221
|
+
type: opts.type,
|
|
222
|
+
content: opts.content,
|
|
223
|
+
gitContext: opts.gitContext,
|
|
224
|
+
references: opts.references ?? [],
|
|
225
|
+
confidence: opts.confidence ?? 0.7,
|
|
226
|
+
tags: opts.tags ?? [],
|
|
227
|
+
isConsolidated: false,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const embedding = await embed(opts.content);
|
|
231
|
+
const table = await getFragmentsTable();
|
|
232
|
+
await table.add([fragmentToRow(fragment, embedding)]);
|
|
233
|
+
|
|
234
|
+
return fragment;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Semantic similarity search over stored fragments.
|
|
239
|
+
* If an embedding is available, uses vector search.
|
|
240
|
+
* Falls back to a full table scan filtered by metadata (tag/session/repo).
|
|
241
|
+
*/
|
|
242
|
+
export async function queryFragments(
|
|
243
|
+
query: string,
|
|
244
|
+
options: MemoryQueryOptions = {},
|
|
245
|
+
): Promise<MemoryFragment[]> {
|
|
246
|
+
const { limit = 10, repoRoot, tags, type, sessionId } = options;
|
|
247
|
+
const table = await getFragmentsTable();
|
|
248
|
+
const queryVector = await embed(query);
|
|
249
|
+
|
|
250
|
+
let rows: Record<string, unknown>[];
|
|
251
|
+
|
|
252
|
+
if (queryVector) {
|
|
253
|
+
// Vector similarity search — LanceDB returns closest by cosine distance
|
|
254
|
+
let search = table.search(queryVector).limit(limit * 3); // over-fetch for post-filter
|
|
255
|
+
const result = await search.toArray();
|
|
256
|
+
rows = result as unknown as Record<string, unknown>[];
|
|
257
|
+
} else {
|
|
258
|
+
// No embedding configured — full scan
|
|
259
|
+
const result = await table.query().limit(500).toArray();
|
|
260
|
+
rows = result as unknown as Record<string, unknown>[];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Post-filter by metadata
|
|
264
|
+
let fragments = rows.map(rowToFragment).filter((f) => {
|
|
265
|
+
if (repoRoot && f.gitContext.repoRoot !== repoRoot) return false;
|
|
266
|
+
if (sessionId && f.sessionId !== sessionId) return false;
|
|
267
|
+
if (type && f.type !== type) return false;
|
|
268
|
+
if (tags && tags.length > 0) {
|
|
269
|
+
const fragTags = new Set(f.tags);
|
|
270
|
+
if (!tags.some((t) => fragTags.has(t))) return false;
|
|
271
|
+
}
|
|
272
|
+
return true;
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// If no vector search, sort by recency
|
|
276
|
+
if (!queryVector) {
|
|
277
|
+
fragments.sort((a, b) => b.timestamp - a.timestamp);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return fragments.slice(0, limit);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Retrieve all fragments for a session (used during consolidation).
|
|
285
|
+
*/
|
|
286
|
+
export async function getSessionFragments(sessionId: string): Promise<MemoryFragment[]> {
|
|
287
|
+
const table = await getFragmentsTable();
|
|
288
|
+
const result = await table.query().limit(10000).toArray();
|
|
289
|
+
return (result as unknown as Record<string, unknown>[])
|
|
290
|
+
.map(rowToFragment)
|
|
291
|
+
.filter((f) => f.sessionId === sessionId);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Consolidate episodic fragments from a session into semantic knowledge.
|
|
296
|
+
* Groups fragments by tag, synthesizes a summary per tag, and stores in
|
|
297
|
+
* the consolidated table.
|
|
298
|
+
*
|
|
299
|
+
* Returns the list of consolidated knowledge items created/updated.
|
|
300
|
+
*
|
|
301
|
+
* Note: Summary synthesis uses simple concatenation by default.
|
|
302
|
+
* In production, replace the `synthesize` function with an LLM call.
|
|
303
|
+
*/
|
|
304
|
+
export async function consolidateSession(
|
|
305
|
+
sessionId: string,
|
|
306
|
+
repoRoot: string,
|
|
307
|
+
): Promise<ConsolidatedKnowledge[]> {
|
|
308
|
+
const fragments = await getSessionFragments(sessionId);
|
|
309
|
+
if (fragments.length === 0) return [];
|
|
310
|
+
|
|
311
|
+
// Group by tag
|
|
312
|
+
const byTag = new Map<string, MemoryFragment[]>();
|
|
313
|
+
for (const f of fragments) {
|
|
314
|
+
for (const tag of f.tags.length > 0 ? f.tags : ["general"]) {
|
|
315
|
+
if (!byTag.has(tag)) byTag.set(tag, []);
|
|
316
|
+
byTag.get(tag)!.push(f);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const table = await getConsolidatedTable();
|
|
321
|
+
const results: ConsolidatedKnowledge[] = [];
|
|
322
|
+
|
|
323
|
+
for (const [tag, tagFragments] of byTag) {
|
|
324
|
+
const avgConfidence = tagFragments.reduce((s, f) => s + f.confidence, 0) / tagFragments.length;
|
|
325
|
+
const summary = synthesize(tag, tagFragments);
|
|
326
|
+
const knowledge: ConsolidatedKnowledge = {
|
|
327
|
+
id: randomUUID(),
|
|
328
|
+
tag,
|
|
329
|
+
summary,
|
|
330
|
+
sourceFragments: tagFragments.map((f) => f.id),
|
|
331
|
+
lastUpdated: Date.now(),
|
|
332
|
+
confidence: avgConfidence,
|
|
333
|
+
repoRoot,
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
await table.add([{
|
|
337
|
+
id: knowledge.id,
|
|
338
|
+
tag: knowledge.tag,
|
|
339
|
+
summary: knowledge.summary,
|
|
340
|
+
sourceFragmentsJson: JSON.stringify(knowledge.sourceFragments),
|
|
341
|
+
lastUpdated: knowledge.lastUpdated,
|
|
342
|
+
confidence: knowledge.confidence,
|
|
343
|
+
repoRoot: knowledge.repoRoot,
|
|
344
|
+
}]);
|
|
345
|
+
|
|
346
|
+
results.push(knowledge);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return results;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Retrieve consolidated knowledge for a repo, optionally filtered by tag.
|
|
354
|
+
*/
|
|
355
|
+
export async function getConsolidatedKnowledge(
|
|
356
|
+
repoRoot: string,
|
|
357
|
+
tag?: string,
|
|
358
|
+
): Promise<ConsolidatedKnowledge[]> {
|
|
359
|
+
const table = await getConsolidatedTable();
|
|
360
|
+
const result = await table.query().limit(1000).toArray();
|
|
361
|
+
return (result as unknown as Record<string, unknown>[])
|
|
362
|
+
.map(rowToConsolidated)
|
|
363
|
+
.filter((k) => k.repoRoot === repoRoot && (!tag || k.tag === tag));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ─── Internal helpers ─────────────────────────────────────────────────────────
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Simple synthesis: concatenate fragment contents into a bullet list.
|
|
370
|
+
* Replace with an LLM call for production-quality summaries.
|
|
371
|
+
*/
|
|
372
|
+
function synthesize(tag: string, fragments: MemoryFragment[]): string {
|
|
373
|
+
const sorted = [...fragments].sort((a, b) => b.confidence - a.confidence);
|
|
374
|
+
const bullets = sorted
|
|
375
|
+
.slice(0, 10)
|
|
376
|
+
.map((f) => `- [${f.type}] ${f.content}`)
|
|
377
|
+
.join("\n");
|
|
378
|
+
return `Knowledge about "${tag}":\n${bullets}`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// ─── Test helpers ─────────────────────────────────────────────────────────────
|
|
382
|
+
|
|
383
|
+
/** Override memory dir for tests — must be called before any DB access. */
|
|
384
|
+
export function _resetForTest(dir: string): void {
|
|
385
|
+
_db = null;
|
|
386
|
+
_fragmentsTable = null;
|
|
387
|
+
_consolidatedTable = null;
|
|
388
|
+
_memoryDir = dir;
|
|
389
|
+
}
|