fastrmemory 0.3.0 → 0.4.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.
Files changed (67) hide show
  1. package/.claude-plugin/marketplace.json +14 -14
  2. package/.claude-plugin/plugin.json +18 -18
  3. package/.cursor/mcp.json +3 -1
  4. package/.cursor/skills/fastr-compress/SKILL.md +33 -33
  5. package/.cursor/skills/fastr-curate/SKILL.md +62 -62
  6. package/.cursor/skills/fastr-dag/SKILL.md +47 -47
  7. package/.cursor/skills/fastr-doctor/SKILL.md +39 -39
  8. package/.cursor/skills/fastr-memory/SKILL.md +51 -51
  9. package/.cursor/skills/fastr-plan-estimate/SKILL.md +45 -45
  10. package/.cursor/skills/fastr-session-commit/SKILL.md +49 -49
  11. package/.cursor/skills/fastr-setup/SKILL.md +45 -45
  12. package/.cursor/skills/fastr-trap-log/SKILL.md +39 -39
  13. package/.cursor/skills/fastr-vision/SKILL.md +35 -35
  14. package/.cursor/skills/skills.json +65 -65
  15. package/README.md +642 -729
  16. package/bin/fastr-guard.mjs +61 -0
  17. package/dist/bin/fastr.js +68 -10
  18. package/dist/bin/fastr.js.map +1 -1
  19. package/dist/src/bench/benchmark.js +13 -13
  20. package/dist/src/bench/harness.js +17 -17
  21. package/dist/src/bench/public-harness.js +5 -5
  22. package/dist/src/branch/manager.js +31 -31
  23. package/dist/src/ci/guardian.js +9 -9
  24. package/dist/src/db/doctor.js +25 -25
  25. package/dist/src/db/hygiene.js +23 -23
  26. package/dist/src/db/repository.js +283 -283
  27. package/dist/src/db/schema.js +268 -268
  28. package/dist/src/db/spool.js +4 -4
  29. package/dist/src/export/obsidian.js +15 -15
  30. package/dist/src/import/local.js +4 -4
  31. package/dist/src/installer/mcp-init.d.ts +65 -0
  32. package/dist/src/installer/mcp-init.d.ts.map +1 -0
  33. package/dist/src/installer/mcp-init.js +293 -0
  34. package/dist/src/installer/mcp-init.js.map +1 -0
  35. package/dist/src/installer/setup.d.ts +2 -0
  36. package/dist/src/installer/setup.d.ts.map +1 -1
  37. package/dist/src/installer/setup.js +24 -18
  38. package/dist/src/installer/setup.js.map +1 -1
  39. package/dist/src/mcp/server.js +25 -25
  40. package/dist/src/mirror/markdown-pages.js +22 -22
  41. package/dist/src/search/backfill.js +6 -6
  42. package/dist/src/search/hybrid-rrf.js +60 -60
  43. package/dist/src/ui/server.js +1259 -1259
  44. package/dist/src/vision/assets.js +6 -6
  45. package/dist-ui/assets/ActionsPage-B3quzFNF.js +11 -11
  46. package/dist-ui/assets/GraphPage-DetiATmC.js +1 -1
  47. package/dist-ui/assets/ObservatoryPage-QIGMwENf.js +16 -16
  48. package/dist-ui/assets/TreemapPage-C7uLZd37.js +2 -2
  49. package/dist-ui/assets/index-BJnPINTu.css +1 -1
  50. package/dist-ui/assets/index-DWAH--Fu.js +329 -329
  51. package/dist-ui/assets/maximize-2-BebkGbZA.js +6 -6
  52. package/dist-ui/build-manifest.json +95 -95
  53. package/dist-ui/index.html +26 -26
  54. package/llms-install.md +77 -69
  55. package/package.json +108 -107
  56. package/server.json +28 -28
  57. package/skills/fastr-compress/SKILL.md +33 -33
  58. package/skills/fastr-curate/SKILL.md +62 -62
  59. package/skills/fastr-dag/SKILL.md +47 -47
  60. package/skills/fastr-doctor/SKILL.md +39 -39
  61. package/skills/fastr-memory/SKILL.md +51 -51
  62. package/skills/fastr-plan-estimate/SKILL.md +45 -45
  63. package/skills/fastr-session-commit/SKILL.md +49 -49
  64. package/skills/fastr-setup/SKILL.md +45 -45
  65. package/skills/fastr-trap-log/SKILL.md +39 -39
  66. package/skills/fastr-vision/SKILL.md +35 -35
  67. package/skills/skills.json +65 -65
@@ -1,269 +1,269 @@
1
1
  import { DatabaseSync } from 'node:sqlite';
2
2
  import * as path from 'node:path';
3
3
  import * as fs from 'node:fs';
