ds4-context-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -0
  3. package/dist/artifacts/artifact-manager.d.ts +58 -0
  4. package/dist/artifacts/artifact-manager.d.ts.map +1 -0
  5. package/dist/artifacts/artifact-manager.js +445 -0
  6. package/dist/artifacts/artifact-manager.js.map +1 -0
  7. package/dist/artifacts/artifact-store.d.ts +33 -0
  8. package/dist/artifacts/artifact-store.d.ts.map +1 -0
  9. package/dist/artifacts/artifact-store.js +116 -0
  10. package/dist/artifacts/artifact-store.js.map +1 -0
  11. package/dist/compaction/compaction-record.d.ts +74 -0
  12. package/dist/compaction/compaction-record.d.ts.map +1 -0
  13. package/dist/compaction/compaction-record.js +113 -0
  14. package/dist/compaction/compaction-record.js.map +1 -0
  15. package/dist/compaction/summary-contract.d.ts +68 -0
  16. package/dist/compaction/summary-contract.d.ts.map +1 -0
  17. package/dist/compaction/summary-contract.js +315 -0
  18. package/dist/compaction/summary-contract.js.map +1 -0
  19. package/dist/compaction/summary-graph.d.ts +48 -0
  20. package/dist/compaction/summary-graph.d.ts.map +1 -0
  21. package/dist/compaction/summary-graph.js +123 -0
  22. package/dist/compaction/summary-graph.js.map +1 -0
  23. package/dist/config/config-loader.d.ts +18 -0
  24. package/dist/config/config-loader.d.ts.map +1 -0
  25. package/dist/config/config-loader.js +305 -0
  26. package/dist/config/config-loader.js.map +1 -0
  27. package/dist/config/config.d.ts +113 -0
  28. package/dist/config/config.d.ts.map +1 -0
  29. package/dist/config/config.js +89 -0
  30. package/dist/config/config.js.map +1 -0
  31. package/dist/continuation/native-continuation.d.ts +107 -0
  32. package/dist/continuation/native-continuation.d.ts.map +1 -0
  33. package/dist/continuation/native-continuation.js +374 -0
  34. package/dist/continuation/native-continuation.js.map +1 -0
  35. package/dist/core/budget-manager.d.ts +26 -0
  36. package/dist/core/budget-manager.d.ts.map +1 -0
  37. package/dist/core/budget-manager.js +42 -0
  38. package/dist/core/budget-manager.js.map +1 -0
  39. package/dist/core/canonical-message.d.ts +69 -0
  40. package/dist/core/canonical-message.d.ts.map +1 -0
  41. package/dist/core/canonical-message.js +2 -0
  42. package/dist/core/canonical-message.js.map +1 -0
  43. package/dist/core/model-awareness.d.ts +59 -0
  44. package/dist/core/model-awareness.d.ts.map +1 -0
  45. package/dist/core/model-awareness.js +177 -0
  46. package/dist/core/model-awareness.js.map +1 -0
  47. package/dist/core/model-profile.d.ts +27 -0
  48. package/dist/core/model-profile.d.ts.map +1 -0
  49. package/dist/core/model-profile.js +29 -0
  50. package/dist/core/model-profile.js.map +1 -0
  51. package/dist/core/token-estimator.d.ts +4 -0
  52. package/dist/core/token-estimator.d.ts.map +1 -0
  53. package/dist/core/token-estimator.js +64 -0
  54. package/dist/core/token-estimator.js.map +1 -0
  55. package/dist/index.d.ts +37 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +37 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/manifest/context-manifest.d.ts +163 -0
  60. package/dist/manifest/context-manifest.d.ts.map +1 -0
  61. package/dist/manifest/context-manifest.js +2 -0
  62. package/dist/manifest/context-manifest.js.map +1 -0
  63. package/dist/manifest/observer.d.ts +63 -0
  64. package/dist/manifest/observer.d.ts.map +1 -0
  65. package/dist/manifest/observer.js +150 -0
  66. package/dist/manifest/observer.js.map +1 -0
  67. package/dist/memory/memory-manager.d.ts +112 -0
  68. package/dist/memory/memory-manager.d.ts.map +1 -0
  69. package/dist/memory/memory-manager.js +499 -0
  70. package/dist/memory/memory-manager.js.map +1 -0
  71. package/dist/memory/memory-types.d.ts +126 -0
  72. package/dist/memory/memory-types.d.ts.map +1 -0
  73. package/dist/memory/memory-types.js +73 -0
  74. package/dist/memory/memory-types.js.map +1 -0
  75. package/dist/persistence/migrations.d.ts +17 -0
  76. package/dist/persistence/migrations.d.ts.map +1 -0
  77. package/dist/persistence/migrations.js +588 -0
  78. package/dist/persistence/migrations.js.map +1 -0
  79. package/dist/persistence/repositories/artifact-repository.d.ts +50 -0
  80. package/dist/persistence/repositories/artifact-repository.d.ts.map +1 -0
  81. package/dist/persistence/repositories/artifact-repository.js +186 -0
  82. package/dist/persistence/repositories/artifact-repository.js.map +1 -0
  83. package/dist/persistence/repositories/context-manifest-repository.d.ts +19 -0
  84. package/dist/persistence/repositories/context-manifest-repository.d.ts.map +1 -0
  85. package/dist/persistence/repositories/context-manifest-repository.js +144 -0
  86. package/dist/persistence/repositories/context-manifest-repository.js.map +1 -0
  87. package/dist/persistence/repositories/memory-repository.d.ts +33 -0
  88. package/dist/persistence/repositories/memory-repository.d.ts.map +1 -0
  89. package/dist/persistence/repositories/memory-repository.js +429 -0
  90. package/dist/persistence/repositories/memory-repository.js.map +1 -0
  91. package/dist/persistence/repositories/project-knowledge-repository.d.ts +65 -0
  92. package/dist/persistence/repositories/project-knowledge-repository.d.ts.map +1 -0
  93. package/dist/persistence/repositories/project-knowledge-repository.js +248 -0
  94. package/dist/persistence/repositories/project-knowledge-repository.js.map +1 -0
  95. package/dist/persistence/repositories/session-index-repository.d.ts +81 -0
  96. package/dist/persistence/repositories/session-index-repository.d.ts.map +1 -0
  97. package/dist/persistence/repositories/session-index-repository.js +220 -0
  98. package/dist/persistence/repositories/session-index-repository.js.map +1 -0
  99. package/dist/persistence/repositories/summary-repository.d.ts +16 -0
  100. package/dist/persistence/repositories/summary-repository.d.ts.map +1 -0
  101. package/dist/persistence/repositories/summary-repository.js +242 -0
  102. package/dist/persistence/repositories/summary-repository.js.map +1 -0
  103. package/dist/persistence/sqlite.d.ts +45 -0
  104. package/dist/persistence/sqlite.d.ts.map +1 -0
  105. package/dist/persistence/sqlite.js +137 -0
  106. package/dist/persistence/sqlite.js.map +1 -0
  107. package/dist/planner/atomic-groups.d.ts +15 -0
  108. package/dist/planner/atomic-groups.d.ts.map +1 -0
  109. package/dist/planner/atomic-groups.js +177 -0
  110. package/dist/planner/atomic-groups.js.map +1 -0
  111. package/dist/planner/context-planner.d.ts +49 -0
  112. package/dist/planner/context-planner.d.ts.map +1 -0
  113. package/dist/planner/context-planner.js +367 -0
  114. package/dist/planner/context-planner.js.map +1 -0
  115. package/dist/privacy/privacy-policy.d.ts +58 -0
  116. package/dist/privacy/privacy-policy.d.ts.map +1 -0
  117. package/dist/privacy/privacy-policy.js +412 -0
  118. package/dist/privacy/privacy-policy.js.map +1 -0
  119. package/dist/project/file-indexer.d.ts +32 -0
  120. package/dist/project/file-indexer.d.ts.map +1 -0
  121. package/dist/project/file-indexer.js +374 -0
  122. package/dist/project/file-indexer.js.map +1 -0
  123. package/dist/project/git-state.d.ts +12 -0
  124. package/dist/project/git-state.d.ts.map +1 -0
  125. package/dist/project/git-state.js +109 -0
  126. package/dist/project/git-state.js.map +1 -0
  127. package/dist/project/project-knowledge.d.ts +63 -0
  128. package/dist/project/project-knowledge.d.ts.map +1 -0
  129. package/dist/project/project-knowledge.js +332 -0
  130. package/dist/project/project-knowledge.js.map +1 -0
  131. package/dist/retrieval/retrieval-engine.d.ts +55 -0
  132. package/dist/retrieval/retrieval-engine.d.ts.map +1 -0
  133. package/dist/retrieval/retrieval-engine.js +290 -0
  134. package/dist/retrieval/retrieval-engine.js.map +1 -0
  135. package/dist/retrieval/task-descriptor.d.ts +17 -0
  136. package/dist/retrieval/task-descriptor.d.ts.map +1 -0
  137. package/dist/retrieval/task-descriptor.js +107 -0
  138. package/dist/retrieval/task-descriptor.js.map +1 -0
  139. package/dist/shared/hash.d.ts +2 -0
  140. package/dist/shared/hash.d.ts.map +1 -0
  141. package/dist/shared/hash.js +5 -0
  142. package/dist/shared/hash.js.map +1 -0
  143. package/dist/shared/logging.d.ts +27 -0
  144. package/dist/shared/logging.d.ts.map +1 -0
  145. package/dist/shared/logging.js +83 -0
  146. package/dist/shared/logging.js.map +1 -0
  147. package/dist/shared/stable-json.d.ts +2 -0
  148. package/dist/shared/stable-json.d.ts.map +1 -0
  149. package/dist/shared/stable-json.js +38 -0
  150. package/dist/shared/stable-json.js.map +1 -0
  151. package/package.json +56 -0
