@shadowforge0/aquifer-memory 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +8 -0
- package/README.md +72 -0
- package/README_CN.md +17 -0
- package/README_TW.md +4 -0
- package/aquifer.config.example.json +19 -0
- package/consumers/cli.js +259 -12
- package/consumers/codex-active-checkpoint.js +186 -0
- package/consumers/codex-current-memory.js +106 -0
- package/consumers/codex-handoff.js +551 -6
- package/consumers/codex.js +209 -25
- package/consumers/mcp.js +144 -6
- package/consumers/shared/config.js +60 -1
- package/consumers/shared/factory.js +10 -3
- package/core/aquifer.js +357 -838
- package/core/backends/capabilities.js +89 -0
- package/core/backends/local.js +430 -0
- package/core/legacy-bootstrap.js +140 -0
- package/core/mcp-manifest.js +66 -2
- package/core/memory-bootstrap.js +20 -8
- package/core/memory-consolidation.js +365 -11
- package/core/memory-promotion.js +157 -26
- package/core/memory-recall.js +341 -22
- package/core/memory-records.js +347 -11
- package/core/memory-serving.js +132 -0
- package/core/postgres-migrations.js +533 -0
- package/core/public-session-filter.js +40 -0
- package/core/recall-runtime.js +115 -0
- package/core/scope-attribution.js +279 -0
- package/core/session-checkpoint-producer.js +412 -0
- package/core/session-checkpoints.js +432 -0
- package/core/session-finalization.js +98 -2
- package/core/storage-checkpoints.js +546 -0
- package/core/storage.js +121 -8
- package/docs/getting-started.md +6 -0
- package/docs/setup.md +66 -3
- package/package.json +8 -4
- package/schema/014-v1-checkpoint-runs.sql +349 -0
- package/schema/015-v1-evidence-items.sql +92 -0
- package/schema/016-v1-evidence-ref-multi-item.sql +19 -0
- package/schema/017-v1-memory-record-embeddings.sql +25 -0
- package/schema/018-v1-finalization-candidate-envelope.sql +39 -0
- package/scripts/codex-checkpoint-commands.js +464 -0
- package/scripts/codex-checkpoint-runtime.js +520 -0
- package/scripts/codex-recovery.js +246 -1
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
-- Aquifer v1 rolling checkpoint ledger
|
|
2
|
+
-- Requires: 007-v1-foundation.sql, 008-session-finalizations.sql, and 010-v1-finalization-review.sql
|
|
3
|
+
-- Usage: replace ${schema} with actual schema name
|
|
4
|
+
--
|
|
5
|
+
-- Adds additive checkpoint-run audit tables plus scope FK/snapshot support on
|
|
6
|
+
-- session_finalizations. This does not change serving truth or promotion.
|
|
7
|
+
|
|
8
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_scopes_tenant_row
|
|
9
|
+
ON ${schema}.scopes (tenant_id, id);
|
|
10
|
+
|
|
11
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_session_finalizations_tenant_row
|
|
12
|
+
ON ${schema}.session_finalizations (tenant_id, id);
|
|
13
|
+
|
|
14
|
+
ALTER TABLE ${schema}.scopes
|
|
15
|
+
DROP CONSTRAINT IF EXISTS scopes_scope_kind_check;
|
|
16
|
+
|
|
17
|
+
ALTER TABLE ${schema}.scopes
|
|
18
|
+
ADD CONSTRAINT scopes_scope_kind_check
|
|
19
|
+
CHECK (scope_kind IN (
|
|
20
|
+
'global','user','workspace','project','event','session',
|
|
21
|
+
'host_runtime','assistant_instance','repo','task'
|
|
22
|
+
));
|
|
23
|
+
|
|
24
|
+
ALTER TABLE ${schema}.session_finalizations
|
|
25
|
+
ADD COLUMN IF NOT EXISTS scope_id BIGINT,
|
|
26
|
+
ADD COLUMN IF NOT EXISTS scope_snapshot JSONB NOT NULL DEFAULT '{}'::jsonb;
|
|
27
|
+
|
|
28
|
+
DO $$
|
|
29
|
+
BEGIN
|
|
30
|
+
IF NOT EXISTS (
|
|
31
|
+
SELECT 1
|
|
32
|
+
FROM pg_constraint
|
|
33
|
+
WHERE conrelid = '${schema}.session_finalizations'::regclass
|
|
34
|
+
AND conname = 'session_finalizations_scope_snapshot_object_check'
|
|
35
|
+
) THEN
|
|
36
|
+
ALTER TABLE ${schema}.session_finalizations
|
|
37
|
+
ADD CONSTRAINT session_finalizations_scope_snapshot_object_check
|
|
38
|
+
CHECK (jsonb_typeof(scope_snapshot) = 'object');
|
|
39
|
+
END IF;
|
|
40
|
+
END;
|
|
41
|
+
$$;
|
|
42
|
+
|
|
43
|
+
UPDATE ${schema}.session_finalizations sf
|
|
44
|
+
SET scope_id = s.id
|
|
45
|
+
FROM ${schema}.scopes s
|
|
46
|
+
WHERE sf.scope_id IS NULL
|
|
47
|
+
AND sf.scope_kind IS NOT NULL
|
|
48
|
+
AND sf.scope_key IS NOT NULL
|
|
49
|
+
AND s.tenant_id = sf.tenant_id
|
|
50
|
+
AND s.scope_kind = sf.scope_kind
|
|
51
|
+
AND s.scope_key = sf.scope_key;
|
|
52
|
+
|
|
53
|
+
UPDATE ${schema}.session_finalizations sf
|
|
54
|
+
SET scope_snapshot = jsonb_strip_nulls(
|
|
55
|
+
jsonb_build_object(
|
|
56
|
+
'scopeId', COALESCE(sf.scope_id, s.id),
|
|
57
|
+
'scopeKind', COALESCE(sf.scope_kind, s.scope_kind),
|
|
58
|
+
'scopeKey', COALESCE(sf.scope_key, s.scope_key),
|
|
59
|
+
'contextKey', COALESCE(sf.context_key, s.context_key),
|
|
60
|
+
'topicKey', COALESCE(sf.topic_key, s.topic_key),
|
|
61
|
+
'parentScopeId', s.parent_scope_id,
|
|
62
|
+
'inheritanceMode', s.inheritance_mode,
|
|
63
|
+
'activeFrom', s.active_from,
|
|
64
|
+
'activeTo', s.active_to
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
FROM ${schema}.scopes s
|
|
68
|
+
WHERE sf.scope_id = s.id
|
|
69
|
+
AND sf.tenant_id = s.tenant_id
|
|
70
|
+
AND sf.scope_snapshot = '{}'::jsonb;
|
|
71
|
+
|
|
72
|
+
UPDATE ${schema}.session_finalizations
|
|
73
|
+
SET scope_snapshot = jsonb_strip_nulls(
|
|
74
|
+
jsonb_build_object(
|
|
75
|
+
'scopeId', scope_id,
|
|
76
|
+
'scopeKind', scope_kind,
|
|
77
|
+
'scopeKey', scope_key,
|
|
78
|
+
'contextKey', context_key,
|
|
79
|
+
'topicKey', topic_key
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
WHERE scope_snapshot = '{}'::jsonb
|
|
83
|
+
AND (
|
|
84
|
+
scope_id IS NOT NULL
|
|
85
|
+
OR scope_kind IS NOT NULL
|
|
86
|
+
OR scope_key IS NOT NULL
|
|
87
|
+
OR context_key IS NOT NULL
|
|
88
|
+
OR topic_key IS NOT NULL
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
DO $$
|
|
92
|
+
BEGIN
|
|
93
|
+
IF NOT EXISTS (
|
|
94
|
+
SELECT 1
|
|
95
|
+
FROM pg_constraint
|
|
96
|
+
WHERE conrelid = '${schema}.session_finalizations'::regclass
|
|
97
|
+
AND conname = 'session_finalizations_scope_fk'
|
|
98
|
+
) THEN
|
|
99
|
+
ALTER TABLE ${schema}.session_finalizations
|
|
100
|
+
ADD CONSTRAINT session_finalizations_scope_fk
|
|
101
|
+
FOREIGN KEY (tenant_id, scope_id)
|
|
102
|
+
REFERENCES ${schema}.scopes (tenant_id, id)
|
|
103
|
+
ON DELETE RESTRICT
|
|
104
|
+
NOT VALID;
|
|
105
|
+
END IF;
|
|
106
|
+
END;
|
|
107
|
+
$$;
|
|
108
|
+
|
|
109
|
+
CREATE INDEX IF NOT EXISTS idx_session_finalizations_scope
|
|
110
|
+
ON ${schema}.session_finalizations (tenant_id, scope_id, finalized_at DESC, updated_at DESC)
|
|
111
|
+
WHERE scope_id IS NOT NULL;
|
|
112
|
+
|
|
113
|
+
COMMENT ON COLUMN ${schema}.session_finalizations.scope_id IS
|
|
114
|
+
'Resolved v1 scope row for this finalization when the producer knows it.';
|
|
115
|
+
|
|
116
|
+
COMMENT ON COLUMN ${schema}.session_finalizations.scope_snapshot IS
|
|
117
|
+
'Compact scope audit snapshot captured at finalization time; serving still reads live curated memory.';
|
|
118
|
+
|
|
119
|
+
CREATE TABLE IF NOT EXISTS ${schema}.checkpoint_runs (
|
|
120
|
+
id BIGSERIAL PRIMARY KEY,
|
|
121
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
122
|
+
scope_id BIGINT NOT NULL,
|
|
123
|
+
checkpoint_key TEXT NOT NULL CHECK (btrim(checkpoint_key) <> ''),
|
|
124
|
+
from_finalization_id_exclusive BIGINT NOT NULL DEFAULT 0 CHECK (from_finalization_id_exclusive >= 0),
|
|
125
|
+
to_finalization_id_inclusive BIGINT,
|
|
126
|
+
status TEXT NOT NULL DEFAULT 'pending'
|
|
127
|
+
CHECK (status IN (
|
|
128
|
+
'pending','processing','finalized','failed','skipped'
|
|
129
|
+
)),
|
|
130
|
+
window_start TIMESTAMPTZ,
|
|
131
|
+
window_end TIMESTAMPTZ,
|
|
132
|
+
scope_snapshot JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
133
|
+
checkpoint_text TEXT,
|
|
134
|
+
checkpoint_payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
135
|
+
error TEXT,
|
|
136
|
+
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
137
|
+
claimed_at TIMESTAMPTZ,
|
|
138
|
+
finalized_at TIMESTAMPTZ,
|
|
139
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
140
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
141
|
+
CHECK (jsonb_typeof(scope_snapshot) = 'object'),
|
|
142
|
+
CHECK (jsonb_typeof(checkpoint_payload) = 'object'),
|
|
143
|
+
CHECK (jsonb_typeof(metadata) = 'object'),
|
|
144
|
+
CHECK (
|
|
145
|
+
to_finalization_id_inclusive IS NULL
|
|
146
|
+
OR to_finalization_id_inclusive > from_finalization_id_exclusive
|
|
147
|
+
),
|
|
148
|
+
CHECK (
|
|
149
|
+
(window_start IS NULL AND window_end IS NULL)
|
|
150
|
+
OR (
|
|
151
|
+
window_start IS NOT NULL
|
|
152
|
+
AND window_end IS NOT NULL
|
|
153
|
+
AND window_end > window_start
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
ALTER TABLE ${schema}.checkpoint_runs
|
|
159
|
+
ADD COLUMN IF NOT EXISTS from_finalization_id_exclusive BIGINT,
|
|
160
|
+
ADD COLUMN IF NOT EXISTS to_finalization_id_inclusive BIGINT;
|
|
161
|
+
|
|
162
|
+
UPDATE ${schema}.checkpoint_runs
|
|
163
|
+
SET from_finalization_id_exclusive = COALESCE(
|
|
164
|
+
from_finalization_id_exclusive,
|
|
165
|
+
substring(checkpoint_key FROM 'finalization:([0-9]+)-')::bigint,
|
|
166
|
+
0
|
|
167
|
+
),
|
|
168
|
+
to_finalization_id_inclusive = COALESCE(
|
|
169
|
+
to_finalization_id_inclusive,
|
|
170
|
+
substring(checkpoint_key FROM '-([0-9]+)$')::bigint
|
|
171
|
+
)
|
|
172
|
+
WHERE from_finalization_id_exclusive IS NULL
|
|
173
|
+
OR to_finalization_id_inclusive IS NULL;
|
|
174
|
+
|
|
175
|
+
ALTER TABLE ${schema}.checkpoint_runs
|
|
176
|
+
ALTER COLUMN from_finalization_id_exclusive SET DEFAULT 0;
|
|
177
|
+
|
|
178
|
+
ALTER TABLE ${schema}.checkpoint_runs
|
|
179
|
+
ALTER COLUMN from_finalization_id_exclusive SET NOT NULL;
|
|
180
|
+
|
|
181
|
+
DO $$
|
|
182
|
+
BEGIN
|
|
183
|
+
IF NOT EXISTS (
|
|
184
|
+
SELECT 1
|
|
185
|
+
FROM pg_constraint
|
|
186
|
+
WHERE conrelid = '${schema}.checkpoint_runs'::regclass
|
|
187
|
+
AND conname = 'checkpoint_runs_from_finalization_nonnegative_check'
|
|
188
|
+
) THEN
|
|
189
|
+
ALTER TABLE ${schema}.checkpoint_runs
|
|
190
|
+
ADD CONSTRAINT checkpoint_runs_from_finalization_nonnegative_check
|
|
191
|
+
CHECK (from_finalization_id_exclusive >= 0)
|
|
192
|
+
NOT VALID;
|
|
193
|
+
END IF;
|
|
194
|
+
END;
|
|
195
|
+
$$;
|
|
196
|
+
|
|
197
|
+
DO $$
|
|
198
|
+
BEGIN
|
|
199
|
+
IF NOT EXISTS (
|
|
200
|
+
SELECT 1
|
|
201
|
+
FROM pg_constraint
|
|
202
|
+
WHERE conrelid = '${schema}.checkpoint_runs'::regclass
|
|
203
|
+
AND conname = 'checkpoint_runs_finalization_range_order_check'
|
|
204
|
+
) THEN
|
|
205
|
+
ALTER TABLE ${schema}.checkpoint_runs
|
|
206
|
+
ADD CONSTRAINT checkpoint_runs_finalization_range_order_check
|
|
207
|
+
CHECK (
|
|
208
|
+
to_finalization_id_inclusive IS NULL
|
|
209
|
+
OR to_finalization_id_inclusive > from_finalization_id_exclusive
|
|
210
|
+
)
|
|
211
|
+
NOT VALID;
|
|
212
|
+
END IF;
|
|
213
|
+
END;
|
|
214
|
+
$$;
|
|
215
|
+
|
|
216
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_checkpoint_runs_identity
|
|
217
|
+
ON ${schema}.checkpoint_runs (tenant_id, scope_id, checkpoint_key);
|
|
218
|
+
|
|
219
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_checkpoint_runs_scope_range
|
|
220
|
+
ON ${schema}.checkpoint_runs (
|
|
221
|
+
tenant_id, scope_id, from_finalization_id_exclusive, to_finalization_id_inclusive
|
|
222
|
+
)
|
|
223
|
+
WHERE to_finalization_id_inclusive IS NOT NULL;
|
|
224
|
+
|
|
225
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_checkpoint_runs_tenant_row
|
|
226
|
+
ON ${schema}.checkpoint_runs (tenant_id, id);
|
|
227
|
+
|
|
228
|
+
CREATE INDEX IF NOT EXISTS idx_checkpoint_runs_status
|
|
229
|
+
ON ${schema}.checkpoint_runs (tenant_id, status, updated_at DESC, id DESC);
|
|
230
|
+
|
|
231
|
+
CREATE INDEX IF NOT EXISTS idx_checkpoint_runs_scope_window
|
|
232
|
+
ON ${schema}.checkpoint_runs (tenant_id, scope_id, window_end DESC, updated_at DESC, id DESC);
|
|
233
|
+
|
|
234
|
+
CREATE INDEX IF NOT EXISTS idx_checkpoint_runs_scope_finalization_range
|
|
235
|
+
ON ${schema}.checkpoint_runs (
|
|
236
|
+
tenant_id, scope_id, from_finalization_id_exclusive, to_finalization_id_inclusive, status
|
|
237
|
+
)
|
|
238
|
+
WHERE to_finalization_id_inclusive IS NOT NULL;
|
|
239
|
+
|
|
240
|
+
DO $$
|
|
241
|
+
BEGIN
|
|
242
|
+
IF NOT EXISTS (
|
|
243
|
+
SELECT 1
|
|
244
|
+
FROM pg_constraint
|
|
245
|
+
WHERE conrelid = '${schema}.checkpoint_runs'::regclass
|
|
246
|
+
AND conname = 'checkpoint_runs_scope_fk'
|
|
247
|
+
) THEN
|
|
248
|
+
ALTER TABLE ${schema}.checkpoint_runs
|
|
249
|
+
ADD CONSTRAINT checkpoint_runs_scope_fk
|
|
250
|
+
FOREIGN KEY (tenant_id, scope_id)
|
|
251
|
+
REFERENCES ${schema}.scopes (tenant_id, id)
|
|
252
|
+
ON DELETE RESTRICT
|
|
253
|
+
NOT VALID;
|
|
254
|
+
END IF;
|
|
255
|
+
END;
|
|
256
|
+
$$;
|
|
257
|
+
|
|
258
|
+
COMMENT ON TABLE ${schema}.checkpoint_runs IS
|
|
259
|
+
'Rolling checkpoint audit ledger. Runs summarize scope-bounded source finalizations without changing serving truth.';
|
|
260
|
+
|
|
261
|
+
CREATE TABLE IF NOT EXISTS ${schema}.checkpoint_run_sources (
|
|
262
|
+
id BIGSERIAL PRIMARY KEY,
|
|
263
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
264
|
+
checkpoint_run_id BIGINT NOT NULL,
|
|
265
|
+
finalization_id BIGINT NOT NULL,
|
|
266
|
+
source_index INTEGER NOT NULL CHECK (source_index >= 0),
|
|
267
|
+
scope_id BIGINT,
|
|
268
|
+
scope_snapshot JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
269
|
+
session_row_id BIGINT,
|
|
270
|
+
session_id TEXT,
|
|
271
|
+
transcript_hash TEXT,
|
|
272
|
+
summary_row_id BIGINT,
|
|
273
|
+
finalized_at TIMESTAMPTZ,
|
|
274
|
+
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
275
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
276
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
277
|
+
CHECK (jsonb_typeof(scope_snapshot) = 'object'),
|
|
278
|
+
CHECK (jsonb_typeof(metadata) = 'object')
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_checkpoint_run_sources_position
|
|
282
|
+
ON ${schema}.checkpoint_run_sources (tenant_id, checkpoint_run_id, source_index);
|
|
283
|
+
|
|
284
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_checkpoint_run_sources_finalization
|
|
285
|
+
ON ${schema}.checkpoint_run_sources (tenant_id, checkpoint_run_id, finalization_id);
|
|
286
|
+
|
|
287
|
+
CREATE INDEX IF NOT EXISTS idx_checkpoint_run_sources_scope
|
|
288
|
+
ON ${schema}.checkpoint_run_sources (tenant_id, scope_id, finalized_at DESC, id DESC)
|
|
289
|
+
WHERE scope_id IS NOT NULL;
|
|
290
|
+
|
|
291
|
+
CREATE INDEX IF NOT EXISTS idx_checkpoint_run_sources_lookup
|
|
292
|
+
ON ${schema}.checkpoint_run_sources (tenant_id, finalization_id, created_at DESC);
|
|
293
|
+
|
|
294
|
+
DO $$
|
|
295
|
+
BEGIN
|
|
296
|
+
IF NOT EXISTS (
|
|
297
|
+
SELECT 1
|
|
298
|
+
FROM pg_constraint
|
|
299
|
+
WHERE conrelid = '${schema}.checkpoint_run_sources'::regclass
|
|
300
|
+
AND conname = 'checkpoint_run_sources_run_fk'
|
|
301
|
+
) THEN
|
|
302
|
+
ALTER TABLE ${schema}.checkpoint_run_sources
|
|
303
|
+
ADD CONSTRAINT checkpoint_run_sources_run_fk
|
|
304
|
+
FOREIGN KEY (tenant_id, checkpoint_run_id)
|
|
305
|
+
REFERENCES ${schema}.checkpoint_runs (tenant_id, id)
|
|
306
|
+
ON DELETE CASCADE
|
|
307
|
+
NOT VALID;
|
|
308
|
+
END IF;
|
|
309
|
+
END;
|
|
310
|
+
$$;
|
|
311
|
+
|
|
312
|
+
DO $$
|
|
313
|
+
BEGIN
|
|
314
|
+
IF NOT EXISTS (
|
|
315
|
+
SELECT 1
|
|
316
|
+
FROM pg_constraint
|
|
317
|
+
WHERE conrelid = '${schema}.checkpoint_run_sources'::regclass
|
|
318
|
+
AND conname = 'checkpoint_run_sources_finalization_fk'
|
|
319
|
+
) THEN
|
|
320
|
+
ALTER TABLE ${schema}.checkpoint_run_sources
|
|
321
|
+
ADD CONSTRAINT checkpoint_run_sources_finalization_fk
|
|
322
|
+
FOREIGN KEY (tenant_id, finalization_id)
|
|
323
|
+
REFERENCES ${schema}.session_finalizations (tenant_id, id)
|
|
324
|
+
ON DELETE CASCADE
|
|
325
|
+
NOT VALID;
|
|
326
|
+
END IF;
|
|
327
|
+
END;
|
|
328
|
+
$$;
|
|
329
|
+
|
|
330
|
+
DO $$
|
|
331
|
+
BEGIN
|
|
332
|
+
IF NOT EXISTS (
|
|
333
|
+
SELECT 1
|
|
334
|
+
FROM pg_constraint
|
|
335
|
+
WHERE conrelid = '${schema}.checkpoint_run_sources'::regclass
|
|
336
|
+
AND conname = 'checkpoint_run_sources_scope_fk'
|
|
337
|
+
) THEN
|
|
338
|
+
ALTER TABLE ${schema}.checkpoint_run_sources
|
|
339
|
+
ADD CONSTRAINT checkpoint_run_sources_scope_fk
|
|
340
|
+
FOREIGN KEY (tenant_id, scope_id)
|
|
341
|
+
REFERENCES ${schema}.scopes (tenant_id, id)
|
|
342
|
+
ON DELETE RESTRICT
|
|
343
|
+
NOT VALID;
|
|
344
|
+
END IF;
|
|
345
|
+
END;
|
|
346
|
+
$$;
|
|
347
|
+
|
|
348
|
+
COMMENT ON TABLE ${schema}.checkpoint_run_sources IS
|
|
349
|
+
'Per-checkpoint source lineage. Each row captures the finalization input used to build a rolling checkpoint.';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
-- Aquifer v1 retrieval-grade evidence items
|
|
2
|
+
-- Requires: 001-base.sql, 007-v1-foundation.sql, 008-session-finalizations.sql
|
|
3
|
+
-- Usage: replace ${schema} with actual schema name
|
|
4
|
+
|
|
5
|
+
CREATE TABLE IF NOT EXISTS ${schema}.evidence_items (
|
|
6
|
+
id BIGSERIAL PRIMARY KEY,
|
|
7
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
8
|
+
source_kind TEXT NOT NULL
|
|
9
|
+
CHECK (source_kind IN (
|
|
10
|
+
'session','session_summary','turn_embedding','insight',
|
|
11
|
+
'entity_state','evidence_item','raw_event','external'
|
|
12
|
+
)),
|
|
13
|
+
source_ref TEXT NOT NULL CHECK (btrim(source_ref) <> ''),
|
|
14
|
+
session_row_id BIGINT REFERENCES ${schema}.sessions(id) ON DELETE SET NULL,
|
|
15
|
+
turn_embedding_id BIGINT REFERENCES ${schema}.turn_embeddings(id) ON DELETE SET NULL,
|
|
16
|
+
summary_row_id BIGINT REFERENCES ${schema}.session_summaries(session_row_id) ON DELETE SET NULL,
|
|
17
|
+
created_by_finalization_id BIGINT REFERENCES ${schema}.session_finalizations(id) ON DELETE SET NULL,
|
|
18
|
+
excerpt_text TEXT NOT NULL CHECK (btrim(excerpt_text) <> ''),
|
|
19
|
+
excerpt_hash TEXT NOT NULL CHECK (btrim(excerpt_hash) <> ''),
|
|
20
|
+
embedding vector(1024),
|
|
21
|
+
search_tsv TSVECTOR,
|
|
22
|
+
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
23
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_evidence_items_dedupe
|
|
27
|
+
ON ${schema}.evidence_items (tenant_id, source_kind, source_ref, excerpt_hash);
|
|
28
|
+
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_items_source
|
|
30
|
+
ON ${schema}.evidence_items (tenant_id, source_kind, source_ref);
|
|
31
|
+
|
|
32
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_items_finalization
|
|
33
|
+
ON ${schema}.evidence_items (tenant_id, created_by_finalization_id)
|
|
34
|
+
WHERE created_by_finalization_id IS NOT NULL;
|
|
35
|
+
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_items_search_tsv
|
|
37
|
+
ON ${schema}.evidence_items USING GIN (search_tsv);
|
|
38
|
+
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_items_excerpt_trgm
|
|
40
|
+
ON ${schema}.evidence_items USING GIN (excerpt_text gin_trgm_ops);
|
|
41
|
+
|
|
42
|
+
CREATE OR REPLACE FUNCTION ${schema}.evidence_items_search_tsv_update()
|
|
43
|
+
RETURNS trigger
|
|
44
|
+
LANGUAGE plpgsql
|
|
45
|
+
AS $$
|
|
46
|
+
BEGIN
|
|
47
|
+
BEGIN
|
|
48
|
+
NEW.search_tsv := to_tsvector('zhcfg', COALESCE(NEW.excerpt_text, ''));
|
|
49
|
+
EXCEPTION WHEN undefined_object OR undefined_function THEN
|
|
50
|
+
NEW.search_tsv := to_tsvector('simple', COALESCE(NEW.excerpt_text, ''));
|
|
51
|
+
END;
|
|
52
|
+
RETURN NEW;
|
|
53
|
+
END;
|
|
54
|
+
$$;
|
|
55
|
+
|
|
56
|
+
DROP TRIGGER IF EXISTS trg_evidence_items_search_tsv
|
|
57
|
+
ON ${schema}.evidence_items;
|
|
58
|
+
|
|
59
|
+
CREATE TRIGGER trg_evidence_items_search_tsv
|
|
60
|
+
BEFORE INSERT OR UPDATE OF excerpt_text
|
|
61
|
+
ON ${schema}.evidence_items
|
|
62
|
+
FOR EACH ROW
|
|
63
|
+
EXECUTE FUNCTION ${schema}.evidence_items_search_tsv_update();
|
|
64
|
+
|
|
65
|
+
ALTER TABLE ${schema}.evidence_refs
|
|
66
|
+
ADD COLUMN IF NOT EXISTS evidence_item_id BIGINT;
|
|
67
|
+
|
|
68
|
+
CREATE INDEX IF NOT EXISTS idx_evidence_refs_evidence_item
|
|
69
|
+
ON ${schema}.evidence_refs (tenant_id, evidence_item_id)
|
|
70
|
+
WHERE evidence_item_id IS NOT NULL;
|
|
71
|
+
|
|
72
|
+
DO $$
|
|
73
|
+
BEGIN
|
|
74
|
+
IF NOT EXISTS (
|
|
75
|
+
SELECT 1
|
|
76
|
+
FROM pg_constraint
|
|
77
|
+
WHERE conrelid = '${schema}.evidence_refs'::regclass
|
|
78
|
+
AND conname = 'evidence_refs_evidence_item_fk'
|
|
79
|
+
) THEN
|
|
80
|
+
ALTER TABLE ${schema}.evidence_refs
|
|
81
|
+
ADD CONSTRAINT evidence_refs_evidence_item_fk
|
|
82
|
+
FOREIGN KEY (evidence_item_id)
|
|
83
|
+
REFERENCES ${schema}.evidence_items(id)
|
|
84
|
+
ON DELETE SET NULL;
|
|
85
|
+
END IF;
|
|
86
|
+
END$$;
|
|
87
|
+
|
|
88
|
+
COMMENT ON TABLE ${schema}.evidence_items IS
|
|
89
|
+
'Retrieval-grade evidence units. Unlike coarse session_summary refs, these are searchable anchors that can support individual memory_records.';
|
|
90
|
+
|
|
91
|
+
COMMENT ON COLUMN ${schema}.evidence_refs.evidence_item_id IS
|
|
92
|
+
'Optional typed link to a retrieval-grade evidence item. source_kind/source_ref remain the audit-compatible source identity.';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- Aquifer v1 evidence refs can point at multiple retrieval-grade evidence items
|
|
2
|
+
-- Requires: 007-v1-foundation.sql, 015-v1-evidence-items.sql
|
|
3
|
+
-- Usage: replace ${schema} with actual schema name
|
|
4
|
+
|
|
5
|
+
DROP INDEX IF EXISTS ${schema}.idx_evidence_refs_dedupe;
|
|
6
|
+
|
|
7
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_evidence_refs_source_dedupe
|
|
8
|
+
ON ${schema}.evidence_refs (tenant_id, owner_kind, owner_id, source_kind, source_ref, relation_kind)
|
|
9
|
+
WHERE evidence_item_id IS NULL;
|
|
10
|
+
|
|
11
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_evidence_refs_evidence_item_dedupe
|
|
12
|
+
ON ${schema}.evidence_refs (tenant_id, owner_kind, owner_id, evidence_item_id, relation_kind)
|
|
13
|
+
WHERE evidence_item_id IS NOT NULL;
|
|
14
|
+
|
|
15
|
+
COMMENT ON INDEX ${schema}.idx_evidence_refs_source_dedupe IS
|
|
16
|
+
'Legacy/coarse provenance dedupe for refs that do not yet point at retrieval-grade evidence_items.';
|
|
17
|
+
|
|
18
|
+
COMMENT ON INDEX ${schema}.idx_evidence_refs_evidence_item_dedupe IS
|
|
19
|
+
'Allows multiple evidence_items for the same owner/source while deduping each typed evidence item link.';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- Aquifer v1 current-memory semantic recall anchors
|
|
2
|
+
-- Requires: 007-v1-foundation.sql
|
|
3
|
+
-- Usage: replace ${schema} with actual schema name
|
|
4
|
+
|
|
5
|
+
ALTER TABLE ${schema}.memory_records
|
|
6
|
+
ADD COLUMN IF NOT EXISTS embedding vector(1024);
|
|
7
|
+
|
|
8
|
+
DO $$
|
|
9
|
+
BEGIN
|
|
10
|
+
BEGIN
|
|
11
|
+
EXECUTE 'CREATE INDEX IF NOT EXISTS idx_memory_records_embedding_hnsw
|
|
12
|
+
ON ${schema}.memory_records USING hnsw (embedding vector_cosine_ops)
|
|
13
|
+
WHERE status = ''active'' AND visible_in_recall = true AND embedding IS NOT NULL';
|
|
14
|
+
EXCEPTION
|
|
15
|
+
WHEN undefined_object THEN
|
|
16
|
+
RAISE WARNING '[aquifer] pgvector HNSW operator class unavailable; memory_records semantic recall will use lexical/coarse anchors until vector index is available';
|
|
17
|
+
WHEN out_of_memory THEN
|
|
18
|
+
RAISE WARNING '[aquifer] HNSW build on memory_records.embedding ran out of memory; raise maintenance_work_mem and re-run migrate()';
|
|
19
|
+
WHEN program_limit_exceeded THEN
|
|
20
|
+
RAISE WARNING '[aquifer] HNSW build on memory_records.embedding exceeded an internal limit; inspect pgvector logs';
|
|
21
|
+
END;
|
|
22
|
+
END$$;
|
|
23
|
+
|
|
24
|
+
COMMENT ON COLUMN ${schema}.memory_records.embedding IS
|
|
25
|
+
'Optional current-memory embedding used by curated semantic/hybrid session_recall. Legacy summaries remain evidence anchors, not current truth.';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
-- Aquifer v1 finalization candidate envelope
|
|
2
|
+
-- Requires: 008-session-finalizations.sql, 010-v1-finalization-review.sql
|
|
3
|
+
-- Usage: replace ${schema} with actual schema name
|
|
4
|
+
--
|
|
5
|
+
-- The candidate envelope is producer material, not serving truth. It records
|
|
6
|
+
-- the structured synthesis input/output that core finalization validated before
|
|
7
|
+
-- promoting active current memory.
|
|
8
|
+
|
|
9
|
+
ALTER TABLE ${schema}.session_finalizations
|
|
10
|
+
ADD COLUMN IF NOT EXISTS candidate_envelope JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
11
|
+
ADD COLUMN IF NOT EXISTS candidate_envelope_hash TEXT,
|
|
12
|
+
ADD COLUMN IF NOT EXISTS candidate_envelope_version TEXT,
|
|
13
|
+
ADD COLUMN IF NOT EXISTS coverage JSONB NOT NULL DEFAULT '{}'::jsonb;
|
|
14
|
+
|
|
15
|
+
COMMENT ON COLUMN ${schema}.session_finalizations.candidate_envelope IS
|
|
16
|
+
'Structured current-memory candidate envelope produced by handoff/recovery synthesis; producer material, not serving truth.';
|
|
17
|
+
|
|
18
|
+
COMMENT ON COLUMN ${schema}.session_finalizations.candidate_envelope_hash IS
|
|
19
|
+
'Stable hash of the candidate envelope used for audit and replay comparison.';
|
|
20
|
+
|
|
21
|
+
COMMENT ON COLUMN ${schema}.session_finalizations.candidate_envelope_version IS
|
|
22
|
+
'Version of the producer envelope contract, for example handoff_current_memory_synthesis_v1.';
|
|
23
|
+
|
|
24
|
+
COMMENT ON COLUMN ${schema}.session_finalizations.coverage IS
|
|
25
|
+
'Coverage metadata for partial transcript, previous bootstrap, checkpoint, or other synthesis inputs.';
|
|
26
|
+
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_session_finalizations_candidate_envelope_hash
|
|
28
|
+
ON ${schema}.session_finalizations (tenant_id, candidate_envelope_hash)
|
|
29
|
+
WHERE candidate_envelope_hash IS NOT NULL;
|
|
30
|
+
|
|
31
|
+
ALTER TABLE ${schema}.finalization_candidates
|
|
32
|
+
ADD COLUMN IF NOT EXISTS candidate_hash TEXT;
|
|
33
|
+
|
|
34
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_finalization_candidates_hash
|
|
35
|
+
ON ${schema}.finalization_candidates (tenant_id, finalization_id, candidate_hash)
|
|
36
|
+
WHERE candidate_hash IS NOT NULL;
|
|
37
|
+
|
|
38
|
+
COMMENT ON COLUMN ${schema}.finalization_candidates.candidate_hash IS
|
|
39
|
+
'Stable per-candidate hash. candidate_index remains an ordered audit position.';
|