squish-memory 1.0.0 → 1.0.2

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 (300) hide show
  1. package/.env.mcp.example +18 -11
  2. package/README.md +59 -24
  3. package/bin/squish-add.mjs +32 -0
  4. package/bin/squish-rm.mjs +21 -0
  5. package/config/plugin-manifest.json +1 -1
  6. package/config/settings.json +51 -0
  7. package/dist/api/web/web.js +479 -452
  8. package/dist/commands/mcp-server.js +7 -3
  9. package/dist/config.d.ts +10 -10
  10. package/dist/config.js +78 -23
  11. package/dist/core/embeddings.d.ts +1 -1
  12. package/dist/core/embeddings.js +10 -67
  13. package/dist/core/local-embeddings.d.ts +3 -11
  14. package/dist/core/local-embeddings.js +2 -76
  15. package/dist/core/mcp/server.js +27 -1
  16. package/dist/core/mcp/types.d.ts +4 -4
  17. package/dist/core/memory/context-collector.js +3 -2
  18. package/dist/core/memory/feedback-tracker.js +10 -6
  19. package/dist/core/memory/hybrid-search.js +32 -32
  20. package/dist/core/memory/memories.js +55 -52
  21. package/dist/core/memory/query-rewriter.js +9 -9
  22. package/dist/core/memory/stats.js +5 -5
  23. package/dist/core/namespaces/index.js +20 -11
  24. package/dist/core/scheduler/cron-scheduler.js +78 -20
  25. package/dist/core/scheduler/job-runner.js +8 -5
  26. package/dist/core/search/conversations.js +33 -33
  27. package/dist/core/session-hooks/self-iteration-job.js +43 -39
  28. package/dist/core/session-hooks/session-hooks.js +6 -3
  29. package/dist/core/tracing/collector.js +25 -13
  30. package/dist/db/adapter.d.ts +6 -1
  31. package/dist/db/adapter.js +122 -126
  32. package/dist/db/bootstrap.js +622 -548
  33. package/dist/db/index.d.ts +5 -1
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.js +195 -49
  36. package/generated/mcp/manifest.json +23 -23
  37. package/package.json +72 -59
  38. package/scripts/install-interactive.mjs +7 -4
  39. package/skills/memory-guide/SKILL.md +94 -18
  40. package/skills/squish-cli/SKILL.md +61 -21
  41. package/skills/squish-mcp/SKILL.md +46 -2
  42. package/skills/squish-memory/SKILL.md +30 -16
  43. package/dist/algorithms/analytics/token-estimator.d.ts.map +0 -1
  44. package/dist/algorithms/analytics/token-estimator.js.map +0 -1
  45. package/dist/algorithms/detection/hash-filters.d.ts.map +0 -1
  46. package/dist/algorithms/detection/hash-filters.js.map +0 -1
  47. package/dist/algorithms/detection/semantic-ranker.d.ts.map +0 -1
  48. package/dist/algorithms/detection/semantic-ranker.js.map +0 -1
  49. package/dist/algorithms/detection/two-stage-detector.d.ts.map +0 -1
  50. package/dist/algorithms/detection/two-stage-detector.js.map +0 -1
  51. package/dist/algorithms/handlers/approve-merge.d.ts.map +0 -1
  52. package/dist/algorithms/handlers/approve-merge.js.map +0 -1
  53. package/dist/algorithms/handlers/detect-duplicates.d.ts.map +0 -1
  54. package/dist/algorithms/handlers/detect-duplicates.js.map +0 -1
  55. package/dist/algorithms/handlers/get-stats.d.ts.map +0 -1
  56. package/dist/algorithms/handlers/get-stats.js.map +0 -1
  57. package/dist/algorithms/handlers/list-proposals.d.ts.map +0 -1
  58. package/dist/algorithms/handlers/list-proposals.js.map +0 -1
  59. package/dist/algorithms/handlers/preview-merge.d.ts.map +0 -1
  60. package/dist/algorithms/handlers/preview-merge.js.map +0 -1
  61. package/dist/algorithms/handlers/reject-merge.d.ts.map +0 -1
  62. package/dist/algorithms/handlers/reject-merge.js.map +0 -1
  63. package/dist/algorithms/handlers/reverse-merge.d.ts.map +0 -1
  64. package/dist/algorithms/handlers/reverse-merge.js.map +0 -1
  65. package/dist/algorithms/safety/safety-checks.d.ts.map +0 -1
  66. package/dist/algorithms/safety/safety-checks.js.map +0 -1
  67. package/dist/algorithms/strategies/merge-strategies.d.ts.map +0 -1
  68. package/dist/algorithms/strategies/merge-strategies.js.map +0 -1
  69. package/dist/algorithms/utils/response-builder.d.ts.map +0 -1
  70. package/dist/algorithms/utils/response-builder.js.map +0 -1
  71. package/dist/api/web/index.d.ts.map +0 -1
  72. package/dist/api/web/index.js.map +0 -1
  73. package/dist/api/web/web-server.d.ts.map +0 -1
  74. package/dist/api/web/web-server.js.map +0 -1
  75. package/dist/api/web/web.d.ts.map +0 -1
  76. package/dist/api/web/web.js.map +0 -1
  77. package/dist/commands/managed-sync.d.ts.map +0 -1
  78. package/dist/commands/managed-sync.js.map +0 -1
  79. package/dist/commands/mcp-server.d.ts.map +0 -1
  80. package/dist/commands/mcp-server.js.map +0 -1
  81. package/dist/config.d.ts.map +0 -1
  82. package/dist/config.js.map +0 -1
  83. package/dist/core/agent-memory.d.ts.map +0 -1
  84. package/dist/core/agent-memory.js.map +0 -1
  85. package/dist/core/associations.d.ts.map +0 -1
  86. package/dist/core/associations.js.map +0 -1
  87. package/dist/core/cache.d.ts.map +0 -1
  88. package/dist/core/cache.js.map +0 -1
  89. package/dist/core/consolidation.d.ts.map +0 -1
  90. package/dist/core/consolidation.js.map +0 -1
  91. package/dist/core/context-paging.d.ts.map +0 -1
  92. package/dist/core/context-paging.js.map +0 -1
  93. package/dist/core/context.d.ts.map +0 -1
  94. package/dist/core/context.js.map +0 -1
  95. package/dist/core/core-memory.d.ts.map +0 -1
  96. package/dist/core/core-memory.js.map +0 -1
  97. package/dist/core/database.d.ts.map +0 -1
  98. package/dist/core/database.js.map +0 -1
  99. package/dist/core/embeddings/google-multimodal.d.ts.map +0 -1
  100. package/dist/core/embeddings/google-multimodal.js.map +0 -1
  101. package/dist/core/embeddings/qmd-client.d.ts.map +0 -1
  102. package/dist/core/embeddings/qmd-client.js.map +0 -1
  103. package/dist/core/embeddings.d.ts.map +0 -1
  104. package/dist/core/embeddings.js.map +0 -1
  105. package/dist/core/governance.d.ts.map +0 -1
  106. package/dist/core/governance.js.map +0 -1
  107. package/dist/core/index.d.ts.map +0 -1
  108. package/dist/core/index.js.map +0 -1
  109. package/dist/core/layers/generator.d.ts.map +0 -1
  110. package/dist/core/layers/generator.js.map +0 -1
  111. package/dist/core/lifecycle.d.ts.map +0 -1
  112. package/dist/core/lifecycle.js.map +0 -1
  113. package/dist/core/local-embeddings.d.ts.map +0 -1
  114. package/dist/core/local-embeddings.js.map +0 -1
  115. package/dist/core/logger.d.ts.map +0 -1
  116. package/dist/core/logger.js.map +0 -1
  117. package/dist/core/mcp/client.d.ts.map +0 -1
  118. package/dist/core/mcp/client.js.map +0 -1
  119. package/dist/core/mcp/index.d.ts.map +0 -1
  120. package/dist/core/mcp/index.js.map +0 -1
  121. package/dist/core/mcp/server.d.ts.map +0 -1
  122. package/dist/core/mcp/server.js.map +0 -1
  123. package/dist/core/mcp/standalone-server.d.ts.map +0 -1
  124. package/dist/core/mcp/standalone-server.js.map +0 -1
  125. package/dist/core/mcp/tools.d.ts.map +0 -1
  126. package/dist/core/mcp/tools.js.map +0 -1
  127. package/dist/core/mcp/types.d.ts.map +0 -1
  128. package/dist/core/mcp/types.js.map +0 -1
  129. package/dist/core/memory/bridge-discovery.d.ts.map +0 -1
  130. package/dist/core/memory/bridge-discovery.js.map +0 -1
  131. package/dist/core/memory/categorizer.d.ts.map +0 -1
  132. package/dist/core/memory/categorizer.js.map +0 -1
  133. package/dist/core/memory/conflict-detector.d.ts.map +0 -1
  134. package/dist/core/memory/conflict-detector.js.map +0 -1
  135. package/dist/core/memory/consolidation.d.ts.map +0 -1
  136. package/dist/core/memory/consolidation.js.map +0 -1
  137. package/dist/core/memory/context-collector.d.ts.map +0 -1
  138. package/dist/core/memory/context-collector.js.map +0 -1
  139. package/dist/core/memory/contradiction-resolver.d.ts.map +0 -1
  140. package/dist/core/memory/contradiction-resolver.js.map +0 -1
  141. package/dist/core/memory/edit-workflow.d.ts.map +0 -1
  142. package/dist/core/memory/edit-workflow.js.map +0 -1
  143. package/dist/core/memory/entity-extractor.d.ts.map +0 -1
  144. package/dist/core/memory/entity-extractor.js.map +0 -1
  145. package/dist/core/memory/entity-resolver.d.ts.map +0 -1
  146. package/dist/core/memory/entity-resolver.js.map +0 -1
  147. package/dist/core/memory/fact-extractor.d.ts.map +0 -1
  148. package/dist/core/memory/fact-extractor.js.map +0 -1
  149. package/dist/core/memory/feedback-tracker.d.ts.map +0 -1
  150. package/dist/core/memory/feedback-tracker.js.map +0 -1
  151. package/dist/core/memory/hybrid-retrieval.d.ts.map +0 -1
  152. package/dist/core/memory/hybrid-retrieval.js.map +0 -1
  153. package/dist/core/memory/hybrid-scorer.d.ts.map +0 -1
  154. package/dist/core/memory/hybrid-scorer.js.map +0 -1
  155. package/dist/core/memory/hybrid-search.d.ts.map +0 -1
  156. package/dist/core/memory/hybrid-search.js.map +0 -1
  157. package/dist/core/memory/importance.d.ts.map +0 -1
  158. package/dist/core/memory/importance.js.map +0 -1
  159. package/dist/core/memory/index.d.ts.map +0 -1
  160. package/dist/core/memory/index.js.map +0 -1
  161. package/dist/core/memory/memories.d.ts.map +0 -1
  162. package/dist/core/memory/memories.js.map +0 -1
  163. package/dist/core/memory/memory-manager.d.ts.map +0 -1
  164. package/dist/core/memory/memory-manager.js.map +0 -1
  165. package/dist/core/memory/progressive-disclosure.d.ts.map +0 -1
  166. package/dist/core/memory/progressive-disclosure.js.map +0 -1
  167. package/dist/core/memory/query-processor.d.ts.map +0 -1
  168. package/dist/core/memory/query-processor.js.map +0 -1
  169. package/dist/core/memory/query-rewriter.d.ts.map +0 -1
  170. package/dist/core/memory/query-rewriter.js.map +0 -1
  171. package/dist/core/memory/response-analyzer.d.ts.map +0 -1
  172. package/dist/core/memory/response-analyzer.js.map +0 -1
  173. package/dist/core/memory/serialization.d.ts.map +0 -1
  174. package/dist/core/memory/serialization.js.map +0 -1
  175. package/dist/core/memory/stats.d.ts.map +0 -1
  176. package/dist/core/memory/stats.js.map +0 -1
  177. package/dist/core/memory/telemetry.d.ts.map +0 -1
  178. package/dist/core/memory/telemetry.js.map +0 -1
  179. package/dist/core/memory/temporal-facts.d.ts.map +0 -1
  180. package/dist/core/memory/temporal-facts.js.map +0 -1
  181. package/dist/core/memory/temporal-parser.d.ts.map +0 -1
  182. package/dist/core/memory/temporal-parser.js.map +0 -1
  183. package/dist/core/memory/trigger-detector.d.ts.map +0 -1
  184. package/dist/core/memory/trigger-detector.js.map +0 -1
  185. package/dist/core/memory/write-gate.d.ts.map +0 -1
  186. package/dist/core/memory/write-gate.js.map +0 -1
  187. package/dist/core/namespaces/index.d.ts.map +0 -1
  188. package/dist/core/namespaces/index.js.map +0 -1
  189. package/dist/core/namespaces/uri-parser.d.ts.map +0 -1
  190. package/dist/core/namespaces/uri-parser.js.map +0 -1
  191. package/dist/core/observations.d.ts.map +0 -1
  192. package/dist/core/observations.js.map +0 -1
  193. package/dist/core/privacy.d.ts.map +0 -1
  194. package/dist/core/privacy.js.map +0 -1
  195. package/dist/core/projects.d.ts.map +0 -1
  196. package/dist/core/projects.js.map +0 -1
  197. package/dist/core/redis.d.ts.map +0 -1
  198. package/dist/core/redis.js.map +0 -1
  199. package/dist/core/requirements.d.ts.map +0 -1
  200. package/dist/core/requirements.js.map +0 -1
  201. package/dist/core/scheduler/cron-scheduler.d.ts.map +0 -1
  202. package/dist/core/scheduler/cron-scheduler.js.map +0 -1
  203. package/dist/core/scheduler/heartbeat.d.ts.map +0 -1
  204. package/dist/core/scheduler/heartbeat.js.map +0 -1
  205. package/dist/core/scheduler/index.d.ts.map +0 -1
  206. package/dist/core/scheduler/index.js.map +0 -1
  207. package/dist/core/scheduler/job-runner.d.ts.map +0 -1
  208. package/dist/core/scheduler/job-runner.js.map +0 -1
  209. package/dist/core/search/conversations.d.ts.map +0 -1
  210. package/dist/core/search/conversations.js.map +0 -1
  211. package/dist/core/search/entities.d.ts.map +0 -1
  212. package/dist/core/search/entities.js.map +0 -1
  213. package/dist/core/search/folder-context.d.ts.map +0 -1
  214. package/dist/core/search/folder-context.js.map +0 -1
  215. package/dist/core/search/index.d.ts.map +0 -1
  216. package/dist/core/search/index.js.map +0 -1
  217. package/dist/core/search/qmd-search.d.ts.map +0 -1
  218. package/dist/core/search/qmd-search.js.map +0 -1
  219. package/dist/core/secret-detector.d.ts.map +0 -1
  220. package/dist/core/secret-detector.js.map +0 -1
  221. package/dist/core/session/auto-load.d.ts.map +0 -1
  222. package/dist/core/session/auto-load.js.map +0 -1
  223. package/dist/core/session/index.d.ts.map +0 -1
  224. package/dist/core/session/index.js.map +0 -1
  225. package/dist/core/session/types.d.ts.map +0 -1
  226. package/dist/core/session/types.js.map +0 -1
  227. package/dist/core/session-hooks/self-iteration-job.d.ts.map +0 -1
  228. package/dist/core/session-hooks/self-iteration-job.js.map +0 -1
  229. package/dist/core/session-hooks/session-hooks.d.ts.map +0 -1
  230. package/dist/core/session-hooks/session-hooks.js.map +0 -1
  231. package/dist/core/snapshots/cleanup.d.ts.map +0 -1
  232. package/dist/core/snapshots/cleanup.js.map +0 -1
  233. package/dist/core/snapshots/comparison.d.ts.map +0 -1
  234. package/dist/core/snapshots/comparison.js.map +0 -1
  235. package/dist/core/snapshots/creation.d.ts.map +0 -1
  236. package/dist/core/snapshots/creation.js.map +0 -1
  237. package/dist/core/snapshots/retrieval.d.ts.map +0 -1
  238. package/dist/core/snapshots/retrieval.js.map +0 -1
  239. package/dist/core/snapshots/stats.d.ts.map +0 -1
  240. package/dist/core/snapshots/stats.js.map +0 -1
  241. package/dist/core/snapshots.d.ts.map +0 -1
  242. package/dist/core/snapshots.js.map +0 -1
  243. package/dist/core/summarization/cleanup.d.ts.map +0 -1
  244. package/dist/core/summarization/cleanup.js.map +0 -1
  245. package/dist/core/summarization/queries.d.ts.map +0 -1
  246. package/dist/core/summarization/queries.js.map +0 -1
  247. package/dist/core/summarization/stats.d.ts.map +0 -1
  248. package/dist/core/summarization/stats.js.map +0 -1
  249. package/dist/core/summarization/strategies.d.ts.map +0 -1
  250. package/dist/core/summarization/strategies.js.map +0 -1
  251. package/dist/core/summarization.d.ts.map +0 -1
  252. package/dist/core/summarization.js.map +0 -1
  253. package/dist/core/sync/qmd-sync.d.ts.map +0 -1
  254. package/dist/core/sync/qmd-sync.js.map +0 -1
  255. package/dist/core/temporal-facts.d.ts.map +0 -1
  256. package/dist/core/temporal-facts.js.map +0 -1
  257. package/dist/core/tracing/collector.d.ts.map +0 -1
  258. package/dist/core/tracing/collector.js.map +0 -1
  259. package/dist/core/tracing/visualizer.d.ts.map +0 -1
  260. package/dist/core/tracing/visualizer.js.map +0 -1
  261. package/dist/core/utils/cleanup-operations.d.ts.map +0 -1
  262. package/dist/core/utils/cleanup-operations.js.map +0 -1
  263. package/dist/core/utils/content-extraction.d.ts.map +0 -1
  264. package/dist/core/utils/content-extraction.js.map +0 -1
  265. package/dist/core/utils/filter-builder.d.ts.map +0 -1
  266. package/dist/core/utils/filter-builder.js.map +0 -1
  267. package/dist/core/utils/history-traversal.d.ts.map +0 -1
  268. package/dist/core/utils/history-traversal.js.map +0 -1
  269. package/dist/core/utils/memory-operations.d.ts.map +0 -1
  270. package/dist/core/utils/memory-operations.js.map +0 -1
  271. package/dist/core/utils/query-operations.d.ts.map +0 -1
  272. package/dist/core/utils/query-operations.js.map +0 -1
  273. package/dist/core/utils/summarization-helpers.d.ts.map +0 -1
  274. package/dist/core/utils/summarization-helpers.js.map +0 -1
  275. package/dist/core/utils/temporal-queries.d.ts.map +0 -1
  276. package/dist/core/utils/temporal-queries.js.map +0 -1
  277. package/dist/core/utils/version-management.d.ts.map +0 -1
  278. package/dist/core/utils/version-management.js.map +0 -1
  279. package/dist/core/utils.d.ts.map +0 -1
  280. package/dist/core/utils.js.map +0 -1
  281. package/dist/core/worker.d.ts.map +0 -1
  282. package/dist/core/worker.js.map +0 -1
  283. package/dist/db/adapter.d.ts.map +0 -1
  284. package/dist/db/adapter.js.map +0 -1
  285. package/dist/db/bootstrap.d.ts.map +0 -1
  286. package/dist/db/bootstrap.js.map +0 -1
  287. package/dist/db/index.d.ts.map +0 -1
  288. package/dist/db/index.js.map +0 -1
  289. package/dist/db/schema.d.ts.map +0 -1
  290. package/dist/db/schema.js.map +0 -1
  291. package/dist/drizzle/schema-sqlite.d.ts.map +0 -1
  292. package/dist/drizzle/schema-sqlite.js.map +0 -1
  293. package/dist/drizzle/schema.d.ts.map +0 -1
  294. package/dist/drizzle/schema.js.map +0 -1
  295. package/dist/index.d.ts.map +0 -1
  296. package/dist/index.js.map +0 -1
  297. package/packages/plugin-claude-code/dist/plugin-wrapper.d.ts.map +0 -1
  298. package/packages/plugin-claude-code/dist/plugin-wrapper.js.map +0 -1
  299. package/packages/plugin-openclaw/dist/index.d.ts.map +0 -1
  300. package/packages/plugin-openclaw/dist/index.js.map +0 -1
