memorix 1.8.9 → 1.9.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/CHANGELOG.md +18 -0
- package/README.md +7 -0
- package/README.zh-CN.md +6 -0
- package/dist/cli/index.js +3261 -1348
- package/dist/index.js +324 -187
- package/dist/maintenance-runner.js +285 -103
- package/dist/media-audio-runner.js +285 -103
- package/dist/media-video-runner.js +285 -103
- package/dist/memcode-runtime/CHANGELOG.md +18 -0
- package/dist/sdk.js +323 -186
- package/dist/vector-backfill-runner.d.ts +4 -1
- package/dist/vector-backfill-runner.js +288 -104
- package/docs/API_REFERENCE.md +14 -0
- package/docs/CONFIGURATION.md +46 -0
- package/docs/DEVELOPMENT.md +2 -2
- package/docs/MULTI-DEVICE-SYNC-RESEARCH.md +344 -0
- package/docs/PERFORMANCE.md +32 -0
- package/docs/README.md +4 -2
- package/llms-full.txt +1 -0
- package/llms.txt +1 -0
- package/package.json +1 -1
- package/plugins/antigravity/memorix/plugin.json +1 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/hermes/memorix/plugin.yaml +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/cli/command-guide.ts +9 -1
- package/src/cli/commands/serve-http.ts +11 -0
- package/src/cli/commands/sync-store.ts +191 -0
- package/src/cli/commands/sync.ts +8 -0
- package/src/memory/observations.ts +15 -2
- package/src/runtime/maintenance-targets.ts +6 -0
- package/src/runtime/vector-backfill-runner.ts +185 -7
- package/src/store/orama-store.ts +85 -102
- package/src/sync/adapters/fs.ts +179 -0
- package/src/sync/adapters/github.ts +258 -0
- package/src/sync/adapters/object-store.ts +267 -0
- package/src/sync/adapters/postgres.ts +269 -0
- package/src/sync/engine.ts +353 -0
- package/src/sync/journal.ts +162 -0
- package/src/sync/merge.ts +116 -0
- package/src/sync/namespace.ts +15 -0
- package/src/sync/paging.ts +45 -0
- package/src/sync/policy.ts +37 -0
- package/src/sync/remote-factory.ts +49 -0
- package/src/sync/store-port.ts +288 -0
- package/src/sync/types.ts +183 -0
package/docs/CONFIGURATION.md
CHANGED
|
@@ -370,6 +370,52 @@ Common keys:
|
|
|
370
370
|
- `dashboard_port = 3210` — default port for `memorix dashboard` when
|
|
371
371
|
`--port` is not given.
|
|
372
372
|
|
|
373
|
+
### Optional store sync
|
|
374
|
+
|
|
375
|
+
Store sync is disabled unless `MEMORIX_SYNC_PROVIDER` is explicitly set. It
|
|
376
|
+
replicates approved project-visible observation events, never the live SQLite
|
|
377
|
+
file or its `-wal` / `-shm` companions. The local SQLite store remains the
|
|
378
|
+
canonical read/write store.
|
|
379
|
+
|
|
380
|
+
```text
|
|
381
|
+
MEMORIX_SYNC_PROVIDER=fs|github|s3|postgres
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
The filesystem relay needs `MEMORIX_SYNC_FS_ROOT`. The GitHub relay needs a
|
|
385
|
+
private relay repository and token:
|
|
386
|
+
|
|
387
|
+
```text
|
|
388
|
+
MEMORIX_SYNC_GITHUB_REPO=owner/repository
|
|
389
|
+
MEMORIX_SYNC_GITHUB_TOKEN=...
|
|
390
|
+
MEMORIX_SYNC_GITHUB_BRANCH=memorix-sync # optional
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
For S3-compatible storage, set the usual bucket/endpoint/credentials/region
|
|
394
|
+
variables and optionally put all Memorix objects below a shared-bucket prefix:
|
|
395
|
+
|
|
396
|
+
```text
|
|
397
|
+
MEMORIX_SYNC_S3_BUCKET=...
|
|
398
|
+
MEMORIX_SYNC_S3_ENDPOINT=...
|
|
399
|
+
MEMORIX_SYNC_S3_ACCESS_KEY_ID=...
|
|
400
|
+
MEMORIX_SYNC_S3_SECRET_ACCESS_KEY=...
|
|
401
|
+
MEMORIX_SYNC_S3_REGION=auto
|
|
402
|
+
MEMORIX_SYNC_S3_PREFIX=memorix # optional, relative key prefix
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
S3-compatible and Postgres details are documented in the multi-device sync
|
|
406
|
+
[plan and acceptance contract](MULTI-DEVICE-SYNC-RESEARCH.md). Credentials are
|
|
407
|
+
read from the process environment and are never written to project config or
|
|
408
|
+
sync event bodies. A copied data directory must be rotated before it can
|
|
409
|
+
publish:
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
memorix sync store device rotate
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Use `memorix sync store status --json` first. It reports the current project
|
|
416
|
+
namespace, eligible/excluded records, pending outbox work, and conflicts without
|
|
417
|
+
creating a remote branch or uploading anything.
|
|
418
|
+
|
|
373
419
|
### Reserved sections (parsed, not yet enforced)
|
|
374
420
|
|
|
375
421
|
These sections exist so future releases do not need a config format change.
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -13,8 +13,8 @@ Memorix is a TypeScript project built around:
|
|
|
13
13
|
|
|
14
14
|
## Current Development Baseline
|
|
15
15
|
|
|
16
|
-
The current release
|
|
17
|
-
|
|
16
|
+
The current release line is **1.9.0** and package metadata is kept synchronized
|
|
17
|
+
with the root package and MCP Registry manifest.
|
|
18
18
|
|
|
19
19
|
Contributors should assume the following areas are part of the current release line:
|
|
20
20
|
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
# Multi-Device Store Sync — Plan and Acceptance Contract
|
|
2
|
+
|
|
3
|
+
> Status: released in 1.9.0. Background sync, broader table replication, and
|
|
4
|
+
> encrypted snapshots remain future work.
|
|
5
|
+
> Scope: keep the existing local-first SQLite runtime; add an optional,
|
|
6
|
+
> provider-agnostic replication path so one user can keep the same memory
|
|
7
|
+
> across several machines.
|
|
8
|
+
>
|
|
9
|
+
> Contribution credit: the design, initial implementation, and the
|
|
10
|
+
> object-store/Postgres follow-up came from Ravi Tharuma's PR #277. The local
|
|
11
|
+
> hardening branch keeps those commits in its merge history and adapts them to
|
|
12
|
+
> the current v3 contract.
|
|
13
|
+
> Prior art in this repo: `docs/CLOUD_SYNC_AND_MULTI_AGENT_RESEARCH.md`
|
|
14
|
+
> already sketched a "local SQLite + background cloud sync" hybrid but did not
|
|
15
|
+
> specify or build it. This document turns that sketch into a concrete,
|
|
16
|
+
> reviewable contract.
|
|
17
|
+
|
|
18
|
+
## Decision Summary
|
|
19
|
+
|
|
20
|
+
The sync layer must replicate **auditable memory events**, never a live SQLite
|
|
21
|
+
file. GitHub can be a low-cost relay for immutable JSONL batches, but GitHub
|
|
22
|
+
LFS is not a database merge engine and is not the production default. The
|
|
23
|
+
runtime keeps SQLite local and treats each remote as an interchangeable event
|
|
24
|
+
transport.
|
|
25
|
+
|
|
26
|
+
The first supported contract is deliberately narrow:
|
|
27
|
+
|
|
28
|
+
- **Scope:** the current Git project by default. A remote namespace includes the
|
|
29
|
+
canonical project ID; one project cannot pull another project's events by
|
|
30
|
+
accident.
|
|
31
|
+
- **Eligibility:** project-visible, non-ephemeral, qualified/legacy durable
|
|
32
|
+
observations only. Personal, agent-targeted, candidate, and ephemeral records
|
|
33
|
+
are excluded unless a future explicit export policy opts them in.
|
|
34
|
+
- **Identity:** every sync key includes the project ID. Unkeyed rows also carry
|
|
35
|
+
origin device and origin ID, so global store IDs can never cross projects.
|
|
36
|
+
- **Transport:** immutable per-device event batches, local cursors, checksums,
|
|
37
|
+
bounded pulls, and idempotent replay. Remote cursors are not authoritative.
|
|
38
|
+
- **Conflict:** logical version order decides the current visible row, while a
|
|
39
|
+
losing concurrent version remains available as conflict/audit evidence rather
|
|
40
|
+
than disappearing silently.
|
|
41
|
+
- **Device safety:** a copied data directory is detected as a clone and must
|
|
42
|
+
rotate its device identity before it can publish new events.
|
|
43
|
+
- **User experience:** manual `status`, `push`, `pull`, and `repair` first;
|
|
44
|
+
background sync is a later opt-in once retention and recovery gates pass.
|
|
45
|
+
|
|
46
|
+
## Implementation Phases
|
|
47
|
+
|
|
48
|
+
| Phase | Deliverable | Gate |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| P0 | Plan, scope, privacy and event contract | docs map updated; no live SQLite sync path |
|
|
51
|
+
| P1 | Project-scoped local journal and safe eligibility filter | private/candidate/other-project fixtures never leave the scope |
|
|
52
|
+
| P2 | Deterministic merge, tombstones, conflicts, clone detection | permutation, delete, replay, clone and conflict tests pass |
|
|
53
|
+
| P3 | Filesystem and GitHub JSONL relays | two real data directories reconcile without DB/WAL files |
|
|
54
|
+
| P4 | Bounded S3/Postgres pulls and remote namespace isolation | paged pull, malformed input, retry and large-log tests pass |
|
|
55
|
+
| P5 | Explicit remote compaction and CLI repair | cleanup is preview-first and only deletes acknowledged events |
|
|
56
|
+
| P6 | Cross-platform smoke and release review | Windows/macOS/Linux checks, docs, package smoke, contributor credit |
|
|
57
|
+
|
|
58
|
+
## GitHub Relay Contract
|
|
59
|
+
|
|
60
|
+
The GitHub adapter uses a private repository as a versioned transport, not as
|
|
61
|
+
the canonical database:
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
events/<project-hash>/<device-id>/<sequence>.jsonl
|
|
65
|
+
snapshots/<project-hash>/<snapshot-id>.json.zst
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Event files are immutable and device-owned. A device never edits another
|
|
69
|
+
device's file and never commits `memorix.db`, `memorix.db-wal`, or
|
|
70
|
+
`memorix.db-shm`. Each event contains a schema version, project namespace,
|
|
71
|
+
device ID, sequence, entity key, operation, logical version, visibility
|
|
72
|
+
decision, content hash, and payload. At-rest encryption is a future transport
|
|
73
|
+
option; this first slice does not pretend to provide it. Credentials never
|
|
74
|
+
enter project files or event bodies.
|
|
75
|
+
|
|
76
|
+
Snapshots are normalized, replayable memory state, not a copy of an open
|
|
77
|
+
SQLite file. SQLite's Online Backup API may be used for a local backup, but a
|
|
78
|
+
backup is not a merge input.
|
|
79
|
+
|
|
80
|
+
## Acceptance Checklist
|
|
81
|
+
|
|
82
|
+
- [x] `memorix sync store status --json` reports scope, eligible/skipped counts,
|
|
83
|
+
remote namespace, cursor, pending batches, and conflicts without sending data.
|
|
84
|
+
- [x] `push` and `pull` are idempotent and resumable after interruption.
|
|
85
|
+
- [x] Two stores with independent local integer IDs converge to the same visible
|
|
86
|
+
records while retaining local IDs locally.
|
|
87
|
+
- [x] A stale upsert cannot revive a newer delete.
|
|
88
|
+
- [x] A concurrent same-key edit keeps both the winner and a reviewable losing
|
|
89
|
+
version/audit record.
|
|
90
|
+
- [x] Personal, agent-targeted, candidate, ephemeral, and other-project rows
|
|
91
|
+
are excluded by default.
|
|
92
|
+
- [x] Copying a data directory produces a clear device-clone error and a
|
|
93
|
+
documented `device rotate` recovery path.
|
|
94
|
+
- [x] GitHub relay protocol smoke proves only JSONL/snapshot artifacts are uploaded;
|
|
95
|
+
no SQLite database or WAL file is present.
|
|
96
|
+
- [x] Pulls are bounded and keyset-paged; remote logs have an explicit
|
|
97
|
+
acknowledged retention/compaction path.
|
|
98
|
+
- [x] Malformed, foreign-project, unsupported-version, and hash-mismatch input
|
|
99
|
+
fails closed without mutating local memory.
|
|
100
|
+
- [x] Local-only users see no behavior or dependency change when sync is unset.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 1. Problem
|
|
105
|
+
|
|
106
|
+
A single user commonly runs Memorix on more than one machine — for example a
|
|
107
|
+
workstation, a home machine, and a portable laptop. Today each machine keeps
|
|
108
|
+
its own independent store at `~/.memorix/data` (`MEMORIX_DATA_DIR`). There is
|
|
109
|
+
no supported way to keep those stores current with each other, so the memory a
|
|
110
|
+
user sees depends on which machine they happen to open.
|
|
111
|
+
|
|
112
|
+
Two deployment shapes are sometimes proposed to solve this. Both have real
|
|
113
|
+
costs:
|
|
114
|
+
|
|
115
|
+
1. **Run one central Memorix service and point every machine at it over HTTP.**
|
|
116
|
+
`serve-http` already supports this (see `docs/DOCKER.md`). It gives a single
|
|
117
|
+
shared store, but it trades away the properties that make Memorix useful on
|
|
118
|
+
a workstation:
|
|
119
|
+
- **Project-scoped features degrade.** Git-root detection, project binding,
|
|
120
|
+
project `memorix.toml`/`.env`, and Git Memory require the service to see
|
|
121
|
+
the repository at the path the client reports. A central host usually
|
|
122
|
+
cannot see a laptop's working copy, so project-scoped semantics break
|
|
123
|
+
while only global/shared memory keeps working. `docs/DOCKER.md` documents
|
|
124
|
+
this as a real runtime limitation.
|
|
125
|
+
- **Latency on the hot path.** Every context call, session start, and hook
|
|
126
|
+
becomes a network round trip instead of a local SQLite read.
|
|
127
|
+
- **Offline stops working.** A portable machine with no connectivity has no
|
|
128
|
+
memory at all.
|
|
129
|
+
- **Single-writer contention.** One SQLite file serving several machines
|
|
130
|
+
serializes writes and invites `SQLITE_BUSY`.
|
|
131
|
+
|
|
132
|
+
2. **Replace SQLite with a central client/server database (e.g. Postgres).**
|
|
133
|
+
This contradicts the stated local-first product boundary in
|
|
134
|
+
`docs/KNOWN_ISSUES_AND_ROADMAP.md`, and would require rewriting the
|
|
135
|
+
synchronous `better-sqlite3` store layer (`src/store/*.ts`) onto an async
|
|
136
|
+
client with a different SQL dialect and a different vector story. It removes
|
|
137
|
+
offline use and adds an always-on infrastructure dependency for what is
|
|
138
|
+
meant to be a local tool.
|
|
139
|
+
|
|
140
|
+
Neither shape preserves local-first. The gap is a **third shape**: keep the
|
|
141
|
+
store local on every machine, and add an **optional replication layer** that
|
|
142
|
+
keeps those local stores consistent through an interchangeable remote.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 2. Goals and non-goals
|
|
147
|
+
|
|
148
|
+
### Goals
|
|
149
|
+
|
|
150
|
+
- Preserve local-first: reads and writes stay on the local SQLite store; sync
|
|
151
|
+
is asynchronous and off the hot path.
|
|
152
|
+
- Keep working offline; reconcile on the next successful sync.
|
|
153
|
+
- Be **provider-agnostic**: the remote is an interface, not a specific vendor.
|
|
154
|
+
A local filesystem/rsync target, an object store (S3-compatible), or a small
|
|
155
|
+
edge datastore must all be valid backends behind the same contract.
|
|
156
|
+
- Be **opt-in and safe**: sync is disabled by default; enabling it never
|
|
157
|
+
changes local behavior for users who do not use it.
|
|
158
|
+
- Deterministic, explainable merge: a user can inspect what would change before
|
|
159
|
+
it is applied, consistent with the preview-first maintenance model.
|
|
160
|
+
|
|
161
|
+
### Non-goals
|
|
162
|
+
|
|
163
|
+
- No real-time multi-writer coordination or distributed locking. That is the
|
|
164
|
+
`team`/orchestration surface, not this.
|
|
165
|
+
- No requirement to run a central Memorix service.
|
|
166
|
+
- No replacement of the SQLite runtime or the retrieval/embedding model.
|
|
167
|
+
- No claim of conflict-free collaboration between different users; this targets
|
|
168
|
+
one user's several machines. Multi-user sharing is a separate concern.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 3. Why the current schema already supports this
|
|
173
|
+
|
|
174
|
+
The canonical store carries most of what a convergent last-writer-wins (LWW)
|
|
175
|
+
merge needs, and the small remainder lives in additive side tables so the
|
|
176
|
+
`observations` schema never changes:
|
|
177
|
+
|
|
178
|
+
- **Stable cross-device identity.** The integer `observations.id` is a
|
|
179
|
+
*replica-local* counter (`meta.next_id`) — two machines can mint the same id
|
|
180
|
+
for unrelated rows — so it cannot be the merge key. Sync instead derives a
|
|
181
|
+
stable `syncKey`: `t:<projectId>:<topicKey>` for topic-keyed observations
|
|
182
|
+
(naturally shared across devices) and `u:<originDevice>:<originId>` for
|
|
183
|
+
unkeyed ones, minted once and recorded in `sync_row_state`. Each replica maps
|
|
184
|
+
a `syncKey` to its own local id on import, so id collisions are impossible.
|
|
185
|
+
- **Convergent versioning.** Merge uses a per-row logical clock
|
|
186
|
+
`(revision, writer)` — a Lamport counter advanced from the last observed
|
|
187
|
+
revision, tiebroken by the originating device id — persisted verbatim in
|
|
188
|
+
`sync_row_state`. This is a strict total order, so replaying batches in any
|
|
189
|
+
delivery order reaches the same state. `writeGeneration`/`updatedAt` remain a
|
|
190
|
+
local storage watermark and are deliberately *not* used as the comparator
|
|
191
|
+
(they are not comparable across replicas).
|
|
192
|
+
- **Durable tombstones.** Deletes are versioned states retained in
|
|
193
|
+
`sync_row_state`, so a stale upsert pulled later cannot resurrect a row a
|
|
194
|
+
newer delete removed.
|
|
195
|
+
- **Provenance.** `source`, `sourceDetail`, `createdByAgentId`, `projectId`,
|
|
196
|
+
and `sessionId` let a merge attribute and scope rows.
|
|
197
|
+
|
|
198
|
+
A first version therefore replicates at the **observation row** granularity
|
|
199
|
+
with no migration of existing data: identity, version, and tombstone state all
|
|
200
|
+
live in the additive `sync_row_state` / `sync_meta` tables.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 4. Proposed architecture
|
|
205
|
+
|
|
206
|
+
### 4.1 Two layers
|
|
207
|
+
|
|
208
|
+
1. **A change journal (local).** A small append-only log of committed mutations
|
|
209
|
+
keyed by table + row id + logical version. This can be derived from existing
|
|
210
|
+
columns for observations in v1, and generalized to other tables later. The
|
|
211
|
+
journal is what gets shipped, so sync never has to diff the whole 900 MB
|
|
212
|
+
database.
|
|
213
|
+
2. **A replication transport (remote).** A narrow interface that can `push`
|
|
214
|
+
local journal segments and `pull` remote ones. The store never talks to a
|
|
215
|
+
vendor directly; it talks to this interface.
|
|
216
|
+
|
|
217
|
+
### 4.2 Transport interface (provider-agnostic)
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
// Final interface (src/sync/types.ts). The cursor maps deviceId -> last applied
|
|
221
|
+
// sequence locally; the page token is an opaque keyset continuation for one
|
|
222
|
+
// bounded pull.
|
|
223
|
+
interface SyncRemote {
|
|
224
|
+
readonly kind: string; // "fs" | "github" | "s3" | "postgres"
|
|
225
|
+
readonly namespace: string;
|
|
226
|
+
init(options?: { create?: boolean }): Promise<void>;
|
|
227
|
+
push(batch: ChangeBatch): Promise<void>; // idempotent by (deviceId, sequence)
|
|
228
|
+
pull(since: Record<string, number>, limit: number, pageToken?: string): Promise<SyncPullPage>;
|
|
229
|
+
compact(through: Record<string, number>, options?: { dryRun?: boolean }): Promise<SyncCompactReport>;
|
|
230
|
+
close(): Promise<void>;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Concrete adapters implement `SyncRemote` only:
|
|
235
|
+
|
|
236
|
+
- **`fs`/`rsync`** — a directory the user already syncs by other means; zero
|
|
237
|
+
new infrastructure, good for a first, fully local test.
|
|
238
|
+
- **`github`** — an optional private-repository JSONL relay; it is convenient
|
|
239
|
+
for a small personal store, not a database.
|
|
240
|
+
- **`s3`** — any S3-compatible object store; batches are immutable objects
|
|
241
|
+
under a project path and optional user prefix.
|
|
242
|
+
- **`postgres`** — a batch-log hub for users who already operate Postgres.
|
|
243
|
+
|
|
244
|
+
The point is that no adapter is privileged. Provider choice is configuration,
|
|
245
|
+
not code, mirroring how `[memory.llm]`, `[embedding]`, and rerank already
|
|
246
|
+
select providers by config.
|
|
247
|
+
|
|
248
|
+
### 4.3 Merge strategy
|
|
249
|
+
|
|
250
|
+
- **Row-level last-writer-wins**, merged by the stable `syncKey` and versioned
|
|
251
|
+
by the per-row logical clock `(revision, writer)`. The comparison is a strict
|
|
252
|
+
total order, so the merge is convergent: any delivery/replay order of the
|
|
253
|
+
same batches yields the same final state.
|
|
254
|
+
- **No lifecycle special-casing.** An earlier design ordered `status`
|
|
255
|
+
transitions in lifecycle rank; that made the merge order-dependent (applying
|
|
256
|
+
`active`@rev2 then `archived`@rev1 diverged from the reverse). Revision
|
|
257
|
+
ordering alone already prevents a stale `active` copy from overriding a newer
|
|
258
|
+
supersession/archival, so the guard was removed in favour of provable
|
|
259
|
+
convergence.
|
|
260
|
+
- **Durable tombstones.** A delete is a versioned state kept in
|
|
261
|
+
`sync_row_state`, so a delete on machine A is never resurrected by machine B's
|
|
262
|
+
stale upsert — even when B pulls the old upsert after the delete.
|
|
263
|
+
- **Preview-first:** `sync --dry` reports the exact set of inserts, updates,
|
|
264
|
+
supersessions, and tombstones that would apply, consistent with the existing
|
|
265
|
+
preview-first maintenance actions.
|
|
266
|
+
|
|
267
|
+
### 4.4 Configuration surface (illustrative)
|
|
268
|
+
|
|
269
|
+
```toml
|
|
270
|
+
[sync]
|
|
271
|
+
enabled = false # opt-in; default off
|
|
272
|
+
provider = "fs" # fs | s3 | edge-kv | ...
|
|
273
|
+
mode = "manual" # manual | interval
|
|
274
|
+
# provider-specific settings live under a provider table, e.g. [sync.s3]
|
|
275
|
+
# credentials come from the environment, never from this file
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Credentials and endpoints are resolved from the environment (as LLM/embedding
|
|
279
|
+
keys already are), never written into a config file that could be shared.
|
|
280
|
+
|
|
281
|
+
### 4.5 CLI surface (illustrative)
|
|
282
|
+
|
|
283
|
+
- `memorix sync push` / `memorix sync pull` / `memorix sync status`
|
|
284
|
+
- `memorix sync --dry` for a preview before applying
|
|
285
|
+
- Optional background interval sync gated behind `[sync].mode = "interval"`
|
|
286
|
+
|
|
287
|
+
> Note: `memorix sync` today handles cross-agent **rules** synchronization
|
|
288
|
+
> (`src/cli/commands/sync.ts`). Store replication must not collide with that
|
|
289
|
+
> surface; it should live under a clearly separated subcommand namespace
|
|
290
|
+
> (for example `memorix sync store ...`) or a new top-level verb, decided by
|
|
291
|
+
> the maintainer.
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## 5. Phasing
|
|
296
|
+
|
|
297
|
+
1. **Phase 1 — journal + local relay, observations only.** Complete.
|
|
298
|
+
Two real SQLite data directories reconcile through a shared filesystem
|
|
299
|
+
relay, with project scope, privacy filtering, merge, delete, and clone
|
|
300
|
+
protection.
|
|
301
|
+
2. **Phase 2 — remote relays + preview.** Complete as a contract and adapter
|
|
302
|
+
test slice. GitHub, S3-compatible, and Postgres transports are opt-in;
|
|
303
|
+
provider credentials and live service acceptance remain deployment work.
|
|
304
|
+
3. **Phase 3 — snapshots and broader tables.** Future. The current release
|
|
305
|
+
does not replicate sessions, knowledge, CodeGraph, or encrypted snapshots.
|
|
306
|
+
4. **Phase 4 — background sync.** Future. Manual status/push/pull remains the
|
|
307
|
+
deliberate first user experience until recovery, privacy, and lifecycle
|
|
308
|
+
telemetry are proven.
|
|
309
|
+
|
|
310
|
+
Each phase is independently useful and independently reviewable.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## 6. Compatibility and safety
|
|
315
|
+
|
|
316
|
+
- **Default off.** Users who never enable `[sync]` see no behavior change.
|
|
317
|
+
- **No hot-path cost.** Sync runs asynchronously; local reads/writes are
|
|
318
|
+
unchanged.
|
|
319
|
+
- **No new required dependency.** The `fs` adapter needs nothing; object-store
|
|
320
|
+
and edge adapters are optional.
|
|
321
|
+
- **Local-first preserved.** Every machine keeps a complete local store and
|
|
322
|
+
works offline.
|
|
323
|
+
- **Preview-first and auditable**, matching the existing maintenance model.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## 7. Open questions for maintainer direction
|
|
328
|
+
|
|
329
|
+
1. Subcommand placement: extend `sync` (`sync store ...`) vs. a new top-level
|
|
330
|
+
verb, given `sync` already means rules sync.
|
|
331
|
+
2. Journal representation: derive from existing columns for v1, or add an
|
|
332
|
+
explicit change-log table from the start.
|
|
333
|
+
3. Which adapters belong in-tree vs. as optional/companion packages.
|
|
334
|
+
4. Whether `serve-http` should be able to act as a sync remote for a user's own
|
|
335
|
+
machines, giving a self-hosted option that still keeps each client local.
|
|
336
|
+
5. Merge policy defaults: is row-level LWW acceptable as the v1 default, with
|
|
337
|
+
lifecycle-aware overrides, or is a stricter policy preferred?
|
|
338
|
+
|
|
339
|
+
The first implementation slice is released behind the opt-in CLI. It covers
|
|
340
|
+
the project-scoped observation event path, filesystem/GitHub/S3/Postgres relay
|
|
341
|
+
contracts, local outbox retry, clone detection, conflict evidence, and bounded
|
|
342
|
+
keyset pulls. It does not enable background sync, upload private memory,
|
|
343
|
+
replicate sessions/knowledge/CodeGraph tables, or claim that GitHub is a
|
|
344
|
+
production database.
|
package/docs/PERFORMANCE.md
CHANGED
|
@@ -14,6 +14,32 @@ Memorix is designed to be light for everyday memory use and explicit about heavi
|
|
|
14
14
|
|
|
15
15
|
The default memory path uses local SQLite as the canonical store and Orama for search/indexing. No cloud service is required.
|
|
16
16
|
|
|
17
|
+
## Resource Safety Model
|
|
18
|
+
|
|
19
|
+
Memorix does not impose a small maximum number of memories. SQLite remains the
|
|
20
|
+
durable corpus, and large projects are processed in batches. The important
|
|
21
|
+
limits are on **simultaneous work and disposable caches**, not on how much the
|
|
22
|
+
user is allowed to remember.
|
|
23
|
+
|
|
24
|
+
- A deferred vector backfill uses one cross-process worker lock per shared data
|
|
25
|
+
directory. Repeated hook events add to the durable queue; they do not spawn a
|
|
26
|
+
new Node process for every event.
|
|
27
|
+
- A retrying vector job stays in cooldown. A provider outage therefore cannot
|
|
28
|
+
create a process storm every time another hook fires.
|
|
29
|
+
- One backfill worker drains due vector jobs sequentially and releases its
|
|
30
|
+
SQLite/Orama state when it exits. A dead worker lock is recoverable by PID
|
|
31
|
+
liveness, with a long stale-lock fallback for interrupted filesystems.
|
|
32
|
+
- Startup index hydration builds Orama documents in bounded batches and cached
|
|
33
|
+
vector attachment is also batched. This limits peak temporary allocations
|
|
34
|
+
without dropping durable observations.
|
|
35
|
+
- The API embedding cache is a disposable working cache. Its default byte
|
|
36
|
+
budget is large and can be raised with `MEMORIX_EMBEDDING_CACHE_MAX_BYTES`,
|
|
37
|
+
but it is deliberately separate from the durable memory corpus. Cache
|
|
38
|
+
eviction never deletes a memory; a later vector backfill can regenerate it.
|
|
39
|
+
- `serve-http` `/health` exposes `rss`, V8 heap, external memory, array-buffer
|
|
40
|
+
memory, and the V8 heap limit so a real saturation report can distinguish
|
|
41
|
+
JavaScript retention from native/model memory.
|
|
42
|
+
|
|
17
43
|
## Retrieval Profiles
|
|
18
44
|
|
|
19
45
|
| Profile | Default? | Network work | Best fit |
|
|
@@ -78,6 +104,7 @@ On the release development machine used for this check, the healthy HTTP service
|
|
|
78
104
|
| `MEMORIX_RERANK_TIMEOUT_MS` | 30000 | Bound HTTP and LLM rerank calls |
|
|
79
105
|
| `MEMORIX_RERANK_PROVIDER` | `off` | Set `http` to enable optional HTTP rerank |
|
|
80
106
|
| `MEMORIX_RERANK_BASE_URL` | `[memory.llm].base_url` | Compatible `/rerank` API root (path `/rerank` is appended) |
|
|
107
|
+
| `MEMORIX_EMBEDDING_CACHE_MAX_BYTES` | `268435456` | Disposable in-process API-vector cache budget; raise for large warm caches, or lower when the host is memory constrained |
|
|
81
108
|
| `memorix memory search --quality fast` | n/a | Force a fully local retrieval path for a latency-sensitive call |
|
|
82
109
|
| `npm run benchmark:retrieval -- --records 1000 --runs 100` | n/a | Reproduce hot in-process lexical retrieval latency; not an end-to-end claim |
|
|
83
110
|
| `npm run gate:large-store -- --records 40000` | n/a | Exercise SDK, HTTP MCP, hook persistence, cache integrity, and reopen behavior against a large isolated store |
|
|
@@ -98,6 +125,11 @@ On the release development machine used for this check, the healthy HTTP service
|
|
|
98
125
|
- When Dashboard shows queued or failed maintenance work, inspect
|
|
99
126
|
`/api/maintenance` on that local dashboard before assuming a Code Memory scan
|
|
100
127
|
or lifecycle task completed.
|
|
128
|
+
- When investigating CPU/RAM growth, inspect `memorix background status --json`
|
|
129
|
+
or `/health` first. Look for multiple `vector-backfill-runner` processes,
|
|
130
|
+
rising `rss` with stable heap, or rising V8 `heapUsed`; those indicate
|
|
131
|
+
different classes of problem and should not be “fixed” by lowering the
|
|
132
|
+
memory corpus limit.
|
|
101
133
|
|
|
102
134
|
## Large-Store Release Gate
|
|
103
135
|
|
package/docs/README.md
CHANGED
|
@@ -96,6 +96,7 @@ The public docs are organized by user intent:
|
|
|
96
96
|
| 1.2 honest Lite and optional semantic CodeGraph provider contract | [1.2 Provider Quality](1.2.0-PROVIDER-QUALITY.md) |
|
|
97
97
|
| 1.3 long-term memory model, source boundary, and lifecycle | [1.3 Memory Architecture](1.3-MEMORY-ARCHITECTURE.md) |
|
|
98
98
|
| Active context-control work | [1.2.2 Memory Control Plane](1.2.2-MEMORY-CONTROL-PLANE.md) |
|
|
99
|
+
| Multi-device memory sync plan and acceptance contract | [Multi-Device Sync](MULTI-DEVICE-SYNC-RESEARCH.md) |
|
|
99
100
|
| 1.8.6 MCP compatibility acceptance contract | [1.8.0 Release Specification](1.8.0-RELEASE-CANDIDATE-SPEC.md) |
|
|
100
101
|
| Historical cloud sync and multi-agent research | [CLOUD_SYNC_AND_MULTI_AGENT_RESEARCH.md](CLOUD_SYNC_AND_MULTI_AGENT_RESEARCH.md) |
|
|
101
102
|
| Known issues and old roadmap notes | [KNOWN_ISSUES_AND_ROADMAP.md](KNOWN_ISSUES_AND_ROADMAP.md) |
|
|
@@ -124,8 +125,9 @@ Historical/deep-reference documents may describe older designs. If they conflict
|
|
|
124
125
|
|
|
125
126
|
## Current Product Line
|
|
126
127
|
|
|
127
|
-
The current product line is **1.
|
|
128
|
-
|
|
128
|
+
The current product line is **1.9.0**. Multi-device store sync is opt-in and
|
|
129
|
+
keeps local SQLite canonical; existing users do not upload anything until they
|
|
130
|
+
configure a relay provider explicitly.
|
|
129
131
|
The authoritative acceptance contract for the current implementation is
|
|
130
132
|
[1.8.0 Release Specification](1.8.0-RELEASE-CANDIDATE-SPEC.md).
|
|
131
133
|
The current baseline has:
|
package/llms-full.txt
CHANGED
|
@@ -36,6 +36,7 @@ It installs the recommended Memorix integration for the target agent:
|
|
|
36
36
|
- Gemini CLI: local Gemini extension package with MCP and `GEMINI.md` context.
|
|
37
37
|
- OpenCode: local plugin file, `opencode.json` MCP config, OpenCode skill, and `AGENTS.md` guidance.
|
|
38
38
|
- Grok Build: native lifecycle hooks in `~/.grok/hooks/memorix.json` and `~/.grok/AGENTS.md` guidance. Grok owns its MCP configuration; project hooks require `/hooks-trust`.
|
|
39
|
+
- Multi-device sync: opt-in `memorix sync store status|push|pull` relays only qualified project-visible observation events. Local SQLite remains canonical; GitHub JSONL, filesystem, S3, and Postgres are transport options. Personal, agent-targeted, candidate, ephemeral, and other-project records are excluded by default.
|
|
39
40
|
- Windsurf, Kiro, Antigravity, Trae: MCP config plus rules/hooks where supported.
|
|
40
41
|
- DeepSeek Harness: MCP patch row (`$DSH_HOME/cordis.patch.yml`) plus `AGENTS.md` guidance and official skills.
|
|
41
42
|
|
package/llms.txt
CHANGED
|
@@ -15,6 +15,7 @@ Memorix gives coding agents persistent, project-aware memory across sessions, ID
|
|
|
15
15
|
- `memorix` and `memcode` open memcode, the bundled terminal agent that uses the same project memory pool.
|
|
16
16
|
- `memorix dashboard` opens the local read-mostly dashboard.
|
|
17
17
|
- `memorix setup --agent grok --global` installs Grok's native lifecycle hooks and `AGENTS.md` guidance; Grok keeps MCP configuration under its own control.
|
|
18
|
+
- `memorix sync store status|push|pull` is an opt-in project-scoped event relay; it never uploads the live SQLite database or WAL files.
|
|
18
19
|
|
|
19
20
|
## Memory Model
|
|
20
21
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memorix",
|
|
3
3
|
"mcpName": "io.github.AVIDS2/memorix",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.9.0",
|
|
5
5
|
"description": "Local-first shared memory layer for AI coding agents across MCP clients, Git history, reasoning context, and project sessions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
package/server.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"url": "https://github.com/AVIDS2/memorix",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.9.0",
|
|
11
11
|
"websiteUrl": "https://mem.rglens.com",
|
|
12
12
|
"icons": [
|
|
13
13
|
{
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
{
|
|
23
23
|
"registryType": "npm",
|
|
24
24
|
"identifier": "memorix",
|
|
25
|
-
"version": "1.
|
|
25
|
+
"version": "1.9.0",
|
|
26
26
|
"runtimeHint": "npx",
|
|
27
27
|
"packageArguments": [
|
|
28
28
|
{
|
package/src/cli/command-guide.ts
CHANGED
|
@@ -209,7 +209,15 @@ const GUIDES: Record<string, CliCommandGuide> = {
|
|
|
209
209
|
},
|
|
210
210
|
sync: {
|
|
211
211
|
summary: 'Synchronize agent rules and workspace artifacts through explicit routes.',
|
|
212
|
-
usage: [
|
|
212
|
+
usage: [
|
|
213
|
+
'memorix sync rules --action status',
|
|
214
|
+
'memorix sync workspace --action scan',
|
|
215
|
+
'memorix sync store status --json',
|
|
216
|
+
'memorix sync store push|pull [--dry] [--json]',
|
|
217
|
+
'memorix sync store compact --dry --through device=sequence',
|
|
218
|
+
'memorix sync store device rotate',
|
|
219
|
+
],
|
|
220
|
+
notes: ['Store sync is opt-in, project-scoped, and never copies the live SQLite database.'],
|
|
213
221
|
},
|
|
214
222
|
ingest: {
|
|
215
223
|
summary: 'Ingest Git and image evidence into project memory.',
|