@cerefox/memory 1.7.1 → 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.
@@ -7438,7 +7438,7 @@ var exports_meta = {};
7438
7438
  __export(exports_meta, {
7439
7439
  PKG_VERSION: () => PKG_VERSION
7440
7440
  });
7441
- var PKG_VERSION = "1.7.1";
7441
+ var PKG_VERSION = "1.8.0";
7442
7442
  var init_meta = () => {};
7443
7443
 
7444
7444
  // ../../_shared/config/paths.ts
@@ -25743,7 +25743,7 @@ var init_bundled_docs = __esm(() => {
25743
25743
  });
25744
25744
 
25745
25745
  // ../../_shared/ef-meta/index.ts
25746
- var EF_VERSION = "1.7.1", CEREFOX_VERSION = "1.7.1", EF_LAST_CHANGED = "1.7.0";
25746
+ var EF_VERSION = "1.8.0", CEREFOX_VERSION = "1.8.0", EF_LAST_CHANGED = "1.7.0";
25747
25747
  var init_ef_meta = () => {};
25748
25748
 
25749
25749
  // ../../_shared/compatibility/index.ts
@@ -72988,7 +72988,13 @@ async function migrationStatus(opts) {
72988
72988
  }
72989
72989
  async function runDbMigrate(opts) {
72990
72990
  const log = opts.log ?? (() => {});
72991
- const sql = src_default(opts.dbUrl, { prepare: false, onnotice: () => {} });
72991
+ const sql = src_default(opts.dbUrl, {
72992
+ prepare: false,
72993
+ onnotice: (n) => {
72994
+ if (n.message)
72995
+ log(` ↳ ${n.message}`);
72996
+ }
72997
+ });
72992
72998
  try {
72993
72999
  await sql.unsafe(BOOTSTRAP_MIGRATIONS_SQL);
72994
73000
  const allFiles = listMigrationFiles(opts.assets.migrationsDir);
@@ -77860,6 +77866,10 @@ async function action17(options) {
77860
77866
  }
77861
77867
  if (remediation) {
77862
77868
  println(cErr.yellow("→ " + remediation));
77869
+ const configDir = (process.env.CEREFOX_CONFIG_DIR ?? "").trim();
77870
+ if (configDir) {
77871
+ println(cErr.dim(` (this doctor ran against CEREFOX_CONFIG_DIR=${configDir} — ` + `prefix the command above the same way, or use your environment alias, ` + `or a bare \`cerefox\` will act on your DEFAULT environment instead)`));
77872
+ }
77863
77873
  println("");
77864
77874
  }
77865
77875
  }
@@ -18,7 +18,7 @@
18
18
  * doesn't touch `supabase/functions/` leaves it alone).
19
19
  */
20
20
 
21
- export const EF_VERSION = "1.7.1";
21
+ export const EF_VERSION = "1.8.0";
22
22
 
23
23
  /**
24
24
  * The Cerefox RELEASE version — what `cerefox --version` reports and what npm
@@ -36,7 +36,7 @@ export const EF_VERSION = "1.7.1";
36
36
  * is imported by the Deno Edge Functions, which cannot reach into the npm
37
37
  * package.
38
38
  */
39
- export const CEREFOX_VERSION = "1.7.1";
39
+ export const CEREFOX_VERSION = "1.8.0";
40
40
 
41
41
  /**
42
42
  * The most recent version whose EF-side SOURCE actually changed (#127).
@@ -5,7 +5,8 @@
5
5
  -- replaces the SAME signature), leaving long-lived databases with BOTH
6
6
  -- overloads. A named 1-arg call is then ambiguous — PostgREST PGRST203
7
7
  -- ("could not choose the best candidate") — which is how the first
8
- -- production acceptance run failed to purge its fixtures (v1.7.0).
8
+ -- acceptance run against a long-lived database failed to purge its
9
+ -- fixtures (v1.7.0).
9
10
  -- Fresh databases never had the old signatures and are unaffected.
10
11
  --
11
12
  -- Schema version 0.12.0 → 0.12.1. The DROPs also run from rpcs.sql on every
@@ -0,0 +1,172 @@
1
+ -- 0027_drop_archived_search_artifacts.sql — archived chunks carry no search
2
+ -- artifacts (#216). THE CANONICAL RATIONALE LIVES HERE; code comments
3
+ -- reference it.
4
+ --
5
+ -- Search is current-chunks-only by design (every search index is partial on
6
+ -- version_id IS NULL), version reconstruction and diffs read `content`, and
7
+ -- restoring an old version is deliberately manual re-ingest (which
8
+ -- re-embeds). Embeddings and fts on archived chunks were therefore never
9
+ -- readable by anything — pure storage cost, measured at ~30-45% of the chunk
10
+ -- relation on long-lived stores — and after a reindex they are stale for the
11
+ -- current embedder besides. There is no config and no maintenance command:
12
+ -- with nothing able to read the artifacts, this is an invariant, not a
13
+ -- policy. The archived content — the actual safety copy — is untouched.
14
+ --
15
+ -- Four parts:
16
+ -- 1. embedding_primary becomes nullable, WITH a replacement guard: a CHECK
17
+ -- that a CURRENT chunk always carries an embedding. Without it, a short
18
+ -- embedding-API response could insert a silently search-invisible chunk
19
+ -- where the old NOT NULL failed loudly (review round 6).
20
+ -- 2. The NEW cerefox_snapshot_version ships INSIDE this migration (repo
21
+ -- precedent: 0005-0008, 0011, 0025 carry function bodies). This closes
22
+ -- two windows where the OLD snapshot could keep archiving WITH
23
+ -- artifacts after 0027 was stamped: `bun scripts/db_migrate.ts` (which
24
+ -- never refreshes rpcs.sql), and a `server deploy` that failed between
25
+ -- the migration step and the RPC refresh.
26
+ -- 3. Back-fill: strip artifacts from existing archived rows, reporting
27
+ -- rows and bytes. embedder_upgrade is nulled with its vector;
28
+ -- embedder_primary is deliberately kept (NOT NULL, harmless provenance).
29
+ -- 4. Space note: Postgres frees the bytes for REUSE via autovacuum rather
30
+ -- than shrinking files immediately — growth stops even if the reported
31
+ -- database size does not drop the same day.
32
+ --
33
+ -- Schema version 0.12.2 → 0.13.0.
34
+
35
+ ALTER TABLE cerefox_chunks ALTER COLUMN embedding_primary DROP NOT NULL;
36
+
37
+ DO $$
38
+ BEGIN
39
+ IF NOT EXISTS (
40
+ SELECT 1 FROM pg_constraint WHERE conname = 'cerefox_chunks_current_has_embedding'
41
+ ) THEN
42
+ ALTER TABLE cerefox_chunks
43
+ ADD CONSTRAINT cerefox_chunks_current_has_embedding
44
+ CHECK (version_id IS NOT NULL OR embedding_primary IS NOT NULL);
45
+ END IF;
46
+ END $$;
47
+
48
+ -- The new snapshot (identical to the rpcs.sql copy this release ships):
49
+
50
+ DROP FUNCTION IF EXISTS cerefox_snapshot_version(UUID, TEXT, INT);
51
+ DROP FUNCTION IF EXISTS cerefox_snapshot_version(UUID, TEXT, INT, BOOLEAN);
52
+ CREATE FUNCTION cerefox_snapshot_version(
53
+ p_document_id UUID,
54
+ p_source TEXT DEFAULT 'manual',
55
+ -- NULL (the new default) means "use the store's policy from
56
+ -- cerefox_config". Passing a value still overrides, for deliberate one-off
57
+ -- admin operations — but callers no longer supply one by accident.
58
+ p_retention_hours INT DEFAULT NULL,
59
+ p_cleanup_enabled BOOLEAN DEFAULT NULL
60
+ )
61
+ RETURNS TABLE (
62
+ version_id UUID,
63
+ version_number INT,
64
+ chunk_count INT,
65
+ total_chars INT
66
+ )
67
+ LANGUAGE plpgsql
68
+ SECURITY DEFINER
69
+ SET search_path = public, pg_catalog
70
+ AS $$
71
+ DECLARE
72
+ v_version_id UUID;
73
+ v_version_number INT;
74
+ v_chunk_count INT;
75
+ v_total_chars INT;
76
+ -- Resolve the retention policy from the STORE, not the caller.
77
+ --
78
+ -- These used to arrive as parameters filled from each client's own env, so
79
+ -- the surviving version history depended on which client wrote last: an
80
+ -- agent running defaults would prune versions that an operator had
81
+ -- configured to keep. Retention describes the data, so it belongs to the
82
+ -- data. Same COALESCE(param, config, default) shape the retrieval tunables
83
+ -- already use.
84
+ v_retention INT := COALESCE(p_retention_hours,
85
+ cerefox_config_int('version_retention_hours', 120));
86
+ v_cleanup BOOLEAN := COALESCE(p_cleanup_enabled,
87
+ cerefox_config_bool('version_cleanup_enabled', TRUE));
88
+ BEGIN
89
+ -- Count current chunks to record in the version metadata
90
+ SELECT COUNT(*), COALESCE(SUM(char_count), 0)
91
+ INTO v_chunk_count, v_total_chars
92
+ FROM cerefox_chunks c
93
+ WHERE c.document_id = p_document_id
94
+ AND c.version_id IS NULL;
95
+
96
+ -- Compute the next version number (sequential per document)
97
+ SELECT COALESCE(MAX(dv.version_number), 0) + 1
98
+ INTO v_version_number
99
+ FROM cerefox_document_versions dv
100
+ WHERE dv.document_id = p_document_id;
101
+
102
+ -- Create the version row
103
+ INSERT INTO cerefox_document_versions (
104
+ document_id, version_number, source, chunk_count, total_chars
105
+ ) VALUES (
106
+ p_document_id, v_version_number, p_source, v_chunk_count, v_total_chars
107
+ )
108
+ RETURNING id INTO v_version_id;
109
+
110
+ -- Archive all current chunks by pointing them at the new version, and
111
+ -- NULL their search artifacts in the same write (0.13.0, #216 — full
112
+ -- rationale in migration 0027). The content — the actual safety copy —
113
+ -- is untouched. embedder_upgrade is nulled with its vector;
114
+ -- embedder_primary is deliberately KEPT (it is NOT NULL, and the label
115
+ -- is harmless provenance for a vector that no longer exists — nothing
116
+ -- reads embedder columns without a version_id IS NULL filter).
117
+ UPDATE cerefox_chunks c
118
+ SET version_id = v_version_id,
119
+ embedding_primary = NULL,
120
+ embedding_upgrade = NULL,
121
+ embedder_upgrade = NULL,
122
+ fts = NULL
123
+ WHERE c.document_id = p_document_id
124
+ AND c.version_id IS NULL;
125
+
126
+ -- Lazy retention: delete versions outside the retention window,
127
+ -- but always keep the most recently created version (the one we just made).
128
+ -- Skip archived versions (archived=true) -- they are protected from cleanup.
129
+ -- Skip cleanup entirely if p_cleanup_enabled is false (immutable mode).
130
+ IF v_cleanup THEN
131
+ DELETE FROM cerefox_document_versions dv
132
+ WHERE dv.document_id = p_document_id
133
+ AND dv.archived IS NOT TRUE
134
+ AND dv.created_at < NOW() - (v_retention || ' hours')::INTERVAL
135
+ AND dv.id != (
136
+ SELECT id FROM cerefox_document_versions
137
+ WHERE document_id = p_document_id
138
+ ORDER BY created_at DESC
139
+ LIMIT 1
140
+ );
141
+ END IF;
142
+
143
+ RETURN QUERY SELECT v_version_id, v_version_number, v_chunk_count, v_total_chars;
144
+ END;
145
+ $$;
146
+
147
+ DO $$
148
+ DECLARE
149
+ v_rows INT;
150
+ v_bytes BIGINT;
151
+ BEGIN
152
+ SELECT count(*),
153
+ COALESCE(SUM(COALESCE(pg_column_size(embedding_primary), 0))
154
+ + SUM(COALESCE(pg_column_size(embedding_upgrade), 0))
155
+ + SUM(COALESCE(pg_column_size(fts), 0)), 0)
156
+ INTO v_rows, v_bytes
157
+ FROM cerefox_chunks
158
+ WHERE version_id IS NOT NULL
159
+ AND (embedding_primary IS NOT NULL OR embedding_upgrade IS NOT NULL OR fts IS NOT NULL);
160
+
161
+ UPDATE cerefox_chunks
162
+ SET embedding_primary = NULL,
163
+ embedding_upgrade = NULL,
164
+ embedder_upgrade = NULL,
165
+ fts = NULL
166
+ WHERE version_id IS NOT NULL
167
+ AND (embedding_primary IS NOT NULL OR embedding_upgrade IS NOT NULL OR fts IS NOT NULL);
168
+
169
+ RAISE NOTICE
170
+ 'Migration 0027: stripped search artifacts from % archived chunk row(s), freeing ~% for reuse. Archived content is untouched; current chunks keep their embeddings.',
171
+ v_rows, pg_size_pretty(v_bytes);
172
+ END $$;
@@ -1009,9 +1009,19 @@ BEGIN
1009
1009
  )
1010
1010
  RETURNING id INTO v_version_id;
1011
1011
 
1012
- -- Archive all current chunks by pointing them at the new version
1012
+ -- Archive all current chunks by pointing them at the new version, and
1013
+ -- NULL their search artifacts in the same write (0.13.0, #216 — full
1014
+ -- rationale in migration 0027). The content — the actual safety copy —
1015
+ -- is untouched. embedder_upgrade is nulled with its vector;
1016
+ -- embedder_primary is deliberately KEPT (it is NOT NULL, and the label
1017
+ -- is harmless provenance for a vector that no longer exists — nothing
1018
+ -- reads embedder columns without a version_id IS NULL filter).
1013
1019
  UPDATE cerefox_chunks c
1014
- SET version_id = v_version_id
1020
+ SET version_id = v_version_id,
1021
+ embedding_primary = NULL,
1022
+ embedding_upgrade = NULL,
1023
+ embedder_upgrade = NULL,
1024
+ fts = NULL
1015
1025
  WHERE c.document_id = p_document_id
1016
1026
  AND c.version_id IS NULL;
1017
1027
 
@@ -1248,7 +1258,7 @@ $$;
1248
1258
  DROP FUNCTION IF EXISTS cerefox_restore_document(UUID, TEXT, TEXT, TEXT);
1249
1259
  DROP FUNCTION IF EXISTS cerefox_restore_document(UUID, TEXT, TEXT);
1250
1260
  -- 0.12.1: the pre-author 1-arg overload survived every CREATE OR REPLACE
1251
- -- since the signature grew (same orphan class as purge; found live on prod).
1261
+ -- since the signature grew (same orphan class as purge; found live on a long-lived database).
1252
1262
  DROP FUNCTION IF EXISTS cerefox_restore_document(UUID);
1253
1263
  CREATE FUNCTION cerefox_restore_document(
1254
1264
  p_document_id UUID,
@@ -1829,9 +1839,9 @@ $$;
1829
1839
  -- Reads chunk title and content directly from the DB -- caller only needs to
1830
1840
  -- supply the new document title.
1831
1841
  --
1832
- -- Only affects current chunks (version_id IS NULL). Archived chunks retain their
1833
- -- original tsvectors (they are excluded from all search indexes and require
1834
- -- re-ingestion to restore anyway).
1842
+ -- Only affects current chunks (version_id IS NULL). Archived chunks carry NO
1843
+ -- tsvector at all since 0.13.0 (#216) fts is nulled at archive time, and a
1844
+ -- restore is a re-ingest that recomputes everything.
1835
1845
 
1836
1846
  DROP FUNCTION IF EXISTS cerefox_update_chunk_fts(UUID, TEXT);
1837
1847
  CREATE FUNCTION cerefox_update_chunk_fts(
@@ -2788,6 +2798,10 @@ SET search_path = public, pg_catalog
2788
2798
  AS $$
2789
2799
  -- Keep in lockstep with the `@version:` marker in schema.sql (cut_release.ts
2790
2800
  -- enforces it). Bump whenever schema.sql OR rpcs.sql changes.
2801
+ -- 0.13.0 (#216): archived chunks carry no search artifacts —
2802
+ -- cerefox_snapshot_version nulls embedding_primary/embedding_upgrade/fts
2803
+ -- at archive time; embedding_primary becomes nullable; migration 0027
2804
+ -- strips existing archived rows.
2791
2805
  -- 0.12.2 (#212, #214): metadata must be a JSON object (ingest input guard
2792
2806
  -- + set_document_metadata stored-state merge guard); cerefox_find_dead_links
2793
2807
  -- (link-integrity phase-2 sweep); cerefox_metadata_health (doctor check).
@@ -2806,7 +2820,7 @@ AS $$
2806
2820
  -- 0.11.0 supersedes 0.10.6 (v1.2.1, #191): this branch carries that fix plus
2807
2821
  -- the partial-edit surface, and both migrations (0019, 0020) are in the
2808
2822
  -- sequence, so a store deploying this gets everything from both lines.
2809
- SELECT '0.12.2'::TEXT;
2823
+ SELECT '0.13.0'::TEXT;
2810
2824
  $$;
2811
2825
 
2812
2826
  -- ── cerefox_find_dead_links ──────────────────────────────────────────────────
@@ -5,7 +5,7 @@
5
5
  -- Requires extensions: vector (pgvector), uuid-ossp
6
6
  -- These are enabled at the top of db_deploy.py before this file is applied.
7
7
  --
8
- -- @version: 0.12.2
8
+ -- @version: 0.13.0
9
9
  -- The `@version` marker above is read by the schema-version-mismatch banner
10
10
  -- (see /api/v1/schema-version). Bump it whenever schema.sql OR rpcs.sql
11
11
  -- changes in a way that requires `cerefox server deploy` to be re-run —
@@ -212,8 +212,16 @@ CREATE TABLE IF NOT EXISTS cerefox_chunks (
212
212
  -- reconstructs with its OWN format. See docs/guides/content-format.md.
213
213
  content_format SMALLINT NOT NULL DEFAULT 1,
214
214
 
215
- -- Primary embedding: always computed, cloud API (default: OpenAI text-embedding-3-small)
216
- embedding_primary VECTOR(768) NOT NULL,
215
+ -- Primary embedding: always computed on write, cloud API (default: OpenAI
216
+ -- text-embedding-3-small). NULL only on ARCHIVED rows (0.13.0, #216 —
217
+ -- rationale in migration 0027): archiving nulls the search artifacts.
218
+ -- The CHECK below preserves the pre-0.13.0 guarantee that a CURRENT
219
+ -- chunk always has an embedding — without it, a short embedding-API
220
+ -- response could insert a silently search-invisible chunk where the old
221
+ -- NOT NULL failed loudly.
222
+ embedding_primary VECTOR(768)
223
+ CONSTRAINT cerefox_chunks_current_has_embedding
224
+ CHECK (version_id IS NOT NULL OR embedding_primary IS NOT NULL),
217
225
  -- Upgrade embedding: optional, alternative model (Fireworks, Vertex, etc.)
218
226
  embedding_upgrade VECTOR(768),
219
227
 
@@ -150,6 +150,15 @@ knowing about:
150
150
  you're coming from a pre-installer 0.1.x clone, see the "old pre-installer
151
151
  clone" note above: install the package and run `cerefox init`.
152
152
 
153
+ ## Notable: v1.8.0 storage reclaim (migration 0027)
154
+
155
+ Upgrading to v1.8.0 strips never-read search artifacts from archived version
156
+ chunks (rationale: #216 / the migration's own header) and the migration
157
+ prints what it freed. Postgres releases the bytes for **reuse** via
158
+ autovacuum rather than shrinking files immediately, so expect growth to stop
159
+ rather than the reported database size to drop the same day. Archived
160
+ version *content* is untouched.
161
+
153
162
  ## After upgrading: AI agents
154
163
 
155
164
  New tools and updated tool signatures are picked up by MCP clients in **new
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cerefox/memory",
3
- "version": "1.7.1",
3
+ "version": "1.8.0",
4
4
  "description": "Cerefox — user-owned shared memory for AI agents. CLI + stdio MCP server + web UI + ingestion for a knowledge base on your own Supabase project (or fully self-hosted with Cerefox Local).",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/fstamatelopoulos/cerefox",
@@ -65,7 +65,7 @@
65
65
  "typescript": "^6.0.3"
66
66
  },
67
67
  "scripts": {
68
- "build": "bun build src/bin/cerefox.ts --outdir dist/bin --target node --format esm --external @huggingface/transformers --external onnxruntime-node",
68
+ "build": "bun run bundle-server-assets && bun build src/bin/cerefox.ts --outdir dist/bin --target node --format esm --external @huggingface/transformers --external onnxruntime-node",
69
69
  "clean": "rm -rf dist docs AGENT_GUIDE.md AGENT_QUICK_REFERENCE.md",
70
70
  "bundle-docs": "bun run ../../scripts/bundle_package_docs.ts",
71
71
  "bundle-server-assets": "bun run ../../scripts/bundle_server_assets.ts",