@devflow-tools/memory-engine 0.17.8 → 0.18.1

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 (155) hide show
  1. package/LICENSE +21 -0
  2. package/dist/consolidator.d.ts.map +1 -1
  3. package/dist/consolidator.js +2 -0
  4. package/dist/consolidator.js.map +1 -1
  5. package/dist/embedding-model-store.d.ts +1 -4
  6. package/dist/embedding-model-store.d.ts.map +1 -1
  7. package/dist/embedding-model-store.js +3 -75
  8. package/dist/embedding-model-store.js.map +1 -1
  9. package/dist/embedding-provider.d.ts +3 -2
  10. package/dist/embedding-provider.d.ts.map +1 -1
  11. package/dist/embedding-provider.js +151 -43
  12. package/dist/embedding-provider.js.map +1 -1
  13. package/dist/hybrid-search.d.ts +5 -1
  14. package/dist/hybrid-search.d.ts.map +1 -1
  15. package/dist/hybrid-search.js +92 -56
  16. package/dist/hybrid-search.js.map +1 -1
  17. package/dist/index.d.ts +16 -3
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +8 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/lease-store.d.ts.map +1 -1
  22. package/dist/lease-store.js +30 -0
  23. package/dist/lease-store.js.map +1 -1
  24. package/dist/memory-command-service.d.ts +73 -0
  25. package/dist/memory-command-service.d.ts.map +1 -0
  26. package/dist/memory-command-service.js +283 -0
  27. package/dist/memory-command-service.js.map +1 -0
  28. package/dist/memory-engine.d.ts +19 -15
  29. package/dist/memory-engine.d.ts.map +1 -1
  30. package/dist/memory-engine.js +207 -158
  31. package/dist/memory-engine.js.map +1 -1
  32. package/dist/memory-episode-repository.d.ts +77 -0
  33. package/dist/memory-episode-repository.d.ts.map +1 -0
  34. package/dist/memory-episode-repository.js +262 -0
  35. package/dist/memory-episode-repository.js.map +1 -0
  36. package/dist/memory-fact-provider-port.d.ts +83 -0
  37. package/dist/memory-fact-provider-port.d.ts.map +1 -0
  38. package/dist/memory-fact-provider-port.js +22 -0
  39. package/dist/memory-fact-provider-port.js.map +1 -0
  40. package/dist/memory-fact-validator.d.ts +16 -0
  41. package/dist/memory-fact-validator.d.ts.map +1 -0
  42. package/dist/memory-fact-validator.js +52 -0
  43. package/dist/memory-fact-validator.js.map +1 -0
  44. package/dist/memory-gate.d.ts +14 -17
  45. package/dist/memory-gate.d.ts.map +1 -1
  46. package/dist/memory-gate.js +369 -329
  47. package/dist/memory-gate.js.map +1 -1
  48. package/dist/memory-identity-compiler.d.ts +18 -0
  49. package/dist/memory-identity-compiler.d.ts.map +1 -0
  50. package/dist/memory-identity-compiler.js +26 -0
  51. package/dist/memory-identity-compiler.js.map +1 -0
  52. package/dist/memory-index-projector.d.ts +22 -0
  53. package/dist/memory-index-projector.d.ts.map +1 -0
  54. package/dist/memory-index-projector.js +11 -0
  55. package/dist/memory-index-projector.js.map +1 -0
  56. package/dist/memory-lifecycle-reducer.d.ts +21 -0
  57. package/dist/memory-lifecycle-reducer.d.ts.map +1 -0
  58. package/dist/memory-lifecycle-reducer.js +34 -0
  59. package/dist/memory-lifecycle-reducer.js.map +1 -0
  60. package/dist/memory-maintenance.d.ts +1 -4
  61. package/dist/memory-maintenance.d.ts.map +1 -1
  62. package/dist/memory-maintenance.js +3 -20
  63. package/dist/memory-maintenance.js.map +1 -1
  64. package/dist/memory-query-service.d.ts +24 -0
  65. package/dist/memory-query-service.d.ts.map +1 -0
  66. package/dist/memory-query-service.js +180 -0
  67. package/dist/memory-query-service.js.map +1 -0
  68. package/dist/memory-relevance.d.ts +16 -1
  69. package/dist/memory-relevance.d.ts.map +1 -1
  70. package/dist/memory-relevance.js +83 -139
  71. package/dist/memory-relevance.js.map +1 -1
  72. package/dist/memory-store.d.ts +52 -36
  73. package/dist/memory-store.d.ts.map +1 -1
  74. package/dist/memory-store.js +406 -704
  75. package/dist/memory-store.js.map +1 -1
  76. package/dist/semantic-memory-compiler.d.ts +0 -2
  77. package/dist/semantic-memory-compiler.d.ts.map +1 -1
  78. package/dist/semantic-memory-compiler.js +4 -4
  79. package/dist/semantic-memory-compiler.js.map +1 -1
  80. package/dist/session-summarizer.d.ts +3 -0
  81. package/dist/session-summarizer.d.ts.map +1 -1
  82. package/dist/session-summarizer.js.map +1 -1
  83. package/dist/signal-analyzer.d.ts +2 -2
  84. package/dist/signal-analyzer.d.ts.map +1 -1
  85. package/dist/signal-analyzer.js +10 -16
  86. package/dist/signal-analyzer.js.map +1 -1
  87. package/package.json +14 -5
  88. package/dist/lease-store.test.d.ts +0 -2
  89. package/dist/lease-store.test.d.ts.map +0 -1
  90. package/dist/lease-store.test.js +0 -182
  91. package/dist/lease-store.test.js.map +0 -1
  92. package/dist/migrations/v10-to-v11.d.ts +0 -3
  93. package/dist/migrations/v10-to-v11.d.ts.map +0 -1
  94. package/dist/migrations/v10-to-v11.js +0 -21
  95. package/dist/migrations/v10-to-v11.js.map +0 -1
  96. package/dist/migrations/v11-to-v12.d.ts +0 -66
  97. package/dist/migrations/v11-to-v12.d.ts.map +0 -1
  98. package/dist/migrations/v11-to-v12.js +0 -577
  99. package/dist/migrations/v11-to-v12.js.map +0 -1
  100. package/dist/migrations/v12-to-v13.d.ts +0 -3
  101. package/dist/migrations/v12-to-v13.d.ts.map +0 -1
  102. package/dist/migrations/v12-to-v13.js +0 -50
  103. package/dist/migrations/v12-to-v13.js.map +0 -1
  104. package/dist/migrations/v2-to-v3.d.ts +0 -3
  105. package/dist/migrations/v2-to-v3.d.ts.map +0 -1
  106. package/dist/migrations/v2-to-v3.js +0 -122
  107. package/dist/migrations/v2-to-v3.js.map +0 -1
  108. package/dist/migrations/v2-to-v3.test.d.ts +0 -2
  109. package/dist/migrations/v2-to-v3.test.d.ts.map +0 -1
  110. package/dist/migrations/v2-to-v3.test.js +0 -88
  111. package/dist/migrations/v2-to-v3.test.js.map +0 -1
  112. package/dist/migrations/v3-to-v4.d.ts +0 -3
  113. package/dist/migrations/v3-to-v4.d.ts.map +0 -1
  114. package/dist/migrations/v3-to-v4.js +0 -97
  115. package/dist/migrations/v3-to-v4.js.map +0 -1
  116. package/dist/migrations/v4-to-v5.d.ts +0 -3
  117. package/dist/migrations/v4-to-v5.d.ts.map +0 -1
  118. package/dist/migrations/v4-to-v5.js +0 -34
  119. package/dist/migrations/v4-to-v5.js.map +0 -1
  120. package/dist/migrations/v4-to-v5.test.d.ts +0 -2
  121. package/dist/migrations/v4-to-v5.test.d.ts.map +0 -1
  122. package/dist/migrations/v4-to-v5.test.js +0 -32
  123. package/dist/migrations/v4-to-v5.test.js.map +0 -1
  124. package/dist/migrations/v6-to-v7.d.ts +0 -3
  125. package/dist/migrations/v6-to-v7.d.ts.map +0 -1
  126. package/dist/migrations/v6-to-v7.js +0 -18
  127. package/dist/migrations/v6-to-v7.js.map +0 -1
  128. package/dist/migrations/v6-to-v7.test.d.ts +0 -2
  129. package/dist/migrations/v6-to-v7.test.d.ts.map +0 -1
  130. package/dist/migrations/v6-to-v7.test.js +0 -33
  131. package/dist/migrations/v6-to-v7.test.js.map +0 -1
  132. package/dist/migrations/v7-to-v8.d.ts +0 -3
  133. package/dist/migrations/v7-to-v8.d.ts.map +0 -1
  134. package/dist/migrations/v7-to-v8.js +0 -20
  135. package/dist/migrations/v7-to-v8.js.map +0 -1
  136. package/dist/migrations/v8-to-v9.d.ts +0 -3
  137. package/dist/migrations/v8-to-v9.d.ts.map +0 -1
  138. package/dist/migrations/v8-to-v9.js +0 -18
  139. package/dist/migrations/v8-to-v9.js.map +0 -1
  140. package/dist/migrations/v9-to-v10.d.ts +0 -3
  141. package/dist/migrations/v9-to-v10.d.ts.map +0 -1
  142. package/dist/migrations/v9-to-v10.js +0 -45
  143. package/dist/migrations/v9-to-v10.js.map +0 -1
  144. package/dist/migrations/v9-to-v10.test.d.ts +0 -2
  145. package/dist/migrations/v9-to-v10.test.d.ts.map +0 -1
  146. package/dist/migrations/v9-to-v10.test.js +0 -40
  147. package/dist/migrations/v9-to-v10.test.js.map +0 -1
  148. package/dist/preference-conflicts.test.d.ts +0 -2
  149. package/dist/preference-conflicts.test.d.ts.map +0 -1
  150. package/dist/preference-conflicts.test.js +0 -80
  151. package/dist/preference-conflicts.test.js.map +0 -1
  152. package/dist/semantic-memory.test.d.ts +0 -2
  153. package/dist/semantic-memory.test.d.ts.map +0 -1
  154. package/dist/semantic-memory.test.js +0 -61
  155. package/dist/semantic-memory.test.js.map +0 -1
