@@ -5,217 +5,48 @@ All notable changes to `@corbits/memory` are documented in this file.
55The format is based on [ Keep a Changelog] ( https://keepachangelog.com/en/1.1.0/ ) ,
66and this project adheres to [ Semantic Versioning] ( https://semver.org/spec/v2.0.0.html ) .
77
8- ## [ Unreleased ]
8+ ## [ 0.2.0 ] — 2026-09-25
99
1010### Changed
1111
12- - ` @intx/* ` , ` drizzle-orm ` , ` hono ` , ` hono-openapi ` and ` postgres ` are peer
13- dependencies; the host supplies them. ` engines ` is removed.
12+ - ** Breaking:** ` @intx/* ` , ` drizzle-orm ` , ` hono ` , ` hono-openapi ` and
13+ ` postgres ` are peer dependencies; the host supplies them. ` engines ` is
14+ removed.
15+ - ** Breaking:** ` createMemoryRoutes({ memory, requireGrant, callerResolver? }) `
16+ returns the memory routes as a ` Hono<TenantEnv> ` sub-app with paths relative
17+ to its mount point; hosts mount it at ` /api/tenants/:tenantId/memory ` . It
18+ replaces ` createMemory({ app, callerResolver }) ` and ` registerMemoryRoutes ` ,
19+ and ` RouteDeps ` no longer carries ` grants ` . ` createMemory ` only builds the
20+ plane.
21+ - ** Breaking:** the package root exports only the public API. Internal
22+ services and helpers (transform, retention, feed, share materialization,
23+ corroboration, embed model registry, degrade metrics, FTS helpers), the test
24+ fakes, and ` resolveGrantConfig ` are no longer exported. The distiller stays
25+ at ` @corbits/memory/distiller ` and migrations at
26+ ` @corbits/memory/migrations ` .
27+ - ** Breaking:** ` runMemoryMigrations(config, { schema, ftsLanguage }) ` takes
28+ the same ` DBConfig ` as Interchange ` runMigrations ` instead of a database
29+ URL. ` schema ` names the host schema holding Interchange's ` tenant ` and
30+ ` principal ` tables (the value passed to ` runMigrations ` , e.g. ` "public" ` );
31+ memory's tables stay in the ` memory ` schema. ` ftsLanguage ` is required, and
32+ the runner no longer reads ` FTS_LANGUAGE ` from the environment or accepts a
33+ ` log ` option.
34+ - Every migration file is idempotent and replayed on each run, with a 5 s
35+ ` lock_timeout ` . The ` memory._migrations ` ledger is dropped.
1436- ` MEMORY_GRANT_REQUIREMENTS ` is read from ` package.json `
1537 ` interchange.grantRequirements ` , now the only declaration.
1638 ` MEMORY_CAPABILITY_IDS ` is typed ` string[] ` .
17- - ` createMemoryRoutes({ memory, requireGrant, callerResolver? }) ` returns the
18- memory routes as a ` Hono<TenantEnv> ` sub-app with paths relative to its
19- mount point; hosts mount it at ` /api/tenants/:tenantId/memory ` . It replaces
20- ` createMemory({ app }) ` and ` registerMemoryRoutes ` , and ` RouteDeps ` no
21- longer carries ` grants ` . ` createMemory ` only builds the plane.
22- - The package root exports only the public API. Internal services and
23- helpers (transform, retention, feed, share materialization, corroboration,
24- embed model registry, degrade metrics, FTS helpers), the test fakes, and
25- ` resolveGrantConfig ` are no longer exported. The distiller stays at
26- ` @corbits/memory/distiller ` and migrations at ` @corbits/memory/migrations ` .
27- - ` runMemoryMigrations(config, { schema, ftsLanguage }) ` takes the same
28- ` DBConfig ` as Interchange ` runMigrations ` instead of a database URL.
29- ` schema ` names the host schema holding Interchange's ` tenant ` and
30- ` principal ` tables (the value passed to ` runMigrations ` , e.g. ` "public" ` );
31- memory's tables stay in the ` memory ` schema. ` ftsLanguage ` is required, and
32- the runner no longer reads ` FTS_LANGUAGE ` from the environment or accepts a ` log ` option.
33- Every migration file is idempotent and replayed on each run. The
34- ` memory._migrations ` ledger is dropped.
35-
36- ### Added
37-
38- - Retention HTTP routes (CL-6288): ` POST …/memory/documents/:documentId/forget `
39- (tombstone, grant ` memory:forget ` ), ` POST …/memory/documents/:documentId/purge `
40- (hard delete, grant ` memory:purge ` ), and
41- ` POST …/memory/versions/:versionId/retention-class ` (grant ` memory:forget ` ).
42- Forget and purge are separate routes with separate grant actions — never one
43- route with a boolean flag — and both are refused with 403 unless the caller
44- is the document/version's creator, independent of any share grant that lets
45- them merely see it. ` sweepEphemeral ` stays off the HTTP surface (maintenance
46- sweep, not a user action); a host schedules it on its own cron against the
47- in-process ` Memory ` . New ` memory:forget ` / ` memory:purge ` grant requirements
48- (` source: "creator" ` ) and ` capabilityIdsForSurface() ` so distiller/tools
49- installs no longer pick up routes-only capabilities by accident.
50- - ` RouteDeps.callerResolver ` — an
51- optional host-supplied resolver from a request to a `{ tenantId,
52- principalId }` scope, for a caller that never goes through the host's
53- tenant-session middleware (e.g. a workflow-run child authenticating with
54- its own sidecar bearer token). Unset by default: every route still reads
55- identity from ` c.get("principal") ` exactly as before. When set, the
56- resolved identity is seated as the request's principal/tenant ahead of
57- ` grantGuard ` , so the same ` requireGrant ` authorization path applies to a
58- machine caller — never a separate, weaker one. Identity from the resolver
59- always wins over anything a request body claims. The resolver's return
60- value is parsed with arktype (non-empty ` tenantId ` /` principalId ` ); a
61- resolver returning a malformed identity is rejected with ` 500 ` (a host
62- bug), never seated as a garbage scope.
63-
64- ### Fixed
65-
66- - ` loadMemoryConfig ` / ` EngineConfig.embed ` no longer requires an embed
67- endpoint: a host with a pgvector Postgres and no embed endpoint now
68- constructs and serves ` add ` + lexical ` search ` . Dense retrieval is skipped
69- (not attempted-and-failed) and ` search ` reports
70- ` degraded: ["dense_unavailable", "lexical_only"] ` so the state stays
71- observable (CL-6287). ** Migration note:** ` dense_unavailable ` is now also
72- emitted on every search for a deliberately-unconfigured engine, not only on
73- a transient dense-retrieval failure — a host with an existing alert rule
74- keyed on ` dense_unavailable ` alone should also check for ` lexical_only ` in
75- the same ` degraded ` array to distinguish "opted into lexical-only" from an
76- actual regression.
77- - ` add ` 's ` degraded ` is now a reason array (` ["embed_unavailable"] ` and/or
78- ` ["embed_unavailable", "lexical_only"] ` ), matching ` search ` 's shape —
79- previously a bare boolean, which made it impossible to write one
80- "is this response degraded" check across both verbs (CL-6287). ** Breaking
81- if a host coded against the boolean:** ` degraded: true ` is now
82- ` degraded: [...] ` ; check array presence/length instead of truthiness (both
83- are still falsy/omitted when the document captured cleanly).
84- - ` Memory.capabilities.embeddingsConfigured ` (and the underlying
85- ` DocumentStore.capabilities ` ) let a host learn recall is lexical-only at
86- construction time, without issuing a search first (CL-6287).
87- - Feed ` nextCursor ` advances past the examined raw page after grant-tag
88- post-filter (a fully denied page no longer stalls the consumer forever).
89- - Distiller default system prompt uses the configured ` agentId ` for
90- ` exclude_generator ` / ` generator_agent_id ` (not only the package default id).
91-
92- ### Changed
93-
94- - ** Breaking:** ` createResidentDistiller ` runs on a ` mail ` trigger (CL-8798).
95- ` CreateResidentDistillerOpts.cron ` is replaced by a required ` mailTo ` , the
96- address the workflow's run is triggered at, and
97- ` RESIDENT_DISTILLER_CRON_DEFAULT ` is removed. The ` schedule ` trigger was
98- reserved with nothing behind it in ` @intx/workflow ` , so the distiller never
99- ticked, and 0.4.0 rejects it at ` defineWorkflow ` . Ticking is the host's job:
100- mail ` mailTo ` on a schedule, e.g. from ` @corbits/cron ` .
101- - ` @intx/* ` dependencies move to 0.4.0 (CL-8797).
102- - ** Product narrative:** default path is ** add → ingest elements → process**
103- (one host pipeline). Pull feed + ` createResidentDistiller ` are optional
104- multi-writer / backfill process helpers, not the primary ingest story.
105- See ` PRODUCT.md ` , ` docs/DISTILLER.md ` , ` docs/FEED.md ` .
106- - ** Breaking:** Postgres schema renamed from ` knowledge ` to ** ` memory ` ** . Fresh
107- installs only — drop/recreate the old schema (or rename) on existing DBs.
108- Citation ` open.type ` is now ` "memory" ` .
109- - ** Breaking:** env var is ` DATABASE_URL ` (was ` KNOWLEDGE_DATABASE_URL ` ); pass
110- ` memory.databaseUrl ` on config as an alternative. No deprecated alias.
111- - ` add ` (plane + HTTP) returns ` { documentId, versionId } ` so share-grant audit
112- and provenance can name the version that carried the write.
113- - Interchange ` defineTool ` factories at ` @corbits/memory/tools ` (` memory_add ` ,
114- ` memory_search ` , ` memory_list ` ) — HTTP clients for mounted hub routes with
115- install env ` memoryBaseUrl ` / ` memoryTenantId ` / ` memoryAuthToken ` . Declared
116- via ` package.json ` ` interchange.tools ` and ` exports["./tools"] ` .
117-
118- ### Previously
119-
120- - ** Breaking:** package and public surface renamed from ` @corbits/knowledge-engine `
121- to ` @corbits/memory ` . Public APIs: ` createMemory ` (optional ` app ` registers HTTP),
122- ` registerMemoryRoutes ` .
123-
124- ` createMemory ` , ` loadMemoryConfig ` , ` runMemoryMigrations ` , ` Memory ` ,
125- ` MemoryConfig ` , ` MemoryError ` . HTTP paths are under
126- ` /api/tenants/:tenantId/memory/ ` ; grants are
127- ` memory:add ` / ` memory:search ` ; access tags use ` memory.owner: ` / ` memory.tenant: `
128- / ` memory.space: ` . Postgres schema is ` memory ` .
129- - ** Breaking:** config reads ` DATABASE_URL ` (was ` KNOWLEDGE_DATABASE_URL ` ) for
130- the engine's own pgvector Postgres connection.
131- - ** Breaking:** memory plane surface is ` add ` / ` search ` / ` list ` with
132- ` principalId ` + ` tenantId ` only. Inference is host-owned (no answer endpoint
133- or personal-memory side-channel on the plane).
134-
135- - ** Breaking:** HTTP routes are ` POST /api/tenants/:tenantId/memory/add ` ,
136- ` POST /api/tenants/:tenantId/memory/search ` ,
137- ` GET /api/tenants/:tenantId/memory/list ` (inherits hub ` resolveTenant ` ).
138- Old unscoped ` /api/memory/* ` paths are not mounted.
139- - ** Breaking:** grant actions are ` add ` and ` search ` (was ` capture ` / ` find ` /
140- knowledge ` search ` ). ` list ` uses the ` search ` grant. Capability resource is
141- ` memory ` . Document-tag checks use action ` search ` .
142- - ** Breaking:** ` add ` returns ` { documentId } ` ; search body uses ` limit ` (not ` k ` );
143- search wire uses ` items ` (not ` hits ` ).
144- - ** Breaking:** document access is Interchange ** grant tags** (` accessTags ` +
145- creator-always + host ` GrantStore ` ), not the visibility-mode / block-list
146- mini-ACL. Share sugar only mints tags. See ` docs/AUTHZ-DOCUMENT-ACCESS.md ` .
147- - ** Breaking:** Postgres baseline is two files (` 0001_extensions ` +
148- ` 0002_memory_baseline ` ) with ` access_tags ` and no ` visibility_* ` columns.
149- Fresh installs only — drop/recreate the ` memory ` schema on existing DBs.
150- - ** Breaking:** ` grantStore ` + ` conditionRegistry ` are top-level ` createMemory `
151- options (no nested ` grants: { … } ` ).
152-
153- ### Added
154-
155- - Optional ` TextExtractor ` + ` file ` XOR ` content ` on ` add `
156- - ` share ` sugar on ` add ` (maps to access tags; principals also materialize
157- grants when the host grant store is writable); ` grantsMaterialized ` on the
158- add result when peers were requested
159- - ` access_tags ` on ` memory.document ` (baseline schema)
160- - Claim-bearing schema, temporal model, transform/replay, share grants
161- (resident memory distillation foundation — CL-5865/5866/5872/5873)
162- - Living relevancy: corroboration factor from supports/contradicts edges;
163- strong evidence gate (CL-5867). See ` docs/RELEVANCY.md `
164- - Capture feed: ` memory.feed ` + ` GET .../memory/feed ` with ` feed_seq ` cursor
165- (CL-5868). See ` docs/FEED.md `
166- - ** Wire attribution (CL-5870):** search hits carry additive ` attribution `
167- (versionId, provenance, source/temporal class, createdByKind,
168- generatorAgentId, occurredAt/validUntil, corroboration counts, derivedFrom)
169- - ** Retention (CL-5871):** ` retention_class ` on versions; plane APIs
170- ` deprecateVersion ` / ` tombstoneDocument ` / ` hardDeleteDocument ` /
171- ` sweepEphemeral ` / ` setRetentionClass ` ; ` includeDeprecated ` on search.
172- See ` docs/RETENTION.md `
173- - ** Resident distiller (CL-5869):** ` @corbits/memory/distiller ` —
174- ` createResidentDistiller({ inference }) ` schedule workflow + ` runDistillTick `
175- + ` buildDistilledClaim ` . Claim-aware ` add ` (generator_agent_id, provenance,
176- derived_from, …). ` memory_feed ` tool. Feed entries include ` accessTags ` .
177- See ` docs/DISTILLER.md `
178-
179- ### Removed
180-
181- - Host-injected generate path on the plane (use host inference + ` add ` / ` search ` )
182-
183- ## [ 0.1.2] — 2026-07-31
184-
185- ### Added
186- - Public ` createMemory ` export for out-of-band capture and search (CLI seeders, batch ingesters, tests) without mounting HTTP routes (` #8 ` )
187-
188- ### Fixed
189-
190- - Fail-closed document ACL: withhold hits whose ` acl_block ` is unreadable or non-string (` #9 ` )
191- - Timeline titles use the same document ACL post-filter as search (` #12 ` )
192- - Return 401 (not 500) when the host has not resolved a principal (` #7 ` )
193- - ACL wiring tests mock ` sql.unsafe ` for FTS verification so the suite stays green under the search path
194-
195- ## [ 0.1.1] — 2026-07-31
196-
197- ### Added
198-
199- - Halfvec expression indexes for embedding models above 2000 dimensions (up to 4000); dense search ORDER BY uses the same expression as the index (` #1 ` )
200- - Configurable lexical FTS language (` FTS_LANGUAGE ` / ` EngineConfig.ftsLanguage ` ) with parse, catalog verify, and rebuild recipe; public ` DEFAULT_FTS_LANGUAGE ` export (` #4 ` )
201- - Named chunk FK with ` ON DELETE CASCADE ` and composite tenant index on per-model embedding tables created at activation (` #3 ` )
202-
203- ### Changed
204-
205- - Dense search scales ` hnsw.ef_search ` to the overfetch limit (floor 40) and probes ` hnsw.iterative_scan = relaxed_order ` once per process when available (` #2 ` )
206- - Package install docs point at ` @corbits/memory ` (` #6 ` )
207-
208- ### Fixed
20939
210- - Rerank document truncation to fit cross-encoder token limits (` #10 ` )
211- - Degrade metrics: surface full rerank failure detail and count DegradeFlags so silent degradation is visible (` #11 ` )
40+ ### Upgrading from 0.1.0
21241
213- ## [ 0.1.0] — 2026-07
42+ - Call the new ` runMemoryMigrations ` once; it upgrades a 0.1.0 database in
43+ place with no data loss.
44+ - After the upgrade, do not run 0.1.0 against the database: its ledger is
45+ gone, so there is no downgrade.
21446
215- ### Added
47+ ## [ 0.1.0 ] — 2026-09-25
21648
217- - Initial cut: mountable memory capture + search SDK for Interchange hubs
49+ First release on npm.
21850
219- [ 0.1.2 ] : https://github.com/corbitsdev/corbits-memory/compare/v0.1.1...v0.1.2
220- [ 0.1.1 ] : https://github.com/corbitsdev/corbits-memory/compare/v0.1.0...v0.1.1
221- [ 0.1.0 ] : https://github.com/corbitsdev/corbits-memory/releases/tag/v0.1.0
51+ [ 0.2.0 ] : https://github.com/corbitsdev/corbits-memory/releases/tag/v0.2.0
52+ [ 0.1.0 ] : https://www.npmjs.com/package/@corbits/memory/v/0.1.0
0 commit comments