@@ -6,531 +6,569 @@ import { getDataDir } from '../config.js';
6
6
  * SQLite uses INTEGER 0/1 for boolean values (no native boolean type)
7
7
  * PostgreSQL uses native BOOLEAN type
8
8
  */
9
- const sqliteSchemaSql = `
10
- PRAGMA foreign_keys = ON;
11
-
12
- CREATE TABLE IF NOT EXISTS users (
13
- id TEXT PRIMARY KEY,
14
- external_id TEXT UNIQUE,
15
- name TEXT,
16
- email TEXT,
17
- preferences TEXT,
18
- created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
19
- updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
20
- );
21
-
22
- CREATE TABLE IF NOT EXISTS projects (
23
- id TEXT PRIMARY KEY,
24
- name TEXT NOT NULL,
25
- path TEXT NOT NULL,
26
- description TEXT,
27
- metadata TEXT,
28
- created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
29
- updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
30
- );
31
-
32
- CREATE INDEX IF NOT EXISTS projects_path_idx ON projects(path);
33
-
34
- CREATE TABLE IF NOT EXISTS memories (
35
- id TEXT PRIMARY KEY,
36
- project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
37
- user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
38
- type TEXT NOT NULL,
39
- content TEXT NOT NULL,
40
- summary TEXT,
41
- embedding_json TEXT,
42
- embedding BLOB,
43
- source TEXT,
44
- confidence INTEGER DEFAULT 100,
45
- tags TEXT,
46
- metadata TEXT,
47
- is_private INTEGER DEFAULT 0,
48
- has_secrets INTEGER DEFAULT 0,
49
- relevance_score INTEGER DEFAULT 50,
50
- is_active INTEGER DEFAULT 1,
51
- expires_at INTEGER,
52
- access_count INTEGER DEFAULT 0,
53
- last_accessed_at INTEGER,
54
- is_merged INTEGER DEFAULT 0,
55
- merged_into_id TEXT,
56
- merged_at INTEGER,
57
- is_canonical INTEGER DEFAULT 0,
58
- merge_source_ids TEXT,
59
- is_mergeable INTEGER DEFAULT 1,
60
- merge_version INTEGER DEFAULT 1,
61
- importance_score INTEGER DEFAULT 50,
62
- importance_decay_rate INTEGER DEFAULT 30,
63
- last_importance_recalc INTEGER,
64
- consolidated_into TEXT,
65
- consolidated_at INTEGER,
66
- is_consolidated INTEGER DEFAULT 0,
67
- sector TEXT DEFAULT 'episodic',
68
- tier TEXT DEFAULT 'hot',
69
- context_status TEXT DEFAULT 'out-of-context',
70
- decay_rate INTEGER DEFAULT 30,
71
- coactivation_score INTEGER DEFAULT 0,
72
- last_decay_at INTEGER DEFAULT (strftime('%s','now')),
73
- agent_id TEXT,
74
- agent_role TEXT,
75
- visibility_scope TEXT DEFAULT 'private',
76
- is_protected INTEGER DEFAULT 0,
77
- is_pinned INTEGER DEFAULT 0,
78
- is_immutable INTEGER DEFAULT 0,
79
- write_scope TEXT,
80
- read_scope TEXT,
81
- triggered_by TEXT,
82
- capture_reason TEXT,
83
- last_used_at INTEGER,
84
- usage_count INTEGER DEFAULT 0,
85
- valid_from INTEGER,
86
- valid_to INTEGER,
87
- superseded_by TEXT,
88
- version INTEGER DEFAULT 1,
89
- created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
90
- updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
91
- );
92
-
93
- CREATE INDEX IF NOT EXISTS memories_project_idx ON memories(project_id);
94
- CREATE INDEX IF NOT EXISTS memories_type_idx ON memories(type);
95
- CREATE INDEX IF NOT EXISTS memories_created_idx ON memories(created_at);
96
- CREATE INDEX IF NOT EXISTS memories_tags_idx ON memories(tags);
97
-
98
- CREATE TABLE IF NOT EXISTS memory_associations (
99
- id TEXT PRIMARY KEY,
100
- from_memory_id TEXT NOT NULL,
101
- to_memory_id TEXT NOT NULL,
102
- association_type TEXT NOT NULL,
103
- weight REAL DEFAULT 1,
104
- coactivation_count INTEGER DEFAULT 1,
105
- metadata TEXT,
106
- last_coactivated_at INTEGER,
107
- created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
108
- UNIQUE(from_memory_id, to_memory_id)
109
- );
110
-
111
- CREATE TABLE IF NOT EXISTS conversations (
112
- id TEXT PRIMARY KEY,
113
- project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
114
- user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
115
- session_id TEXT NOT NULL,
116
- title TEXT,
117
- summary TEXT,
118
- message_count INTEGER DEFAULT 0,
119
- token_count INTEGER DEFAULT 0,
120
- started_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
121
- ended_at INTEGER,
122
- metadata TEXT,
123
- created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
124
- updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
125
- );
126
-
127
- CREATE INDEX IF NOT EXISTS conversations_project_idx ON conversations(project_id);
128
- CREATE INDEX IF NOT EXISTS conversations_session_idx ON conversations(session_id);
129
- CREATE INDEX IF NOT EXISTS conversations_started_idx ON conversations(started_at);
130
-
131
- CREATE TABLE IF NOT EXISTS messages (
132
- id TEXT PRIMARY KEY,
133
- conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
134
- role TEXT NOT NULL,
135
- content TEXT NOT NULL,
136
- embedding_json TEXT,
137
- token_count INTEGER,
138
- tool_calls TEXT,
139
- metadata TEXT,
140
- created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
141
- );
142
-
143
- CREATE INDEX IF NOT EXISTS messages_conversation_idx ON messages(conversation_id);
144
- CREATE INDEX IF NOT EXISTS messages_role_idx ON messages(role);
145
- CREATE INDEX IF NOT EXISTS messages_created_idx ON messages(created_at);
146
-
147
- CREATE TABLE IF NOT EXISTS observations (
148
- id TEXT PRIMARY KEY,
149
- project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
150
- conversation_id TEXT REFERENCES conversations(id) ON DELETE SET NULL,
151
- type TEXT NOT NULL,
152
- action TEXT NOT NULL,
153
- target TEXT,
154
- summary TEXT NOT NULL,
155
- details TEXT,
156
- embedding_json TEXT,
157
- category TEXT,
158
- importance INTEGER DEFAULT 50,
159
- metadata TEXT,
160
- created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
161
- );
162
-
163
- CREATE INDEX IF NOT EXISTS observations_project_idx ON observations(project_id);
164
- CREATE INDEX IF NOT EXISTS observations_type_idx ON observations(type);
165
- CREATE INDEX IF NOT EXISTS observations_action_idx ON observations(action);
166
- CREATE INDEX IF NOT EXISTS observations_created_idx ON observations(created_at);
167
-
168
- CREATE TABLE IF NOT EXISTS entities (
169
- id TEXT PRIMARY KEY,
170
- project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
171
- name TEXT NOT NULL,
172
- type TEXT NOT NULL,
173
- description TEXT,
174
- embedding_json TEXT,
175
- properties TEXT,
176
- created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
177
- updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
178
- );
179
-
180
- CREATE INDEX IF NOT EXISTS entities_project_idx ON entities(project_id);
181
- CREATE INDEX IF NOT EXISTS entities_type_idx ON entities(type);
182
- CREATE INDEX IF NOT EXISTS entities_name_idx ON entities(name);
183
-
184
- CREATE TABLE IF NOT EXISTS entity_relations (
185
- id TEXT PRIMARY KEY,
186
- from_entity_id TEXT NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
187
- to_entity_id TEXT NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
188
- type TEXT NOT NULL,
189
- weight INTEGER DEFAULT 1,
190
- properties TEXT,
191
- created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
192
- );
193
-
194
- CREATE INDEX IF NOT EXISTS relations_from_idx ON entity_relations(from_entity_id);
195
- CREATE INDEX IF NOT EXISTS relations_to_idx ON entity_relations(to_entity_id);
196
- CREATE INDEX IF NOT EXISTS relations_type_idx ON entity_relations(type);
197
-
198
- CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
199
- content,
200
- tags,
201
- content='memories',
202
- content_rowid='rowid'
203
- );
204
-
205
- CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
206
- INSERT INTO memories_fts(rowid, content, tags)
207
- VALUES (new.rowid, new.content, COALESCE(new.tags, ''));
208
- END;
209
-
210
- CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
211
- INSERT INTO memories_fts(memories_fts, rowid, content, tags)
212
- VALUES ('delete', old.rowid, old.content, COALESCE(old.tags, ''));
213
- END;
214
-
215
- CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
216
- INSERT INTO memories_fts(memories_fts, rowid, content, tags)
217
- VALUES ('delete', old.rowid, old.content, COALESCE(old.tags, ''));
218
- INSERT INTO memories_fts(rowid, content, tags)
219
- VALUES (new.rowid, new.content, COALESCE(new.tags, ''));
220
- END;
221
-
222
- CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
223
- content,
224
- content='messages',
225
- content_rowid='rowid'
226
- );
227
-
228
- CREATE TRIGGER IF NOT EXISTS messages_ai AFTER INSERT ON messages BEGIN
229
- INSERT INTO messages_fts(rowid, content)
230
- VALUES (new.rowid, new.content);
231
- END;
232
-
233
- CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN
234
- INSERT INTO messages_fts(messages_fts, rowid, content)
235
- VALUES ('delete', old.rowid, old.content);
236
- END;
237
-
238
- CREATE TRIGGER IF NOT EXISTS messages_au AFTER UPDATE ON messages BEGIN
239
- INSERT INTO messages_fts(messages_fts, rowid, content)
240
- VALUES ('delete', old.rowid, old.content);
241
- INSERT INTO messages_fts(rowid, content)
242
- VALUES (new.rowid, new.content);
243
- END;
244
-
245
- CREATE TABLE IF NOT EXISTS core_memory (
246
- id TEXT PRIMARY KEY,
247
- project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
248
- user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
249
- section TEXT NOT NULL,
250
- content TEXT NOT NULL DEFAULT '',
251
- size_bytes INTEGER DEFAULT 0 NOT NULL,
252
- tokens_estimate INTEGER DEFAULT 0 NOT NULL,
253
- version INTEGER DEFAULT 1 NOT NULL,
254
- created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
255
- updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
256
- );
257
-
258
- CREATE INDEX IF NOT EXISTS core_memory_project_idx ON core_memory(project_id);
259
- CREATE INDEX IF NOT EXISTS core_memory_user_idx ON core_memory(user_id);
260
- CREATE INDEX IF NOT EXISTS core_memory_section_idx ON core_memory(section);
261
-
262
- CREATE TABLE IF NOT EXISTS context_sessions (
263
- id TEXT PRIMARY KEY,
264
- session_id TEXT NOT NULL UNIQUE,
265
- project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
266
- user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
267
- loaded_memory_ids TEXT,
268
- token_budget INTEGER DEFAULT 8000 NOT NULL,
269
- tokens_used INTEGER DEFAULT 0 NOT NULL,
270
- core_memory_tokens INTEGER DEFAULT 0 NOT NULL,
271
- loaded_memories_tokens INTEGER DEFAULT 0 NOT NULL,
272
- metadata TEXT,
273
- created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
274
- updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
275
- );
276
-
277
- CREATE INDEX IF NOT EXISTS context_sessions_session_idx ON context_sessions(session_id);
278
- CREATE INDEX IF NOT EXISTS context_sessions_project_idx ON context_sessions(project_id);
279
- CREATE INDEX IF NOT EXISTS context_sessions_created_idx ON context_sessions(created_at);
280
-
281
- -- v0.8.0: Memory Merge Tables
282
- CREATE TABLE IF NOT EXISTS memory_merge_proposals (
283
- id TEXT PRIMARY KEY,
284
- project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
285
- user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
286
- source_memory_ids TEXT NOT NULL,
287
- proposed_content TEXT NOT NULL,
288
- proposed_summary TEXT,
289
- proposed_tags TEXT,
290
- proposed_metadata TEXT,
291
- detection_method TEXT NOT NULL,
292
- similarity_score TEXT NOT NULL,
293
- confidence_level TEXT NOT NULL,
294
- merge_reason TEXT NOT NULL,
295
- conflict_warnings TEXT,
296
- status TEXT DEFAULT 'pending' NOT NULL,
297
- reviewed_at INTEGER,
298
- review_notes TEXT,
299
- created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
300
- expires_at INTEGER
301
- );
302
-
303
- CREATE INDEX IF NOT EXISTS memory_merge_proposals_project_status_idx ON memory_merge_proposals(project_id, status);
304
- CREATE INDEX IF NOT EXISTS memory_merge_proposals_created_at_idx ON memory_merge_proposals(created_at);
305
-
306
- CREATE TABLE IF NOT EXISTS memory_merge_history (
307
- id TEXT PRIMARY KEY,
308
- project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
309
- user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
310
- proposal_id TEXT REFERENCES memory_merge_proposals(id) ON DELETE SET NULL,
311
- source_memory_ids TEXT NOT NULL,
312
- canonical_memory_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
313
- source_memories_snapshot TEXT NOT NULL,
314
- merge_strategy TEXT NOT NULL,
315
- tokens_saved INTEGER,
316
- is_reversed INTEGER DEFAULT 0,
317
- reversed_at INTEGER,
318
- reversed_by TEXT,
319
- merged_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
320
- );
321
-
322
- CREATE TABLE IF NOT EXISTS memory_hash_cache (
323
- memory_id TEXT PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
324
- project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
325
- simhash TEXT,
326
- minhash TEXT,
327
- content_hash TEXT NOT NULL,
328
- last_updated INTEGER DEFAULT (strftime('%s','now')) NOT NULL
329
- );
330
-
331
- CREATE INDEX IF NOT EXISTS memory_hash_cache_project_id_idx ON memory_hash_cache(project_id);
332
- CREATE INDEX IF NOT EXISTS memory_hash_cache_simhash_idx ON memory_hash_cache(simhash);
9
+ const sqliteSchemaSql = `
10
+ PRAGMA foreign_keys = ON;
11
+
12
+ CREATE TABLE IF NOT EXISTS users (
13
+ id TEXT PRIMARY KEY,
14
+ external_id TEXT UNIQUE,
15
+ name TEXT,
16
+ email TEXT,
17
+ preferences TEXT,
18
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
19
+ updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
20
+ );
21
+
22
+ CREATE TABLE IF NOT EXISTS projects (
23
+ id TEXT PRIMARY KEY,
24
+ name TEXT NOT NULL,
25
+ path TEXT NOT NULL,
26
+ description TEXT,
27
+ metadata TEXT,
28
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
29
+ updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
30
+ );
31
+
32
+ CREATE INDEX IF NOT EXISTS projects_path_idx ON projects(path);
33
+
34
+ CREATE TABLE IF NOT EXISTS memories (
35
+ id TEXT PRIMARY KEY,
36
+ project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
37
+ user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
38
+ type TEXT NOT NULL,
39
+ content TEXT NOT NULL,
40
+ summary TEXT,
41
+ embedding_json TEXT,
42
+ embedding BLOB,
43
+ source TEXT,
44
+ confidence INTEGER DEFAULT 100,
45
+ tags TEXT,
46
+ metadata TEXT,
47
+ is_private INTEGER DEFAULT 0,
48
+ has_secrets INTEGER DEFAULT 0,
49
+ relevance_score INTEGER DEFAULT 50,
50
+ is_active INTEGER DEFAULT 1,
51
+ expires_at INTEGER,
52
+ access_count INTEGER DEFAULT 0,
53
+ last_accessed_at INTEGER,
54
+ is_merged INTEGER DEFAULT 0,
55
+ merged_into_id TEXT,
56
+ merged_at INTEGER,
57
+ is_canonical INTEGER DEFAULT 0,
58
+ merge_source_ids TEXT,
59
+ is_mergeable INTEGER DEFAULT 1,
60
+ merge_version INTEGER DEFAULT 1,
61
+ importance_score INTEGER DEFAULT 50,
62
+ importance_decay_rate INTEGER DEFAULT 30,
63
+ last_importance_recalc INTEGER,
64
+ consolidated_into TEXT,
65
+ consolidated_at INTEGER,
66
+ is_consolidated INTEGER DEFAULT 0,
67
+ sector TEXT DEFAULT 'episodic',
68
+ tier TEXT DEFAULT 'hot',
69
+ context_status TEXT DEFAULT 'out-of-context',
70
+ decay_rate INTEGER DEFAULT 30,
71
+ coactivation_score INTEGER DEFAULT 0,
72
+ last_decay_at INTEGER DEFAULT (strftime('%s','now')),
73
+ agent_id TEXT,
74
+ agent_role TEXT,
75
+ visibility_scope TEXT DEFAULT 'private',
76
+ is_protected INTEGER DEFAULT 0,
77
+ is_pinned INTEGER DEFAULT 0,
78
+ is_immutable INTEGER DEFAULT 0,
79
+ write_scope TEXT,
80
+ read_scope TEXT,
81
+ triggered_by TEXT,
82
+ capture_reason TEXT,
83
+ last_used_at INTEGER,
84
+ usage_count INTEGER DEFAULT 0,
85
+ valid_from INTEGER,
86
+ valid_to INTEGER,
87
+ superseded_by TEXT,
88
+ version INTEGER DEFAULT 1,
89
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
90
+ updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
91
+ );
92
+
93
+ CREATE INDEX IF NOT EXISTS memories_project_idx ON memories(project_id);
94
+ CREATE INDEX IF NOT EXISTS memories_type_idx ON memories(type);
95
+ CREATE INDEX IF NOT EXISTS memories_created_idx ON memories(created_at);
96
+ CREATE INDEX IF NOT EXISTS memories_tags_idx ON memories(tags);
97
+
98
+ CREATE TABLE IF NOT EXISTS memory_associations (
99
+ id TEXT PRIMARY KEY,
100
+ from_memory_id TEXT NOT NULL,
101
+ to_memory_id TEXT NOT NULL,
102
+ association_type TEXT NOT NULL,
103
+ weight REAL DEFAULT 1,
104
+ coactivation_count INTEGER DEFAULT 1,
105
+ metadata TEXT,
106
+ last_coactivated_at INTEGER,
107
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
108
+ UNIQUE(from_memory_id, to_memory_id)
109
+ );
110
+
111
+ CREATE TABLE IF NOT EXISTS conversations (
112
+ id TEXT PRIMARY KEY,
113
+ project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
114
+ user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
115
+ session_id TEXT NOT NULL,
116
+ title TEXT,
117
+ summary TEXT,
118
+ message_count INTEGER DEFAULT 0,
119
+ token_count INTEGER DEFAULT 0,
120
+ started_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
121
+ ended_at INTEGER,
122
+ metadata TEXT,
123
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
124
+ updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
125
+ );
126
+
127
+ CREATE INDEX IF NOT EXISTS conversations_project_idx ON conversations(project_id);
128
+ CREATE INDEX IF NOT EXISTS conversations_session_idx ON conversations(session_id);
129
+ CREATE INDEX IF NOT EXISTS conversations_started_idx ON conversations(started_at);
130
+
131
+ CREATE TABLE IF NOT EXISTS messages (
132
+ id TEXT PRIMARY KEY,
133
+ conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
134
+ role TEXT NOT NULL,
135
+ content TEXT NOT NULL,
136
+ embedding_json TEXT,
137
+ token_count INTEGER,
138
+ tool_calls TEXT,
139
+ metadata TEXT,
140
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
141
+ );
142
+
143
+ CREATE INDEX IF NOT EXISTS messages_conversation_idx ON messages(conversation_id);
144
+ CREATE INDEX IF NOT EXISTS messages_role_idx ON messages(role);
145
+ CREATE INDEX IF NOT EXISTS messages_created_idx ON messages(created_at);
146
+
147
+ CREATE TABLE IF NOT EXISTS observations (
148
+ id TEXT PRIMARY KEY,
149
+ project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
150
+ conversation_id TEXT REFERENCES conversations(id) ON DELETE SET NULL,
151
+ type TEXT NOT NULL,
152
+ action TEXT NOT NULL,
153
+ target TEXT,
154
+ summary TEXT NOT NULL,
155
+ details TEXT,
156
+ embedding_json TEXT,
157
+ category TEXT,
158
+ importance INTEGER DEFAULT 50,
159
+ metadata TEXT,
160
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
161
+ );
162
+
163
+ CREATE INDEX IF NOT EXISTS observations_project_idx ON observations(project_id);
164
+ CREATE INDEX IF NOT EXISTS observations_type_idx ON observations(type);
165
+ CREATE INDEX IF NOT EXISTS observations_action_idx ON observations(action);
166
+ CREATE INDEX IF NOT EXISTS observations_created_idx ON observations(created_at);
167
+
168
+ CREATE TABLE IF NOT EXISTS entities (
169
+ id TEXT PRIMARY KEY,
170
+ project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
171
+ name TEXT NOT NULL,
172
+ type TEXT NOT NULL,
173
+ description TEXT,
174
+ embedding_json TEXT,
175
+ properties TEXT,
176
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
177
+ updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
178
+ );
179
+
180
+ CREATE INDEX IF NOT EXISTS entities_project_idx ON entities(project_id);
181
+ CREATE INDEX IF NOT EXISTS entities_type_idx ON entities(type);
182
+ CREATE INDEX IF NOT EXISTS entities_name_idx ON entities(name);
183
+
184
+ CREATE TABLE IF NOT EXISTS entity_relations (
185
+ id TEXT PRIMARY KEY,
186
+ from_entity_id TEXT NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
187
+ to_entity_id TEXT NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
188
+ type TEXT NOT NULL,
189
+ weight INTEGER DEFAULT 1,
190
+ properties TEXT,
191
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
192
+ );
193
+
194
+ CREATE INDEX IF NOT EXISTS relations_from_idx ON entity_relations(from_entity_id);
195
+ CREATE INDEX IF NOT EXISTS relations_to_idx ON entity_relations(to_entity_id);
196
+ CREATE INDEX IF NOT EXISTS relations_type_idx ON entity_relations(type);
197
+
198
+ CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
199
+ content,
200
+ tags,
201
+ content='memories',
202
+ content_rowid='rowid'
203
+ );
204
+
205
+ CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
206
+ INSERT INTO memories_fts(rowid, content, tags)
207
+ VALUES (new.rowid, new.content, COALESCE(new.tags, ''));
208
+ END;
209
+
210
+ CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
211
+ INSERT INTO memories_fts(memories_fts, rowid, content, tags)
212
+ VALUES ('delete', old.rowid, old.content, COALESCE(old.tags, ''));
213
+ END;
214
+
215
+ CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
216
+ INSERT INTO memories_fts(memories_fts, rowid, content, tags)
217
+ VALUES ('delete', old.rowid, old.content, COALESCE(old.tags, ''));
218
+ INSERT INTO memories_fts(rowid, content, tags)
219
+ VALUES (new.rowid, new.content, COALESCE(new.tags, ''));
220
+ END;
221
+
222
+ CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
223
+ content,
224
+ content='messages',
225
+ content_rowid='rowid'
226
+ );
227
+
228
+ CREATE TRIGGER IF NOT EXISTS messages_ai AFTER INSERT ON messages BEGIN
229
+ INSERT INTO messages_fts(rowid, content)
230
+ VALUES (new.rowid, new.content);
231
+ END;
232
+
233
+ CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN
234
+ INSERT INTO messages_fts(messages_fts, rowid, content)
235
+ VALUES ('delete', old.rowid, old.content);
236
+ END;
237
+
238
+ CREATE TRIGGER IF NOT EXISTS messages_au AFTER UPDATE ON messages BEGIN
239
+ INSERT INTO messages_fts(messages_fts, rowid, content)
240
+ VALUES ('delete', old.rowid, old.content);
241
+ INSERT INTO messages_fts(rowid, content)
242
+ VALUES (new.rowid, new.content);
243
+ END;
244
+
245
+ CREATE TABLE IF NOT EXISTS core_memory (
246
+ id TEXT PRIMARY KEY,
247
+ project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
248
+ user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
249
+ section TEXT NOT NULL,
250
+ content TEXT NOT NULL DEFAULT '',
251
+ size_bytes INTEGER DEFAULT 0 NOT NULL,
252
+ tokens_estimate INTEGER DEFAULT 0 NOT NULL,
253
+ version INTEGER DEFAULT 1 NOT NULL,
254
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
255
+ updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
256
+ );
257
+
258
+ CREATE INDEX IF NOT EXISTS core_memory_project_idx ON core_memory(project_id);
259
+ CREATE INDEX IF NOT EXISTS core_memory_user_idx ON core_memory(user_id);
260
+ CREATE INDEX IF NOT EXISTS core_memory_section_idx ON core_memory(section);
261
+
262
+ CREATE TABLE IF NOT EXISTS context_sessions (
263
+ id TEXT PRIMARY KEY,
264
+ session_id TEXT NOT NULL UNIQUE,
265
+ project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
266
+ user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
267
+ loaded_memory_ids TEXT,
268
+ token_budget INTEGER DEFAULT 8000 NOT NULL,
269
+ tokens_used INTEGER DEFAULT 0 NOT NULL,
270
+ core_memory_tokens INTEGER DEFAULT 0 NOT NULL,
271
+ loaded_memories_tokens INTEGER DEFAULT 0 NOT NULL,
272
+ metadata TEXT,
273
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
274
+ updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
275
+ );
276
+
277
+ CREATE INDEX IF NOT EXISTS context_sessions_session_idx ON context_sessions(session_id);
278
+ CREATE INDEX IF NOT EXISTS context_sessions_project_idx ON context_sessions(project_id);
279
+ CREATE INDEX IF NOT EXISTS context_sessions_created_idx ON context_sessions(created_at);
280
+
281
+ -- v0.8.0: Memory Merge Tables
282
+ CREATE TABLE IF NOT EXISTS memory_merge_proposals (
283
+ id TEXT PRIMARY KEY,
284
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
285
+ user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
286
+ source_memory_ids TEXT NOT NULL,
287
+ proposed_content TEXT NOT NULL,
288
+ proposed_summary TEXT,
289
+ proposed_tags TEXT,
290
+ proposed_metadata TEXT,
291
+ detection_method TEXT NOT NULL,
292
+ similarity_score TEXT NOT NULL,
293
+ confidence_level TEXT NOT NULL,
294
+ merge_reason TEXT NOT NULL,
295
+ conflict_warnings TEXT,
296
+ status TEXT DEFAULT 'pending' NOT NULL,
297
+ reviewed_at INTEGER,
298
+ review_notes TEXT,
299
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
300
+ expires_at INTEGER
301
+ );
302
+
303
+ CREATE INDEX IF NOT EXISTS memory_merge_proposals_project_status_idx ON memory_merge_proposals(project_id, status);
304
+ CREATE INDEX IF NOT EXISTS memory_merge_proposals_created_at_idx ON memory_merge_proposals(created_at);
305
+
306
+ CREATE TABLE IF NOT EXISTS memory_merge_history (
307
+ id TEXT PRIMARY KEY,
308
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
309
+ user_id TEXT REFERENCES users(id) ON DELETE SET NULL,
310
+ proposal_id TEXT REFERENCES memory_merge_proposals(id) ON DELETE SET NULL,
311
+ source_memory_ids TEXT NOT NULL,
312
+ canonical_memory_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
313
+ source_memories_snapshot TEXT NOT NULL,
314
+ merge_strategy TEXT NOT NULL,
315
+ tokens_saved INTEGER,
316
+ is_reversed INTEGER DEFAULT 0,
317
+ reversed_at INTEGER,
318
+ reversed_by TEXT,
319
+ merged_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
320
+ );
321
+
322
+ CREATE TABLE IF NOT EXISTS memory_hash_cache (
323
+ memory_id TEXT PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
324
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
325
+ simhash TEXT,
326
+ minhash TEXT,
327
+ content_hash TEXT NOT NULL,
328
+ last_updated INTEGER DEFAULT (strftime('%s','now')) NOT NULL
329
+ );
330
+
331
+ CREATE INDEX IF NOT EXISTS memory_hash_cache_project_id_idx ON memory_hash_cache(project_id);
332
+ CREATE INDEX IF NOT EXISTS memory_hash_cache_simhash_idx ON memory_hash_cache(simhash);
333
+
334
+ -- Namespaces table (v1.0.x) - Hierarchical organization
335
+ CREATE TABLE IF NOT EXISTS namespaces (
336
+ id TEXT PRIMARY KEY,
337
+ project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
338
+ name TEXT NOT NULL,
339
+ path TEXT,
340
+ description TEXT,
341
+ parent_id TEXT REFERENCES namespaces(id) ON DELETE SET NULL,
342
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
343
+ updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
344
+ );
345
+ CREATE INDEX IF NOT EXISTS namespaces_project_idx ON namespaces(project_id);
346
+ CREATE INDEX IF NOT EXISTS namespaces_parent_idx ON namespaces(parent_id);
347
+
348
+ -- Maintenance jobs table (v1.0.x) - Cron scheduler
349
+ CREATE TABLE IF NOT EXISTS maintenance_jobs (
350
+ id TEXT PRIMARY KEY,
351
+ job_name TEXT NOT NULL UNIQUE,
352
+ job_type TEXT NOT NULL,
353
+ cron_expression TEXT,
354
+ enabled INTEGER DEFAULT 1 NOT NULL,
355
+ last_run_at INTEGER,
356
+ last_run_duration INTEGER,
357
+ last_run_status TEXT,
358
+ last_run_error TEXT,
359
+ total_runs INTEGER DEFAULT 0,
360
+ success_count INTEGER DEFAULT 0,
361
+ failure_count INTEGER DEFAULT 0,
362
+ job_config TEXT,
363
+ next_run_at INTEGER,
364
+ created_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL,
365
+ updated_at INTEGER DEFAULT (strftime('%s','now')) NOT NULL
366
+ );
367
+ CREATE INDEX IF NOT EXISTS maintenance_jobs_name_idx ON maintenance_jobs(job_name);
368
+ CREATE INDEX IF NOT EXISTS maintenance_jobs_next_run_idx ON maintenance_jobs(next_run_at);
369
+ CREATE INDEX IF NOT EXISTS maintenance_jobs_type_idx ON maintenance_jobs(job_type);
370
+ CREATE INDEX IF NOT EXISTS maintenance_jobs_enabled_idx ON maintenance_jobs(enabled);
333
371
  `;
334
372
  const postgresStatements = [
335
373
  `CREATE EXTENSION IF NOT EXISTS pgcrypto;`,
336
374
  `CREATE EXTENSION IF NOT EXISTS vector;`,
337
375
  `CREATE EXTENSION IF NOT EXISTS pg_trgm;`,
338
- `CREATE TABLE IF NOT EXISTS users (
339
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
340
- external_id TEXT UNIQUE,
341
- name TEXT,
342
- email TEXT,
343
- preferences JSONB,
344
- created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
345
- updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
376
+ `CREATE TABLE IF NOT EXISTS users (
377
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
378
+ external_id TEXT UNIQUE,
379
+ name TEXT,
380
+ email TEXT,
381
+ preferences JSONB,
382
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
383
+ updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
346
384
  );`,
347
- `CREATE TABLE IF NOT EXISTS projects (
348
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
349
- name TEXT NOT NULL,
350
- path TEXT NOT NULL,
351
- description TEXT,
352
- metadata JSONB,
353
- created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
354
- updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
385
+ `CREATE TABLE IF NOT EXISTS projects (
386
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
387
+ name TEXT NOT NULL,
388
+ path TEXT NOT NULL,
389
+ description TEXT,
390
+ metadata JSONB,
391
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
392
+ updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
355
393
  );`,
356
394
  `CREATE INDEX IF NOT EXISTS projects_path_idx ON projects(path);`,
357
- `CREATE TABLE IF NOT EXISTS memories (
358
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
359
- project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
360
- user_id UUID REFERENCES users(id) ON DELETE SET NULL,
361
- type TEXT NOT NULL,
362
- content TEXT NOT NULL,
363
- summary TEXT,
364
- embedding vector(1536),
365
- source TEXT,
366
- confidence INTEGER DEFAULT 100,
367
- tags TEXT[],
368
- metadata JSONB,
369
- is_active BOOLEAN DEFAULT TRUE,
370
- expires_at TIMESTAMPTZ,
371
- access_count INTEGER DEFAULT 0,
372
- last_accessed_at TIMESTAMPTZ,
373
- created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
374
- updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
395
+ `CREATE TABLE IF NOT EXISTS memories (
396
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
397
+ project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
398
+ user_id UUID REFERENCES users(id) ON DELETE SET NULL,
399
+ type TEXT NOT NULL,
400
+ content TEXT NOT NULL,
401
+ summary TEXT,
402
+ embedding vector(1536),
403
+ source TEXT,
404
+ confidence INTEGER DEFAULT 100,
405
+ tags TEXT[],
406
+ metadata JSONB,
407
+ is_active BOOLEAN DEFAULT TRUE,
408
+ expires_at TIMESTAMPTZ,
409
+ access_count INTEGER DEFAULT 0,
410
+ last_accessed_at TIMESTAMPTZ,
411
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
412
+ updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
375
413
  );`,
376
414
  `CREATE INDEX IF NOT EXISTS memories_project_idx ON memories(project_id);`,
377
415
  `CREATE INDEX IF NOT EXISTS memories_type_idx ON memories(type);`,
378
416
  `CREATE INDEX IF NOT EXISTS memories_created_idx ON memories(created_at);`,
379
417
  `CREATE INDEX IF NOT EXISTS memories_tags_idx ON memories USING GIN(tags);`,
380
418
  `CREATE INDEX IF NOT EXISTS memories_content_trgm_idx ON memories USING GIN (content gin_trgm_ops);`,
381
- `CREATE TABLE IF NOT EXISTS conversations (
382
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
383
- project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
384
- user_id UUID REFERENCES users(id) ON DELETE SET NULL,
385
- session_id TEXT NOT NULL,
386
- title TEXT,
387
- summary TEXT,
388
- message_count INTEGER DEFAULT 0,
389
- token_count INTEGER DEFAULT 0,
390
- started_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
391
- ended_at TIMESTAMPTZ,
392
- metadata JSONB,
393
- created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
394
- updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
419
+ `CREATE TABLE IF NOT EXISTS conversations (
420
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
421
+ project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
422
+ user_id UUID REFERENCES users(id) ON DELETE SET NULL,
423
+ session_id TEXT NOT NULL,
424
+ title TEXT,
425
+ summary TEXT,
426
+ message_count INTEGER DEFAULT 0,
427
+ token_count INTEGER DEFAULT 0,
428
+ started_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
429
+ ended_at TIMESTAMPTZ,
430
+ metadata JSONB,
431
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
432
+ updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
395
433
  );`,
396
434
  `CREATE INDEX IF NOT EXISTS conversations_project_idx ON conversations(project_id);`,
397
435
  `CREATE INDEX IF NOT EXISTS conversations_session_idx ON conversations(session_id);`,
398
436
  `CREATE INDEX IF NOT EXISTS conversations_started_idx ON conversations(started_at);`,
399
- `CREATE TABLE IF NOT EXISTS messages (
400
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
401
- conversation_id UUID NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
402
- role TEXT NOT NULL,
403
- content TEXT NOT NULL,
404
- embedding vector(1536),
405
- token_count INTEGER,
406
- tool_calls JSONB,
407
- metadata JSONB,
408
- created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
437
+ `CREATE TABLE IF NOT EXISTS messages (
438
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
439
+ conversation_id UUID NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
440
+ role TEXT NOT NULL,
441
+ content TEXT NOT NULL,
442
+ embedding vector(1536),
443
+ token_count INTEGER,
444
+ tool_calls JSONB,
445
+ metadata JSONB,
446
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
409
447
  );`,
410
448
  `CREATE INDEX IF NOT EXISTS messages_conversation_idx ON messages(conversation_id);`,
411
449
  `CREATE INDEX IF NOT EXISTS messages_role_idx ON messages(role);`,
412
450
  `CREATE INDEX IF NOT EXISTS messages_created_idx ON messages(created_at);`,
413
451
  `CREATE INDEX IF NOT EXISTS messages_content_trgm_idx ON messages USING GIN (content gin_trgm_ops);`,
414
- `CREATE TABLE IF NOT EXISTS observations (
415
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
416
- project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
417
- conversation_id UUID REFERENCES conversations(id) ON DELETE SET NULL,
418
- type TEXT NOT NULL,
419
- action TEXT NOT NULL,
420
- target TEXT,
421
- summary TEXT NOT NULL,
422
- details JSONB,
423
- embedding vector(1536),
424
- category TEXT,
425
- importance INTEGER DEFAULT 50,
426
- metadata JSONB,
427
- created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
452
+ `CREATE TABLE IF NOT EXISTS observations (
453
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
454
+ project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
455
+ conversation_id UUID REFERENCES conversations(id) ON DELETE SET NULL,
456
+ type TEXT NOT NULL,
457
+ action TEXT NOT NULL,
458
+ target TEXT,
459
+ summary TEXT NOT NULL,
460
+ details JSONB,
461
+ embedding vector(1536),
462
+ category TEXT,
463
+ importance INTEGER DEFAULT 50,
464
+ metadata JSONB,
465
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
428
466
  );`,
429
467
  `CREATE INDEX IF NOT EXISTS observations_project_idx ON observations(project_id);`,
430
468
  `CREATE INDEX IF NOT EXISTS observations_type_idx ON observations(type);`,
431
469
  `CREATE INDEX IF NOT EXISTS observations_action_idx ON observations(action);`,
432
470
  `CREATE INDEX IF NOT EXISTS observations_created_idx ON observations(created_at);`,
433
- `CREATE TABLE IF NOT EXISTS entities (
434
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
435
- project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
436
- name TEXT NOT NULL,
437
- type TEXT NOT NULL,
438
- description TEXT,
439
- embedding vector(1536),
440
- properties JSONB,
441
- created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
442
- updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
471
+ `CREATE TABLE IF NOT EXISTS entities (
472
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
473
+ project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
474
+ name TEXT NOT NULL,
475
+ type TEXT NOT NULL,
476
+ description TEXT,
477
+ embedding vector(1536),
478
+ properties JSONB,
479
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
480
+ updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
443
481
  );`,
444
482
  `CREATE INDEX IF NOT EXISTS entities_project_idx ON entities(project_id);`,
445
483
  `CREATE INDEX IF NOT EXISTS entities_type_idx ON entities(type);`,
446
484
  `CREATE INDEX IF NOT EXISTS entities_name_idx ON entities(name);`,
447
- `CREATE TABLE IF NOT EXISTS entity_relations (
448
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
449
- from_entity_id UUID NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
450
- to_entity_id UUID NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
451
- type TEXT NOT NULL,
452
- weight INTEGER DEFAULT 1,
453
- properties JSONB,
454
- created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
485
+ `CREATE TABLE IF NOT EXISTS entity_relations (
486
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
487
+ from_entity_id UUID NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
488
+ to_entity_id UUID NOT NULL REFERENCES entities(id) ON DELETE CASCADE,
489
+ type TEXT NOT NULL,
490
+ weight INTEGER DEFAULT 1,
491
+ properties JSONB,
492
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
455
493
  );`,
456
494
  `CREATE INDEX IF NOT EXISTS relations_from_idx ON entity_relations(from_entity_id);`,
457
495
  `CREATE INDEX IF NOT EXISTS relations_to_idx ON entity_relations(to_entity_id);`,
458
496
  `CREATE INDEX IF NOT EXISTS relations_type_idx ON entity_relations(type);`,
459
- `CREATE TABLE IF NOT EXISTS core_memory (
460
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
461
- project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
462
- user_id UUID REFERENCES users(id) ON DELETE SET NULL,
463
- section TEXT NOT NULL,
464
- content TEXT NOT NULL DEFAULT '',
465
- size_bytes INTEGER DEFAULT 0 NOT NULL,
466
- version INTEGER DEFAULT 1 NOT NULL,
467
- created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
468
- updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
497
+ `CREATE TABLE IF NOT EXISTS core_memory (
498
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
499
+ project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
500
+ user_id UUID REFERENCES users(id) ON DELETE SET NULL,
501
+ section TEXT NOT NULL,
502
+ content TEXT NOT NULL DEFAULT '',
503
+ size_bytes INTEGER DEFAULT 0 NOT NULL,
504
+ version INTEGER DEFAULT 1 NOT NULL,
505
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
506
+ updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
469
507
  );`,
470
508
  `CREATE INDEX IF NOT EXISTS core_memory_project_idx ON core_memory(project_id);`,
471
509
  `CREATE INDEX IF NOT EXISTS core_memory_user_idx ON core_memory(user_id);`,
472
510
  `CREATE INDEX IF NOT EXISTS core_memory_section_idx ON core_memory(section);`,
473
- `CREATE TABLE IF NOT EXISTS context_sessions (
474
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
475
- session_id TEXT NOT NULL UNIQUE,
476
- project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
477
- user_id UUID REFERENCES users(id) ON DELETE SET NULL,
478
- loaded_memory_ids JSONB,
479
- token_budget INTEGER DEFAULT 8000 NOT NULL,
480
- tokens_used INTEGER DEFAULT 0 NOT NULL,
481
- core_memory_tokens INTEGER DEFAULT 0 NOT NULL,
482
- loaded_memories_tokens INTEGER DEFAULT 0 NOT NULL,
483
- metadata JSONB,
484
- created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
485
- updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
511
+ `CREATE TABLE IF NOT EXISTS context_sessions (
512
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
513
+ session_id TEXT NOT NULL UNIQUE,
514
+ project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
515
+ user_id UUID REFERENCES users(id) ON DELETE SET NULL,
516
+ loaded_memory_ids JSONB,
517
+ token_budget INTEGER DEFAULT 8000 NOT NULL,
518
+ tokens_used INTEGER DEFAULT 0 NOT NULL,
519
+ core_memory_tokens INTEGER DEFAULT 0 NOT NULL,
520
+ loaded_memories_tokens INTEGER DEFAULT 0 NOT NULL,
521
+ metadata JSONB,
522
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
523
+ updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
486
524
  );`,
487
525
  `CREATE INDEX IF NOT EXISTS context_sessions_session_idx ON context_sessions(session_id);`,
488
526
  `CREATE INDEX IF NOT EXISTS context_sessions_project_idx ON context_sessions(project_id);`,
489
527
  `CREATE INDEX IF NOT EXISTS context_sessions_created_idx ON context_sessions(created_at);`,
490
- `CREATE TABLE IF NOT EXISTS memory_merge_proposals (
491
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
492
- project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
493
- user_id UUID REFERENCES users(id) ON DELETE SET NULL,
494
- source_memory_ids TEXT NOT NULL,
495
- proposed_content TEXT NOT NULL,
496
- proposed_summary TEXT,
497
- proposed_tags TEXT[],
498
- proposed_metadata JSONB,
499
- detection_method TEXT NOT NULL,
500
- similarity_score TEXT NOT NULL,
501
- confidence_level TEXT NOT NULL,
502
- merge_reason TEXT NOT NULL,
503
- conflict_warnings JSONB,
504
- status TEXT DEFAULT 'pending' NOT NULL,
505
- reviewed_at TIMESTAMPTZ,
506
- review_notes TEXT,
507
- created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
508
- expires_at TIMESTAMPTZ
528
+ `CREATE TABLE IF NOT EXISTS memory_merge_proposals (
529
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
530
+ project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
531
+ user_id UUID REFERENCES users(id) ON DELETE SET NULL,
532
+ source_memory_ids TEXT NOT NULL,
533
+ proposed_content TEXT NOT NULL,
534
+ proposed_summary TEXT,
535
+ proposed_tags TEXT[],
536
+ proposed_metadata JSONB,
537
+ detection_method TEXT NOT NULL,
538
+ similarity_score TEXT NOT NULL,
539
+ confidence_level TEXT NOT NULL,
540
+ merge_reason TEXT NOT NULL,
541
+ conflict_warnings JSONB,
542
+ status TEXT DEFAULT 'pending' NOT NULL,
543
+ reviewed_at TIMESTAMPTZ,
544
+ review_notes TEXT,
545
+ created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
546
+ expires_at TIMESTAMPTZ
509
547
  );`,
510
548
  `CREATE INDEX IF NOT EXISTS memory_merge_proposals_project_status_idx ON memory_merge_proposals(project_id, status);`,
511
549
  `CREATE INDEX IF NOT EXISTS memory_merge_proposals_created_at_idx ON memory_merge_proposals(created_at);`,
512
- `CREATE TABLE IF NOT EXISTS memory_merge_history (
513
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
514
- project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
515
- user_id UUID REFERENCES users(id) ON DELETE SET NULL,
516
- proposal_id UUID REFERENCES memory_merge_proposals(id) ON DELETE SET NULL,
517
- source_memory_ids TEXT NOT NULL,
518
- canonical_memory_id UUID NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
519
- source_memories_snapshot JSONB NOT NULL,
520
- merge_strategy TEXT NOT NULL,
521
- tokens_saved INTEGER,
522
- is_reversed BOOLEAN DEFAULT FALSE,
523
- reversed_at TIMESTAMPTZ,
524
- reversed_by UUID,
525
- merged_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
550
+ `CREATE TABLE IF NOT EXISTS memory_merge_history (
551
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
552
+ project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
553
+ user_id UUID REFERENCES users(id) ON DELETE SET NULL,
554
+ proposal_id UUID REFERENCES memory_merge_proposals(id) ON DELETE SET NULL,
555
+ source_memory_ids TEXT NOT NULL,
556
+ canonical_memory_id UUID NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
557
+ source_memories_snapshot JSONB NOT NULL,
558
+ merge_strategy TEXT NOT NULL,
559
+ tokens_saved INTEGER,
560
+ is_reversed BOOLEAN DEFAULT FALSE,
561
+ reversed_at TIMESTAMPTZ,
562
+ reversed_by UUID,
563
+ merged_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
526
564
  );`,
527
- `CREATE TABLE IF NOT EXISTS memory_hash_cache (
528
- memory_id UUID PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
529
- project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
530
- simhash TEXT,
531
- minhash TEXT,
532
- content_hash TEXT NOT NULL,
533
- last_updated TIMESTAMPTZ DEFAULT NOW() NOT NULL
565
+ `CREATE TABLE IF NOT EXISTS memory_hash_cache (
566
+ memory_id UUID PRIMARY KEY REFERENCES memories(id) ON DELETE CASCADE,
567
+ project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
568
+ simhash TEXT,
569
+ minhash TEXT,
570
+ content_hash TEXT NOT NULL,
571
+ last_updated TIMESTAMPTZ DEFAULT NOW() NOT NULL
534
572
  );`,
535
573
  `CREATE INDEX IF NOT EXISTS memory_hash_cache_project_id_idx ON memory_hash_cache(project_id);`,
536
574
  `CREATE INDEX IF NOT EXISTS memory_hash_cache_simhash_idx ON memory_hash_cache(simhash);`
@@ -565,97 +603,71 @@ async function runSqliteMigrations(sqlite) {
565
603
  // No migrations needed
566
604
  return;
567
605
  }
568
- // Migrations for memories table
606
+ // Migrations for memories table (deduplicated, ordered by version)
569
607
  const memoriesMigrations = [
608
+ // Base columns (v0.1.x - v0.5.x)
570
609
  { col: 'embedding', sql: 'ALTER TABLE memories ADD COLUMN embedding BLOB' },
571
- { col: 'is_private', sql: 'ALTER TABLE memories ADD COLUMN is_private INTEGER DEFAULT 0' },
572
- { col: 'has_secrets', sql: 'ALTER TABLE memories ADD COLUMN has_secrets INTEGER DEFAULT 0' },
573
610
  { col: 'relevance_score', sql: 'ALTER TABLE memories ADD COLUMN relevance_score INTEGER DEFAULT 50' },
611
+ // Merge tracking (v0.6.x)
574
612
  { col: 'is_merged', sql: 'ALTER TABLE memories ADD COLUMN is_merged INTEGER DEFAULT 0' },
575
613
  { col: 'merged_into_id', sql: 'ALTER TABLE memories ADD COLUMN merged_into_id TEXT' },
576
614
  { col: 'is_mergeable', sql: 'ALTER TABLE memories ADD COLUMN is_mergeable INTEGER DEFAULT 1' },
577
615
  { col: 'is_canonical', sql: 'ALTER TABLE memories ADD COLUMN is_canonical INTEGER DEFAULT 0' },
578
- // v0.8.0: Importance scoring
616
+ { col: 'merged_at', sql: 'ALTER TABLE memories ADD COLUMN merged_at INTEGER' },
617
+ { col: 'merge_source_ids', sql: 'ALTER TABLE memories ADD COLUMN merge_source_ids TEXT' },
618
+ { col: 'merge_version', sql: 'ALTER TABLE memories ADD COLUMN merge_version INTEGER DEFAULT 1' },
619
+ // Importance scoring (v0.8.0)
579
620
  { col: 'importance_score', sql: 'ALTER TABLE memories ADD COLUMN importance_score INTEGER DEFAULT 50' },
580
621
  { col: 'importance_decay_rate', sql: 'ALTER TABLE memories ADD COLUMN importance_decay_rate INTEGER DEFAULT 30' },
581
622
  { col: 'last_importance_recalc', sql: 'ALTER TABLE memories ADD COLUMN last_importance_recalc INTEGER' },
582
- // v0.8.0: Consolidation
623
+ // Consolidation (v0.8.0)
583
624
  { col: 'consolidated_into', sql: 'ALTER TABLE memories ADD COLUMN consolidated_into TEXT' },
584
625
  { col: 'consolidated_at', sql: 'ALTER TABLE memories ADD COLUMN consolidated_at INTEGER' },
585
626
  { col: 'is_consolidated', sql: 'ALTER TABLE memories ADD COLUMN is_consolidated INTEGER DEFAULT 0' },
586
- { col: 'merged_at', sql: 'ALTER TABLE memories ADD COLUMN merged_at INTEGER' },
627
+ // Memory lifecycle (v0.8.0)
587
628
  { col: 'sector', sql: 'ALTER TABLE memories ADD COLUMN sector TEXT DEFAULT "episodic"' },
588
629
  { col: 'tier', sql: 'ALTER TABLE memories ADD COLUMN tier TEXT DEFAULT "hot"' },
589
630
  { col: 'context_status', sql: 'ALTER TABLE memories ADD COLUMN context_status TEXT DEFAULT "out-of-context"' },
590
631
  { col: 'decay_rate', sql: 'ALTER TABLE memories ADD COLUMN decay_rate INTEGER DEFAULT 30' },
591
632
  { col: 'coactivation_score', sql: 'ALTER TABLE memories ADD COLUMN coactivation_score INTEGER DEFAULT 0' },
592
633
  { col: 'last_decay_at', sql: 'ALTER TABLE memories ADD COLUMN last_decay_at INTEGER DEFAULT (strftime(\'%s\',\'now\'))' },
634
+ // Agent tracking (v0.8.0)
593
635
  { col: 'agent_id', sql: 'ALTER TABLE memories ADD COLUMN agent_id TEXT' },
594
636
  { col: 'agent_role', sql: 'ALTER TABLE memories ADD COLUMN agent_role TEXT' },
595
637
  { col: 'retrieval_priority', sql: 'ALTER TABLE memories ADD COLUMN retrieval_priority INTEGER DEFAULT 50' },
596
- // v0.9.0: New schema columns
638
+ // Data governance (v0.9.0)
597
639
  { col: 'recorded_at', sql: 'ALTER TABLE memories ADD COLUMN recorded_at INTEGER DEFAULT (strftime(\'%s\',\'now\'))' },
598
640
  { col: 'confidence', sql: 'ALTER TABLE memories ADD COLUMN confidence INTEGER DEFAULT 100' },
599
- { col: 'is_private', sql: 'ALTER TABLE memories ADD COLUMN is_private INTEGER DEFAULT 0' },
600
- { col: 'has_secrets', sql: 'ALTER TABLE memories ADD COLUMN has_secrets INTEGER DEFAULT 0' },
601
641
  { col: 'valid_from', sql: 'ALTER TABLE memories ADD COLUMN valid_from INTEGER' },
602
642
  { col: 'valid_to', sql: 'ALTER TABLE memories ADD COLUMN valid_to INTEGER' },
603
643
  { col: 'superseded_by', sql: 'ALTER TABLE memories ADD COLUMN superseded_by TEXT' },
604
644
  { col: 'version', sql: 'ALTER TABLE memories ADD COLUMN version INTEGER DEFAULT 1' },
605
- { col: 'merge_source_ids', sql: 'ALTER TABLE memories ADD COLUMN merge_source_ids TEXT' },
606
- { col: 'merge_version', sql: 'ALTER TABLE memories ADD COLUMN merge_version INTEGER DEFAULT 1' },
607
- { col: 'user_id', sql: 'ALTER TABLE memories ADD COLUMN user_id TEXT' },
608
- { col: 'confidence', sql: 'ALTER TABLE memories ADD COLUMN confidence INTEGER DEFAULT 100' },
609
645
  { col: 'is_active', sql: 'ALTER TABLE memories ADD COLUMN is_active INTEGER DEFAULT 1' },
610
646
  { col: 'expires_at', sql: 'ALTER TABLE memories ADD COLUMN expires_at INTEGER' },
611
- { col: 'decay_rate', sql: 'ALTER TABLE memories ADD COLUMN decay_rate INTEGER DEFAULT 30' },
612
- { col: 'coactivation_score', sql: 'ALTER TABLE memories ADD COLUMN coactivation_score INTEGER DEFAULT 0' },
613
- { col: 'last_decay_at', sql: 'ALTER TABLE memories ADD COLUMN last_decay_at INTEGER' },
614
- { col: 'agent_id', sql: 'ALTER TABLE memories ADD COLUMN agent_id TEXT' },
615
- { col: 'agent_role', sql: 'ALTER TABLE memories ADD COLUMN agent_role TEXT' },
616
- { col: 'retrieval_priority', sql: 'ALTER TABLE memories ADD COLUMN retrieval_priority INTEGER DEFAULT 50' },
617
- // v0.8.0: Consolidation
618
- { col: 'consolidated_into', sql: 'ALTER TABLE memories ADD COLUMN consolidated_into TEXT' },
619
- { col: 'consolidated_at', sql: 'ALTER TABLE memories ADD COLUMN consolidated_at INTEGER' },
620
- { col: 'is_consolidated', sql: 'ALTER TABLE memories ADD COLUMN is_consolidated INTEGER DEFAULT 0' },
621
- { col: 'merged_at', sql: 'ALTER TABLE memories ADD COLUMN merged_at INTEGER' },
622
- { col: 'sector', sql: 'ALTER TABLE memories ADD COLUMN sector TEXT DEFAULT "episodic"' },
623
- { col: 'tier', sql: 'ALTER TABLE memories ADD COLUMN tier TEXT DEFAULT "hot"' },
624
- { col: 'context_status', sql: 'ALTER TABLE memories ADD COLUMN context_status TEXT DEFAULT "out-of-context"' },
625
- { col: 'decay_rate', sql: 'ALTER TABLE memories ADD COLUMN decay_rate INTEGER DEFAULT 30' },
626
- { col: 'coactivation_score', sql: 'ALTER TABLE memories ADD COLUMN coactivation_score INTEGER DEFAULT 0' },
627
- { col: 'last_decay_at', sql: 'ALTER TABLE memories ADD COLUMN last_decay_at INTEGER DEFAULT (strftime(\'%s\',\'now\'))' },
628
- { col: 'agent_id', sql: 'ALTER TABLE memories ADD COLUMN agent_id TEXT' },
629
- { col: 'agent_role', sql: 'ALTER TABLE memories ADD COLUMN agent_role TEXT' },
647
+ // Privacy & access (v0.9.0)
648
+ { col: 'is_private', sql: 'ALTER TABLE memories ADD COLUMN is_private INTEGER DEFAULT 0' },
649
+ { col: 'has_secrets', sql: 'ALTER TABLE memories ADD COLUMN has_secrets INTEGER DEFAULT 0' },
630
650
  { col: 'visibility_scope', sql: 'ALTER TABLE memories ADD COLUMN visibility_scope TEXT DEFAULT "private"' },
631
651
  { col: 'is_protected', sql: 'ALTER TABLE memories ADD COLUMN is_protected INTEGER DEFAULT 0' },
632
652
  { col: 'is_pinned', sql: 'ALTER TABLE memories ADD COLUMN is_pinned INTEGER DEFAULT 0' },
633
653
  { col: 'is_immutable', sql: 'ALTER TABLE memories ADD COLUMN is_immutable INTEGER DEFAULT 0' },
634
654
  { col: 'write_scope', sql: 'ALTER TABLE memories ADD COLUMN write_scope TEXT' },
635
655
  { col: 'read_scope', sql: 'ALTER TABLE memories ADD COLUMN read_scope TEXT' },
656
+ // Usage tracking (v0.9.0)
636
657
  { col: 'triggered_by', sql: 'ALTER TABLE memories ADD COLUMN triggered_by TEXT' },
637
658
  { col: 'capture_reason', sql: 'ALTER TABLE memories ADD COLUMN capture_reason TEXT' },
638
659
  { col: 'last_used_at', sql: 'ALTER TABLE memories ADD COLUMN last_used_at INTEGER' },
639
660
  { col: 'usage_count', sql: 'ALTER TABLE memories ADD COLUMN usage_count INTEGER DEFAULT 0' },
640
- { col: 'valid_from', sql: 'ALTER TABLE memories ADD COLUMN valid_from INTEGER' },
641
- { col: 'valid_to', sql: 'ALTER TABLE memories ADD COLUMN valid_to INTEGER' },
642
- { col: 'superseded_by', sql: 'ALTER TABLE memories ADD COLUMN superseded_by TEXT' },
643
- { col: 'version', sql: 'ALTER TABLE memories ADD COLUMN version INTEGER DEFAULT 1' },
644
- { col: 'merge_source_ids', sql: 'ALTER TABLE memories ADD COLUMN merge_source_ids TEXT' },
645
- { col: 'merge_version', sql: 'ALTER TABLE memories ADD COLUMN merge_version INTEGER DEFAULT 1' },
646
661
  { col: 'user_id', sql: 'ALTER TABLE memories ADD COLUMN user_id TEXT' },
647
- { col: 'confidence', sql: 'ALTER TABLE memories ADD COLUMN confidence INTEGER DEFAULT 100' },
648
- { col: 'is_active', sql: 'ALTER TABLE memories ADD COLUMN is_active INTEGER DEFAULT 1' },
649
- { col: 'expires_at', sql: 'ALTER TABLE memories ADD COLUMN expires_at INTEGER' },
650
- { col: 'decay_rate', sql: 'ALTER TABLE memories ADD COLUMN decay_rate INTEGER DEFAULT 30' },
651
- { col: 'coactivation_score', sql: 'ALTER TABLE memories ADD COLUMN coactivation_score INTEGER DEFAULT 0' },
652
- { col: 'last_decay_at', sql: 'ALTER TABLE memories ADD COLUMN last_decay_at INTEGER' },
653
- { col: 'agent_id', sql: 'ALTER TABLE memories ADD COLUMN agent_id TEXT' },
654
- { col: 'agent_role', sql: 'ALTER TABLE memories ADD COLUMN agent_role TEXT' },
655
- { col: 'retrieval_priority', sql: 'ALTER TABLE memories ADD COLUMN retrieval_priority INTEGER DEFAULT 50' },
656
- { col: 'importance_score', sql: 'ALTER TABLE memories ADD COLUMN importance_score INTEGER DEFAULT 50' },
657
- { col: 'importance_decay_rate', sql: 'ALTER TABLE memories ADD COLUMN importance_decay_rate INTEGER DEFAULT 30' },
658
- { col: 'last_importance_recalc', sql: 'ALTER TABLE memories ADD COLUMN last_importance_recalc INTEGER' },
662
+ // Layer tracking (v0.9.x)
663
+ { col: 'has_l0_abstract', sql: 'ALTER TABLE memories ADD COLUMN has_l0_abstract INTEGER DEFAULT 0' },
664
+ { col: 'has_l1_overview', sql: 'ALTER TABLE memories ADD COLUMN has_l1_overview INTEGER DEFAULT 0' },
665
+ { col: 'last_layer_update', sql: 'ALTER TABLE memories ADD COLUMN last_layer_update INTEGER' },
666
+ // Namespace support (v1.0.x)
667
+ { col: 'namespace_id', sql: 'ALTER TABLE memories ADD COLUMN namespace_id TEXT REFERENCES namespaces(id) ON DELETE SET NULL' },
668
+ { col: 'namespace_path', sql: 'ALTER TABLE memories ADD COLUMN namespace_path TEXT' },
669
+ // Token tracking (v1.0.x)
670
+ { col: 'tokens_estimate', sql: 'ALTER TABLE memories ADD COLUMN tokens_estimate INTEGER DEFAULT 0' },
659
671
  ];
660
672
  // Get existing columns for memories table
661
673
  const tableInfo = sqlite.prepare("PRAGMA table_info(memories)").all();
@@ -676,28 +688,90 @@ async function runSqliteMigrations(sqlite) {
676
688
  throw new Error(`Migration failed for column ${migration.col}: ${msg}`);
677
689
  }
678
690
  }
679
- // v0.9.2: Add tokens_estimate to core_memory
680
- const coreMemoryTableCheck = sqlite.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='core_memory'").get();
681
- if (coreMemoryTableCheck) {
682
- const coreMemoryInfo = sqlite.prepare("PRAGMA table_info(core_memory)").all();
683
- const existingCoreMemoryColumns = new Set(coreMemoryInfo.map(col => col.name));
684
- const coreMemoryMigrations = [
685
- { col: 'tokens_estimate', sql: 'ALTER TABLE core_memory ADD COLUMN tokens_estimate INTEGER DEFAULT 0 NOT NULL' },
686
- ];
687
- for (const migration of coreMemoryMigrations) {
688
- if (!existingCoreMemoryColumns.has(migration.col)) {
689
- try {
690
- sqlite.exec(migration.sql);
691
- logger.info(`Migration: Added column ${migration.col} to core_memory table`);
691
+ }
692
+ }
693
+ // v0.9.2: Add tokens_estimate to core_memory
694
+ const coreMemoryTableCheck = sqlite.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='core_memory'").get();
695
+ if (coreMemoryTableCheck) {
696
+ const coreMemoryInfo = sqlite.prepare("PRAGMA table_info(core_memory)").all();
697
+ const existingCoreMemoryColumns = new Set(coreMemoryInfo.map(col => col.name));
698
+ const coreMemoryMigrations = [
699
+ { col: 'tokens_estimate', sql: 'ALTER TABLE core_memory ADD COLUMN tokens_estimate INTEGER DEFAULT 0 NOT NULL' },
700
+ ];
701
+ for (const migration of coreMemoryMigrations) {
702
+ if (!existingCoreMemoryColumns.has(migration.col)) {
703
+ try {
704
+ sqlite.exec(migration.sql);
705
+ logger.info(`Migration: Added column ${migration.col} to core_memory table`);
706
+ }
707
+ catch (error) {
708
+ const msg = error instanceof Error ? error.message : String(error);
709
+ if (msg.includes('duplicate column name')) {
710
+ logger.debug(`Migration skipped for ${migration.col}: column already exists`);
711
+ }
712
+ else {
713
+ throw new Error(`Migration failed for column ${migration.col}: ${msg}`);
714
+ }
715
+ }
716
+ }
717
+ }
718
+ }
719
+ // Migrations for maintenance_jobs table (v1.0.x)
720
+ const maintenanceJobsTableCheck = sqlite.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='maintenance_jobs'").get();
721
+ if (maintenanceJobsTableCheck) {
722
+ const maintenanceJobsInfo = sqlite.prepare("PRAGMA table_info(maintenance_jobs)").all();
723
+ const existingMaintenanceJobsColumns = new Set(maintenanceJobsInfo.map(col => col.name));
724
+ // Check if table has wrong schema (camelCase columns from bug in earlier version)
725
+ const hasCamelCaseColumns = existingMaintenanceJobsColumns.has('jobName') ||
726
+ existingMaintenanceJobsColumns.has('jobType') ||
727
+ existingMaintenanceJobsColumns.has('cronExpression');
728
+ if (hasCamelCaseColumns) {
729
+ // Table has incorrect camelCase schema - need to recreate it
730
+ logger.warn('Maintenance jobs table has incorrect schema (camelCase columns). Recreating...');
731
+ try {
732
+ // Drop the malformed table
733
+ sqlite.exec('DROP TABLE IF EXISTS maintenance_jobs');
734
+ // Recreate with correct schema - it will be created by the schema SQL
735
+ logger.info('Dropped malformed maintenance_jobs table. It will be recreated with correct schema.');
736
+ }
737
+ catch (error) {
738
+ logger.error('Failed to recreate maintenance_jobs table:', error);
739
+ }
740
+ }
741
+ else {
742
+ // Normal migrations for correct schema
743
+ const maintenanceJobsMigrations = [
744
+ { col: 'schedule', sql: 'ALTER TABLE maintenance_jobs DROP COLUMN schedule' },
745
+ { col: 'cron_expression', sql: 'ALTER TABLE maintenance_jobs ADD COLUMN cron_expression TEXT' },
746
+ { col: 'last_run_at', sql: 'ALTER TABLE maintenance_jobs ADD COLUMN last_run_at INTEGER' },
747
+ { col: 'last_run_duration', sql: 'ALTER TABLE maintenance_jobs ADD COLUMN last_run_duration INTEGER' },
748
+ { col: 'last_run_status', sql: 'ALTER TABLE maintenance_jobs ADD COLUMN last_run_status TEXT' },
749
+ { col: 'last_run_error', sql: 'ALTER TABLE maintenance_jobs ADD COLUMN last_run_error TEXT' },
750
+ { col: 'total_runs', sql: 'ALTER TABLE maintenance_jobs ADD COLUMN total_runs INTEGER DEFAULT 0' },
751
+ { col: 'success_count', sql: 'ALTER TABLE maintenance_jobs ADD COLUMN success_count INTEGER DEFAULT 0' },
752
+ { col: 'failure_count', sql: 'ALTER TABLE maintenance_jobs ADD COLUMN failure_count INTEGER DEFAULT 0' },
753
+ { col: 'job_config', sql: 'ALTER TABLE maintenance_jobs ADD COLUMN job_config TEXT' },
754
+ { col: 'next_run_at', sql: 'ALTER TABLE maintenance_jobs ADD COLUMN next_run_at INTEGER' },
755
+ { col: 'run_count', sql: 'ALTER TABLE maintenance_jobs DROP COLUMN run_count' },
756
+ ];
757
+ for (const migration of maintenanceJobsMigrations) {
758
+ // For DROP migrations, only run if column EXISTS
759
+ // For ADD migrations, only run if column does NOT exist
760
+ const shouldRun = migration.sql.startsWith('ALTER TABLE maintenance_jobs DROP COLUMN')
761
+ ? existingMaintenanceJobsColumns.has(migration.col)
762
+ : !existingMaintenanceJobsColumns.has(migration.col);
763
+ if (shouldRun) {
764
+ try {
765
+ sqlite.exec(migration.sql);
766
+ logger.info(`Migration: ${migration.col} on maintenance_jobs table`);
767
+ }
768
+ catch (error) {
769
+ const msg = error instanceof Error ? error.message : String(error);
770
+ if (msg.includes('duplicate column name') || msg.includes('no such column')) {
771
+ logger.debug(`Migration skipped for ${migration.col}: ${msg.includes('duplicate column name') ? 'column already exists' : 'column does not exist'}`);
692
772
  }
693
- catch (error) {
694
- const msg = error instanceof Error ? error.message : String(error);
695
- if (msg.includes('duplicate column name')) {
696
- logger.debug(`Migration skipped for ${migration.col}: column already exists`);
697
- }
698
- else {
699
- throw new Error(`Migration failed for column ${migration.col}: ${msg}`);
700
- }
773
+ else {
774
+ throw new Error(`Migration failed for column ${migration.col}: ${msg}`);
701
775
  }
702
776
  }
703
777
  }