4
- export const SCHEMA_SQL = `
5
- PRAGMA journal_mode = WAL;
6
- PRAGMA synchronous = NORMAL;
7
- PRAGMA foreign_keys = ON;
8
- PRAGMA busy_timeout = 5000;
9
-
10
- -- 1. Immutable raw observations
11
- CREATE TABLE IF NOT EXISTS raw_events (
12
- id TEXT PRIMARY KEY,
13
- project_id TEXT NOT NULL,
14
- session_id TEXT NOT NULL,
15
- agent_id TEXT DEFAULT 'default',
16
- event_type TEXT NOT NULL,
17
- tool_name TEXT,
18
- file_path TEXT,
19
- raw_payload TEXT NOT NULL,
20
- content_hash TEXT NOT NULL,
21
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
22
- );
23
- CREATE INDEX IF NOT EXISTS idx_raw_events_session ON raw_events(project_id, session_id);
24
- CREATE INDEX IF NOT EXISTS idx_raw_events_hash ON raw_events(content_hash);
25
- CREATE INDEX IF NOT EXISTS idx_raw_events_project_created ON raw_events(project_id, created_at);
26
-
27
- -- 2. Structured memories with Bi-Temporal Validity Windows & Outcome Multipliers
28
- CREATE TABLE IF NOT EXISTS memories (
29
- id TEXT PRIMARY KEY,
30
- project_id TEXT NOT NULL,
31
- agent_id TEXT DEFAULT 'default',
32
- module TEXT NOT NULL DEFAULT 'general',
33
- kind TEXT NOT NULL CHECK (kind IN ('rule', 'decision', 'error_trap', 'workflow')),
34
- title TEXT NOT NULL,
35
- content TEXT NOT NULL,
36
- status TEXT NOT NULL DEFAULT 'staging' CHECK (status IN ('staging', 'verified', 'superseded', 'dormant')),
37
- pinned INTEGER NOT NULL DEFAULT 0,
38
- reward_score REAL NOT NULL DEFAULT 1.0,
39
- retrieval_count INTEGER NOT NULL DEFAULT 0,
40
- valid_from TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
41
- valid_to TEXT DEFAULT NULL,
42
- superseded_by_id TEXT DEFAULT NULL,
43
- superseded_reason TEXT DEFAULT NULL,
44
- source_event_ids TEXT NOT NULL DEFAULT '[]',
45
- embedding BLOB DEFAULT NULL,
46
- embedded INTEGER NOT NULL DEFAULT 0,
47
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
48
- last_recalled_at TEXT DEFAULT NULL,
49
- provenance_hash TEXT DEFAULT NULL
50
- );
51
- CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(project_id, module, valid_to, status);
52
- CREATE INDEX IF NOT EXISTS idx_memories_embedded ON memories(project_id, embedded);
53
-
54
- -- 2b. Per-ID Feedback Audit (confidence tiers and 3-consecutive-bad dormancy)
55
- CREATE TABLE IF NOT EXISTS memory_feedback (
56
- id TEXT PRIMARY KEY,
57
- project_id TEXT NOT NULL,
58
- memory_id TEXT NOT NULL,
59
- outcome TEXT NOT NULL CHECK (outcome IN ('good', 'bad')),
60
- note TEXT DEFAULT NULL,
61
- reward_before REAL NOT NULL,
62
- reward_after REAL NOT NULL,
63
- status_before TEXT NOT NULL,
64
- status_after TEXT NOT NULL,
65
- confidence_tier TEXT NOT NULL CHECK (confidence_tier IN ('high', 'medium', 'low', 'dormant', 'superseded')),
66
- bad_streak INTEGER NOT NULL DEFAULT 0,
67
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
68
- );
69
- CREATE INDEX IF NOT EXISTS idx_memory_feedback_memory ON memory_feedback(memory_id, created_at DESC);
70
- CREATE INDEX IF NOT EXISTS idx_memory_feedback_project ON memory_feedback(project_id, created_at DESC);
71
-
72
- -- 3. FTS5 Virtual Table for sub-millisecond BM25 keyword search
73
- CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
74
- id UNINDEXED,
75
- title,
76
- module,
77
- content,
78
- tokenize='porter unicode61'
79
- );
80
-
81
- -- Keep FTS5 automatically synced via triggers
82
- CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
83
- INSERT INTO memories_fts(id, title, module, content) VALUES (new.id, new.title, new.module, new.content);
84
- END;
85
-
86
- CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
87
- DELETE FROM memories_fts WHERE id = old.id;
88
- END;
89
-
90
- -- Only re-index when searchable columns change (not on retrieval counters / scores)
91
- CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE OF title, module, content ON memories BEGIN
92
- DELETE FROM memories_fts WHERE id = old.id;
93
- INSERT INTO memories_fts(id, title, module, content) VALUES (new.id, new.title, new.module, new.content);
94
- END;
95
-
96
- -- 4. File & Symbol Graph Links
97
- CREATE TABLE IF NOT EXISTS file_links (
98
- memory_id TEXT NOT NULL,
99
- file_path TEXT NOT NULL,
100
- symbol_name TEXT DEFAULT '',
101
- PRIMARY KEY (memory_id, file_path, symbol_name),
102
- FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE CASCADE
103
- );
104
- CREATE INDEX IF NOT EXISTS idx_file_links_path ON file_links(file_path);
105
-
106
- -- 5. Active Task Snapshot & Cross-Agent Handoffs
107
- CREATE TABLE IF NOT EXISTS handoffs (
108
- id INTEGER PRIMARY KEY AUTOINCREMENT,
109
- project_id TEXT NOT NULL,
110
- session_id TEXT NOT NULL,
111
- task_title TEXT NOT NULL,
112
- summary TEXT NOT NULL,
113
- next_step TEXT NOT NULL,
114
- active_files TEXT NOT NULL DEFAULT '[]',
115
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
116
- );
117
-
118
- -- 6. Memory Snapshots & Git-Style Point-in-Time Versioning
119
- CREATE TABLE IF NOT EXISTS memory_snapshots (
120
- id TEXT PRIMARY KEY,
121
- project_id TEXT NOT NULL,
122
- tag TEXT NOT NULL,
123
- description TEXT,
124
- memory_count INTEGER NOT NULL,
125
- state_json TEXT NOT NULL,
126
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
127
- );
128
- CREATE INDEX IF NOT EXISTS idx_snapshots_project ON memory_snapshots(project_id, created_at DESC);
129
-
130
- -- 6b. Memory Branches and Merge Audit Trail
131
- CREATE TABLE IF NOT EXISTS memory_branches (
132
- id TEXT PRIMARY KEY,
133
- project_id TEXT NOT NULL,
134
- name TEXT NOT NULL COLLATE NOCASE,
135
- base_snapshot_id TEXT NOT NULL,
136
- head_snapshot_id TEXT NOT NULL,
137
- description TEXT DEFAULT NULL,
138
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
139
- updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
140
- UNIQUE (project_id, name),
141
- FOREIGN KEY (base_snapshot_id) REFERENCES memory_snapshots(id) ON DELETE RESTRICT,
142
- FOREIGN KEY (head_snapshot_id) REFERENCES memory_snapshots(id) ON DELETE RESTRICT
143
- );
144
- CREATE INDEX IF NOT EXISTS idx_memory_branches_project ON memory_branches(project_id, created_at DESC);
145
-
146
- CREATE TABLE IF NOT EXISTS memory_branch_merges (
147
- id TEXT PRIMARY KEY,
148
- project_id TEXT NOT NULL,
149
- branch_id TEXT NOT NULL,
150
- branch_name TEXT NOT NULL,
151
- source_snapshot_id TEXT NOT NULL,
152
- strategy TEXT NOT NULL CHECK (strategy IN ('target', 'branch')),
153
- applied INTEGER NOT NULL DEFAULT 0,
154
- added_count INTEGER NOT NULL DEFAULT 0,
155
- conflict_count INTEGER NOT NULL DEFAULT 0,
156
- report_json TEXT NOT NULL,
157
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
158
- FOREIGN KEY (branch_id) REFERENCES memory_branches(id) ON DELETE RESTRICT
159
- );
160
- CREATE INDEX IF NOT EXISTS idx_branch_merges_branch ON memory_branch_merges(branch_id, created_at DESC);
161
-
162
- -- 7. Knowledge Graph Entities
163
- CREATE TABLE IF NOT EXISTS entities (
164
- id TEXT PRIMARY KEY,
165
- project_id TEXT NOT NULL,
166
- name TEXT NOT NULL,
167
- entity_type TEXT NOT NULL CHECK (entity_type IN ('symbol', 'file', 'tech', 'service', 'pattern')),
168
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
169
- );
170
- CREATE INDEX IF NOT EXISTS idx_entities_name ON entities(project_id, name);
171
-
172
- -- 8. Multi-Edge Knowledge Graph Links
173
- CREATE TABLE IF NOT EXISTS graph_edges (
174
- id TEXT PRIMARY KEY,
175
- project_id TEXT NOT NULL,
176
- source_id TEXT NOT NULL,
177
- target_id TEXT NOT NULL,
178
- edge_type TEXT NOT NULL CHECK (edge_type IN ('causal', 'entity_link', 'temporal', 'association', 'supersedes')),
179
- weight REAL NOT NULL DEFAULT 1.0,
180
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
181
- );
182
- CREATE INDEX IF NOT EXISTS idx_graph_edges_source ON graph_edges(project_id, source_id);
183
- CREATE INDEX IF NOT EXISTS idx_graph_edges_target ON graph_edges(project_id, target_id);
184
-
185
- -- 9. Proactive Contradiction Conflicts
186
- CREATE TABLE IF NOT EXISTS memory_conflicts (
187
- id INTEGER PRIMARY KEY AUTOINCREMENT,
188
- project_id TEXT NOT NULL,
189
- memory_a_id TEXT NOT NULL,
190
- memory_b_id TEXT NOT NULL,
191
- reason TEXT NOT NULL,
192
- score REAL NOT NULL,
193
- status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'resolved', 'ignored')),
194
- resolved_winner_id TEXT DEFAULT NULL,
195
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
196
- resolved_at TEXT DEFAULT NULL,
197
- FOREIGN KEY (memory_a_id) REFERENCES memories(id) ON DELETE CASCADE,
198
- FOREIGN KEY (memory_b_id) REFERENCES memories(id) ON DELETE CASCADE
199
- );
200
- CREATE INDEX IF NOT EXISTS idx_conflicts_status ON memory_conflicts(project_id, status);
201
-
202
- -- 10. Planning Fallacy Predictions & Baserate
203
- CREATE TABLE IF NOT EXISTS predictions (
204
- id INTEGER PRIMARY KEY AUTOINCREMENT,
205
- project_id TEXT NOT NULL,
206
- class_tag TEXT NOT NULL,
207
- claim_text TEXT NOT NULL,
208
- estimate_value REAL,
209
- estimate_unit TEXT,
210
- actual_value REAL DEFAULT NULL,
211
- closure_state TEXT NOT NULL DEFAULT 'open' CHECK (closure_state IN ('open', 'closed', 'closed-unknown')),
212
- closed_at TEXT DEFAULT NULL,
213
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
214
- );
215
- CREATE INDEX IF NOT EXISTS idx_predictions_class ON predictions(project_id, class_tag, closure_state);
216
-
217
- -- 11. Multi-Agent Work Item Action DAG
218
- CREATE TABLE IF NOT EXISTS actions (
219
- id TEXT PRIMARY KEY,
220
- project_id TEXT NOT NULL,
221
- title TEXT NOT NULL,
222
- description TEXT NOT NULL DEFAULT '',
223
- status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'active', 'done', 'blocked', 'cancelled')),
224
- priority INTEGER NOT NULL DEFAULT 5,
225
- assigned_to TEXT DEFAULT NULL,
226
- crystallized_into TEXT DEFAULT NULL,
227
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
228
- completed_at TEXT DEFAULT NULL
229
- );
230
- CREATE INDEX IF NOT EXISTS idx_actions_status ON actions(project_id, status, priority DESC);
231
-
232
- CREATE TABLE IF NOT EXISTS action_edges (
233
- source_action_id TEXT NOT NULL,
234
- target_action_id TEXT NOT NULL,
235
- edge_type TEXT NOT NULL CHECK (edge_type IN ('requires', 'unlocks', 'conflicts_with')),
236
- PRIMARY KEY (source_action_id, target_action_id, edge_type),
237
- FOREIGN KEY (source_action_id) REFERENCES actions(id) ON DELETE CASCADE,
238
- FOREIGN KEY (target_action_id) REFERENCES actions(id) ON DELETE CASCADE
239
- );
240
-
241
- -- 12. Bounded Working Memory Slots
242
- CREATE TABLE IF NOT EXISTS memory_slots (
243
- project_id TEXT NOT NULL,
244
- slot_key TEXT NOT NULL,
245
- content TEXT NOT NULL,
246
- size_limit INTEGER NOT NULL DEFAULT 4000,
247
- updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
248
- PRIMARY KEY (project_id, slot_key)
249
- );
250
-
251
- -- 13. Multimodal Vision Assets & Screenshots
252
- CREATE TABLE IF NOT EXISTS vision_assets (
253
- id TEXT PRIMARY KEY,
254
- project_id TEXT NOT NULL,
255
- file_path TEXT NOT NULL,
256
- description TEXT NOT NULL,
257
- ocr_text TEXT DEFAULT NULL,
258
- memory_id TEXT DEFAULT NULL,
259
- task_id TEXT DEFAULT NULL,
260
- embedding BLOB DEFAULT NULL,
261
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
262
- FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE SET NULL,
263
- FOREIGN KEY (task_id) REFERENCES actions(id) ON DELETE SET NULL
264
- );
265
- CREATE INDEX IF NOT EXISTS idx_vision_project ON vision_assets(project_id, created_at DESC);
266
- CREATE INDEX IF NOT EXISTS idx_vision_memory ON vision_assets(memory_id);
4
+ export const SCHEMA_SQL = `
5
+ PRAGMA journal_mode = WAL;
6
+ PRAGMA synchronous = NORMAL;
7
+ PRAGMA foreign_keys = ON;
8
+ PRAGMA busy_timeout = 5000;
9
+
10
+ -- 1. Immutable raw observations
11
+ CREATE TABLE IF NOT EXISTS raw_events (
12
+ id TEXT PRIMARY KEY,
13
+ project_id TEXT NOT NULL,
14
+ session_id TEXT NOT NULL,
15
+ agent_id TEXT DEFAULT 'default',
16
+ event_type TEXT NOT NULL,
17
+ tool_name TEXT,
18
+ file_path TEXT,
19
+ raw_payload TEXT NOT NULL,
20
+ content_hash TEXT NOT NULL,
21
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
22
+ );
23
+ CREATE INDEX IF NOT EXISTS idx_raw_events_session ON raw_events(project_id, session_id);
24
+ CREATE INDEX IF NOT EXISTS idx_raw_events_hash ON raw_events(content_hash);
25
+ CREATE INDEX IF NOT EXISTS idx_raw_events_project_created ON raw_events(project_id, created_at);
26
+
27
+ -- 2. Structured memories with Bi-Temporal Validity Windows & Outcome Multipliers
28
+ CREATE TABLE IF NOT EXISTS memories (
29
+ id TEXT PRIMARY KEY,
30
+ project_id TEXT NOT NULL,
31
+ agent_id TEXT DEFAULT 'default',
32
+ module TEXT NOT NULL DEFAULT 'general',
33
+ kind TEXT NOT NULL CHECK (kind IN ('rule', 'decision', 'error_trap', 'workflow')),
34
+ title TEXT NOT NULL,
35
+ content TEXT NOT NULL,
36
+ status TEXT NOT NULL DEFAULT 'staging' CHECK (status IN ('staging', 'verified', 'superseded', 'dormant')),
37
+ pinned INTEGER NOT NULL DEFAULT 0,
38
+ reward_score REAL NOT NULL DEFAULT 1.0,
39
+ retrieval_count INTEGER NOT NULL DEFAULT 0,
40
+ valid_from TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
41
+ valid_to TEXT DEFAULT NULL,
42
+ superseded_by_id TEXT DEFAULT NULL,
43
+ superseded_reason TEXT DEFAULT NULL,
44
+ source_event_ids TEXT NOT NULL DEFAULT '[]',
45
+ embedding BLOB DEFAULT NULL,
46
+ embedded INTEGER NOT NULL DEFAULT 0,
47
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
48
+ last_recalled_at TEXT DEFAULT NULL,
49
+ provenance_hash TEXT DEFAULT NULL
50
+ );
51
+ CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(project_id, module, valid_to, status);
52
+ CREATE INDEX IF NOT EXISTS idx_memories_embedded ON memories(project_id, embedded);
53
+
54
+ -- 2b. Per-ID Feedback Audit (confidence tiers and 3-consecutive-bad dormancy)
55
+ CREATE TABLE IF NOT EXISTS memory_feedback (
56
+ id TEXT PRIMARY KEY,
57
+ project_id TEXT NOT NULL,
58
+ memory_id TEXT NOT NULL,
59
+ outcome TEXT NOT NULL CHECK (outcome IN ('good', 'bad')),
60
+ note TEXT DEFAULT NULL,
61
+ reward_before REAL NOT NULL,
62
+ reward_after REAL NOT NULL,
63
+ status_before TEXT NOT NULL,
64
+ status_after TEXT NOT NULL,
65
+ confidence_tier TEXT NOT NULL CHECK (confidence_tier IN ('high', 'medium', 'low', 'dormant', 'superseded')),
66
+ bad_streak INTEGER NOT NULL DEFAULT 0,
67
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
68
+ );
69
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_memory ON memory_feedback(memory_id, created_at DESC);
70
+ CREATE INDEX IF NOT EXISTS idx_memory_feedback_project ON memory_feedback(project_id, created_at DESC);
71
+
72
+ -- 3. FTS5 Virtual Table for sub-millisecond BM25 keyword search
73
+ CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
74
+ id UNINDEXED,
75
+ title,
76
+ module,
77
+ content,
78
+ tokenize='porter unicode61'
79
+ );
80
+
81
+ -- Keep FTS5 automatically synced via triggers
82
+ CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
83
+ INSERT INTO memories_fts(id, title, module, content) VALUES (new.id, new.title, new.module, new.content);
84
+ END;
85
+
86
+ CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
87
+ DELETE FROM memories_fts WHERE id = old.id;
88
+ END;
89
+
90
+ -- Only re-index when searchable columns change (not on retrieval counters / scores)
91
+ CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE OF title, module, content ON memories BEGIN
92
+ DELETE FROM memories_fts WHERE id = old.id;
93
+ INSERT INTO memories_fts(id, title, module, content) VALUES (new.id, new.title, new.module, new.content);
94
+ END;
95
+
96
+ -- 4. File & Symbol Graph Links
97
+ CREATE TABLE IF NOT EXISTS file_links (
98
+ memory_id TEXT NOT NULL,
99
+ file_path TEXT NOT NULL,
100
+ symbol_name TEXT DEFAULT '',
101
+ PRIMARY KEY (memory_id, file_path, symbol_name),
102
+ FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE CASCADE
103
+ );
104
+ CREATE INDEX IF NOT EXISTS idx_file_links_path ON file_links(file_path);
105
+
106
+ -- 5. Active Task Snapshot & Cross-Agent Handoffs
107
+ CREATE TABLE IF NOT EXISTS handoffs (
108
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
109
+ project_id TEXT NOT NULL,
110
+ session_id TEXT NOT NULL,
111
+ task_title TEXT NOT NULL,
112
+ summary TEXT NOT NULL,
113
+ next_step TEXT NOT NULL,
114
+ active_files TEXT NOT NULL DEFAULT '[]',
115
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
116
+ );
117
+
118
+ -- 6. Memory Snapshots & Git-Style Point-in-Time Versioning
119
+ CREATE TABLE IF NOT EXISTS memory_snapshots (
120
+ id TEXT PRIMARY KEY,
121
+ project_id TEXT NOT NULL,
122
+ tag TEXT NOT NULL,
123
+ description TEXT,
124
+ memory_count INTEGER NOT NULL,
125
+ state_json TEXT NOT NULL,
126
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
127
+ );
128
+ CREATE INDEX IF NOT EXISTS idx_snapshots_project ON memory_snapshots(project_id, created_at DESC);
129
+
130
+ -- 6b. Memory Branches and Merge Audit Trail
131
+ CREATE TABLE IF NOT EXISTS memory_branches (
132
+ id TEXT PRIMARY KEY,
133
+ project_id TEXT NOT NULL,
134
+ name TEXT NOT NULL COLLATE NOCASE,
135
+ base_snapshot_id TEXT NOT NULL,
136
+ head_snapshot_id TEXT NOT NULL,
137
+ description TEXT DEFAULT NULL,
138
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
139
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
140
+ UNIQUE (project_id, name),
141
+ FOREIGN KEY (base_snapshot_id) REFERENCES memory_snapshots(id) ON DELETE RESTRICT,
142
+ FOREIGN KEY (head_snapshot_id) REFERENCES memory_snapshots(id) ON DELETE RESTRICT
143
+ );
144
+ CREATE INDEX IF NOT EXISTS idx_memory_branches_project ON memory_branches(project_id, created_at DESC);
145
+
146
+ CREATE TABLE IF NOT EXISTS memory_branch_merges (
147
+ id TEXT PRIMARY KEY,
148
+ project_id TEXT NOT NULL,
149
+ branch_id TEXT NOT NULL,
150
+ branch_name TEXT NOT NULL,
151
+ source_snapshot_id TEXT NOT NULL,
152
+ strategy TEXT NOT NULL CHECK (strategy IN ('target', 'branch')),
153
+ applied INTEGER NOT NULL DEFAULT 0,
154
+ added_count INTEGER NOT NULL DEFAULT 0,
155
+ conflict_count INTEGER NOT NULL DEFAULT 0,
156
+ report_json TEXT NOT NULL,
157
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
158
+ FOREIGN KEY (branch_id) REFERENCES memory_branches(id) ON DELETE RESTRICT
159
+ );
160
+ CREATE INDEX IF NOT EXISTS idx_branch_merges_branch ON memory_branch_merges(branch_id, created_at DESC);
161
+
162
+ -- 7. Knowledge Graph Entities
163
+ CREATE TABLE IF NOT EXISTS entities (
164
+ id TEXT PRIMARY KEY,
165
+ project_id TEXT NOT NULL,
166
+ name TEXT NOT NULL,
167
+ entity_type TEXT NOT NULL CHECK (entity_type IN ('symbol', 'file', 'tech', 'service', 'pattern')),
168
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
169
+ );
170
+ CREATE INDEX IF NOT EXISTS idx_entities_name ON entities(project_id, name);
171
+
172
+ -- 8. Multi-Edge Knowledge Graph Links
173
+ CREATE TABLE IF NOT EXISTS graph_edges (
174
+ id TEXT PRIMARY KEY,
175
+ project_id TEXT NOT NULL,
176
+ source_id TEXT NOT NULL,
177
+ target_id TEXT NOT NULL,
178
+ edge_type TEXT NOT NULL CHECK (edge_type IN ('causal', 'entity_link', 'temporal', 'association', 'supersedes')),
179
+ weight REAL NOT NULL DEFAULT 1.0,
180
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
181
+ );
182
+ CREATE INDEX IF NOT EXISTS idx_graph_edges_source ON graph_edges(project_id, source_id);
183
+ CREATE INDEX IF NOT EXISTS idx_graph_edges_target ON graph_edges(project_id, target_id);
184
+
185
+ -- 9. Proactive Contradiction Conflicts
186
+ CREATE TABLE IF NOT EXISTS memory_conflicts (
187
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
188
+ project_id TEXT NOT NULL,
189
+ memory_a_id TEXT NOT NULL,
190
+ memory_b_id TEXT NOT NULL,
191
+ reason TEXT NOT NULL,
192
+ score REAL NOT NULL,
193
+ status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'resolved', 'ignored')),
194
+ resolved_winner_id TEXT DEFAULT NULL,
195
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
196
+ resolved_at TEXT DEFAULT NULL,
197
+ FOREIGN KEY (memory_a_id) REFERENCES memories(id) ON DELETE CASCADE,
198
+ FOREIGN KEY (memory_b_id) REFERENCES memories(id) ON DELETE CASCADE
199
+ );
200
+ CREATE INDEX IF NOT EXISTS idx_conflicts_status ON memory_conflicts(project_id, status);
201
+
202
+ -- 10. Planning Fallacy Predictions & Baserate
203
+ CREATE TABLE IF NOT EXISTS predictions (
204
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
205
+ project_id TEXT NOT NULL,
206
+ class_tag TEXT NOT NULL,
207
+ claim_text TEXT NOT NULL,
208
+ estimate_value REAL,
209
+ estimate_unit TEXT,
210
+ actual_value REAL DEFAULT NULL,
211
+ closure_state TEXT NOT NULL DEFAULT 'open' CHECK (closure_state IN ('open', 'closed', 'closed-unknown')),
212
+ closed_at TEXT DEFAULT NULL,
213
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
214
+ );
215
+ CREATE INDEX IF NOT EXISTS idx_predictions_class ON predictions(project_id, class_tag, closure_state);
216
+
217
+ -- 11. Multi-Agent Work Item Action DAG
218
+ CREATE TABLE IF NOT EXISTS actions (
219
+ id TEXT PRIMARY KEY,
220
+ project_id TEXT NOT NULL,
221
+ title TEXT NOT NULL,
222
+ description TEXT NOT NULL DEFAULT '',
223
+ status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'active', 'done', 'blocked', 'cancelled')),
224
+ priority INTEGER NOT NULL DEFAULT 5,
225
+ assigned_to TEXT DEFAULT NULL,
226
+ crystallized_into TEXT DEFAULT NULL,
227
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
228
+ completed_at TEXT DEFAULT NULL
229
+ );
230
+ CREATE INDEX IF NOT EXISTS idx_actions_status ON actions(project_id, status, priority DESC);
231
+
232
+ CREATE TABLE IF NOT EXISTS action_edges (
233
+ source_action_id TEXT NOT NULL,
234
+ target_action_id TEXT NOT NULL,
235
+ edge_type TEXT NOT NULL CHECK (edge_type IN ('requires', 'unlocks', 'conflicts_with')),
236
+ PRIMARY KEY (source_action_id, target_action_id, edge_type),
237
+ FOREIGN KEY (source_action_id) REFERENCES actions(id) ON DELETE CASCADE,
238
+ FOREIGN KEY (target_action_id) REFERENCES actions(id) ON DELETE CASCADE
239
+ );
240
+
241
+ -- 12. Bounded Working Memory Slots
242
+ CREATE TABLE IF NOT EXISTS memory_slots (
243
+ project_id TEXT NOT NULL,
244
+ slot_key TEXT NOT NULL,
245
+ content TEXT NOT NULL,
246
+ size_limit INTEGER NOT NULL DEFAULT 4000,
247
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
248
+ PRIMARY KEY (project_id, slot_key)
249
+ );
250
+
251
+ -- 13. Multimodal Vision Assets & Screenshots
252
+ CREATE TABLE IF NOT EXISTS vision_assets (
253
+ id TEXT PRIMARY KEY,
254
+ project_id TEXT NOT NULL,
255
+ file_path TEXT NOT NULL,
256
+ description TEXT NOT NULL,
257
+ ocr_text TEXT DEFAULT NULL,
258
+ memory_id TEXT DEFAULT NULL,
259
+ task_id TEXT DEFAULT NULL,
260
+ embedding BLOB DEFAULT NULL,
261
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
262
+ FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE SET NULL,
263
+ FOREIGN KEY (task_id) REFERENCES actions(id) ON DELETE SET NULL
264
+ );
265
+ CREATE INDEX IF NOT EXISTS idx_vision_project ON vision_assets(project_id, created_at DESC);
266
+ CREATE INDEX IF NOT EXISTS idx_vision_memory ON vision_assets(memory_id);
267
267
  `;
268
268
  export function configurePragmas(db) {
269
269
  try {
@@ -293,11 +293,11 @@ export function applySchema(db) {
293
293
  catch { }
294
294
  migrateMemoriesUpdateTrigger(db);
295
295
  }
296
- const MEMORIES_AU_TRIGGER_SQL = `
297
- CREATE TRIGGER memories_au AFTER UPDATE OF title, module, content ON memories BEGIN
298
- DELETE FROM memories_fts WHERE id = old.id;
299
- INSERT INTO memories_fts(id, title, module, content) VALUES (new.id, new.title, new.module, new.content);
300
- END;
296
+ const MEMORIES_AU_TRIGGER_SQL = `
297
+ CREATE TRIGGER memories_au AFTER UPDATE OF title, module, content ON memories BEGIN
298
+ DELETE FROM memories_fts WHERE id = old.id;
299
+ INSERT INTO memories_fts(id, title, module, content) VALUES (new.id, new.title, new.module, new.content);
300
+ END;
301
301
  `;
