@engram-mem/supabase 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.
@@ -0,0 +1,154 @@
1
+ import { generateId } from '@engram-mem/core';
2
+ import { sanitizeIlike } from './search.js';
3
+ export class SupabaseDigestStorage {
4
+ client;
5
+ constructor(client) {
6
+ this.client = client;
7
+ }
8
+ async insert(digest) {
9
+ const id = generateId();
10
+ // Insert into memories table first (FK requirement)
11
+ const { error: memErr } = await this.client
12
+ .from('memories')
13
+ .insert({ id, type: 'digest' });
14
+ if (memErr)
15
+ throw new Error(`Digest memory insert failed: ${memErr.message}`);
16
+ const { data, error } = await this.client
17
+ .from('memory_digests')
18
+ .insert({
19
+ id,
20
+ session_id: digest.sessionId,
21
+ summary: digest.summary,
22
+ key_topics: digest.keyTopics,
23
+ source_episode_ids: digest.sourceEpisodeIds,
24
+ source_digest_ids: digest.sourceDigestIds,
25
+ level: digest.level,
26
+ embedding: digest.embedding ?? null,
27
+ metadata: digest.metadata,
28
+ })
29
+ .select()
30
+ .single();
31
+ if (error)
32
+ throw new Error(`Digest insert failed: ${error.message}`);
33
+ return rowToDigest(data);
34
+ }
35
+ async search(query, opts) {
36
+ const limit = opts?.limit ?? 10;
37
+ const embedding = opts?.embedding;
38
+ if (embedding) {
39
+ // Prefer hybrid RRF search when query text is also available
40
+ if (query) {
41
+ const { data, error } = await this.client.rpc('engram_hybrid_recall', {
42
+ p_query_text: query,
43
+ p_query_embedding: JSON.stringify(embedding),
44
+ p_match_count: limit,
45
+ p_include_episodes: false,
46
+ p_include_digests: true,
47
+ p_include_semantic: false,
48
+ p_include_procedural: false,
49
+ });
50
+ if (error)
51
+ throw new Error(`Digest search (hybrid) failed: ${error.message}`);
52
+ const rows = (data ?? []);
53
+ const RRF_MAX = 2.0 / 61.0;
54
+ return rows.map((r) => ({
55
+ item: recallRowToDigest(r),
56
+ similarity: Math.min(1.0, (r.similarity || 0) / RRF_MAX),
57
+ }));
58
+ }
59
+ // Embedding only — fall back to pure vector search
60
+ const { data, error } = await this.client.rpc('engram_recall', {
61
+ p_query_embedding: embedding,
62
+ p_session_id: null,
63
+ p_match_count: limit,
64
+ p_min_similarity: opts?.minScore ?? 0.15,
65
+ p_include_episodes: false,
66
+ p_include_digests: true,
67
+ p_include_semantic: false,
68
+ p_include_procedural: false,
69
+ });
70
+ if (error)
71
+ throw new Error(`Digest search (vector) failed: ${error.message}`);
72
+ const rows = (data ?? []);
73
+ return rows.map((r) => ({
74
+ item: recallRowToDigest(r),
75
+ similarity: r.similarity,
76
+ }));
77
+ }
78
+ // Text fallback
79
+ const { data, error } = await this.client
80
+ .from('memory_digests')
81
+ .select('*')
82
+ .ilike('summary', `%${sanitizeIlike(query)}%`)
83
+ .limit(limit);
84
+ if (error)
85
+ throw new Error(`Digest search (text) failed: ${error.message}`);
86
+ return (data ?? []).map((r) => ({
87
+ item: rowToDigest(r),
88
+ similarity: 0.5,
89
+ }));
90
+ }
91
+ async getBySession(sessionId) {
92
+ const { data, error } = await this.client
93
+ .from('memory_digests')
94
+ .select('*')
95
+ .eq('session_id', sessionId)
96
+ .order('created_at', { ascending: true });
97
+ if (error)
98
+ throw new Error(`Digest getBySession failed: ${error.message}`);
99
+ return (data ?? []).map(rowToDigest);
100
+ }
101
+ async getRecent(days) {
102
+ const since = new Date(Date.now() - days * 86400000).toISOString();
103
+ const { data, error } = await this.client
104
+ .from('memory_digests')
105
+ .select('*')
106
+ .gte('created_at', since)
107
+ .order('created_at', { ascending: false });
108
+ if (error)
109
+ throw new Error(`Digest getRecent failed: ${error.message}`);
110
+ return (data ?? []).map(rowToDigest);
111
+ }
112
+ async getCountBySession() {
113
+ const { data, error } = await this.client
114
+ .from('memory_digests')
115
+ .select('session_id');
116
+ if (error)
117
+ throw new Error(`Digest getCountBySession failed: ${error.message}`);
118
+ const rows = (data ?? []);
119
+ const counts = {};
120
+ for (const row of rows) {
121
+ counts[row.session_id] = (counts[row.session_id] ?? 0) + 1;
122
+ }
123
+ return counts;
124
+ }
125
+ }
126
+ function rowToDigest(row) {
127
+ return {
128
+ id: row.id,
129
+ sessionId: row.session_id,
130
+ summary: row.summary,
131
+ keyTopics: row.key_topics ?? [],
132
+ sourceEpisodeIds: row.source_episode_ids ?? [],
133
+ sourceDigestIds: row.source_digest_ids ?? [],
134
+ level: row.level,
135
+ embedding: row.embedding ?? null,
136
+ metadata: row.metadata ?? {},
137
+ createdAt: new Date(row.created_at),
138
+ };
139
+ }
140
+ function recallRowToDigest(row) {
141
+ return {
142
+ id: row.id,
143
+ sessionId: '',
144
+ summary: row.content,
145
+ keyTopics: row.entities ?? [],
146
+ sourceEpisodeIds: [],
147
+ sourceDigestIds: [],
148
+ level: 0,
149
+ embedding: null,
150
+ metadata: {},
151
+ createdAt: new Date(row.created_at),
152
+ };
153
+ }
154
+ //# sourceMappingURL=digests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digests.js","sourceRoot":"","sources":["../src/digests.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { SupabaseClient } from '@supabase/supabase-js';
2
+ import type { Episode, SearchOptions, SearchResult } from '@engram-mem/core';
3
+ import type { EpisodeStorage } from '@engram-mem/core';
4
+ export declare class SupabaseEpisodeStorage implements EpisodeStorage {
5
+ private readonly client;
6
+ private readonly legacyMode;
7
+ /**
8
+ * @param legacyMode When true, skip the memories pool table insert and
9
+ * omit columns that don't exist in the legacy schema (salience, entities,
10
+ * access_count, etc.). Legacy mode is detected automatically by the adapter
11
+ * and used until migrations 004-007 are applied.
12
+ */
13
+ constructor(client: SupabaseClient, legacyMode?: boolean);
14
+ insert(episode: Omit<Episode, 'id' | 'createdAt'>): Promise<Episode>;
15
+ search(query: string, opts?: SearchOptions): Promise<SearchResult<Episode>[]>;
16
+ getByIds(ids: string[]): Promise<Episode[]>;
17
+ getBySession(sessionId: string, opts?: {
18
+ since?: Date;
19
+ }): Promise<Episode[]>;
20
+ getUnconsolidated(sessionId: string): Promise<Episode[]>;
21
+ getUnconsolidatedSessions(): Promise<string[]>;
22
+ markConsolidated(ids: string[]): Promise<void>;
23
+ recordAccess(id: string): Promise<void>;
24
+ }
25
+ //# sourceMappingURL=episodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"episodes.d.ts","sourceRoot":"","sources":["../src/episodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAE5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAGtD,qBAAa,sBAAuB,YAAW,cAAc;IAQzD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAR7B;;;;;OAKG;gBAEgB,MAAM,EAAE,cAAc,EACtB,UAAU,GAAE,OAAe;IAGxC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IA4CpE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;IAyF7E,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAU3C,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAgB5E,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAgBxD,yBAAyB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAW9C,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAS9C,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAQ9C"}
@@ -0,0 +1,260 @@
1
+ import { generateId } from '@engram-mem/core';
2
+ import { sanitizeIlike } from './search.js';
3
+ export class SupabaseEpisodeStorage {
4
+ client;
5
+ legacyMode;
6
+ /**
7
+ * @param legacyMode When true, skip the memories pool table insert and
8
+ * omit columns that don't exist in the legacy schema (salience, entities,
9
+ * access_count, etc.). Legacy mode is detected automatically by the adapter
10
+ * and used until migrations 004-007 are applied.
11
+ */
12
+ constructor(client, legacyMode = false) {
13
+ this.client = client;
14
+ this.legacyMode = legacyMode;
15
+ }
16
+ async insert(episode) {
17
+ const id = generateId();
18
+ if (!this.legacyMode) {
19
+ // Insert into memories table first (FK requirement — full schema only)
20
+ const { error: memErr } = await this.client
21
+ .from('memories')
22
+ .insert({ id, type: 'episode' });
23
+ if (memErr)
24
+ throw new Error(`Episode memory insert failed: ${memErr.message}`);
25
+ }
26
+ // Build the row — legacy schema only has: id, session_id, role, content,
27
+ // embedding, metadata, created_at. Full schema adds salience, access_count,
28
+ // last_accessed, consolidated_at, entities, searchable_content.
29
+ const searchableContent = episode.metadata?.searchableContent ?? null;
30
+ const row = {
31
+ id,
32
+ session_id: episode.sessionId,
33
+ role: episode.role,
34
+ content: episode.content,
35
+ embedding: episode.embedding ?? null,
36
+ metadata: episode.metadata,
37
+ };
38
+ if (!this.legacyMode) {
39
+ row.salience = episode.salience;
40
+ row.access_count = episode.accessCount;
41
+ row.last_accessed = episode.lastAccessed?.toISOString() ?? null;
42
+ row.consolidated_at = episode.consolidatedAt?.toISOString() ?? null;
43
+ row.entities = episode.entities;
44
+ row.searchable_content = searchableContent;
45
+ }
46
+ const { data, error } = await this.client
47
+ .from('memory_episodes')
48
+ .insert(row)
49
+ .select()
50
+ .single();
51
+ if (error)
52
+ throw new Error(`Episode insert failed: ${error.message}`);
53
+ return rowToEpisode(data, this.legacyMode);
54
+ }
55
+ async search(query, opts) {
56
+ const limit = opts?.limit ?? 10;
57
+ const embedding = opts?.embedding;
58
+ if (embedding) {
59
+ if (this.legacyMode) {
60
+ // Legacy schema: use the old match_episodes RPC
61
+ // Format embedding as '[x,y,z]' string (legacy RPC accepts text)
62
+ const embStr = `[${embedding.join(',')}]`;
63
+ const { data, error } = await this.client.rpc('match_episodes', {
64
+ query_embedding: embStr,
65
+ filter_session_id: opts?.sessionId ?? null,
66
+ match_count: limit,
67
+ min_similarity: opts?.minScore ?? 0.15,
68
+ });
69
+ if (error)
70
+ throw new Error(`Episode search (legacy vector) failed: ${error.message}`);
71
+ const rows = (data ?? []);
72
+ return rows.map((r) => ({
73
+ item: legacyRowToEpisode(r),
74
+ similarity: r.similarity,
75
+ }));
76
+ }
77
+ // Full schema: prefer hybrid RRF search when query text is also available
78
+ if (query) {
79
+ const { data, error } = await this.client.rpc('engram_hybrid_recall', {
80
+ p_query_text: query,
81
+ p_query_embedding: JSON.stringify(embedding),
82
+ p_match_count: limit,
83
+ p_session_id: opts?.sessionId ?? null,
84
+ p_include_episodes: true,
85
+ p_include_digests: false,
86
+ p_include_semantic: false,
87
+ p_include_procedural: false,
88
+ });
89
+ if (error)
90
+ throw new Error(`Episode search (hybrid) failed: ${error.message}`);
91
+ const rows = (data ?? []);
92
+ // RRF scores are in ~0.001-0.033 range (1/(k+rank) * weight).
93
+ // Normalize to 0-1 by dividing by theoretical max (both searches rank #1).
94
+ // With k=60, max = 1/61 * 1.0 + 1/61 * 1.0 ≈ 0.0328
95
+ const RRF_MAX = 2.0 / 61.0;
96
+ return rows.map((r) => ({
97
+ item: recallRowToEpisode(r),
98
+ similarity: Math.min(1.0, (r.similarity || 0) / RRF_MAX),
99
+ }));
100
+ }
101
+ // Embedding only — fall back to pure vector search
102
+ const { data, error } = await this.client.rpc('engram_recall', {
103
+ p_query_embedding: embedding,
104
+ p_session_id: opts?.sessionId ?? null,
105
+ p_match_count: limit,
106
+ p_min_similarity: opts?.minScore ?? 0.15,
107
+ p_include_episodes: true,
108
+ p_include_digests: false,
109
+ p_include_semantic: false,
110
+ p_include_procedural: false,
111
+ });
112
+ if (error)
113
+ throw new Error(`Episode search (vector) failed: ${error.message}`);
114
+ const rows = (data ?? []);
115
+ return rows.map((r) => ({
116
+ item: recallRowToEpisode(r),
117
+ similarity: r.similarity,
118
+ }));
119
+ }
120
+ // Text fallback via ilike — works on both legacy and full schema
121
+ let queryBuilder = this.client
122
+ .from('memory_episodes')
123
+ .select('*')
124
+ .ilike('content', `%${sanitizeIlike(query)}%`)
125
+ .limit(limit);
126
+ if (opts?.sessionId) {
127
+ queryBuilder = queryBuilder.eq('session_id', opts.sessionId);
128
+ }
129
+ const { data, error } = await queryBuilder;
130
+ if (error)
131
+ throw new Error(`Episode search (text) failed: ${error.message}`);
132
+ const rows = (data ?? []);
133
+ return rows.map((r) => ({
134
+ item: rowToEpisode(r, this.legacyMode),
135
+ similarity: 0.5,
136
+ }));
137
+ }
138
+ async getByIds(ids) {
139
+ if (ids.length === 0)
140
+ return [];
141
+ const { data, error } = await this.client
142
+ .from('memory_episodes')
143
+ .select('*')
144
+ .in('id', ids);
145
+ if (error)
146
+ throw new Error(`Episode getByIds failed: ${error.message}`);
147
+ return (data ?? []).map((r) => rowToEpisode(r, this.legacyMode));
148
+ }
149
+ async getBySession(sessionId, opts) {
150
+ let queryBuilder = this.client
151
+ .from('memory_episodes')
152
+ .select('*')
153
+ .eq('session_id', sessionId)
154
+ .order('created_at', { ascending: true });
155
+ if (opts?.since) {
156
+ queryBuilder = queryBuilder.gte('created_at', opts.since.toISOString());
157
+ }
158
+ const { data, error } = await queryBuilder;
159
+ if (error)
160
+ throw new Error(`Episode getBySession failed: ${error.message}`);
161
+ return (data ?? []).map((r) => rowToEpisode(r, this.legacyMode));
162
+ }
163
+ async getUnconsolidated(sessionId) {
164
+ if (this.legacyMode) {
165
+ // Legacy schema lacks consolidated_at and salience — return all unprocessed
166
+ // episodes for the session (consolidation is a no-op in legacy mode)
167
+ return [];
168
+ }
169
+ const { data, error } = await this.client
170
+ .from('memory_episodes')
171
+ .select('*')
172
+ .eq('session_id', sessionId)
173
+ .is('consolidated_at', null)
174
+ .order('salience', { ascending: false });
175
+ if (error)
176
+ throw new Error(`Episode getUnconsolidated failed: ${error.message}`);
177
+ return (data ?? []).map((r) => rowToEpisode(r, false));
178
+ }
179
+ async getUnconsolidatedSessions() {
180
+ if (this.legacyMode)
181
+ return [];
182
+ const { data, error } = await this.client
183
+ .from('memory_episodes')
184
+ .select('session_id')
185
+ .is('consolidated_at', null);
186
+ if (error)
187
+ throw new Error(`Episode getUnconsolidatedSessions failed: ${error.message}`);
188
+ const rows = (data ?? []);
189
+ return [...new Set(rows.map((r) => r.session_id))];
190
+ }
191
+ async markConsolidated(ids) {
192
+ if (ids.length === 0 || this.legacyMode)
193
+ return;
194
+ const { error } = await this.client
195
+ .from('memory_episodes')
196
+ .update({ consolidated_at: new Date().toISOString() })
197
+ .in('id', ids);
198
+ if (error)
199
+ throw new Error(`Episode markConsolidated failed: ${error.message}`);
200
+ }
201
+ async recordAccess(id) {
202
+ if (this.legacyMode)
203
+ return; // no access tracking in legacy schema
204
+ const { error } = await this.client.rpc('engram_record_access', {
205
+ p_id: id,
206
+ p_memory_type: 'episode',
207
+ });
208
+ if (error)
209
+ throw new Error(`Episode recordAccess failed: ${error.message}`);
210
+ }
211
+ }
212
+ function rowToEpisode(row, legacyMode = false) {
213
+ return {
214
+ id: row.id,
215
+ sessionId: row.session_id,
216
+ role: row.role,
217
+ content: row.content,
218
+ salience: legacyMode ? 0.3 : (row.salience ?? 0.3),
219
+ accessCount: legacyMode ? 0 : (row.access_count ?? 0),
220
+ lastAccessed: legacyMode ? null : (row.last_accessed ? new Date(row.last_accessed) : null),
221
+ consolidatedAt: legacyMode ? null : (row.consolidated_at ? new Date(row.consolidated_at) : null),
222
+ embedding: row.embedding ?? null,
223
+ entities: legacyMode ? [] : (row.entities ?? []),
224
+ metadata: row.metadata ?? {},
225
+ createdAt: new Date(row.created_at),
226
+ };
227
+ }
228
+ function legacyRowToEpisode(row) {
229
+ return {
230
+ id: row.id,
231
+ sessionId: row.session_id,
232
+ role: row.role,
233
+ content: row.content,
234
+ salience: 0.3,
235
+ accessCount: 0,
236
+ lastAccessed: null,
237
+ consolidatedAt: null,
238
+ embedding: null,
239
+ entities: [],
240
+ metadata: row.metadata ?? {},
241
+ createdAt: new Date(row.created_at),
242
+ };
243
+ }
244
+ function recallRowToEpisode(row) {
245
+ return {
246
+ id: row.id,
247
+ sessionId: '',
248
+ role: 'user',
249
+ content: row.content,
250
+ salience: row.salience,
251
+ accessCount: row.access_count,
252
+ lastAccessed: null,
253
+ consolidatedAt: null,
254
+ embedding: null,
255
+ entities: row.entities ?? [],
256
+ metadata: {},
257
+ createdAt: new Date(row.created_at),
258
+ };
259
+ }
260
+ //# sourceMappingURL=episodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"episodes.js","sourceRoot":"","sources":["../src/episodes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export { SupabaseStorageAdapter } from './adapter.js';
2
+ export type { SupabaseAdapterOptions } from './adapter.js';
3
+ export { getMigrationSQL, MIGRATION_004, MIGRATION_005, MIGRATION_006, MIGRATION_007 } from './migrations.js';
4
+ import { SupabaseStorageAdapter } from './adapter.js';
5
+ export declare function supabaseAdapter(opts: {
6
+ url: string;
7
+ key: string;
8
+ embeddingDimensions?: number;
9
+ }): SupabaseStorageAdapter;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACrD,YAAY,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE7G,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAErD,wBAAgB,eAAe,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAE,0BAE/F"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { SupabaseStorageAdapter } from './adapter.js';
2
+ export { getMigrationSQL, MIGRATION_004, MIGRATION_005, MIGRATION_006, MIGRATION_007 } from './migrations.js';
3
+ import { SupabaseStorageAdapter } from './adapter.js';
4
+ export function supabaseAdapter(opts) {
5
+ return new SupabaseStorageAdapter(opts);
6
+ }
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAErD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE7G,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAErD,MAAM,UAAU,eAAe,CAAC,IAAgE;IAC9F,OAAO,IAAI,sBAAsB,CAAC,IAAI,CAAC,CAAA;AACzC,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Migration 004: Add Engram columns to existing tables and rename memory_knowledge.
3
+ * Applies on top of the legacy 001–003 migrations (initial schema, search functions,
4
+ * enable RLS).
5
+ */
6
+ export declare const MIGRATION_004 = "\n-- Migration 004: Engram v1 \u2014 extend existing tables\n-- Run this after 001_initial_schema, 002_search_functions, 003_enable_rls\n\n-- Add missing columns to memory_episodes\nALTER TABLE memory_episodes\n ADD COLUMN IF NOT EXISTS salience real NOT NULL DEFAULT 0.3\n CHECK (salience >= 0.0 AND salience <= 1.0),\n ADD COLUMN IF NOT EXISTS access_count integer NOT NULL DEFAULT 0,\n ADD COLUMN IF NOT EXISTS last_accessed timestamptz,\n ADD COLUMN IF NOT EXISTS consolidated_at timestamptz,\n ADD COLUMN IF NOT EXISTS entities text[] NOT NULL DEFAULT '{}';\n\n-- Add missing columns to memory_digests\nALTER TABLE memory_digests\n ADD COLUMN IF NOT EXISTS source_digest_ids uuid[] NOT NULL DEFAULT '{}',\n ADD COLUMN IF NOT EXISTS level integer NOT NULL DEFAULT 0\n CHECK (level >= 0 AND level <= 10);\n\n-- Rename memory_knowledge -> memory_semantic (catalog-only, instantaneous)\n-- Skip if already renamed\nDO $$\nBEGIN\n IF EXISTS (\n SELECT 1 FROM information_schema.tables\n WHERE table_schema = 'public' AND table_name = 'memory_knowledge'\n ) AND NOT EXISTS (\n SELECT 1 FROM information_schema.tables\n WHERE table_schema = 'public' AND table_name = 'memory_semantic'\n ) THEN\n ALTER TABLE memory_knowledge RENAME TO memory_semantic;\n END IF;\nEND $$;\n\n-- Add missing columns to memory_semantic\nALTER TABLE memory_semantic\n ADD COLUMN IF NOT EXISTS source_episode_ids uuid[] NOT NULL DEFAULT '{}',\n ADD COLUMN IF NOT EXISTS decay_rate real NOT NULL DEFAULT 0.02\n CHECK (decay_rate > 0.0 AND decay_rate <= 1.0),\n ADD COLUMN IF NOT EXISTS supersedes uuid REFERENCES memories(id),\n ADD COLUMN IF NOT EXISTS superseded_by uuid REFERENCES memories(id),\n ADD COLUMN IF NOT EXISTS updated_at timestamptz NOT NULL DEFAULT now();\n\n-- Record migration\nINSERT INTO schema_migrations (version, checksum)\nVALUES ('004_engram_v1', md5('004'))\nON CONFLICT DO NOTHING;\n";
7
+ /**
8
+ * Migration 005: Create new tables — memories pool, memory_procedural,
9
+ * memory_associations, consolidation_runs, sensory_snapshots.
10
+ */
11
+ export declare const MIGRATION_005 = "\n-- Migration 005: New Engram tables\n\n-- Enable required extensions\nCREATE EXTENSION IF NOT EXISTS vector;\nCREATE EXTENSION IF NOT EXISTS pg_trgm;\n\n-- Memory ID Pool (base table for FK integrity across all memory types)\nCREATE TABLE IF NOT EXISTS memories (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n type text NOT NULL\n CHECK (type IN ('episode', 'digest', 'semantic', 'procedural')),\n created_at timestamptz NOT NULL DEFAULT now()\n);\n\n-- Episodic Memory (full schema \u2014 idempotent)\nCREATE TABLE IF NOT EXISTS memory_episodes (\n id uuid PRIMARY KEY REFERENCES memories(id),\n session_id text NOT NULL,\n role text NOT NULL\n CHECK (role IN ('user', 'assistant', 'system')),\n content text NOT NULL,\n salience real NOT NULL DEFAULT 0.3\n CHECK (salience >= 0.0 AND salience <= 1.0),\n access_count integer NOT NULL DEFAULT 0,\n last_accessed timestamptz,\n consolidated_at timestamptz,\n embedding vector(1536),\n entities text[] NOT NULL DEFAULT '{}',\n metadata jsonb NOT NULL DEFAULT '{}',\n created_at timestamptz NOT NULL DEFAULT now()\n);\n\nCREATE INDEX IF NOT EXISTS idx_episodes_session\n ON memory_episodes(session_id);\nCREATE INDEX IF NOT EXISTS idx_episodes_unconsolidated\n ON memory_episodes(session_id, salience DESC)\n WHERE consolidated_at IS NULL;\nCREATE INDEX IF NOT EXISTS idx_episodes_created\n ON memory_episodes(created_at DESC);\nCREATE INDEX IF NOT EXISTS idx_episodes_last_accessed\n ON memory_episodes(last_accessed)\n WHERE last_accessed IS NOT NULL;\nCREATE INDEX IF NOT EXISTS idx_episodes_entities\n ON memory_episodes USING GIN(entities);\nCREATE INDEX IF NOT EXISTS idx_episodes_content_trgm\n ON memory_episodes USING GIN(content gin_trgm_ops);\n\n-- Digest Layer\nCREATE TABLE IF NOT EXISTS memory_digests (\n id uuid PRIMARY KEY REFERENCES memories(id),\n session_id text NOT NULL,\n summary text NOT NULL,\n key_topics text[] NOT NULL DEFAULT '{}',\n source_episode_ids uuid[] NOT NULL DEFAULT '{}',\n source_digest_ids uuid[] NOT NULL DEFAULT '{}',\n level integer NOT NULL DEFAULT 0\n CHECK (level >= 0 AND level <= 10),\n embedding vector(1536),\n metadata jsonb NOT NULL DEFAULT '{}',\n created_at timestamptz NOT NULL DEFAULT now()\n);\n\nCREATE INDEX IF NOT EXISTS idx_digests_session\n ON memory_digests(session_id);\nCREATE INDEX IF NOT EXISTS idx_digests_created\n ON memory_digests(created_at DESC);\nCREATE INDEX IF NOT EXISTS idx_digests_topics\n ON memory_digests USING GIN(key_topics);\nCREATE INDEX IF NOT EXISTS idx_digests_source_episodes\n ON memory_digests USING GIN(source_episode_ids);\n\n-- Semantic Memory\nCREATE TABLE IF NOT EXISTS memory_semantic (\n id uuid PRIMARY KEY REFERENCES memories(id),\n topic text NOT NULL,\n content text NOT NULL,\n confidence real NOT NULL DEFAULT 0.5\n CHECK (confidence >= 0.0 AND confidence <= 1.0),\n source_digest_ids uuid[] NOT NULL DEFAULT '{}',\n source_episode_ids uuid[] NOT NULL DEFAULT '{}',\n access_count integer NOT NULL DEFAULT 0,\n last_accessed timestamptz,\n decay_rate real NOT NULL DEFAULT 0.02\n CHECK (decay_rate > 0.0 AND decay_rate <= 1.0),\n supersedes uuid REFERENCES memories(id),\n superseded_by uuid REFERENCES memories(id),\n embedding vector(1536),\n metadata jsonb NOT NULL DEFAULT '{}',\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\n\nCREATE INDEX IF NOT EXISTS idx_semantic_topic\n ON memory_semantic(topic);\nCREATE INDEX IF NOT EXISTS idx_semantic_confidence\n ON memory_semantic(confidence DESC)\n WHERE superseded_by IS NULL;\nCREATE INDEX IF NOT EXISTS idx_semantic_last_accessed\n ON memory_semantic(last_accessed);\nCREATE INDEX IF NOT EXISTS idx_semantic_created\n ON memory_semantic(created_at DESC);\n\nCREATE OR REPLACE FUNCTION update_updated_at_column()\nRETURNS TRIGGER LANGUAGE plpgsql AS $$\nBEGIN\n NEW.updated_at = now();\n RETURN NEW;\nEND;\n$$;\n\nDO $$\nBEGIN\n IF NOT EXISTS (\n SELECT 1 FROM pg_trigger WHERE tgname = 'semantic_updated_at'\n ) THEN\n CREATE TRIGGER semantic_updated_at\n BEFORE UPDATE ON memory_semantic\n FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();\n END IF;\nEND $$;\n\n-- Procedural Memory\nCREATE TABLE IF NOT EXISTS memory_procedural (\n id uuid PRIMARY KEY REFERENCES memories(id),\n category text NOT NULL\n CHECK (category IN ('workflow', 'preference', 'habit', 'pattern', 'convention')),\n trigger_text text NOT NULL,\n procedure text NOT NULL,\n confidence real NOT NULL DEFAULT 0.5\n CHECK (confidence >= 0.0 AND confidence <= 1.0),\n observation_count integer NOT NULL DEFAULT 1,\n last_observed timestamptz NOT NULL DEFAULT now(),\n first_observed timestamptz NOT NULL DEFAULT now(),\n access_count integer NOT NULL DEFAULT 0,\n last_accessed timestamptz,\n decay_rate real NOT NULL DEFAULT 0.01\n CHECK (decay_rate > 0.0 AND decay_rate <= 1.0),\n source_episode_ids uuid[] NOT NULL DEFAULT '{}',\n embedding vector(1536),\n metadata jsonb NOT NULL DEFAULT '{}',\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\n\nCREATE INDEX IF NOT EXISTS idx_procedural_category\n ON memory_procedural(category);\nCREATE INDEX IF NOT EXISTS idx_procedural_confidence\n ON memory_procedural(confidence DESC);\nCREATE INDEX IF NOT EXISTS idx_procedural_last_accessed\n ON memory_procedural(last_accessed);\nCREATE INDEX IF NOT EXISTS idx_procedural_created\n ON memory_procedural(created_at DESC);\n\nDO $$\nBEGIN\n IF NOT EXISTS (\n SELECT 1 FROM pg_trigger WHERE tgname = 'procedural_updated_at'\n ) THEN\n CREATE TRIGGER procedural_updated_at\n BEFORE UPDATE ON memory_procedural\n FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();\n END IF;\nEND $$;\n\n-- Associative Network\nCREATE TABLE IF NOT EXISTS memory_associations (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n source_id uuid NOT NULL REFERENCES memories(id),\n source_type text NOT NULL\n CHECK (source_type IN ('episode', 'digest', 'semantic', 'procedural')),\n target_id uuid NOT NULL REFERENCES memories(id),\n target_type text NOT NULL\n CHECK (target_type IN ('episode', 'digest', 'semantic', 'procedural')),\n edge_type text NOT NULL\n CHECK (edge_type IN ('temporal', 'causal', 'topical', 'supports',\n 'contradicts', 'elaborates', 'derives_from', 'co_recalled')),\n strength real NOT NULL DEFAULT 0.3\n CHECK (strength >= 0.0 AND strength <= 1.0),\n last_activated timestamptz,\n metadata jsonb NOT NULL DEFAULT '{}',\n created_at timestamptz NOT NULL DEFAULT now(),\n\n CONSTRAINT uq_association_pair UNIQUE (source_id, target_id, edge_type)\n);\n\nCREATE INDEX IF NOT EXISTS idx_assoc_source_strength\n ON memory_associations(source_id, strength DESC);\nCREATE INDEX IF NOT EXISTS idx_assoc_target_strength\n ON memory_associations(target_id, strength DESC);\nCREATE INDEX IF NOT EXISTS idx_assoc_source_type_strength\n ON memory_associations(source_id, edge_type, strength DESC);\nCREATE INDEX IF NOT EXISTS idx_assoc_prune\n ON memory_associations(strength, last_activated)\n WHERE strength < 0.1;\n\n-- Consolidation Run Tracking\nCREATE TABLE IF NOT EXISTS consolidation_runs (\n id uuid PRIMARY KEY DEFAULT gen_random_uuid(),\n cycle text NOT NULL\n CHECK (cycle IN ('light', 'deep', 'dream', 'decay')),\n started_at timestamptz NOT NULL DEFAULT now(),\n completed_at timestamptz,\n status text NOT NULL DEFAULT 'running'\n CHECK (status IN ('running', 'completed', 'failed')),\n metadata jsonb NOT NULL DEFAULT '{}'\n);\n\nCREATE INDEX IF NOT EXISTS idx_consolidation_runs_status\n ON consolidation_runs(status, started_at DESC);\n\n-- Sensory Buffer Persistence\nCREATE TABLE IF NOT EXISTS sensory_snapshots (\n session_id text PRIMARY KEY,\n snapshot jsonb NOT NULL DEFAULT '{}',\n saved_at timestamptz NOT NULL DEFAULT now()\n);\n\n-- Schema migrations tracking\nCREATE TABLE IF NOT EXISTS schema_migrations (\n version text PRIMARY KEY,\n applied_at timestamptz NOT NULL DEFAULT now(),\n checksum text NOT NULL\n);\n\nINSERT INTO schema_migrations (version, checksum) VALUES\n ('005_engram_tables', md5('005'))\nON CONFLICT DO NOTHING;\n";
12
+ /**
13
+ * Migration 006: RPC functions — engram_recall, engram_association_walk,
14
+ * engram_record_access, engram_upsert_co_recalled, engram_decay_pass,
15
+ * engram_dream_cycle.
16
+ */
17
+ export declare const MIGRATION_006 = "\n-- Migration 006: RPC Functions\n\n-- Unified recall across all memory types\nCREATE OR REPLACE FUNCTION engram_recall(\n p_query_embedding vector,\n p_session_id text DEFAULT NULL,\n p_match_count int DEFAULT 10,\n p_min_similarity float DEFAULT 0.3,\n p_include_episodes bool DEFAULT true,\n p_include_digests bool DEFAULT true,\n p_include_semantic bool DEFAULT true,\n p_include_procedural bool DEFAULT true\n)\nRETURNS TABLE (\n id uuid,\n memory_type text,\n content text,\n salience float,\n access_count int,\n created_at timestamptz,\n similarity float,\n entities text[]\n)\nLANGUAGE sql STABLE PARALLEL SAFE\nSECURITY DEFINER\nSET search_path = public\nAS $$\n SELECT id, 'episode'::text, content, salience::float, access_count,\n created_at, (1-(embedding<=>p_query_embedding))::float, entities\n FROM memory_episodes\n WHERE p_include_episodes AND embedding IS NOT NULL\n AND (p_session_id IS NULL OR session_id = p_session_id)\n AND (1-(embedding<=>p_query_embedding)) >= p_min_similarity\n ORDER BY embedding<=>p_query_embedding\n LIMIT p_match_count\n UNION ALL\n SELECT id, 'digest'::text, summary, 0.5::float, 0,\n created_at, (1-(embedding<=>p_query_embedding))::float, key_topics\n FROM memory_digests\n WHERE p_include_digests AND embedding IS NOT NULL\n AND (1-(embedding<=>p_query_embedding)) >= p_min_similarity\n ORDER BY embedding<=>p_query_embedding\n LIMIT p_match_count\n UNION ALL\n SELECT id, 'semantic'::text, content, confidence::float, access_count,\n created_at, (1-(embedding<=>p_query_embedding))::float, ARRAY[]::text[]\n FROM memory_semantic\n WHERE p_include_semantic AND embedding IS NOT NULL\n AND superseded_by IS NULL\n AND (1-(embedding<=>p_query_embedding)) >= p_min_similarity\n ORDER BY embedding<=>p_query_embedding\n LIMIT p_match_count\n UNION ALL\n SELECT id, 'procedural'::text, procedure, confidence::float, access_count,\n created_at, (1-(embedding<=>p_query_embedding))::float, ARRAY[]::text[]\n FROM memory_procedural\n WHERE p_include_procedural AND embedding IS NOT NULL\n AND (1-(embedding<=>p_query_embedding)) >= p_min_similarity\n ORDER BY embedding<=>p_query_embedding\n LIMIT p_match_count\n$$;\n\n-- Association walk (single SQL call, replaces N+1 queries)\nCREATE OR REPLACE FUNCTION engram_association_walk(\n p_seed_ids uuid[],\n p_max_hops int DEFAULT 2,\n p_min_strength float DEFAULT 0.2,\n p_limit int DEFAULT 20\n)\nRETURNS TABLE (\n memory_id uuid,\n memory_type text,\n depth int,\n path_strength float\n)\nLANGUAGE sql STABLE\nSECURITY DEFINER\nSET search_path = public\nAS $$\n WITH RECURSIVE walk AS (\n SELECT\n s.id AS memory_id,\n NULL::text AS memory_type,\n 0 AS depth,\n ARRAY[s.id] AS visited_ids,\n 1.0::float AS path_strength\n FROM unnest(p_seed_ids) AS s(id)\n\n UNION ALL\n\n SELECT\n CASE WHEN a.source_id = w.memory_id THEN a.target_id ELSE a.source_id END,\n CASE WHEN a.source_id = w.memory_id THEN a.target_type ELSE a.source_type END,\n w.depth + 1,\n w.visited_ids ||\n (CASE WHEN a.source_id = w.memory_id THEN a.target_id ELSE a.source_id END),\n (w.path_strength * a.strength)::float\n FROM walk w\n JOIN memory_associations a ON\n (a.source_id = w.memory_id OR a.target_id = w.memory_id)\n WHERE\n w.depth < p_max_hops\n AND a.strength >= p_min_strength\n AND NOT (\n CASE WHEN a.source_id = w.memory_id THEN a.target_id\n ELSE a.source_id END\n ) = ANY(w.visited_ids)\n )\n SELECT DISTINCT ON (memory_id)\n memory_id,\n memory_type,\n depth,\n path_strength\n FROM walk\n WHERE depth > 0\n ORDER BY memory_id, path_strength DESC, depth ASC\n LIMIT p_limit;\n$$;\n\n-- Atomic reconsolidation update\nCREATE OR REPLACE FUNCTION engram_record_access(\n p_id uuid,\n p_memory_type text,\n p_conf_boost float DEFAULT 0.0\n)\nRETURNS void\nLANGUAGE plpgsql\nSECURITY DEFINER\nSET search_path = public\nAS $$\nBEGIN\n IF p_memory_type = 'episode' THEN\n UPDATE memory_episodes\n SET access_count = access_count + 1,\n last_accessed = now()\n WHERE id = p_id;\n\n ELSIF p_memory_type = 'semantic' THEN\n UPDATE memory_semantic\n SET access_count = access_count + 1,\n last_accessed = now(),\n confidence = LEAST(1.0, confidence + p_conf_boost),\n updated_at = now()\n WHERE id = p_id;\n\n ELSIF p_memory_type = 'procedural' THEN\n UPDATE memory_procedural\n SET access_count = access_count + 1,\n last_accessed = now(),\n confidence = LEAST(1.0, confidence + p_conf_boost),\n updated_at = now()\n WHERE id = p_id;\n END IF;\nEND;\n$$;\n\n-- Upsert co_recalled association\nCREATE OR REPLACE FUNCTION engram_upsert_co_recalled(\n p_source_id uuid,\n p_source_type text,\n p_target_id uuid,\n p_target_type text\n)\nRETURNS void\nLANGUAGE sql\nSECURITY DEFINER\nSET search_path = public\nAS $$\n INSERT INTO memory_associations\n (source_id, source_type, target_id, target_type, edge_type, strength, last_activated)\n VALUES\n (p_source_id, p_source_type, p_target_id, p_target_type, 'co_recalled', 0.2, now())\n ON CONFLICT (source_id, target_id, edge_type) DO UPDATE SET\n strength = LEAST(1.0, memory_associations.strength + 0.1),\n last_activated = now();\n$$;\n\n-- Batch decay pass\nCREATE OR REPLACE FUNCTION engram_decay_pass(\n p_semantic_decay_rate float DEFAULT 0.02,\n p_procedural_decay_rate float DEFAULT 0.01,\n p_semantic_days int DEFAULT 30,\n p_procedural_days int DEFAULT 60,\n p_edge_prune_strength float DEFAULT 0.05,\n p_edge_prune_days int DEFAULT 90\n)\nRETURNS TABLE (\n semantic_decayed int,\n procedural_decayed int,\n edges_pruned int\n)\nLANGUAGE plpgsql\nSECURITY DEFINER\nSET search_path = public\nAS $$\nDECLARE\n v_semantic_decayed int;\n v_procedural_decayed int;\n v_edges_pruned int;\nBEGIN\n UPDATE memory_semantic\n SET confidence = GREATEST(0.05, confidence - p_semantic_decay_rate),\n updated_at = now()\n WHERE confidence > 0.05\n AND (last_accessed IS NULL OR last_accessed < now() - (p_semantic_days || ' days')::interval);\n GET DIAGNOSTICS v_semantic_decayed = ROW_COUNT;\n\n UPDATE memory_procedural\n SET confidence = GREATEST(0.05, confidence - p_procedural_decay_rate),\n updated_at = now()\n WHERE confidence > 0.05\n AND (last_accessed IS NULL OR last_accessed < now() - (p_procedural_days || ' days')::interval);\n GET DIAGNOSTICS v_procedural_decayed = ROW_COUNT;\n\n DELETE FROM memory_associations\n WHERE strength < p_edge_prune_strength\n AND (last_activated IS NULL OR\n last_activated < now() - (p_edge_prune_days || ' days')::interval);\n GET DIAGNOSTICS v_edges_pruned = ROW_COUNT;\n\n RETURN QUERY SELECT v_semantic_decayed, v_procedural_decayed, v_edges_pruned;\nEND;\n$$;\n\n-- Dream cycle: SQL-side entity co-occurrence\nCREATE OR REPLACE FUNCTION engram_dream_cycle(\n p_days_lookback int DEFAULT 30,\n p_max_new_associations int DEFAULT 50\n)\nRETURNS TABLE (\n source_id uuid,\n source_type text,\n target_id uuid,\n target_type text,\n shared_entity text,\n entity_count int\n)\nLANGUAGE sql\nSECURITY DEFINER\nSET search_path = public\nAS $$\n WITH entity_memories AS (\n SELECT e.id AS memory_id, 'episode'::text AS memory_type,\n LOWER(unnest(e.entities)) AS entity\n FROM memory_episodes e\n WHERE e.created_at >= now() - (p_days_lookback || ' days')::interval\n AND array_length(e.entities, 1) > 0\n\n UNION ALL\n\n SELECT s.id, 'semantic'::text, LOWER(s.topic)\n FROM memory_semantic s\n WHERE s.created_at >= now() - (p_days_lookback || ' days')::interval\n ),\n pairs AS (\n SELECT\n em1.memory_id AS source_id,\n em1.memory_type AS source_type,\n em2.memory_id AS target_id,\n em2.memory_type AS target_type,\n em1.entity AS shared_entity,\n COUNT(*) AS entity_count\n FROM entity_memories em1\n JOIN entity_memories em2\n ON em1.entity = em2.entity\n AND em1.memory_id < em2.memory_id\n WHERE NOT EXISTS (\n SELECT 1 FROM memory_associations a\n WHERE (a.source_id = em1.memory_id AND a.target_id = em2.memory_id)\n OR (a.source_id = em2.memory_id AND a.target_id = em1.memory_id)\n )\n GROUP BY em1.memory_id, em1.memory_type, em2.memory_id, em2.memory_type, em1.entity\n )\n SELECT source_id, source_type, target_id, target_type, shared_entity, entity_count::int\n FROM pairs\n ORDER BY entity_count DESC, source_id, target_id\n LIMIT p_max_new_associations;\n$$;\n\nINSERT INTO schema_migrations (version, checksum)\nVALUES ('006_rpc_functions', md5('006'))\nON CONFLICT DO NOTHING;\n";
18
+ /**
19
+ * Migration 007: Row Level Security policies and GRANT statements.
20
+ */
21
+ export declare const MIGRATION_007 = "\n-- Migration 007: RLS policies\n\nALTER TABLE memories ENABLE ROW LEVEL SECURITY;\nALTER TABLE memory_episodes ENABLE ROW LEVEL SECURITY;\nALTER TABLE memory_digests ENABLE ROW LEVEL SECURITY;\nALTER TABLE memory_semantic ENABLE ROW LEVEL SECURITY;\nALTER TABLE memory_procedural ENABLE ROW LEVEL SECURITY;\nALTER TABLE memory_associations ENABLE ROW LEVEL SECURITY;\nALTER TABLE consolidation_runs ENABLE ROW LEVEL SECURITY;\nALTER TABLE sensory_snapshots ENABLE ROW LEVEL SECURITY;\n\n-- Option A: Supabase Auth (user-specific memory isolation)\nCREATE POLICY episodes_auth_policy ON memory_episodes\n FOR ALL USING (\n session_id LIKE ('agent:' || auth.uid()::text || ':%')\n OR auth.role() = 'service_role'\n );\n\nCREATE POLICY digests_auth_policy ON memory_digests\n FOR ALL USING (\n session_id LIKE ('agent:' || auth.uid()::text || ':%')\n OR auth.role() = 'service_role'\n );\n\n-- Semantic, procedural, associations: cross-session, service role only\nCREATE POLICY semantic_auth_policy ON memory_semantic\n FOR ALL USING (auth.role() = 'service_role');\n\nCREATE POLICY procedural_auth_policy ON memory_procedural\n FOR ALL USING (auth.role() = 'service_role');\n\nCREATE POLICY associations_auth_policy ON memory_associations\n FOR ALL USING (auth.role() = 'service_role');\n\nCREATE POLICY consolidation_runs_policy ON consolidation_runs\n FOR ALL USING (auth.role() = 'service_role');\n\nCREATE POLICY sensory_snapshots_policy ON sensory_snapshots\n FOR ALL USING (\n session_id LIKE ('agent:' || auth.uid()::text || ':%')\n OR auth.role() = 'service_role'\n );\n\nCREATE POLICY memories_policy ON memories\n FOR ALL USING (auth.role() = 'service_role');\n\n-- Grant execute on RPC functions\nGRANT EXECUTE ON FUNCTION engram_recall TO authenticated, service_role;\nGRANT EXECUTE ON FUNCTION engram_association_walk TO authenticated, service_role;\nGRANT EXECUTE ON FUNCTION engram_record_access TO authenticated, service_role;\nGRANT EXECUTE ON FUNCTION engram_upsert_co_recalled TO authenticated, service_role;\nGRANT EXECUTE ON FUNCTION engram_decay_pass TO service_role;\nGRANT EXECUTE ON FUNCTION engram_dream_cycle TO service_role;\nGRANT EXECUTE ON FUNCTION update_updated_at_column TO service_role;\n\nINSERT INTO schema_migrations (version, checksum)\nVALUES ('007_rls_policies', md5('007'))\nON CONFLICT DO NOTHING;\n";
22
+ /**
23
+ * Returns all migration SQL concatenated in order.
24
+ * Apply this to a fresh Supabase project or run each migration individually.
25
+ */
26
+ export declare function getMigrationSQL(): string;
27
+ //# sourceMappingURL=migrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,eAAO,MAAM,aAAa,k/DA+CzB,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,wsSAoOzB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,+0RA2RzB,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,q3EA2DzB,CAAA;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC"}