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,685 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import * as sqliteVec from "sqlite-vec";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
// ── Database Manager ─────────────────────────────────────────
|
|
6
|
+
// Uses better-sqlite3 (native SQLite bindings) for performance,
|
|
7
|
+
// FTS5 support, and extension loading (sqlite-vec).
|
|
8
|
+
// Auto-persists to disk — no manual persist() calls needed.
|
|
9
|
+
export class BeerCanDB {
|
|
10
|
+
db;
|
|
11
|
+
constructor(dbPath) {
|
|
12
|
+
const dir = path.dirname(dbPath);
|
|
13
|
+
if (!fs.existsSync(dir)) {
|
|
14
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
this.db = new Database(dbPath);
|
|
17
|
+
sqliteVec.load(this.db);
|
|
18
|
+
this.db.pragma("journal_mode = WAL");
|
|
19
|
+
this.db.pragma("foreign_keys = ON");
|
|
20
|
+
this.migrate();
|
|
21
|
+
}
|
|
22
|
+
/** Returns the underlying better-sqlite3 Database instance */
|
|
23
|
+
getDb() {
|
|
24
|
+
return this.db;
|
|
25
|
+
}
|
|
26
|
+
// ── Migrations ───────────────────────────────────────────
|
|
27
|
+
migrate() {
|
|
28
|
+
this.db.exec(`
|
|
29
|
+
CREATE TABLE IF NOT EXISTS _migrations (
|
|
30
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
31
|
+
name TEXT NOT NULL UNIQUE,
|
|
32
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
33
|
+
);
|
|
34
|
+
`);
|
|
35
|
+
const applied = new Set();
|
|
36
|
+
const rows = this.db.prepare("SELECT name FROM _migrations").all();
|
|
37
|
+
for (const row of rows) {
|
|
38
|
+
applied.add(row.name);
|
|
39
|
+
}
|
|
40
|
+
for (const [name, sql] of Object.entries(MIGRATIONS)) {
|
|
41
|
+
if (!applied.has(name)) {
|
|
42
|
+
this.db.exec(sql);
|
|
43
|
+
this.db.prepare("INSERT INTO _migrations (name) VALUES (?)").run(name);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// ── Projects ─────────────────────────────────────────────
|
|
48
|
+
createProject(project) {
|
|
49
|
+
this.db.prepare(`INSERT INTO projects (id, name, slug, description, work_dir, context, allowed_tools, token_budget, created_at, updated_at)
|
|
50
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(project.id, project.name, project.slug, project.description ?? null, project.workDir ?? null, JSON.stringify(project.context), JSON.stringify(project.allowedTools), JSON.stringify(project.tokenBudget), project.createdAt, project.updatedAt);
|
|
51
|
+
}
|
|
52
|
+
getProject(id) {
|
|
53
|
+
const row = this.db.prepare("SELECT * FROM projects WHERE id = ?").get(id);
|
|
54
|
+
return row ? this.rowToProject(row) : null;
|
|
55
|
+
}
|
|
56
|
+
getProjectBySlug(slug) {
|
|
57
|
+
const row = this.db.prepare("SELECT * FROM projects WHERE slug = ?").get(slug);
|
|
58
|
+
return row ? this.rowToProject(row) : null;
|
|
59
|
+
}
|
|
60
|
+
listProjects() {
|
|
61
|
+
const rows = this.db.prepare("SELECT * FROM projects ORDER BY created_at DESC").all();
|
|
62
|
+
return rows.map((row) => this.rowToProject(row));
|
|
63
|
+
}
|
|
64
|
+
// ── Bloops ───────────────────────────────────────────────
|
|
65
|
+
createBloop(bloop) {
|
|
66
|
+
this.db.prepare(`INSERT INTO loops (id, project_id, parent_loop_id, trigger, status, goal, system_prompt, messages, result, tool_calls, tokens_used, iterations, max_iterations, created_at, updated_at, completed_at)
|
|
67
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(bloop.id, bloop.projectId, bloop.parentBloopId, bloop.trigger, bloop.status, bloop.goal, bloop.systemPrompt ?? null, JSON.stringify(bloop.messages), JSON.stringify(bloop.result), JSON.stringify(bloop.toolCalls), bloop.tokensUsed, bloop.iterations, bloop.maxIterations, bloop.createdAt, bloop.updatedAt, bloop.completedAt);
|
|
68
|
+
}
|
|
69
|
+
updateBloop(bloop) {
|
|
70
|
+
this.db.prepare(`UPDATE loops SET
|
|
71
|
+
status = ?, messages = ?, result = ?, tool_calls = ?,
|
|
72
|
+
tokens_used = ?, iterations = ?, updated_at = ?, completed_at = ?
|
|
73
|
+
WHERE id = ?`).run(bloop.status, JSON.stringify(bloop.messages), JSON.stringify(bloop.result), JSON.stringify(bloop.toolCalls), bloop.tokensUsed, bloop.iterations, bloop.updatedAt, bloop.completedAt, bloop.id);
|
|
74
|
+
}
|
|
75
|
+
getBloop(id) {
|
|
76
|
+
const row = this.db.prepare("SELECT * FROM loops WHERE id = ?").get(id);
|
|
77
|
+
return row ? this.rowToBloop(row) : null;
|
|
78
|
+
}
|
|
79
|
+
getProjectBloops(projectId, status) {
|
|
80
|
+
let query = "SELECT * FROM loops WHERE project_id = ?";
|
|
81
|
+
const params = [projectId];
|
|
82
|
+
if (status) {
|
|
83
|
+
query += " AND status = ?";
|
|
84
|
+
params.push(status);
|
|
85
|
+
}
|
|
86
|
+
query += " ORDER BY created_at DESC";
|
|
87
|
+
const rows = this.db.prepare(query).all(...params);
|
|
88
|
+
return rows.map((row) => this.rowToBloop(row));
|
|
89
|
+
}
|
|
90
|
+
// ── Row Mappers ──────────────────────────────────────────
|
|
91
|
+
rowToProject(row) {
|
|
92
|
+
return {
|
|
93
|
+
id: row.id,
|
|
94
|
+
name: row.name,
|
|
95
|
+
slug: row.slug,
|
|
96
|
+
description: row.description,
|
|
97
|
+
workDir: row.work_dir ?? undefined,
|
|
98
|
+
context: JSON.parse(row.context),
|
|
99
|
+
allowedTools: JSON.parse(row.allowed_tools),
|
|
100
|
+
tokenBudget: JSON.parse(row.token_budget),
|
|
101
|
+
createdAt: row.created_at,
|
|
102
|
+
updatedAt: row.updated_at,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
rowToBloop(row) {
|
|
106
|
+
return {
|
|
107
|
+
id: row.id,
|
|
108
|
+
projectId: row.project_id,
|
|
109
|
+
parentBloopId: row.parent_loop_id,
|
|
110
|
+
trigger: row.trigger,
|
|
111
|
+
status: row.status,
|
|
112
|
+
goal: row.goal,
|
|
113
|
+
systemPrompt: row.system_prompt,
|
|
114
|
+
messages: JSON.parse(row.messages),
|
|
115
|
+
result: JSON.parse(row.result),
|
|
116
|
+
toolCalls: JSON.parse(row.tool_calls),
|
|
117
|
+
tokensUsed: row.tokens_used,
|
|
118
|
+
iterations: row.iterations,
|
|
119
|
+
maxIterations: row.max_iterations,
|
|
120
|
+
createdAt: row.created_at,
|
|
121
|
+
updatedAt: row.updated_at,
|
|
122
|
+
completedAt: row.completed_at,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// ── Schedules ──────────────────────────────────────────────
|
|
126
|
+
createSchedule(schedule) {
|
|
127
|
+
this.db.prepare(`INSERT INTO schedules (id, project_id, project_slug, cron_expression, goal, team, description, enabled, last_run_at, next_run_at, created_at, updated_at)
|
|
128
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(schedule.id, schedule.projectId, schedule.projectSlug, schedule.cronExpression, schedule.goal, schedule.team, schedule.description ?? null, schedule.enabled ? 1 : 0, schedule.lastRunAt, schedule.nextRunAt, schedule.createdAt, schedule.updatedAt);
|
|
129
|
+
}
|
|
130
|
+
getSchedule(id) {
|
|
131
|
+
const row = this.db.prepare("SELECT * FROM schedules WHERE id = ?").get(id);
|
|
132
|
+
return row ? this.rowToSchedule(row) : null;
|
|
133
|
+
}
|
|
134
|
+
listSchedules(projectSlug) {
|
|
135
|
+
let query = "SELECT * FROM schedules";
|
|
136
|
+
const params = [];
|
|
137
|
+
if (projectSlug) {
|
|
138
|
+
query += " WHERE project_slug = ?";
|
|
139
|
+
params.push(projectSlug);
|
|
140
|
+
}
|
|
141
|
+
query += " ORDER BY created_at DESC";
|
|
142
|
+
const rows = this.db.prepare(query).all(...params);
|
|
143
|
+
return rows.map((row) => this.rowToSchedule(row));
|
|
144
|
+
}
|
|
145
|
+
deleteSchedule(id) {
|
|
146
|
+
this.db.prepare("DELETE FROM schedules WHERE id = ?").run(id);
|
|
147
|
+
}
|
|
148
|
+
updateScheduleRun(id, lastRunAt) {
|
|
149
|
+
this.db.prepare("UPDATE schedules SET last_run_at = ?, updated_at = ? WHERE id = ?").run(lastRunAt, new Date().toISOString(), id);
|
|
150
|
+
}
|
|
151
|
+
// ── Triggers ──────────────────────────────────────────────
|
|
152
|
+
createTrigger(trigger) {
|
|
153
|
+
this.db.prepare(`INSERT INTO triggers (id, project_id, project_slug, event_type, filter_pattern, filter_data, goal_template, team, enabled, created_at, updated_at)
|
|
154
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(trigger.id, trigger.projectId, trigger.projectSlug, trigger.eventType, trigger.filterPattern, JSON.stringify(trigger.filterData), trigger.goalTemplate, trigger.team, trigger.enabled ? 1 : 0, trigger.createdAt, trigger.updatedAt);
|
|
155
|
+
}
|
|
156
|
+
listTriggers(projectSlug) {
|
|
157
|
+
let query = "SELECT * FROM triggers";
|
|
158
|
+
const params = [];
|
|
159
|
+
if (projectSlug) {
|
|
160
|
+
query += " WHERE project_slug = ?";
|
|
161
|
+
params.push(projectSlug);
|
|
162
|
+
}
|
|
163
|
+
query += " ORDER BY created_at DESC";
|
|
164
|
+
const rows = this.db.prepare(query).all(...params);
|
|
165
|
+
return rows.map((row) => this.rowToTrigger(row));
|
|
166
|
+
}
|
|
167
|
+
deleteTrigger(id) {
|
|
168
|
+
this.db.prepare("DELETE FROM triggers WHERE id = ?").run(id);
|
|
169
|
+
}
|
|
170
|
+
// ── Events Log ────────────────────────────────────────────
|
|
171
|
+
logEvent(event) {
|
|
172
|
+
this.db.prepare(`INSERT INTO events_log (id, project_id, event_type, event_data, trigger_id, created_at)
|
|
173
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(event.id, event.projectId, event.eventType, JSON.stringify(event.eventData), event.triggerId, event.createdAt);
|
|
174
|
+
}
|
|
175
|
+
// ── Row Mappers (new) ─────────────────────────────────────
|
|
176
|
+
rowToSchedule(row) {
|
|
177
|
+
return {
|
|
178
|
+
id: row.id,
|
|
179
|
+
projectId: row.project_id,
|
|
180
|
+
projectSlug: row.project_slug,
|
|
181
|
+
cronExpression: row.cron_expression,
|
|
182
|
+
goal: row.goal,
|
|
183
|
+
team: row.team,
|
|
184
|
+
description: row.description,
|
|
185
|
+
enabled: !!row.enabled,
|
|
186
|
+
lastRunAt: row.last_run_at,
|
|
187
|
+
nextRunAt: row.next_run_at,
|
|
188
|
+
createdAt: row.created_at,
|
|
189
|
+
updatedAt: row.updated_at,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
rowToTrigger(row) {
|
|
193
|
+
return {
|
|
194
|
+
id: row.id,
|
|
195
|
+
projectId: row.project_id,
|
|
196
|
+
projectSlug: row.project_slug,
|
|
197
|
+
eventType: row.event_type,
|
|
198
|
+
filterPattern: row.filter_pattern,
|
|
199
|
+
filterData: JSON.parse(row.filter_data || "{}"),
|
|
200
|
+
goalTemplate: row.goal_template,
|
|
201
|
+
team: row.team,
|
|
202
|
+
enabled: !!row.enabled,
|
|
203
|
+
createdAt: row.created_at,
|
|
204
|
+
updatedAt: row.updated_at,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
// ── Memory Entries ────────────────────────────────────────
|
|
208
|
+
createMemoryEntry(entry) {
|
|
209
|
+
this.db.prepare(`INSERT INTO memory_entries (id, project_id, memory_type, title, content, source_loop_id, superseded_by, confidence, tags, created_at, updated_at)
|
|
210
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(entry.id, entry.projectId, entry.memoryType, entry.title, entry.content, entry.sourceBloopId, entry.supersededBy, entry.confidence, JSON.stringify(entry.tags), entry.createdAt, entry.updatedAt);
|
|
211
|
+
// Mirror to FTS5
|
|
212
|
+
this.db.prepare(`INSERT INTO memory_entries_fts (rowid, title, content, memory_type, tags)
|
|
213
|
+
SELECT rowid, title, content, memory_type, tags FROM memory_entries WHERE id = ?`).run(entry.id);
|
|
214
|
+
}
|
|
215
|
+
getMemoryEntry(id) {
|
|
216
|
+
const row = this.db.prepare("SELECT * FROM memory_entries WHERE id = ?").get(id);
|
|
217
|
+
return row ? this.rowToMemoryEntry(row) : null;
|
|
218
|
+
}
|
|
219
|
+
listMemoryEntries(projectId, type) {
|
|
220
|
+
let query = "SELECT * FROM memory_entries WHERE project_id = ? AND superseded_by IS NULL";
|
|
221
|
+
const params = [projectId];
|
|
222
|
+
if (type) {
|
|
223
|
+
query += " AND memory_type = ?";
|
|
224
|
+
params.push(type);
|
|
225
|
+
}
|
|
226
|
+
query += " ORDER BY created_at DESC";
|
|
227
|
+
const rows = this.db.prepare(query).all(...params);
|
|
228
|
+
return rows.map((row) => this.rowToMemoryEntry(row));
|
|
229
|
+
}
|
|
230
|
+
supersedeMemoryEntry(oldId, newEntry) {
|
|
231
|
+
const txn = this.db.transaction(() => {
|
|
232
|
+
// Insert new entry first (so FK reference is valid)
|
|
233
|
+
this.createMemoryEntry(newEntry);
|
|
234
|
+
// Then mark old entry as superseded
|
|
235
|
+
this.db.prepare("UPDATE memory_entries SET superseded_by = ?, updated_at = ? WHERE id = ?").run(newEntry.id, new Date().toISOString(), oldId);
|
|
236
|
+
});
|
|
237
|
+
txn();
|
|
238
|
+
}
|
|
239
|
+
searchMemoryFTS(projectId, query, limit = 10) {
|
|
240
|
+
// FTS5 MATCH with built-in BM25 ranking
|
|
241
|
+
const rows = this.db.prepare(`SELECT me.*, rank
|
|
242
|
+
FROM memory_entries_fts fts
|
|
243
|
+
JOIN memory_entries me ON me.rowid = fts.rowid
|
|
244
|
+
WHERE memory_entries_fts MATCH ?
|
|
245
|
+
AND me.project_id = ?
|
|
246
|
+
AND me.superseded_by IS NULL
|
|
247
|
+
ORDER BY rank
|
|
248
|
+
LIMIT ?`).all(query, projectId, limit);
|
|
249
|
+
return rows.map((row) => this.rowToMemoryEntry(row));
|
|
250
|
+
}
|
|
251
|
+
deleteMemoryEntry(id) {
|
|
252
|
+
// Delete FTS mirror first
|
|
253
|
+
const row = this.db.prepare("SELECT rowid FROM memory_entries WHERE id = ?").get(id);
|
|
254
|
+
if (row) {
|
|
255
|
+
this.db.prepare("DELETE FROM memory_entries_fts WHERE rowid = ?").run(row.rowid);
|
|
256
|
+
}
|
|
257
|
+
this.db.prepare("DELETE FROM memory_entries WHERE id = ?").run(id);
|
|
258
|
+
}
|
|
259
|
+
rowToMemoryEntry(row) {
|
|
260
|
+
return {
|
|
261
|
+
id: row.id,
|
|
262
|
+
projectId: row.project_id,
|
|
263
|
+
memoryType: row.memory_type,
|
|
264
|
+
title: row.title,
|
|
265
|
+
content: row.content,
|
|
266
|
+
sourceBloopId: row.source_loop_id,
|
|
267
|
+
supersededBy: row.superseded_by,
|
|
268
|
+
confidence: row.confidence,
|
|
269
|
+
tags: JSON.parse(row.tags || "[]"),
|
|
270
|
+
createdAt: row.created_at,
|
|
271
|
+
updatedAt: row.updated_at,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
// ── Knowledge Graph ─────────────────────────────────────
|
|
275
|
+
createKGEntity(entity) {
|
|
276
|
+
this.db.prepare(`INSERT INTO kg_entities (id, project_id, name, entity_type, description, properties, source_loop_id, source_memory_id, created_at, updated_at)
|
|
277
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(entity.id, entity.projectId, entity.name, entity.entityType, entity.description, JSON.stringify(entity.properties), entity.sourceBloopId, entity.sourceMemoryId, entity.createdAt, entity.updatedAt);
|
|
278
|
+
}
|
|
279
|
+
getKGEntity(id) {
|
|
280
|
+
const row = this.db.prepare("SELECT * FROM kg_entities WHERE id = ?").get(id);
|
|
281
|
+
return row ? this.rowToKGEntity(row) : null;
|
|
282
|
+
}
|
|
283
|
+
findKGEntityByName(projectId, name) {
|
|
284
|
+
const row = this.db.prepare("SELECT * FROM kg_entities WHERE project_id = ? AND name = ?").get(projectId, name);
|
|
285
|
+
return row ? this.rowToKGEntity(row) : null;
|
|
286
|
+
}
|
|
287
|
+
listKGEntities(projectId, type) {
|
|
288
|
+
let query = "SELECT * FROM kg_entities WHERE project_id = ?";
|
|
289
|
+
const params = [projectId];
|
|
290
|
+
if (type) {
|
|
291
|
+
query += " AND entity_type = ?";
|
|
292
|
+
params.push(type);
|
|
293
|
+
}
|
|
294
|
+
query += " ORDER BY created_at DESC";
|
|
295
|
+
const rows = this.db.prepare(query).all(...params);
|
|
296
|
+
return rows.map((row) => this.rowToKGEntity(row));
|
|
297
|
+
}
|
|
298
|
+
searchKGEntities(projectId, query) {
|
|
299
|
+
const pattern = `%${query}%`;
|
|
300
|
+
const rows = this.db.prepare(`SELECT * FROM kg_entities WHERE project_id = ? AND (name LIKE ? OR description LIKE ?)
|
|
301
|
+
ORDER BY created_at DESC LIMIT 20`).all(projectId, pattern, pattern);
|
|
302
|
+
return rows.map((row) => this.rowToKGEntity(row));
|
|
303
|
+
}
|
|
304
|
+
updateKGEntity(id, updates) {
|
|
305
|
+
const entity = this.getKGEntity(id);
|
|
306
|
+
if (!entity)
|
|
307
|
+
return;
|
|
308
|
+
this.db.prepare("UPDATE kg_entities SET description = ?, properties = ?, updated_at = ? WHERE id = ?").run(updates.description ?? entity.description, JSON.stringify(updates.properties ?? entity.properties), new Date().toISOString(), id);
|
|
309
|
+
}
|
|
310
|
+
createKGEdge(edge) {
|
|
311
|
+
this.db.prepare(`INSERT INTO kg_edges (id, project_id, source_id, target_id, edge_type, weight, properties, source_loop_id, created_at)
|
|
312
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(edge.id, edge.projectId, edge.sourceId, edge.targetId, edge.edgeType, edge.weight, JSON.stringify(edge.properties), edge.sourceBloopId, edge.createdAt);
|
|
313
|
+
}
|
|
314
|
+
getKGEdgesFrom(entityId) {
|
|
315
|
+
const rows = this.db.prepare("SELECT * FROM kg_edges WHERE source_id = ?").all(entityId);
|
|
316
|
+
return rows.map((row) => this.rowToKGEdge(row));
|
|
317
|
+
}
|
|
318
|
+
getKGEdgesTo(entityId) {
|
|
319
|
+
const rows = this.db.prepare("SELECT * FROM kg_edges WHERE target_id = ?").all(entityId);
|
|
320
|
+
return rows.map((row) => this.rowToKGEdge(row));
|
|
321
|
+
}
|
|
322
|
+
getKGEdgesBoth(entityId) {
|
|
323
|
+
const rows = this.db.prepare("SELECT * FROM kg_edges WHERE source_id = ? OR target_id = ?").all(entityId, entityId);
|
|
324
|
+
return rows.map((row) => this.rowToKGEdge(row));
|
|
325
|
+
}
|
|
326
|
+
createKGEntityMemoryLink(entityId, memoryId) {
|
|
327
|
+
this.db.prepare("INSERT OR IGNORE INTO kg_entity_memories (entity_id, memory_id) VALUES (?, ?)").run(entityId, memoryId);
|
|
328
|
+
}
|
|
329
|
+
getKGEntityMemoryIds(entityId) {
|
|
330
|
+
const rows = this.db.prepare("SELECT memory_id FROM kg_entity_memories WHERE entity_id = ?").all(entityId);
|
|
331
|
+
return rows.map((r) => r.memory_id);
|
|
332
|
+
}
|
|
333
|
+
getKGMemoryEntityIds(memoryId) {
|
|
334
|
+
const rows = this.db.prepare("SELECT entity_id FROM kg_entity_memories WHERE memory_id = ?").all(memoryId);
|
|
335
|
+
return rows.map((r) => r.entity_id);
|
|
336
|
+
}
|
|
337
|
+
rowToKGEntity(row) {
|
|
338
|
+
return {
|
|
339
|
+
id: row.id,
|
|
340
|
+
projectId: row.project_id,
|
|
341
|
+
name: row.name,
|
|
342
|
+
entityType: row.entity_type,
|
|
343
|
+
description: row.description,
|
|
344
|
+
properties: JSON.parse(row.properties || "{}"),
|
|
345
|
+
sourceBloopId: row.source_loop_id,
|
|
346
|
+
sourceMemoryId: row.source_memory_id,
|
|
347
|
+
createdAt: row.created_at,
|
|
348
|
+
updatedAt: row.updated_at,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
rowToKGEdge(row) {
|
|
352
|
+
return {
|
|
353
|
+
id: row.id,
|
|
354
|
+
projectId: row.project_id,
|
|
355
|
+
sourceId: row.source_id,
|
|
356
|
+
targetId: row.target_id,
|
|
357
|
+
edgeType: row.edge_type,
|
|
358
|
+
weight: row.weight,
|
|
359
|
+
properties: JSON.parse(row.properties || "{}"),
|
|
360
|
+
sourceBloopId: row.source_loop_id,
|
|
361
|
+
createdAt: row.created_at,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
// ── Working Memory ──────────────────────────────────────
|
|
365
|
+
setWorkingMemory(bloopId, key, value) {
|
|
366
|
+
this.db.prepare(`INSERT INTO working_memory (loop_id, key, value, updated_at)
|
|
367
|
+
VALUES (?, ?, ?, ?)
|
|
368
|
+
ON CONFLICT(loop_id, key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`).run(bloopId, key, value, new Date().toISOString());
|
|
369
|
+
}
|
|
370
|
+
getWorkingMemory(bloopId, key) {
|
|
371
|
+
const row = this.db.prepare("SELECT value FROM working_memory WHERE loop_id = ? AND key = ?").get(bloopId, key);
|
|
372
|
+
return row?.value;
|
|
373
|
+
}
|
|
374
|
+
listWorkingMemory(bloopId) {
|
|
375
|
+
const rows = this.db.prepare("SELECT key, value FROM working_memory WHERE loop_id = ? ORDER BY key").all(bloopId);
|
|
376
|
+
return rows;
|
|
377
|
+
}
|
|
378
|
+
deleteWorkingMemory(bloopId, key) {
|
|
379
|
+
this.db.prepare("DELETE FROM working_memory WHERE loop_id = ? AND key = ?").run(bloopId, key);
|
|
380
|
+
}
|
|
381
|
+
clearWorkingMemory(bloopId) {
|
|
382
|
+
this.db.prepare("DELETE FROM working_memory WHERE loop_id = ?").run(bloopId);
|
|
383
|
+
}
|
|
384
|
+
// ── Memory Vectors (sqlite-vec) ────────────────────────
|
|
385
|
+
toBuffer(f32) {
|
|
386
|
+
return Buffer.from(f32.buffer, f32.byteOffset, f32.byteLength);
|
|
387
|
+
}
|
|
388
|
+
storeVector(memoryId, embedding) {
|
|
389
|
+
this.db.prepare("INSERT INTO memory_vectors (memory_id, embedding) VALUES (?, ?)").run(memoryId, this.toBuffer(embedding));
|
|
390
|
+
}
|
|
391
|
+
updateVector(memoryId, embedding) {
|
|
392
|
+
this.db.prepare("UPDATE memory_vectors SET embedding = ? WHERE memory_id = ?").run(this.toBuffer(embedding), memoryId);
|
|
393
|
+
}
|
|
394
|
+
deleteVector(memoryId) {
|
|
395
|
+
this.db.prepare("DELETE FROM memory_vectors WHERE memory_id = ?").run(memoryId);
|
|
396
|
+
}
|
|
397
|
+
queryVectors(embedding, topK = 10) {
|
|
398
|
+
const rows = this.db.prepare(`SELECT memory_id, distance
|
|
399
|
+
FROM memory_vectors
|
|
400
|
+
WHERE embedding MATCH ?
|
|
401
|
+
AND k = ?
|
|
402
|
+
ORDER BY distance`).all(this.toBuffer(embedding), topK);
|
|
403
|
+
return rows.map((r) => ({ memoryId: r.memory_id, distance: r.distance }));
|
|
404
|
+
}
|
|
405
|
+
hasVectors() {
|
|
406
|
+
const row = this.db.prepare("SELECT COUNT(*) as cnt FROM memory_vectors").get();
|
|
407
|
+
return row.cnt > 0;
|
|
408
|
+
}
|
|
409
|
+
// ── Job Queue ────────────────────────────────────────────
|
|
410
|
+
createJob(job) {
|
|
411
|
+
this.db.prepare(`INSERT INTO job_queue (id, project_slug, goal, team, priority, status, source, source_id, extra_context, loop_id, error, created_at, started_at, completed_at)
|
|
412
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(job.id, job.projectSlug, job.goal, job.team, job.priority, job.status, job.source, job.sourceId, job.extraContext, job.bloopId, job.error, job.createdAt, job.startedAt, job.completedAt);
|
|
413
|
+
}
|
|
414
|
+
/** Atomically claim the highest-priority pending job. */
|
|
415
|
+
claimNextJob() {
|
|
416
|
+
const txn = this.db.transaction(() => {
|
|
417
|
+
const row = this.db.prepare(`SELECT * FROM job_queue WHERE status = 'pending'
|
|
418
|
+
ORDER BY priority DESC, created_at ASC LIMIT 1`).get();
|
|
419
|
+
if (!row)
|
|
420
|
+
return null;
|
|
421
|
+
this.db.prepare("UPDATE job_queue SET status = 'running', started_at = ? WHERE id = ?").run(new Date().toISOString(), row.id);
|
|
422
|
+
return this.rowToJob({ ...row, status: "running", started_at: new Date().toISOString() });
|
|
423
|
+
});
|
|
424
|
+
return txn() ?? null;
|
|
425
|
+
}
|
|
426
|
+
updateJob(id, updates) {
|
|
427
|
+
const parts = [];
|
|
428
|
+
const params = [];
|
|
429
|
+
if (updates.status) {
|
|
430
|
+
parts.push("status = ?");
|
|
431
|
+
params.push(updates.status);
|
|
432
|
+
}
|
|
433
|
+
if (updates.bloopId && updates.bloopId.length > 0) {
|
|
434
|
+
parts.push("loop_id = ?");
|
|
435
|
+
params.push(updates.bloopId);
|
|
436
|
+
}
|
|
437
|
+
if (updates.error !== undefined) {
|
|
438
|
+
parts.push("error = ?");
|
|
439
|
+
params.push(updates.error);
|
|
440
|
+
}
|
|
441
|
+
if (updates.completedAt) {
|
|
442
|
+
parts.push("completed_at = ?");
|
|
443
|
+
params.push(updates.completedAt);
|
|
444
|
+
}
|
|
445
|
+
if (parts.length === 0)
|
|
446
|
+
return;
|
|
447
|
+
params.push(id);
|
|
448
|
+
this.db.prepare(`UPDATE job_queue SET ${parts.join(", ")} WHERE id = ?`).run(...params);
|
|
449
|
+
}
|
|
450
|
+
getJobStats() {
|
|
451
|
+
const row = this.db.prepare(`
|
|
452
|
+
SELECT
|
|
453
|
+
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,
|
|
454
|
+
SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) as running,
|
|
455
|
+
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed,
|
|
456
|
+
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed
|
|
457
|
+
FROM job_queue
|
|
458
|
+
`).get();
|
|
459
|
+
return {
|
|
460
|
+
pending: row.pending ?? 0,
|
|
461
|
+
running: row.running ?? 0,
|
|
462
|
+
completed: row.completed ?? 0,
|
|
463
|
+
failed: row.failed ?? 0,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
listJobs(status, limit = 20) {
|
|
467
|
+
let query = "SELECT * FROM job_queue";
|
|
468
|
+
const params = [];
|
|
469
|
+
if (status) {
|
|
470
|
+
query += " WHERE status = ?";
|
|
471
|
+
params.push(status);
|
|
472
|
+
}
|
|
473
|
+
query += " ORDER BY created_at DESC LIMIT ?";
|
|
474
|
+
params.push(limit);
|
|
475
|
+
const rows = this.db.prepare(query).all(...params);
|
|
476
|
+
return rows.map((r) => this.rowToJob(r));
|
|
477
|
+
}
|
|
478
|
+
rowToJob(row) {
|
|
479
|
+
return {
|
|
480
|
+
id: row.id,
|
|
481
|
+
projectSlug: row.project_slug,
|
|
482
|
+
goal: row.goal,
|
|
483
|
+
team: row.team,
|
|
484
|
+
priority: row.priority,
|
|
485
|
+
status: row.status,
|
|
486
|
+
source: row.source,
|
|
487
|
+
sourceId: row.source_id,
|
|
488
|
+
extraContext: row.extra_context,
|
|
489
|
+
bloopId: row.loop_id,
|
|
490
|
+
error: row.error,
|
|
491
|
+
createdAt: row.created_at,
|
|
492
|
+
startedAt: row.started_at,
|
|
493
|
+
completedAt: row.completed_at,
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
close() {
|
|
497
|
+
this.db.close();
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
// ── Migrations ───────────────────────────────────────────────
|
|
501
|
+
const MIGRATIONS = {
|
|
502
|
+
"001_initial": `
|
|
503
|
+
CREATE TABLE projects (
|
|
504
|
+
id TEXT PRIMARY KEY,
|
|
505
|
+
name TEXT NOT NULL,
|
|
506
|
+
slug TEXT NOT NULL UNIQUE,
|
|
507
|
+
description TEXT,
|
|
508
|
+
context TEXT NOT NULL DEFAULT '{}',
|
|
509
|
+
allowed_tools TEXT NOT NULL DEFAULT '["*"]',
|
|
510
|
+
token_budget TEXT NOT NULL DEFAULT '{}',
|
|
511
|
+
created_at TEXT NOT NULL,
|
|
512
|
+
updated_at TEXT NOT NULL
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
CREATE TABLE loops (
|
|
516
|
+
id TEXT PRIMARY KEY,
|
|
517
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
518
|
+
parent_loop_id TEXT REFERENCES loops(id),
|
|
519
|
+
trigger TEXT NOT NULL DEFAULT 'manual',
|
|
520
|
+
status TEXT NOT NULL DEFAULT 'created',
|
|
521
|
+
goal TEXT NOT NULL,
|
|
522
|
+
system_prompt TEXT,
|
|
523
|
+
messages TEXT NOT NULL DEFAULT '[]',
|
|
524
|
+
result TEXT,
|
|
525
|
+
tool_calls TEXT NOT NULL DEFAULT '[]',
|
|
526
|
+
tokens_used INTEGER NOT NULL DEFAULT 0,
|
|
527
|
+
iterations INTEGER NOT NULL DEFAULT 0,
|
|
528
|
+
max_iterations INTEGER NOT NULL DEFAULT 50,
|
|
529
|
+
created_at TEXT NOT NULL,
|
|
530
|
+
updated_at TEXT NOT NULL,
|
|
531
|
+
completed_at TEXT
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
CREATE INDEX idx_loops_project ON loops(project_id);
|
|
535
|
+
CREATE INDEX idx_loops_status ON loops(status);
|
|
536
|
+
CREATE INDEX idx_loops_parent ON loops(parent_loop_id);
|
|
537
|
+
`,
|
|
538
|
+
"002_schedules": `
|
|
539
|
+
CREATE TABLE IF NOT EXISTS schedules (
|
|
540
|
+
id TEXT PRIMARY KEY,
|
|
541
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
542
|
+
project_slug TEXT NOT NULL,
|
|
543
|
+
cron_expression TEXT NOT NULL,
|
|
544
|
+
goal TEXT NOT NULL,
|
|
545
|
+
team TEXT NOT NULL DEFAULT 'solo',
|
|
546
|
+
description TEXT,
|
|
547
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
548
|
+
last_run_at TEXT,
|
|
549
|
+
next_run_at TEXT,
|
|
550
|
+
created_at TEXT NOT NULL,
|
|
551
|
+
updated_at TEXT NOT NULL
|
|
552
|
+
);
|
|
553
|
+
CREATE INDEX IF NOT EXISTS idx_schedules_project ON schedules(project_id);
|
|
554
|
+
CREATE INDEX IF NOT EXISTS idx_schedules_enabled ON schedules(enabled);
|
|
555
|
+
`,
|
|
556
|
+
"003_triggers": `
|
|
557
|
+
CREATE TABLE IF NOT EXISTS triggers (
|
|
558
|
+
id TEXT PRIMARY KEY,
|
|
559
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
560
|
+
project_slug TEXT NOT NULL,
|
|
561
|
+
event_type TEXT NOT NULL,
|
|
562
|
+
filter_pattern TEXT NOT NULL DEFAULT '.*',
|
|
563
|
+
filter_data TEXT NOT NULL DEFAULT '{}',
|
|
564
|
+
goal_template TEXT NOT NULL,
|
|
565
|
+
team TEXT NOT NULL DEFAULT 'solo',
|
|
566
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
567
|
+
created_at TEXT NOT NULL,
|
|
568
|
+
updated_at TEXT NOT NULL
|
|
569
|
+
);
|
|
570
|
+
CREATE INDEX IF NOT EXISTS idx_triggers_project ON triggers(project_id);
|
|
571
|
+
CREATE INDEX IF NOT EXISTS idx_triggers_enabled ON triggers(enabled);
|
|
572
|
+
`,
|
|
573
|
+
"004_events_log": `
|
|
574
|
+
CREATE TABLE IF NOT EXISTS events_log (
|
|
575
|
+
id TEXT PRIMARY KEY,
|
|
576
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
577
|
+
event_type TEXT NOT NULL,
|
|
578
|
+
event_data TEXT NOT NULL DEFAULT '{}',
|
|
579
|
+
trigger_id TEXT REFERENCES triggers(id),
|
|
580
|
+
spawned_loop_id TEXT REFERENCES loops(id),
|
|
581
|
+
created_at TEXT NOT NULL
|
|
582
|
+
);
|
|
583
|
+
CREATE INDEX IF NOT EXISTS idx_events_project ON events_log(project_id);
|
|
584
|
+
CREATE INDEX IF NOT EXISTS idx_events_type ON events_log(event_type);
|
|
585
|
+
`,
|
|
586
|
+
"005_memory_entries": `
|
|
587
|
+
CREATE TABLE IF NOT EXISTS memory_entries (
|
|
588
|
+
id TEXT PRIMARY KEY,
|
|
589
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
590
|
+
memory_type TEXT NOT NULL DEFAULT 'note',
|
|
591
|
+
title TEXT NOT NULL,
|
|
592
|
+
content TEXT NOT NULL,
|
|
593
|
+
source_loop_id TEXT REFERENCES loops(id),
|
|
594
|
+
superseded_by TEXT REFERENCES memory_entries(id),
|
|
595
|
+
confidence REAL NOT NULL DEFAULT 1.0,
|
|
596
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
597
|
+
created_at TEXT NOT NULL,
|
|
598
|
+
updated_at TEXT NOT NULL
|
|
599
|
+
);
|
|
600
|
+
CREATE INDEX IF NOT EXISTS idx_memory_project ON memory_entries(project_id);
|
|
601
|
+
CREATE INDEX IF NOT EXISTS idx_memory_type ON memory_entries(memory_type);
|
|
602
|
+
CREATE INDEX IF NOT EXISTS idx_memory_superseded ON memory_entries(superseded_by);
|
|
603
|
+
|
|
604
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_entries_fts USING fts5(
|
|
605
|
+
title, content, memory_type, tags,
|
|
606
|
+
content=memory_entries, content_rowid=rowid
|
|
607
|
+
);
|
|
608
|
+
`,
|
|
609
|
+
"006_knowledge_graph": `
|
|
610
|
+
CREATE TABLE IF NOT EXISTS kg_entities (
|
|
611
|
+
id TEXT PRIMARY KEY,
|
|
612
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
613
|
+
name TEXT NOT NULL,
|
|
614
|
+
entity_type TEXT NOT NULL DEFAULT 'concept',
|
|
615
|
+
description TEXT,
|
|
616
|
+
properties TEXT NOT NULL DEFAULT '{}',
|
|
617
|
+
source_loop_id TEXT REFERENCES loops(id),
|
|
618
|
+
source_memory_id TEXT REFERENCES memory_entries(id),
|
|
619
|
+
created_at TEXT NOT NULL,
|
|
620
|
+
updated_at TEXT NOT NULL
|
|
621
|
+
);
|
|
622
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_kg_entity_name_project ON kg_entities(name, project_id);
|
|
623
|
+
CREATE INDEX IF NOT EXISTS idx_kg_entities_type ON kg_entities(entity_type);
|
|
624
|
+
|
|
625
|
+
CREATE TABLE IF NOT EXISTS kg_edges (
|
|
626
|
+
id TEXT PRIMARY KEY,
|
|
627
|
+
project_id TEXT NOT NULL REFERENCES projects(id),
|
|
628
|
+
source_id TEXT NOT NULL REFERENCES kg_entities(id),
|
|
629
|
+
target_id TEXT NOT NULL REFERENCES kg_entities(id),
|
|
630
|
+
edge_type TEXT NOT NULL DEFAULT 'relates_to',
|
|
631
|
+
weight REAL NOT NULL DEFAULT 1.0,
|
|
632
|
+
properties TEXT NOT NULL DEFAULT '{}',
|
|
633
|
+
source_loop_id TEXT REFERENCES loops(id),
|
|
634
|
+
created_at TEXT NOT NULL
|
|
635
|
+
);
|
|
636
|
+
CREATE INDEX IF NOT EXISTS idx_kg_edges_source ON kg_edges(source_id);
|
|
637
|
+
CREATE INDEX IF NOT EXISTS idx_kg_edges_target ON kg_edges(target_id);
|
|
638
|
+
|
|
639
|
+
CREATE TABLE IF NOT EXISTS kg_entity_memories (
|
|
640
|
+
entity_id TEXT NOT NULL REFERENCES kg_entities(id),
|
|
641
|
+
memory_id TEXT NOT NULL REFERENCES memory_entries(id),
|
|
642
|
+
PRIMARY KEY (entity_id, memory_id)
|
|
643
|
+
);
|
|
644
|
+
`,
|
|
645
|
+
"007_working_memory": `
|
|
646
|
+
CREATE TABLE IF NOT EXISTS working_memory (
|
|
647
|
+
loop_id TEXT NOT NULL REFERENCES loops(id),
|
|
648
|
+
key TEXT NOT NULL,
|
|
649
|
+
value TEXT NOT NULL,
|
|
650
|
+
updated_at TEXT NOT NULL,
|
|
651
|
+
PRIMARY KEY (loop_id, key)
|
|
652
|
+
);
|
|
653
|
+
CREATE INDEX IF NOT EXISTS idx_working_memory_loop ON working_memory(loop_id);
|
|
654
|
+
`,
|
|
655
|
+
"008_project_workdir": `
|
|
656
|
+
ALTER TABLE projects ADD COLUMN work_dir TEXT;
|
|
657
|
+
`,
|
|
658
|
+
"009_memory_vectors": `
|
|
659
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_vectors USING vec0(
|
|
660
|
+
memory_id TEXT PRIMARY KEY,
|
|
661
|
+
embedding float[512]
|
|
662
|
+
);
|
|
663
|
+
`,
|
|
664
|
+
"010_job_queue": `
|
|
665
|
+
CREATE TABLE IF NOT EXISTS job_queue (
|
|
666
|
+
id TEXT PRIMARY KEY,
|
|
667
|
+
project_slug TEXT NOT NULL,
|
|
668
|
+
goal TEXT NOT NULL,
|
|
669
|
+
team TEXT NOT NULL DEFAULT 'auto',
|
|
670
|
+
priority INTEGER NOT NULL DEFAULT 0,
|
|
671
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
672
|
+
source TEXT NOT NULL DEFAULT 'manual',
|
|
673
|
+
source_id TEXT,
|
|
674
|
+
extra_context TEXT,
|
|
675
|
+
loop_id TEXT REFERENCES loops(id),
|
|
676
|
+
error TEXT,
|
|
677
|
+
created_at TEXT NOT NULL,
|
|
678
|
+
started_at TEXT,
|
|
679
|
+
completed_at TEXT
|
|
680
|
+
);
|
|
681
|
+
CREATE INDEX IF NOT EXISTS idx_job_queue_status ON job_queue(status);
|
|
682
|
+
CREATE INDEX IF NOT EXISTS idx_job_queue_priority ON job_queue(priority DESC, created_at ASC);
|
|
683
|
+
`,
|
|
684
|
+
};
|
|
685
|
+
//# sourceMappingURL=database.js.map
|