302
302
  /**
303
303
  * Older databases carry an unconditional AFTER UPDATE trigger that re-indexes FTS on every
@@ -97,10 +97,10 @@ export class SpoolManager {
97
97
  }
98
98
  if (events.length > 0) {
99
99
  try {
100
- const insertStmt = db.prepare(`
101
- INSERT OR IGNORE INTO raw_events (
102
- id, project_id, session_id, agent_id, event_type, tool_name, file_path, raw_payload, content_hash, created_at
103
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, COALESCE(?, strftime('%Y-%m-%dT%H:%M:%SZ', 'now')))
100
+ const insertStmt = db.prepare(`
101
+ INSERT OR IGNORE INTO raw_events (
102
+ id, project_id, session_id, agent_id, event_type, tool_name, file_path, raw_payload, content_hash, created_at
103
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, COALESCE(?, strftime('%Y-%m-%dT%H:%M:%SZ', 'now')))
104
104
  `);
105
105
  db.exec('BEGIN TRANSACTION');
106
106
  try {
@@ -55,25 +55,25 @@ export function exportToObsidianVault(db, projectId, targetDir) {
55
55
  fs.mkdirSync(targetDir, { recursive: true });
56
56
  }
57
57
  // 1. Fetch active memories
58
- const memories = db.prepare(`
59
- SELECT id, kind, title, module, content, status, reward_score, created_at, valid_to
60
- FROM memories
61
- WHERE project_id = ?
62
- AND (valid_to IS NULL OR valid_to = '')
63
- AND status != 'superseded'
64
- ORDER BY kind ASC, reward_score DESC, created_at DESC
58
+ const memories = db.prepare(`
59
+ SELECT id, kind, title, module, content, status, reward_score, created_at, valid_to
60
+ FROM memories
61
+ WHERE project_id = ?
62
+ AND (valid_to IS NULL OR valid_to = '')
63
+ AND status != 'superseded'
64
+ ORDER BY kind ASC, reward_score DESC, created_at DESC
65
65
  `).all(projectId);
66
66
  // 2. Fetch entities
67
- const entities = db.prepare(`
68
- SELECT id, name, entity_type
69
- FROM entities
70
- WHERE project_id = ?
71
- ORDER BY name ASC
67
+ const entities = db.prepare(`
68
+ SELECT id, name, entity_type
69
+ FROM entities
70
+ WHERE project_id = ?
71
+ ORDER BY name ASC
72
72
  `).all(projectId);
73
73
  // 3. Fetch file links
74
- const fileLinks = db.prepare(`
75
- SELECT memory_id, file_path, symbol_name
76
- FROM file_links
74
+ const fileLinks = db.prepare(`
75
+ SELECT memory_id, file_path, symbol_name
76
+ FROM file_links
77
77
  `).all();
78
78
  const memoryFilesMap = new Map();
79
79
  for (const fl of fileLinks) {
@@ -125,10 +125,10 @@ export function parseMarkdownMemories(markdown) {
125
125
  });
126
126
  }
127
127
  function existsInProject(db, projectId, title, content) {
128
- const row = db.prepare(`
129
- SELECT id FROM memories
130
- WHERE project_id = ? AND title = ? AND content = ? AND status != 'superseded'
131
- LIMIT 1
128
+ const row = db.prepare(`
129
+ SELECT id FROM memories
130
+ WHERE project_id = ? AND title = ? AND content = ? AND status != 'superseded'
131
+ LIMIT 1
132
132
  `).get(projectId, title, content);
133
133
  return Boolean(row);
134
134
  }