@@ -0,0 +1,73 @@
1
+ import { isPrivacyClassification } from "../privacy/privacy-policy.js";
2
+ export const MEMORY_CUSTOM_ENTRY_TYPE = "ds4-context-memory-v1";
3
+ export const PIN_CUSTOM_ENTRY_TYPE = "ds4-context-pin-v1";
4
+ export const MEMORY_MUTATION_SCHEMA_VERSION = 1;
5
+ function isRecord(value) {
6
+ return value !== null && typeof value === "object" && !Array.isArray(value);
7
+ }
8
+ function validBase(value) {
9
+ return value.schemaVersion === MEMORY_MUTATION_SCHEMA_VERSION
10
+ && typeof value.mutationId === "string"
11
+ && value.mutationId.length > 0
12
+ && typeof value.createdAt === "number"
13
+ && Number.isFinite(value.createdAt);
14
+ }
15
+ function validNewMemory(value) {
16
+ if (!isRecord(value))
17
+ return false;
18
+ return typeof value.id === "string"
19
+ && (value.scope === "session" || value.scope === "project")
20
+ && typeof value.claim === "string"
21
+ && typeof value.createdAt === "number"
22
+ && Array.isArray(value.sourceEntryIds)
23
+ && value.sourceEntryIds.every((entryId) => typeof entryId === "string")
24
+ && (value.projectPath === undefined || typeof value.projectPath === "string")
25
+ && (value.key === undefined || typeof value.key === "string")
26
+ && (value.classification === undefined || isPrivacyClassification(value.classification));
27
+ }
28
+ function validNewPin(value) {
29
+ if (!isRecord(value))
30
+ return false;
31
+ return typeof value.id === "string"
32
+ && (value.scope === "session" || value.scope === "branch" || value.scope === "project")
33
+ && typeof value.content === "string"
34
+ && typeof value.createdAt === "number"
35
+ && (value.projectPath === undefined || typeof value.projectPath === "string")
36
+ && (value.branchLeafId === undefined || typeof value.branchLeafId === "string")
37
+ && (value.classification === undefined || isPrivacyClassification(value.classification))
38
+ && (value.sourceEntryId === undefined || typeof value.sourceEntryId === "string")
39
+ && (value.sourceFile === undefined || typeof value.sourceFile === "string");
40
+ }
41
+ export function parseMemoryMutation(value) {
42
+ if (!isRecord(value) || !validBase(value))
43
+ return undefined;
44
+ if (value.operation === "add" && validNewMemory(value.item))
45
+ return value;
46
+ if (value.operation === "supersede" && typeof value.previousId === "string" && validNewMemory(value.item)) {
47
+ return value;
48
+ }
49
+ if (value.operation === "status"
50
+ && typeof value.memoryId === "string"
51
+ && (value.status === "invalid" || value.status === "expired")
52
+ && (value.reason === undefined || typeof value.reason === "string")) {
53
+ return value;
54
+ }
55
+ return undefined;
56
+ }
57
+ export function parsePinMutation(value) {
58
+ if (!isRecord(value) || !validBase(value))
59
+ return undefined;
60
+ if (value.operation === "add" && validNewPin(value.item))
61
+ return value;
62
+ if (value.operation === "supersede" && typeof value.previousId === "string" && validNewPin(value.item)) {
63
+ return value;
64
+ }
65
+ if (value.operation === "status"
66
+ && typeof value.pinId === "string"
67
+ && value.status === "deleted"
68
+ && (value.reason === undefined || typeof value.reason === "string")) {
69
+ return value;
70
+ }
71
+ return undefined;
72
+ }
73
+ //# sourceMappingURL=memory-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-types.js","sourceRoot":"","sources":["../../src/memory/memory-types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { DatabaseSync } from "node:sqlite";
2
+ export interface Migration {
3
+ version: number;
4
+ name: string;
5
+ sql: string;
6
+ }
7
+ export interface AppliedMigration {
8
+ version: number;
9
+ name: string;
10
+ checksum: string;
11
+ appliedAt: number;
12
+ }
13
+ export declare const MIGRATIONS: readonly Migration[];
14
+ export declare const CURRENT_SCHEMA_VERSION: number;
15
+ export declare function listAppliedMigrations(database: DatabaseSync): AppliedMigration[];
16
+ export declare function applyMigrations(database: DatabaseSync, now?: number): AppliedMigration[];
17
+ //# sourceMappingURL=migrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/persistence/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,UAAU,EAAE,SAAS,SAAS,EAohB1C,CAAC;AAEF,eAAO,MAAM,sBAAsB,QAAkC,CAAC;AAatE,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,GAAG,gBAAgB,EAAE,CAWhF;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,SAAa,GAAG,gBAAgB,EAAE,CAyC5F"}
@@ -0,0 +1,588 @@
1
+ import { sha256 } from "../shared/hash.js";
2
+ export const MIGRATIONS = [
3
+ {
4
+ version: 1,
5
+ name: "initial-derived-store",
6
+ sql: `
7
+ CREATE TABLE sessions (
8
+ session_id TEXT PRIMARY KEY,
9
+ session_file TEXT NOT NULL,
10
+ project_path TEXT,
11
+ indexed_at INTEGER NOT NULL
12
+ ) STRICT;
13
+
14
+ CREATE TABLE entries (
15
+ entry_id TEXT PRIMARY KEY,
16
+ session_id TEXT NOT NULL REFERENCES sessions(session_id) ON DELETE CASCADE,
17
+ parent_id TEXT,
18
+ entry_type TEXT NOT NULL,
19
+ role TEXT,
20
+ created_at INTEGER,
21
+ content_hash TEXT NOT NULL,
22
+ searchable_text TEXT,
23
+ token_estimate INTEGER,
24
+ indexed_at INTEGER NOT NULL
25
+ ) STRICT;
26
+ CREATE INDEX entries_session_parent_idx ON entries(session_id, parent_id);
27
+ CREATE INDEX entries_session_created_idx ON entries(session_id, created_at);
28
+ CREATE INDEX entries_content_hash_idx ON entries(content_hash);
29
+
30
+ CREATE VIRTUAL TABLE entries_fts USING fts5(
31
+ searchable_text,
32
+ entry_id UNINDEXED,
33
+ session_id UNINDEXED,
34
+ tokenize = 'unicode61 remove_diacritics 2'
35
+ );
36
+
37
+ CREATE TABLE summaries (
38
+ summary_id TEXT PRIMARY KEY,
39
+ session_id TEXT NOT NULL REFERENCES sessions(session_id) ON DELETE CASCADE,
40
+ summary_kind TEXT NOT NULL,
41
+ content TEXT NOT NULL,
42
+ source_hash TEXT NOT NULL,
43
+ created_at INTEGER NOT NULL,
44
+ validation_status TEXT NOT NULL,
45
+ provider TEXT,
46
+ model TEXT
47
+ ) STRICT;
48
+ CREATE INDEX summaries_session_created_idx ON summaries(session_id, created_at);
49
+
50
+ CREATE TABLE summary_sources (
51
+ summary_id TEXT NOT NULL REFERENCES summaries(summary_id) ON DELETE CASCADE,
52
+ entry_id TEXT NOT NULL REFERENCES entries(entry_id) ON DELETE CASCADE,
53
+ PRIMARY KEY(summary_id, entry_id)
54
+ ) WITHOUT ROWID, STRICT;
55
+
56
+ CREATE TABLE summary_edges (
57
+ parent_summary_id TEXT NOT NULL REFERENCES summaries(summary_id) ON DELETE CASCADE,
58
+ child_summary_id TEXT NOT NULL REFERENCES summaries(summary_id) ON DELETE CASCADE,
59
+ PRIMARY KEY(parent_summary_id, child_summary_id),
60
+ CHECK(parent_summary_id <> child_summary_id)
61
+ ) WITHOUT ROWID, STRICT;
62
+
63
+ CREATE TABLE memory_items (
64
+ memory_id TEXT PRIMARY KEY,
65
+ scope TEXT NOT NULL,
66
+ session_id TEXT REFERENCES sessions(session_id) ON DELETE CASCADE,
67
+ project_path TEXT,
68
+ claim TEXT NOT NULL,
69
+ status TEXT NOT NULL,
70
+ created_at INTEGER NOT NULL,
71
+ superseded_by TEXT REFERENCES memory_items(memory_id),
72
+ CHECK(scope IN ('session', 'project')),
73
+ CHECK(status IN ('active', 'superseded', 'invalid', 'expired'))
74
+ ) STRICT;
75
+
76
+ CREATE TABLE memory_sources (
77
+ memory_id TEXT NOT NULL REFERENCES memory_items(memory_id) ON DELETE CASCADE,
78
+ entry_id TEXT NOT NULL REFERENCES entries(entry_id) ON DELETE CASCADE,
79
+ PRIMARY KEY(memory_id, entry_id)
80
+ ) WITHOUT ROWID, STRICT;
81
+
82
+ CREATE VIRTUAL TABLE memory_fts USING fts5(
83
+ claim,
84
+ memory_id UNINDEXED,
85
+ tokenize = 'unicode61 remove_diacritics 2'
86
+ );
87
+
88
+ CREATE TABLE pins (
89
+ pin_id TEXT PRIMARY KEY,
90
+ scope TEXT NOT NULL,
91
+ session_id TEXT REFERENCES sessions(session_id) ON DELETE CASCADE,
92
+ project_path TEXT,
93
+ content TEXT NOT NULL,
94
+ source_entry_id TEXT REFERENCES entries(entry_id) ON DELETE SET NULL,
95
+ source_file TEXT,
96
+ status TEXT NOT NULL,
97
+ created_at INTEGER NOT NULL,
98
+ CHECK(scope IN ('session', 'branch', 'project')),
99
+ CHECK(status IN ('active', 'superseded', 'deleted'))
100
+ ) STRICT;
101
+
102
+ CREATE TABLE context_manifests (
103
+ manifest_id TEXT PRIMARY KEY,
104
+ session_id TEXT NOT NULL REFERENCES sessions(session_id) ON DELETE CASCADE,
105
+ created_at INTEGER NOT NULL,
106
+ provider TEXT NOT NULL,
107
+ model TEXT NOT NULL,
108
+ estimated_tokens INTEGER,
109
+ actual_tokens INTEGER,
110
+ prompt_hash TEXT,
111
+ policy_version TEXT NOT NULL,
112
+ planner_version TEXT NOT NULL,
113
+ manifest_json TEXT NOT NULL
114
+ ) STRICT;
115
+ CREATE INDEX context_manifests_session_created_idx
116
+ ON context_manifests(session_id, created_at DESC);
117
+
118
+ CREATE TABLE artifacts (
119
+ artifact_id TEXT PRIMARY KEY,
120
+ sha256 TEXT NOT NULL UNIQUE,
121
+ file_path TEXT NOT NULL,
122
+ mime_type TEXT,
123
+ size_bytes INTEGER NOT NULL,
124
+ created_at INTEGER NOT NULL,
125
+ source_entry_id TEXT REFERENCES entries(entry_id) ON DELETE SET NULL
126
+ ) STRICT;
127
+
128
+ CREATE TABLE token_calibration (
129
+ calibration_id INTEGER PRIMARY KEY AUTOINCREMENT,
130
+ provider TEXT NOT NULL,
131
+ model TEXT NOT NULL,
132
+ estimated INTEGER NOT NULL,
133
+ actual INTEGER NOT NULL,
134
+ ratio REAL NOT NULL,
135
+ created_at INTEGER NOT NULL
136
+ ) STRICT;
137
+ CREATE INDEX token_calibration_model_created_idx
138
+ ON token_calibration(provider, model, created_at DESC);
139
+ `,
140
+ },
141
+ {
142
+ version: 2,
143
+ name: "session-index-checkpoints-and-scoped-entry-keys",
144
+ sql: `
145
+ CREATE TABLE entries_v2 (
146
+ entry_key TEXT PRIMARY KEY,
147
+ entry_id TEXT NOT NULL,
148
+ session_id TEXT NOT NULL REFERENCES sessions(session_id) ON DELETE CASCADE,
149
+ parent_id TEXT,
150
+ entry_type TEXT NOT NULL,
151
+ role TEXT,
152
+ created_at INTEGER,
153
+ content_hash TEXT NOT NULL,
154
+ searchable_text TEXT,
155
+ token_estimate INTEGER,
156
+ indexed_at INTEGER NOT NULL,
157
+ UNIQUE(session_id, entry_id)
158
+ ) STRICT;
159
+ INSERT INTO entries_v2(
160
+ entry_key, entry_id, session_id, parent_id, entry_type, role,
161
+ created_at, content_hash, searchable_text, token_estimate, indexed_at
162
+ )
163
+ SELECT
164
+ session_id || ':' || entry_id, entry_id, session_id, parent_id, entry_type, role,
165
+ created_at, content_hash, searchable_text, token_estimate, indexed_at
166
+ FROM entries;
167
+
168
+ CREATE TABLE summary_sources_v2 (
169
+ summary_id TEXT NOT NULL REFERENCES summaries(summary_id) ON DELETE CASCADE,
170
+ entry_key TEXT NOT NULL REFERENCES entries_v2(entry_key) ON DELETE CASCADE,
171
+ PRIMARY KEY(summary_id, entry_key)
172
+ ) WITHOUT ROWID, STRICT;
173
+ INSERT INTO summary_sources_v2(summary_id, entry_key)
174
+ SELECT source.summary_id, entry.session_id || ':' || source.entry_id
175
+ FROM summary_sources AS source
176
+ JOIN entries AS entry ON entry.entry_id = source.entry_id;
177
+
178
+ CREATE TABLE memory_sources_v2 (
179
+ memory_id TEXT NOT NULL REFERENCES memory_items(memory_id) ON DELETE CASCADE,
180
+ entry_key TEXT NOT NULL REFERENCES entries_v2(entry_key) ON DELETE CASCADE,
181
+ PRIMARY KEY(memory_id, entry_key)
182
+ ) WITHOUT ROWID, STRICT;
183
+ INSERT INTO memory_sources_v2(memory_id, entry_key)
184
+ SELECT source.memory_id, entry.session_id || ':' || source.entry_id
185
+ FROM memory_sources AS source
186
+ JOIN entries AS entry ON entry.entry_id = source.entry_id;
187
+
188
+ CREATE TABLE pins_v2 (
189
+ pin_id TEXT PRIMARY KEY,
190
+ scope TEXT NOT NULL,
191
+ session_id TEXT REFERENCES sessions(session_id) ON DELETE CASCADE,
192
+ project_path TEXT,
193
+ content TEXT NOT NULL,
194
+ source_entry_key TEXT REFERENCES entries_v2(entry_key) ON DELETE SET NULL,
195
+ source_entry_id TEXT,
196
+ source_file TEXT,
197
+ status TEXT NOT NULL,
198
+ created_at INTEGER NOT NULL,
199
+ CHECK(scope IN ('session', 'branch', 'project')),
200
+ CHECK(status IN ('active', 'superseded', 'deleted'))
201
+ ) STRICT;
202
+ INSERT INTO pins_v2(
203
+ pin_id, scope, session_id, project_path, content, source_entry_key,
204
+ source_entry_id, source_file, status, created_at
205
+ )
206
+ SELECT
207
+ pin.pin_id, pin.scope, pin.session_id, pin.project_path, pin.content,
208
+ (SELECT entry.session_id || ':' || entry.entry_id FROM entries AS entry
209
+ WHERE entry.entry_id = pin.source_entry_id LIMIT 1),
210
+ pin.source_entry_id, pin.source_file, pin.status, pin.created_at
211
+ FROM pins AS pin;
212
+
213
+ CREATE TABLE artifacts_v2 (
214
+ artifact_id TEXT PRIMARY KEY,
215
+ sha256 TEXT NOT NULL UNIQUE,
216
+ file_path TEXT NOT NULL,
217
+ mime_type TEXT,
218
+ size_bytes INTEGER NOT NULL,
219
+ created_at INTEGER NOT NULL,
220
+ source_entry_key TEXT REFERENCES entries_v2(entry_key) ON DELETE SET NULL,
221
+ source_entry_id TEXT
222
+ ) STRICT;
223
+ INSERT INTO artifacts_v2(
224
+ artifact_id, sha256, file_path, mime_type, size_bytes, created_at,
225
+ source_entry_key, source_entry_id
226
+ )
227
+ SELECT
228
+ artifact.artifact_id, artifact.sha256, artifact.file_path, artifact.mime_type,
229
+ artifact.size_bytes, artifact.created_at,
230
+ (SELECT entry.session_id || ':' || entry.entry_id FROM entries AS entry
231
+ WHERE entry.entry_id = artifact.source_entry_id LIMIT 1),
232
+ artifact.source_entry_id
233
+ FROM artifacts AS artifact;
234
+
235
+ DROP TABLE summary_sources;
236
+ DROP TABLE memory_sources;
237
+ DROP TABLE pins;
238
+ DROP TABLE artifacts;
239
+ DROP TABLE entries_fts;
240
+ DROP TABLE entries;
241
+
242
+ ALTER TABLE entries_v2 RENAME TO entries;
243
+ ALTER TABLE summary_sources_v2 RENAME TO summary_sources;
244
+ ALTER TABLE memory_sources_v2 RENAME TO memory_sources;
245
+ ALTER TABLE pins_v2 RENAME TO pins;
246
+ ALTER TABLE artifacts_v2 RENAME TO artifacts;
247
+
248
+ CREATE INDEX entries_session_parent_idx ON entries(session_id, parent_id);
249
+ CREATE INDEX entries_session_created_idx ON entries(session_id, created_at);
250
+ CREATE INDEX entries_content_hash_idx ON entries(content_hash);
251
+ CREATE VIRTUAL TABLE entries_fts USING fts5(
252
+ searchable_text,
253
+ entry_key UNINDEXED,
254
+ entry_id UNINDEXED,
255
+ session_id UNINDEXED,
256
+ tokenize = 'unicode61 remove_diacritics 2'
257
+ );
258
+ INSERT INTO entries_fts(searchable_text, entry_key, entry_id, session_id)
259
+ SELECT COALESCE(searchable_text, ''), entry_key, entry_id, session_id FROM entries;
260
+
261
+ CREATE TABLE session_index_state (
262
+ session_id TEXT PRIMARY KEY REFERENCES sessions(session_id) ON DELETE CASCADE,
263
+ session_file TEXT NOT NULL,
264
+ header_hash TEXT NOT NULL,
265
+ file_size INTEGER NOT NULL,
266
+ file_mtime_ms REAL NOT NULL,
267
+ checkpoint_offset INTEGER NOT NULL,
268
+ checkpoint_line_start INTEGER NOT NULL,
269
+ checkpoint_hash TEXT,
270
+ indexed_entries INTEGER NOT NULL,
271
+ malformed_lines INTEGER NOT NULL,
272
+ indexed_at INTEGER NOT NULL
273
+ ) STRICT;
274
+ `,
275
+ },
276
+ {
277
+ version: 3,
278
+ name: "bounded-checkpoint-fingerprint",
279
+ sql: `
280
+ ALTER TABLE session_index_state
281
+ RENAME COLUMN checkpoint_line_start TO checkpoint_hash_start;
282
+ `,
283
+ },
284
+ {
285
+ version: 4,
286
+ name: "observer-context-manifest-columns",
287
+ sql: `
288
+ ALTER TABLE context_manifests ADD COLUMN branch_leaf_id TEXT;
289
+ ALTER TABLE context_manifests ADD COLUMN context_window INTEGER;
290
+ ALTER TABLE context_manifests ADD COLUMN output_reserve INTEGER;
291
+ ALTER TABLE context_manifests ADD COLUMN hard_input_limit INTEGER;
292
+ ALTER TABLE context_manifests ADD COLUMN target_input_tokens INTEGER;
293
+ ALTER TABLE context_manifests ADD COLUMN pi_reported_tokens INTEGER;
294
+ CREATE INDEX context_manifests_prompt_hash_idx
295
+ ON context_manifests(prompt_hash);
296
+ `,
297
+ },
298
+ {
299
+ version: 5,
300
+ name: "compaction-summary-lifecycle",
301
+ sql: `
302
+ ALTER TABLE summaries ADD COLUMN first_kept_entry_id TEXT;
303
+ ALTER TABLE summaries ADD COLUMN tokens_before INTEGER;
304
+ ALTER TABLE summaries ADD COLUMN compaction_reason TEXT;
305
+ ALTER TABLE summaries ADD COLUMN lifecycle_status TEXT NOT NULL DEFAULT 'committed'
306
+ CHECK(lifecycle_status IN ('prepared', 'committed', 'failed'));
307
+ ALTER TABLE summaries ADD COLUMN pi_compaction_entry_id TEXT;
308
+ ALTER TABLE summaries ADD COLUMN metadata_json TEXT NOT NULL DEFAULT '{}';
309
+ CREATE INDEX summaries_session_lifecycle_idx
310
+ ON summaries(session_id, lifecycle_status, created_at DESC);
311
+ CREATE UNIQUE INDEX summaries_pi_compaction_entry_idx
312
+ ON summaries(session_id, pi_compaction_entry_id)
313
+ WHERE pi_compaction_entry_id IS NOT NULL;
314
+ `,
315
+ },
316
+ {
317
+ version: 6,
318
+ name: "hierarchical-summary-graph",
319
+ sql: `
320
+ ALTER TABLE summaries ADD COLUMN graph_level INTEGER NOT NULL DEFAULT 0
321
+ CHECK(graph_level >= 0);
322
+ ALTER TABLE summary_edges ADD COLUMN child_order INTEGER NOT NULL DEFAULT 0
323
+ CHECK(child_order >= 0);
324
+ CREATE INDEX summaries_session_kind_level_idx
325
+ ON summaries(session_id, summary_kind, graph_level, created_at DESC);
326
+ CREATE INDEX summary_edges_child_idx
327
+ ON summary_edges(child_summary_id, parent_summary_id);
328
+ `,
329
+ },
330
+ {
331
+ version: 7,
332
+ name: "project-knowledge-index",
333
+ sql: `
334
+ CREATE TABLE project_states (
335
+ project_path TEXT PRIMARY KEY,
336
+ git_root TEXT,
337
+ git_branch TEXT,
338
+ git_head TEXT,
339
+ dirty INTEGER NOT NULL CHECK(dirty IN (0, 1)),
340
+ changed_files_json TEXT NOT NULL,
341
+ indexed_at INTEGER NOT NULL
342
+ ) STRICT;
343
+
344
+ CREATE TABLE project_files (
345
+ project_path TEXT NOT NULL REFERENCES project_states(project_path) ON DELETE CASCADE,
346
+ file_path TEXT NOT NULL,
347
+ content_hash TEXT NOT NULL,
348
+ size_bytes INTEGER NOT NULL CHECK(size_bytes >= 0),
349
+ mtime_ms REAL NOT NULL,
350
+ language TEXT,
351
+ git_commit TEXT,
352
+ modified INTEGER NOT NULL CHECK(modified IN (0, 1)),
353
+ tracked INTEGER NOT NULL CHECK(tracked IN (0, 1)),
354
+ status TEXT NOT NULL CHECK(status IN ('current', 'deleted')),
355
+ indexed_at INTEGER NOT NULL,
356
+ PRIMARY KEY(project_path, file_path)
357
+ ) WITHOUT ROWID, STRICT;
358
+ CREATE INDEX project_files_hash_idx ON project_files(project_path, content_hash);
359
+ CREATE INDEX project_files_status_idx ON project_files(project_path, status, file_path);
360
+
361
+ CREATE TABLE project_snippets (
362
+ snippet_id TEXT PRIMARY KEY,
363
+ project_path TEXT NOT NULL,
364
+ file_path TEXT NOT NULL,
365
+ file_hash TEXT NOT NULL,
366
+ start_line INTEGER NOT NULL CHECK(start_line > 0),
367
+ end_line INTEGER NOT NULL CHECK(end_line >= start_line),
368
+ content TEXT NOT NULL,
369
+ symbols TEXT NOT NULL,
370
+ token_estimate INTEGER NOT NULL CHECK(token_estimate >= 0),
371
+ stale INTEGER NOT NULL CHECK(stale IN (0, 1)),
372
+ indexed_at INTEGER NOT NULL,
373
+ FOREIGN KEY(project_path, file_path)
374
+ REFERENCES project_files(project_path, file_path) ON DELETE CASCADE
375
+ ) STRICT;
376
+ CREATE INDEX project_snippets_file_idx
377
+ ON project_snippets(project_path, file_path, stale, start_line);
378
+ CREATE INDEX project_snippets_hash_idx
379
+ ON project_snippets(project_path, file_hash, stale);
380
+
381
+ CREATE VIRTUAL TABLE project_snippets_fts USING fts5(
382
+ content,
383
+ file_path,
384
+ symbols,
385
+ snippet_id UNINDEXED,
386
+ project_path UNINDEXED,
387
+ tokenize = 'unicode61 remove_diacritics 2'
388
+ );
389
+ `,
390
+ },
391
+ {
392
+ version: 8,
393
+ name: "content-addressed-artifact-store",
394
+ sql: `
395
+ CREATE TABLE artifact_objects (
396
+ sha256 TEXT PRIMARY KEY CHECK(length(sha256) = 64),
397
+ file_path TEXT NOT NULL UNIQUE,
398
+ mime_type TEXT NOT NULL,
399
+ size_bytes INTEGER NOT NULL CHECK(size_bytes >= 0),
400
+ created_at INTEGER NOT NULL,
401
+ last_verified_at INTEGER,
402
+ status TEXT NOT NULL CHECK(status IN ('available', 'missing', 'corrupt'))
403
+ ) STRICT;
404
+ INSERT OR IGNORE INTO artifact_objects(
405
+ sha256, file_path, mime_type, size_bytes, created_at, status
406
+ )
407
+ SELECT sha256, file_path, COALESCE(mime_type, 'application/octet-stream'),
408
+ size_bytes, created_at, 'available'
409
+ FROM artifacts;
410
+
411
+ CREATE TABLE artifacts_v3 (
412
+ artifact_id TEXT PRIMARY KEY,
413
+ sha256 TEXT NOT NULL REFERENCES artifact_objects(sha256) ON DELETE RESTRICT,
414
+ source_session_id TEXT NOT NULL REFERENCES sessions(session_id) ON DELETE CASCADE,
415
+ source_entry_key TEXT NOT NULL REFERENCES entries(entry_key) ON DELETE CASCADE,
416
+ source_entry_id TEXT NOT NULL,
417
+ source_tool_call_id TEXT NOT NULL,
418
+ source_tool_name TEXT NOT NULL,
419
+ is_error INTEGER NOT NULL CHECK(is_error IN (0, 1)),
420
+ original_chars INTEGER NOT NULL CHECK(original_chars >= 0),
421
+ original_tokens INTEGER NOT NULL CHECK(original_tokens >= 0),
422
+ condensed_chars INTEGER NOT NULL CHECK(condensed_chars >= 0),
423
+ condensed_tokens INTEGER NOT NULL CHECK(condensed_tokens >= 0),
424
+ created_at INTEGER NOT NULL,
425
+ metadata_json TEXT NOT NULL,
426
+ UNIQUE(source_session_id, source_entry_id, source_tool_call_id, sha256)
427
+ ) STRICT;
428
+ INSERT OR IGNORE INTO artifacts_v3(
429
+ artifact_id, sha256, source_session_id, source_entry_key, source_entry_id,
430
+ source_tool_call_id, source_tool_name, is_error, original_chars,
431
+ original_tokens, condensed_chars, condensed_tokens, created_at, metadata_json
432
+ )
433
+ SELECT
434
+ artifact.artifact_id, artifact.sha256, entry.session_id,
435
+ artifact.source_entry_key, artifact.source_entry_id,
436
+ 'legacy', 'unknown', 0, artifact.size_bytes, 0, 0, 0,
437
+ artifact.created_at, '{}'
438
+ FROM artifacts AS artifact
439
+ JOIN entries AS entry ON entry.entry_key = artifact.source_entry_key
440
+ WHERE artifact.source_entry_key IS NOT NULL AND artifact.source_entry_id IS NOT NULL;
441
+
442
+ DROP TABLE artifacts;
443
+ ALTER TABLE artifacts_v3 RENAME TO artifacts;
444
+ CREATE INDEX artifacts_session_created_idx
445
+ ON artifacts(source_session_id, created_at DESC);
446
+ CREATE INDEX artifacts_source_entry_idx
447
+ ON artifacts(source_session_id, source_entry_id);
448
+ CREATE INDEX artifacts_sha_idx ON artifacts(sha256);
449
+ `,
450
+ },
451
+ {
452
+ version: 9,
453
+ name: "event-sourced-memory-and-pins",
454
+ sql: `
455
+ ALTER TABLE memory_items ADD COLUMN memory_key TEXT;
456
+ ALTER TABLE memory_items ADD COLUMN origin_session_id TEXT
457
+ REFERENCES sessions(session_id) ON DELETE CASCADE;
458
+ ALTER TABLE memory_items ADD COLUMN updated_at INTEGER NOT NULL DEFAULT 0;
459
+ ALTER TABLE memory_items ADD COLUMN status_reason TEXT;
460
+ ALTER TABLE memory_items ADD COLUMN metadata_json TEXT NOT NULL DEFAULT '{}';
461
+ CREATE INDEX memory_items_scope_status_idx
462
+ ON memory_items(scope, session_id, project_path, status, created_at DESC);
463
+ CREATE UNIQUE INDEX memory_items_active_key_idx
464
+ ON memory_items(
465
+ scope,
466
+ COALESCE(session_id, ''),
467
+ COALESCE(project_path, ''),
468
+ memory_key
469
+ )
470
+ WHERE status = 'active' AND memory_key IS NOT NULL;
471
+
472
+ ALTER TABLE pins ADD COLUMN branch_leaf_id TEXT;
473
+ ALTER TABLE pins ADD COLUMN superseded_by TEXT REFERENCES pins(pin_id);
474
+ ALTER TABLE pins ADD COLUMN updated_at INTEGER NOT NULL DEFAULT 0;
475
+ ALTER TABLE pins ADD COLUMN status_reason TEXT;
476
+ ALTER TABLE pins ADD COLUMN metadata_json TEXT NOT NULL DEFAULT '{}';
477
+ CREATE INDEX pins_scope_status_idx
478
+ ON pins(scope, session_id, project_path, status, created_at DESC);
479
+ CREATE INDEX pins_branch_leaf_idx
480
+ ON pins(session_id, branch_leaf_id, status);
481
+
482
+ CREATE TABLE memory_mutations (
483
+ mutation_key TEXT PRIMARY KEY REFERENCES entries(entry_key) ON DELETE CASCADE,
484
+ mutation_id TEXT NOT NULL,
485
+ session_id TEXT NOT NULL REFERENCES sessions(session_id) ON DELETE CASCADE,
486
+ created_at INTEGER NOT NULL,
487
+ entry_order INTEGER NOT NULL CHECK(entry_order >= 0),
488
+ payload_json TEXT NOT NULL
489
+ ) STRICT;
490
+ CREATE INDEX memory_mutations_session_created_idx
491
+ ON memory_mutations(session_id, created_at, mutation_id);
492
+
493
+ CREATE TABLE pin_mutations (
494
+ mutation_key TEXT PRIMARY KEY REFERENCES entries(entry_key) ON DELETE CASCADE,
495
+ mutation_id TEXT NOT NULL,
496
+ session_id TEXT NOT NULL REFERENCES sessions(session_id) ON DELETE CASCADE,
497
+ created_at INTEGER NOT NULL,
498
+ entry_order INTEGER NOT NULL CHECK(entry_order >= 0),
499
+ payload_json TEXT NOT NULL
500
+ ) STRICT;
501
+ CREATE INDEX pin_mutations_session_created_idx
502
+ ON pin_mutations(session_id, created_at, mutation_id);
503
+ `,
504
+ },
505
+ {
506
+ version: 10,
507
+ name: "model-calibration-and-provider-cache-metrics",
508
+ sql: `
509
+ ALTER TABLE context_manifests ADD COLUMN input_tokens INTEGER
510
+ CHECK(input_tokens IS NULL OR input_tokens >= 0);
511
+ ALTER TABLE context_manifests ADD COLUMN cache_read_tokens INTEGER
512
+ CHECK(cache_read_tokens IS NULL OR cache_read_tokens >= 0);
513
+ ALTER TABLE context_manifests ADD COLUMN cache_write_tokens INTEGER
514
+ CHECK(cache_write_tokens IS NULL OR cache_write_tokens >= 0);
515
+
516
+ ALTER TABLE token_calibration ADD COLUMN manifest_id TEXT
517
+ REFERENCES context_manifests(manifest_id) ON DELETE CASCADE;
518
+ ALTER TABLE token_calibration ADD COLUMN estimator_version TEXT NOT NULL DEFAULT 'chars-v1';
519
+ ALTER TABLE token_calibration ADD COLUMN input_tokens INTEGER
520
+ CHECK(input_tokens IS NULL OR input_tokens >= 0);
521
+ ALTER TABLE token_calibration ADD COLUMN cache_read_tokens INTEGER
522
+ CHECK(cache_read_tokens IS NULL OR cache_read_tokens >= 0);
523
+ ALTER TABLE token_calibration ADD COLUMN cache_write_tokens INTEGER
524
+ CHECK(cache_write_tokens IS NULL OR cache_write_tokens >= 0);
525
+ UPDATE token_calibration
526
+ SET input_tokens = actual, cache_read_tokens = 0, cache_write_tokens = 0
527
+ WHERE input_tokens IS NULL;
528
+ CREATE UNIQUE INDEX token_calibration_manifest_idx
529
+ ON token_calibration(manifest_id) WHERE manifest_id IS NOT NULL;
530
+ CREATE INDEX token_calibration_model_estimator_created_idx
531
+ ON token_calibration(provider, model, estimator_version, created_at DESC);
532
+ `,
533
+ },
534
+ ];
535
+ export const CURRENT_SCHEMA_VERSION = MIGRATIONS.at(-1)?.version ?? 0;
536
+ function migrationChecksum(migration) {
537
+ return sha256(`${migration.version}\n${migration.name}\n${migration.sql}`);
538
+ }
539
+ export function listAppliedMigrations(database) {
540
+ const rows = database
541
+ .prepare("SELECT version, name, checksum, applied_at FROM schema_migrations ORDER BY version")
542
+ .all();
543
+ return rows.map((row) => ({
544
+ version: row.version,
545
+ name: row.name,
546
+ checksum: row.checksum,
547
+ appliedAt: row.applied_at,
548
+ }));
549
+ }
550
+ export function applyMigrations(database, now = Date.now()) {
551
+ database.exec(`
552
+ CREATE TABLE IF NOT EXISTS schema_migrations (
553
+ version INTEGER PRIMARY KEY,
554
+ name TEXT NOT NULL,
555
+ checksum TEXT NOT NULL,
556
+ applied_at INTEGER NOT NULL
557
+ ) STRICT;
558
+ `);
559
+ const applied = new Map(listAppliedMigrations(database).map((migration) => [migration.version, migration]));
560
+ for (const existing of applied.values()) {
561
+ const known = MIGRATIONS.find((migration) => migration.version === existing.version);
562
+ if (!known) {
563
+ throw new Error(`Database schema version ${existing.version} is newer than this extension supports`);
564
+ }
565
+ if (existing.checksum !== migrationChecksum(known)) {
566
+ throw new Error(`Migration checksum mismatch for version ${existing.version}`);
567
+ }
568
+ }
569
+ const insert = database.prepare("INSERT INTO schema_migrations(version, name, checksum, applied_at) VALUES (?, ?, ?, ?)");
570
+ for (const migration of MIGRATIONS) {
571
+ if (applied.has(migration.version))
572
+ continue;
573
+ database.exec("BEGIN IMMEDIATE");
574
+ try {
575
+ database.exec(migration.sql);
576
+ insert.run(migration.version, migration.name, migrationChecksum(migration), now);
577
+ database.exec(`PRAGMA user_version = ${migration.version}`);
578
+ database.exec("COMMIT");
579
+ }
580
+ catch (error) {
581
+ if (database.isTransaction)
582
+ database.exec("ROLLBACK");
583
+ throw error;
584
+ }
585
+ }
586
+ return listAppliedMigrations(database);
587
+ }
588
+ //# sourceMappingURL=migrations.js.map