@@ -1,50 +0,0 @@
1
- export function applyV12ToV13Migration(db) {
2
- if (db.prepare('SELECT 1 FROM schema_versions WHERE version = 13').get())
3
- return;
4
- db.transaction(() => {
5
- const columns = db.pragma('table_info(observations)');
6
- const addColumn = (name, sql) => {
7
- if (!columns.some(column => column.name === name))
8
- db.exec(sql);
9
- };
10
- addColumn('capsule', "ALTER TABLE observations ADD COLUMN capsule TEXT NOT NULL DEFAULT '{}'");
11
- addColumn('projection', "ALTER TABLE observations ADD COLUMN projection TEXT NOT NULL DEFAULT '{}'");
12
- addColumn('extraction_version', "ALTER TABLE observations ADD COLUMN extraction_version TEXT NOT NULL DEFAULT 'legacy'");
13
- db.exec(`
14
- CREATE TABLE observation_relations_v13 (
15
- source_observation_id TEXT NOT NULL REFERENCES observations(id) ON DELETE CASCADE,
16
- target_observation_id TEXT NOT NULL REFERENCES observations(id) ON DELETE CASCADE,
17
- type TEXT NOT NULL CHECK(type IN ('related_to', 'supersedes', 'references', 'contradicts', 'continues')),
18
- created_at INTEGER NOT NULL,
19
- PRIMARY KEY (source_observation_id, target_observation_id, type)
20
- );
21
- INSERT OR IGNORE INTO observation_relations_v13
22
- SELECT source_observation_id, target_observation_id, type, created_at
23
- FROM observation_relations;
24
- DROP TABLE observation_relations;
25
- ALTER TABLE observation_relations_v13 RENAME TO observation_relations;
26
- CREATE INDEX idx_observation_relations_target
27
- ON observation_relations(target_observation_id, type);
28
- `);
29
- db.exec(`
30
- UPDATE observations
31
- SET capsule = json_object(
32
- 'narrative', narrative,
33
- 'sourceEvidenceIds', json(source_event_ids),
34
- 'entities', json(entities),
35
- 'temporalStatus', COALESCE(json_extract(temporal, '$.status'), 'current'),
36
- 'extractionVersion', 'legacy-backfill.v1'
37
- ),
38
- projection = json_object(
39
- 'subject', semantic_subject,
40
- 'predicate', semantic_predicate,
41
- 'value', semantic_value,
42
- 'polarity', semantic_polarity,
43
- 'facet', 'generic'
44
- )
45
- WHERE capsule = '{}' OR projection = '{}';
46
- `);
47
- db.prepare('INSERT OR REPLACE INTO schema_versions(version, applied_at) VALUES (13, ?)').run(Date.now());
48
- })();
49
- }
50
- //# sourceMappingURL=v12-to-v13.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v12-to-v13.js","sourceRoot":"","sources":["../../src/migrations/v12-to-v13.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,sBAAsB,CAAC,EAAqB;IAC1D,IAAI,EAAE,CAAC,OAAO,CAAC,kDAAkD,CAAC,CAAC,GAAG,EAAE;QAAE,OAAO;IACjF,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE;QAClB,MAAM,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,0BAA0B,CAA4B,CAAC;QACjF,MAAM,SAAS,GAAG,CAAC,IAAY,EAAE,GAAW,EAAQ,EAAE;YACpD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC;gBAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClE,CAAC,CAAC;QACF,SAAS,CAAC,SAAS,EAAE,wEAAwE,CAAC,CAAC;QAC/F,SAAS,CAAC,YAAY,EAAE,2EAA2E,CAAC,CAAC;QACrG,SAAS,CAAC,oBAAoB,EAAE,uFAAuF,CAAC,CAAC;QAEzH,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;KAeP,CAAC,CAAC;QACH,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;KAiBP,CAAC,CAAC;QACH,EAAE,CAAC,OAAO,CAAC,4EAA4E,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC3G,CAAC,CAAC,EAAE,CAAC;AACP,CAAC"}
@@ -1,3 +0,0 @@
1
- import type Database from "better-sqlite3";
2
- export declare function applyV2ToV3Migration(db: Database.Database): void;
3
- //# sourceMappingURL=v2-to-v3.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v2-to-v3.d.ts","sourceRoot":"","sources":["../../src/migrations/v2-to-v3.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAO3C,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAiIhE"}
@@ -1,122 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- function normalizeTitle(title) {
3
- return title.normalize('NFKC').trim().toLocaleLowerCase('en-US').replace(/\s+/g, ' ');
4
- }
5
- export function applyV2ToV3Migration(db) {
6
- db.exec(`
7
- CREATE TABLE IF NOT EXISTS schema_versions (
8
- version INTEGER PRIMARY KEY,
9
- applied_at INTEGER
10
- );
11
- `);
12
- const currentMax = db.prepare(`SELECT COALESCE(MAX(version), 0) as v FROM schema_versions`).get().v;
13
- db.exec(`
14
- CREATE TABLE IF NOT EXISTS distill_leases (
15
- batch_id TEXT PRIMARY KEY,
16
- event_ids TEXT NOT NULL,
17
- session_ids TEXT NOT NULL,
18
- event_count INTEGER NOT NULL,
19
- created_at INTEGER NOT NULL,
20
- expires_at INTEGER NOT NULL,
21
- status TEXT NOT NULL CHECK (status IN ('pending','committed','expired'))
22
- );
23
- CREATE INDEX IF NOT EXISTS idx_leases_expires ON distill_leases(status, expires_at);
24
- `);
25
- if (currentMax < 3) {
26
- db.prepare(`INSERT INTO schema_versions (version, applied_at) VALUES (3, ?)`).run(Date.now());
27
- }
28
- // v3 quality fields are additive for fresh databases and are rebuilt here for
29
- // v2 databases whose observations still have the retired CHECK constraint.
30
- const memoryColumns = db.pragma('table_info(memories)');
31
- if (memoryColumns.length > 0 && !memoryColumns.some(column => column.name === 'last_accessed_at')) {
32
- db.exec('ALTER TABLE memories ADD COLUMN last_accessed_at INTEGER');
33
- }
34
- const observationColumns = db.pragma('table_info(observations)');
35
- const observationSql = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'observations'").get()?.sql ?? '';
36
- const needsRebuild = observationColumns.length > 0 && (!observationColumns.some(column => column.name === 'source')
37
- || !observationColumns.some(column => column.name === 'dedup_key')
38
- || observationSql.includes('session_summary'));
39
- if (needsRebuild) {
40
- db.pragma('foreign_keys = OFF');
41
- db.exec(`
42
- CREATE TABLE observations_v3_new (
43
- id TEXT PRIMARY KEY,
44
- session_id TEXT NOT NULL,
45
- project TEXT NOT NULL DEFAULT '',
46
- type TEXT NOT NULL,
47
- title TEXT NOT NULL,
48
- narrative TEXT NOT NULL DEFAULT '',
49
- facts TEXT NOT NULL DEFAULT '[]',
50
- files TEXT NOT NULL DEFAULT '[]',
51
- tools TEXT NOT NULL DEFAULT '[]',
52
- concepts TEXT NOT NULL DEFAULT '[]',
53
- importance INTEGER NOT NULL DEFAULT 5,
54
- confidence REAL NOT NULL DEFAULT 0.5,
55
- source TEXT NOT NULL DEFAULT 'distill',
56
- dedup_key TEXT,
57
- access_count INTEGER NOT NULL DEFAULT 0,
58
- updated_at INTEGER NOT NULL,
59
- source_event_ids TEXT NOT NULL DEFAULT '[]',
60
- memory_id TEXT,
61
- embedding BLOB,
62
- timestamp INTEGER NOT NULL
63
- );
64
- INSERT INTO observations_v3_new
65
- (id, session_id, project, type, title, narrative, facts, files, tools,
66
- concepts, importance, confidence, source, dedup_key, access_count,
67
- updated_at, source_event_ids, memory_id, embedding, timestamp)
68
- SELECT id, session_id, project, type, title, narrative, facts, files, tools,
69
- concepts, importance, confidence, 'distill', NULL, 0, timestamp,
70
- source_event_ids, memory_id, embedding, timestamp
71
- FROM observations;
72
- DROP TABLE observations;
73
- ALTER TABLE observations_v3_new RENAME TO observations;
74
- CREATE INDEX IF NOT EXISTS idx_observations_session ON observations(session_id);
75
- CREATE INDEX IF NOT EXISTS idx_observations_project ON observations(project);
76
- CREATE INDEX IF NOT EXISTS idx_observations_memory ON observations(memory_id);
77
- CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type);
78
- CREATE UNIQUE INDEX IF NOT EXISTS idx_observations_dedup ON observations(dedup_key);
79
- `);
80
- db.pragma('foreign_keys = ON');
81
- }
82
- else if (observationColumns.length > 0) {
83
- const addColumn = (name, sql) => {
84
- if (!observationColumns.some(column => column.name === name))
85
- db.exec(sql);
86
- };
87
- addColumn('source', "ALTER TABLE observations ADD COLUMN source TEXT NOT NULL DEFAULT 'distill'");
88
- addColumn('dedup_key', 'ALTER TABLE observations ADD COLUMN dedup_key TEXT');
89
- addColumn('access_count', 'ALTER TABLE observations ADD COLUMN access_count INTEGER NOT NULL DEFAULT 0');
90
- addColumn('updated_at', 'ALTER TABLE observations ADD COLUMN updated_at INTEGER NOT NULL DEFAULT 0');
91
- db.exec('UPDATE observations SET updated_at = timestamp WHERE updated_at = 0');
92
- db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_observations_dedup ON observations(dedup_key)');
93
- }
94
- const migratedObservationColumns = db.pragma('table_info(observations)');
95
- if (currentMax < 3 && migratedObservationColumns.some(column => column.name === 'dedup_key')) {
96
- const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'table'").all().map(row => row.name));
97
- const rows = db.prepare('SELECT id, type, title FROM observations ORDER BY timestamp DESC, id DESC').all();
98
- const updateKey = db.prepare('UPDATE observations SET dedup_key = ? WHERE id = ?');
99
- const deleteDuplicate = db.prepare('DELETE FROM observations WHERE id = ?');
100
- const seen = new Set();
101
- db.transaction(() => {
102
- for (const row of rows) {
103
- const key = createHash('sha256').update(`${row.type}|${normalizeTitle(row.title)}`).digest('hex');
104
- if (seen.has(key)) {
105
- if (tables.has('embeddings')) {
106
- db.prepare("DELETE FROM embeddings WHERE source_type = 'observation' AND source_id = ?").run(row.id);
107
- }
108
- if (tables.has('observation_concepts')) {
109
- db.prepare('DELETE FROM observation_concepts WHERE observation_id = ?').run(row.id);
110
- }
111
- deleteDuplicate.run(row.id);
112
- }
113
- else {
114
- seen.add(key);
115
- updateKey.run(key, row.id);
116
- }
117
- }
118
- db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_observations_dedup ON observations(dedup_key)');
119
- })();
120
- }
121
- }
122
- //# sourceMappingURL=v2-to-v3.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v2-to-v3.js","sourceRoot":"","sources":["../../src/migrations/v2-to-v3.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=v2-to-v3.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v2-to-v3.test.d.ts","sourceRoot":"","sources":["../../src/migrations/v2-to-v3.test.ts"],"names":[],"mappings":""}
@@ -1,88 +0,0 @@
1
- import { describe, it, expect, beforeEach } from "vitest";
2
- import Database from "better-sqlite3";
3
- import { applyV2ToV3Migration } from "./v2-to-v3.js";
4
- describe("v2-to-v3 migration", () => {
5
- let db;
6
- beforeEach(() => {
7
- db = new Database(":memory:");
8
- db.exec(`CREATE TABLE schema_versions (version INTEGER PRIMARY KEY, applied_at INTEGER)`);
9
- db.prepare(`INSERT INTO schema_versions VALUES (2, ?)`).run(Date.now());
10
- });
11
- it("creates distill_leases table with correct schema", () => {
12
- applyV2ToV3Migration(db);
13
- const cols = db.prepare(`PRAGMA table_info(distill_leases)`).all();
14
- const names = cols.map(c => c.name).sort();
15
- expect(names).toEqual(["batch_id", "created_at", "event_count", "event_ids", "expires_at", "session_ids", "status"]);
16
- });
17
- it("creates index on (status, expires_at)", () => {
18
- applyV2ToV3Migration(db);
19
- const indexes = db.prepare(`PRAGMA index_list(distill_leases)`).all();
20
- expect(indexes.some(i => i.name === "idx_leases_expires")).toBe(true);
21
- });
22
- it("updates schema_versions to 3", () => {
23
- applyV2ToV3Migration(db);
24
- const rows = db.prepare(`SELECT version FROM schema_versions ORDER BY version`).all();
25
- expect(rows.map(r => r.version)).toContain(3);
26
- });
27
- it("is idempotent (re-run does not throw or duplicate)", () => {
28
- applyV2ToV3Migration(db);
29
- expect(() => applyV2ToV3Migration(db)).not.toThrow();
30
- const count = db.prepare(`SELECT COUNT(*) as n FROM schema_versions WHERE version = 3`).get().n;
31
- expect(count).toBe(1);
32
- });
33
- it("does not duplicate version 3 if it already exists", () => {
34
- db.prepare(`INSERT INTO schema_versions VALUES (3, ?)`).run(Date.now());
35
- expect(() => applyV2ToV3Migration(db)).not.toThrow();
36
- const count = db.prepare(`SELECT COUNT(*) as n FROM schema_versions WHERE version = 3`).get().n;
37
- expect(count).toBe(1);
38
- });
39
- it("allows inserting a lease row", () => {
40
- applyV2ToV3Migration(db);
41
- db.prepare(`
42
- INSERT INTO distill_leases (batch_id, event_ids, session_ids, event_count, created_at, expires_at, status)
43
- VALUES (?, ?, ?, ?, ?, ?, ?)
44
- `).run("batch1", '["e1","e2"]', '["s1"]', 2, Date.now(), Date.now() + 600_000, "pending");
45
- const row = db.prepare(`SELECT * FROM distill_leases WHERE batch_id = 'batch1'`).get();
46
- expect(row).toBeDefined();
47
- });
48
- it("rejects invalid lease status", () => {
49
- applyV2ToV3Migration(db);
50
- expect(() => db.prepare(`
51
- INSERT INTO distill_leases (batch_id, event_ids, session_ids, event_count, created_at, expires_at, status)
52
- VALUES (?, ?, ?, ?, ?, ?, ?)
53
- `).run("batch1", "[]", "[]", 0, Date.now(), Date.now(), "open")).toThrow();
54
- });
55
- it("rebuilds legacy observations with quality columns and exact dedup", () => {
56
- db.exec(`
57
- CREATE TABLE memories (id TEXT PRIMARY KEY);
58
- CREATE TABLE observations (
59
- id TEXT PRIMARY KEY, session_id TEXT NOT NULL, project TEXT NOT NULL DEFAULT '',
60
- type TEXT NOT NULL CHECK(type IN ('session_summary','config_discovery')),
61
- title TEXT NOT NULL, narrative TEXT NOT NULL DEFAULT '', facts TEXT NOT NULL DEFAULT '[]',
62
- files TEXT NOT NULL DEFAULT '[]', tools TEXT NOT NULL DEFAULT '[]', concepts TEXT NOT NULL DEFAULT '[]',
63
- importance INTEGER NOT NULL DEFAULT 5, confidence REAL NOT NULL DEFAULT 0.5,
64
- source_event_ids TEXT NOT NULL DEFAULT '[]', memory_id TEXT, embedding BLOB, timestamp INTEGER NOT NULL
65
- );
66
- INSERT INTO observations (id, session_id, type, title, timestamp) VALUES
67
- ('old-1', 's1', 'config_discovery', 'Workspace Config', 1),
68
- ('old-2', 's2', 'config_discovery', ' workspace config ', 2);
69
- `);
70
- applyV2ToV3Migration(db);
71
- const memoryColumns = db.pragma('table_info(memories)');
72
- const observationColumns = db.pragma('table_info(observations)');
73
- expect(memoryColumns.map(column => column.name)).toContain('last_accessed_at');
74
- expect(observationColumns.map(column => column.name)).toEqual(expect.arrayContaining([
75
- 'source', 'dedup_key', 'access_count', 'updated_at',
76
- ]));
77
- expect(db.prepare('SELECT COUNT(*) count FROM observations').get()).toMatchObject({ count: 1 });
78
- expect(db.prepare('SELECT source, dedup_key FROM observations').get()).toMatchObject({
79
- source: 'distill', dedup_key: expect.stringMatching(/^[a-f0-9]{64}$/),
80
- });
81
- expect(() => db.prepare(`
82
- INSERT INTO observations
83
- (id, session_id, type, title, updated_at, timestamp)
84
- VALUES ('new-1', 's3', 'user_preference', 'Use npm run dev', 3, 3)
85
- `).run()).not.toThrow();
86
- });
87
- });
88
- //# sourceMappingURL=v2-to-v3.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v2-to-v3.test.js","sourceRoot":"","sources":["../../src/migrations/v2-to-v3.test.ts"],"names":[],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- import type Database from 'better-sqlite3';
2
- export declare function applyV3ToV4Migration(db: Database.Database): void;
3
- //# sourceMappingURL=v3-to-v4.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v3-to-v4.d.ts","sourceRoot":"","sources":["../../src/migrations/v3-to-v4.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAkB3C,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAkGhE"}
@@ -1,97 +0,0 @@
1
- function addColumn(db, table, name, definition) {
2
- const columns = db.pragma(`table_info(${table})`);
3
- if (!columns.some(column => column.name === name)) {
4
- db.exec(`ALTER TABLE ${table} ADD COLUMN ${name} ${definition}`);
5
- }
6
- }
7
- export function applyV3ToV4Migration(db) {
8
- db.exec(`
9
- CREATE TABLE IF NOT EXISTS schema_versions (
10
- version INTEGER PRIMARY KEY,
11
- applied_at INTEGER
12
- );
13
- `);
14
- addColumn(db, 'observations', 'quality_status', "TEXT NOT NULL DEFAULT 'accepted'");
15
- addColumn(db, 'observations', 'quarantine_reason', 'TEXT');
16
- addColumn(db, 'observations', 'quality_details', 'TEXT');
17
- addColumn(db, 'observations', 'quarantined_at', 'INTEGER');
18
- addColumn(db, 'observations', 'resolution_state', "TEXT NOT NULL DEFAULT 'current'");
19
- addColumn(db, 'observations', 'superseded_by_observation', 'TEXT');
20
- addColumn(db, 'observations', 'failure_category', 'TEXT');
21
- addColumn(db, 'session_events', 'maintenance_status', "TEXT NOT NULL DEFAULT 'active'");
22
- addColumn(db, 'session_events', 'quarantine_reason', 'TEXT');
23
- addColumn(db, 'access_log', 'query', 'TEXT');
24
- addColumn(db, 'access_log', 'purpose', 'TEXT');
25
- addColumn(db, 'access_log', 'rank', 'INTEGER');
26
- addColumn(db, 'access_log', 'score', 'REAL');
27
- db.exec(`
28
- CREATE INDEX IF NOT EXISTS idx_observations_quality
29
- ON observations(quality_status, resolution_state, timestamp);
30
- CREATE INDEX IF NOT EXISTS idx_observations_failure_category
31
- ON observations(failure_category, timestamp);
32
- CREATE INDEX IF NOT EXISTS idx_session_events_maintenance
33
- ON session_events(maintenance_status, processed, created_at);
34
- CREATE INDEX IF NOT EXISTS idx_session_events_reconciliation
35
- ON session_events(session_id, processed, maintenance_status, created_at, id);
36
-
37
- CREATE TABLE IF NOT EXISTS retrieval_access_log (
38
- id TEXT PRIMARY KEY,
39
- source_type TEXT NOT NULL CHECK(source_type IN ('memory','observation')),
40
- source_id TEXT NOT NULL,
41
- access_type TEXT NOT NULL,
42
- session_id TEXT,
43
- query TEXT,
44
- purpose TEXT,
45
- rank INTEGER,
46
- score REAL,
47
- timestamp INTEGER NOT NULL
48
- );
49
- CREATE INDEX IF NOT EXISTS idx_retrieval_access_source
50
- ON retrieval_access_log(source_type, source_id, timestamp);
51
- CREATE INDEX IF NOT EXISTS idx_retrieval_access_timestamp
52
- ON retrieval_access_log(timestamp);
53
-
54
- CREATE TABLE IF NOT EXISTS maintenance_checkpoints (
55
- action TEXT PRIMARY KEY,
56
- cursor TEXT,
57
- details TEXT NOT NULL DEFAULT '{}',
58
- updated_at INTEGER NOT NULL
59
- );
60
-
61
- CREATE TABLE IF NOT EXISTS memory_maintenance_runs (
62
- id TEXT PRIMARY KEY,
63
- mode TEXT NOT NULL CHECK(mode IN ('dry-run','apply')),
64
- actions TEXT NOT NULL,
65
- manifest TEXT NOT NULL,
66
- created_at INTEGER NOT NULL
67
- );
68
- CREATE INDEX IF NOT EXISTS idx_memory_maintenance_runs_created
69
- ON memory_maintenance_runs(created_at);
70
-
71
- CREATE TABLE IF NOT EXISTS event_processing_audit (
72
- id TEXT PRIMARY KEY,
73
- event_id TEXT NOT NULL,
74
- disposition TEXT NOT NULL,
75
- reason TEXT NOT NULL,
76
- run_id TEXT,
77
- timestamp INTEGER NOT NULL
78
- );
79
- CREATE INDEX IF NOT EXISTS idx_event_processing_event
80
- ON event_processing_audit(event_id, timestamp);
81
- CREATE INDEX IF NOT EXISTS idx_event_processing_run_disposition
82
- ON event_processing_audit(run_id, disposition);
83
-
84
- CREATE TABLE IF NOT EXISTS event_duplicate_quarantine (
85
- group_key TEXT PRIMARY KEY,
86
- event_ids TEXT NOT NULL,
87
- reason TEXT NOT NULL,
88
- created_at INTEGER NOT NULL,
89
- resolved_at INTEGER
90
- );
91
- `);
92
- const currentMax = db.prepare('SELECT COALESCE(MAX(version), 0) AS version FROM schema_versions').get().version;
93
- if (currentMax < 4) {
94
- db.prepare('INSERT INTO schema_versions (version, applied_at) VALUES (4, ?)').run(Date.now());
95
- }
96
- }
97
- //# sourceMappingURL=v3-to-v4.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v3-to-v4.js","sourceRoot":"","sources":["../../src/migrations/v3-to-v4.ts"],"names":[],"mappings":"AAMA,SAAS,SAAS,CAChB,EAAqB,EACrB,KAAa,EACb,IAAY,EACZ,UAAkB;IAElB,MAAM,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,cAAc,KAAK,GAAG,CAAgB,CAAC;IACjE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QAClD,EAAE,CAAC,IAAI,CAAC,eAAe,KAAK,eAAe,IAAI,IAAI,UAAU,EAAE,CAAC,CAAC;IACnE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,EAAqB;IACxD,EAAE,CAAC,IAAI,CAAC;;;;;GAKP,CAAC,CAAC;IAEH,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,gBAAgB,EAAE,kCAAkC,CAAC,CAAC;IACpF,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,CAAC,CAAC;IAC3D,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,CAAC,CAAC;IACzD,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,gBAAgB,EAAE,SAAS,CAAC,CAAC;IAC3D,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,iCAAiC,CAAC,CAAC;IACrF,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,2BAA2B,EAAE,MAAM,CAAC,CAAC;IACnE,SAAS,CAAC,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,CAAC,CAAC;IAE1D,SAAS,CAAC,EAAE,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,gCAAgC,CAAC,CAAC;IACxF,SAAS,CAAC,EAAE,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,CAAC,CAAC;IAE7D,SAAS,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7C,SAAS,CAAC,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAC/C,SAAS,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAC/C,SAAS,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAE7C,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEP,CAAC,CAAC;IAEH,MAAM,UAAU,GACd,EAAE,CAAC,OAAO,CAAC,kEAAkE,CAAC,CAAC,GAAG,EAGnF,CAAC,OAAO,CAAC;IACV,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;QACnB,EAAE,CAAC,OAAO,CAAC,iEAAiE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAChG,CAAC;AACH,CAAC"}
@@ -1,3 +0,0 @@
1
- import type Database from 'better-sqlite3';
2
- export declare function applyV4ToV5Migration(db: Database.Database): void;
3
- //# sourceMappingURL=v4-to-v5.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v4-to-v5.d.ts","sourceRoot":"","sources":["../../src/migrations/v4-to-v5.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAmChE"}
@@ -1,34 +0,0 @@
1
- export function applyV4ToV5Migration(db) {
2
- db.exec(`
3
- CREATE TABLE IF NOT EXISTS schema_versions (
4
- version INTEGER PRIMARY KEY,
5
- applied_at INTEGER
6
- );
7
-
8
- CREATE TABLE IF NOT EXISTS distill_lease_pages (
9
- batch_id TEXT NOT NULL,
10
- page INTEGER NOT NULL,
11
- total_pages INTEGER NOT NULL,
12
- read_at INTEGER NOT NULL,
13
- PRIMARY KEY (batch_id, page),
14
- FOREIGN KEY (batch_id) REFERENCES distill_leases(batch_id) ON DELETE CASCADE
15
- );
16
- CREATE INDEX IF NOT EXISTS idx_distill_pages_batch
17
- ON distill_lease_pages(batch_id, page);
18
-
19
- CREATE TABLE IF NOT EXISTS distill_receipts (
20
- receipt_id TEXT PRIMARY KEY,
21
- batch_id TEXT NOT NULL UNIQUE,
22
- status TEXT NOT NULL CHECK(status IN ('committed', 'committed_empty')),
23
- observation_count INTEGER NOT NULL,
24
- event_count INTEGER NOT NULL,
25
- created_at INTEGER NOT NULL,
26
- FOREIGN KEY (batch_id) REFERENCES distill_leases(batch_id) ON DELETE CASCADE
27
- );
28
- `);
29
- const current = db.prepare('SELECT COALESCE(MAX(version), 0) AS version FROM schema_versions').get();
30
- if (current.version < 5) {
31
- db.prepare('INSERT INTO schema_versions (version, applied_at) VALUES (5, ?)').run(Date.now());
32
- }
33
- }
34
- //# sourceMappingURL=v4-to-v5.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v4-to-v5.js","sourceRoot":"","sources":["../../src/migrations/v4-to-v5.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,oBAAoB,CAAC,EAAqB;IACxD,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BP,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CACxB,kEAAkE,CACnE,CAAC,GAAG,EAAyB,CAAC;IAC/B,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,OAAO,CAAC,iEAAiE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAChG,CAAC;AACH,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=v4-to-v5.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v4-to-v5.test.d.ts","sourceRoot":"","sources":["../../src/migrations/v4-to-v5.test.ts"],"names":[],"mappings":""}
@@ -1,32 +0,0 @@
1
- import Database from 'better-sqlite3';
2
- import { describe, expect, it } from 'vitest';
3
- import { applyV4ToV5Migration } from './v4-to-v5.js';
4
- describe('v4 to v5 memory migration', () => {
5
- it('creates distill page and receipt tables idempotently', () => {
6
- const database = new Database(':memory:');
7
- database.exec(`
8
- CREATE TABLE schema_versions (version INTEGER PRIMARY KEY, applied_at INTEGER);
9
- INSERT INTO schema_versions VALUES (4, 1);
10
- CREATE TABLE distill_leases (
11
- batch_id TEXT PRIMARY KEY,
12
- event_ids TEXT NOT NULL,
13
- session_ids TEXT NOT NULL,
14
- event_count INTEGER NOT NULL,
15
- created_at INTEGER NOT NULL,
16
- expires_at INTEGER NOT NULL,
17
- status TEXT NOT NULL
18
- );
19
- `);
20
- applyV4ToV5Migration(database);
21
- applyV4ToV5Migration(database);
22
- const tables = database.prepare("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name").all();
23
- expect(tables.map(table => table.name)).toEqual(expect.arrayContaining([
24
- 'distill_lease_pages',
25
- 'distill_receipts',
26
- ]));
27
- expect(database.prepare('SELECT MAX(version) AS version FROM schema_versions').get())
28
- .toEqual({ version: 5 });
29
- database.close();
30
- });
31
- });
32
- //# sourceMappingURL=v4-to-v5.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v4-to-v5.test.js","sourceRoot":"","sources":["../../src/migrations/v4-to-v5.test.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAErD,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC;;;;;;;;;;;;KAYb,CAAC,CAAC;QAEH,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAC/B,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAE/B,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAC7B,iEAAiE,CAClE,CAAC,GAAG,EAA6B,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC;YACrE,qBAAqB;YACrB,kBAAkB;SACnB,CAAC,CAAC,CAAC;QACJ,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,qDAAqD,CAAC,CAAC,GAAG,EAAE,CAAC;aAClF,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3B,QAAQ,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,3 +0,0 @@
1
- import type Database from 'better-sqlite3';
2
- export declare function applyV6ToV7Migration(db: Database.Database): void;
3
- //# sourceMappingURL=v6-to-v7.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v6-to-v7.d.ts","sourceRoot":"","sources":["../../src/migrations/v6-to-v7.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAoBhE"}
@@ -1,18 +0,0 @@
1
- export function applyV6ToV7Migration(db) {
2
- const columns = db.pragma('table_info(observations)');
3
- if (!columns.some(column => column.name === 'explicit_accepted_at')) {
4
- db.exec('ALTER TABLE observations ADD COLUMN explicit_accepted_at INTEGER');
5
- }
6
- db.exec(`
7
- UPDATE observations
8
- SET explicit_accepted_at = timestamp
9
- WHERE explicit_accepted_at IS NULL
10
- AND type = 'user_preference'
11
- AND source = 'explicit_intent';
12
-
13
- CREATE INDEX IF NOT EXISTS idx_observations_explicit_acceptance
14
- ON observations(project, type, source, explicit_accepted_at);
15
- `);
16
- db.prepare('INSERT OR REPLACE INTO schema_versions (version, applied_at) VALUES (7, ?)').run(Date.now());
17
- }
18
- //# sourceMappingURL=v6-to-v7.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v6-to-v7.js","sourceRoot":"","sources":["../../src/migrations/v6-to-v7.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,oBAAoB,CAAC,EAAqB;IACxD,MAAM,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,0BAA0B,CAA4B,CAAC;IACjF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,sBAAsB,CAAC,EAAE,CAAC;QACpE,EAAE,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAC;IAC9E,CAAC;IAED,EAAE,CAAC,IAAI,CAAC;;;;;;;;;GASP,CAAC,CAAC;IAEH,EAAE,CAAC,OAAO,CACR,4EAA4E,CAC7E,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;AACpB,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=v6-to-v7.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v6-to-v7.test.d.ts","sourceRoot":"","sources":["../../src/migrations/v6-to-v7.test.ts"],"names":[],"mappings":""}
@@ -1,33 +0,0 @@
1
- import Database from 'better-sqlite3';
2
- import { describe, expect, it } from 'vitest';
3
- import { applyV6ToV7Migration } from './v6-to-v7.js';
4
- describe('v6 to v7 memory migration', () => {
5
- it('backfills stable explicit acceptance time idempotently', () => {
6
- const database = new Database(':memory:');
7
- database.exec(`
8
- CREATE TABLE schema_versions (version INTEGER PRIMARY KEY, applied_at INTEGER);
9
- INSERT INTO schema_versions VALUES (6, 1);
10
- CREATE TABLE observations (
11
- id TEXT PRIMARY KEY,
12
- project TEXT NOT NULL,
13
- type TEXT NOT NULL,
14
- source TEXT NOT NULL,
15
- timestamp INTEGER NOT NULL,
16
- updated_at INTEGER NOT NULL
17
- );
18
- INSERT INTO observations VALUES
19
- ('explicit', '/project', 'user_preference', 'explicit_intent', 100, 900),
20
- ('distilled', '/project', 'user_preference', 'distill', 200, 800);
21
- `);
22
- applyV6ToV7Migration(database);
23
- applyV6ToV7Migration(database);
24
- expect(database.prepare('SELECT id, explicit_accepted_at FROM observations ORDER BY id').all()).toEqual([
25
- { id: 'distilled', explicit_accepted_at: null },
26
- { id: 'explicit', explicit_accepted_at: 100 },
27
- ]);
28
- expect(database.prepare('SELECT MAX(version) AS version FROM schema_versions').get())
29
- .toEqual({ version: 7 });
30
- database.close();
31
- });
32
- });
33
- //# sourceMappingURL=v6-to-v7.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v6-to-v7.test.js","sourceRoot":"","sources":["../../src/migrations/v6-to-v7.test.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAErD,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC;;;;;;;;;;;;;;KAcb,CAAC,CAAC;QAEH,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAC/B,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAE/B,MAAM,CAAC,QAAQ,CAAC,OAAO,CACrB,+DAA+D,CAChE,CAAC,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC;YACf,EAAE,EAAE,EAAE,WAAW,EAAE,oBAAoB,EAAE,IAAI,EAAE;YAC/C,EAAE,EAAE,EAAE,UAAU,EAAE,oBAAoB,EAAE,GAAG,EAAE;SAC9C,CAAC,CAAC;QACH,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,qDAAqD,CAAC,CAAC,GAAG,EAAE,CAAC;aAClF,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3B,QAAQ,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,3 +0,0 @@
1
- import type Database from 'better-sqlite3';
2
- export declare function applyV7ToV8Migration(db: Database.Database): void;
3
- //# sourceMappingURL=v7-to-v8.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v7-to-v8.d.ts","sourceRoot":"","sources":["../../src/migrations/v7-to-v8.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAsBhE"}
@@ -1,20 +0,0 @@
1
- export function applyV7ToV8Migration(db) {
2
- const columns = db.pragma('table_info(observations)');
3
- if (!columns.some(column => column.name === 'evidence_status')) {
4
- db.exec("ALTER TABLE observations ADD COLUMN evidence_status TEXT NOT NULL DEFAULT 'unverified'");
5
- }
6
- if (!columns.some(column => column.name === 'evidence_details')) {
7
- db.exec("ALTER TABLE observations ADD COLUMN evidence_details TEXT NOT NULL DEFAULT '[]'");
8
- }
9
- db.exec(`
10
- UPDATE observations
11
- SET evidence_status = 'unverified', evidence_details = COALESCE(evidence_details, '[]')
12
- WHERE source = 'explicit_intent'
13
- AND evidence_status NOT IN ('verified', 'unverified');
14
-
15
- CREATE INDEX IF NOT EXISTS idx_observations_evidence
16
- ON observations(project, source, evidence_status, resolution_state);
17
- `);
18
- db.prepare('INSERT OR REPLACE INTO schema_versions (version, applied_at) VALUES (8, ?)').run(Date.now());
19
- }
20
- //# sourceMappingURL=v7-to-v8.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v7-to-v8.js","sourceRoot":"","sources":["../../src/migrations/v7-to-v8.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,oBAAoB,CAAC,EAAqB;IACxD,MAAM,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,0BAA0B,CAA4B,CAAC;IACjF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,iBAAiB,CAAC,EAAE,CAAC;QAC/D,EAAE,CAAC,IAAI,CAAC,wFAAwF,CAAC,CAAC;IACpG,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,kBAAkB,CAAC,EAAE,CAAC;QAChE,EAAE,CAAC,IAAI,CAAC,iFAAiF,CAAC,CAAC;IAC7F,CAAC;IAED,EAAE,CAAC,IAAI,CAAC;;;;;;;;GAQP,CAAC,CAAC;IAEH,EAAE,CAAC,OAAO,CACR,4EAA4E,CAC7E,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;AACpB,CAAC"}
@@ -1,3 +0,0 @@
1
- import type Database from 'better-sqlite3';
2
- export declare function applyV8ToV9Migration(db: Database.Database): void;
3
- //# sourceMappingURL=v8-to-v9.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v8-to-v9.d.ts","sourceRoot":"","sources":["../../src/migrations/v8-to-v9.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAmBhE"}