memorio 4.7.1 → 4.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.
@@ -0,0 +1,170 @@
1
+ # Synchronization & Cloud (optional)
2
+
3
+ `memorio.memory` is **local-first**. Data is created and served from the device;
4
+ the cloud is only ever a **transport/persistence provider**, never the source of
5
+ truth. Enabling sync does not replace local storage — it *mirrors* it.
6
+
7
+ ```
8
+ memorio
9
+
10
+ ┌────────┴────────┐
11
+ │ Memory Engine │
12
+ └────────┬────────┘
13
+ ┌────────────┼────────────┐
14
+ ▼ ▼ ▼
15
+ local SQLite cloud
16
+ memory durable sync
17
+ ```
18
+
19
+ ## 1. The rule: the data is born local
20
+
21
+ ```ts
22
+ memorio.memory.remember('user.language', 'Italian', { scope: 'local' })
23
+ // ↓ local first
24
+ // store / sessionStorage / IndexedDB / sql.js
25
+ // ↓ sync / push (when online)
26
+ // cloud provider
27
+ ```
28
+
29
+ The cloud therefore does not **replace** memory: it **replicates** it. This
30
+ gives you: offline-first, lowest latency, data available immediately,
31
+ synchronization when online, multi-device, multi-user, centralized persistence.
32
+
33
+ We deliberately do **not** provide:
34
+
35
+ ```ts
36
+ // ❌ two mental models
37
+ memory.cloud.save(...)
38
+ ```
39
+
40
+ Instead:
41
+
42
+ ```ts
43
+ memorio.memory.remember('user.language', 'Italian')
44
+ // and a single configuration point:
45
+ memorio.memory.configure({ sync: { provider: myCloudProvider, namespace: '…' } })
46
+ ```
47
+
48
+ ## 2. Scopes (isolation, not a security boundary)
49
+
50
+ | Scope | Lifetime | Syncs by default |
51
+ |---|---|---|
52
+ | `'device'` | this browser/device only | no (sticky) |
53
+ | `'user'` | follows the user across devices | yes (requires provider + namespace) |
54
+ | `'shared'` | shared across users / tenant | yes (requires provider + namespace) |
55
+
56
+ > As with `memorio.createContext`, **scoping is a naming convention, not a
57
+ > security boundary.** Enforce real isolation server-side.
58
+
59
+ ## 3. SQLite as the local durable store
60
+
61
+ SQLite (`sql.js`) is **in-memory by default** (volatie per page load). It becomes
62
+ the durable journal/value store when you opt in:
63
+
64
+ - `sqlite.config({ persistence: true })` / `sqlite.db.create('app', { persistence: true })`
65
+ snapshot the database to `store` (localStorage) and restore it on reopen.
66
+ - Writes are snapshotted via sql.js `updateHook` (debounced).
67
+ - `sqlite.db.persist(name)` forces an immediate save; `sqlite.db.close(name)`
68
+ flushes + closes; `sqlite.db.download(name, file?)` triggers a browser
69
+ `.sqlite` download (dev convenience).
70
+
71
+ See `docs/markdown/SQLITE.md` for the full SQLite reference.
72
+
73
+ ## 4. The local operation journal
74
+
75
+ The **sync journal** is the durable op log that drives cloud reconciliation.
76
+ It is persisted on `store` (localStorage) — **not** on an in-memory sql.js db,
77
+ because pending operations must survive a refresh for offline-first to work.
78
+
79
+ | Method | Returns | Notes |
80
+ |---|---|---|
81
+ | `memory.journal.append(entry, operation)` | `Promise<MemoryEntry>` | records `remember\|update\|forget\|expire\|confirm\|supersede` with `sync:'pending'` |
82
+ | `memory.journal.pending()` | `Promise<MemoryEntry[]>` | rows where `sync != 'synced'`, for the current namespace |
83
+ | `memory.journal.markSynced(ids)` | `Promise<number>` | advances rows to `synced` (namespace-scoped) |
84
+ | `memory.journal.get(id)` | `Promise<MemoryEntry \| null>` | single entry, namespace-scoped |
85
+ | `memory.journal.clear()` | `Promise<void>` | wipes the current namespace's journal |
86
+ | `memory.journal.replay()` | `Promise<SyncAck>` | pushes `pending()` to the provider, marks synced, optional `pull` |
87
+ | `memory.journal.status()` | `Promise<'store'>` | the substrate in use |
88
+
89
+ We sync **operations of memory**, never a raw database dump:
90
+
91
+ ```
92
+ user A device A
93
+ remember X ─────► local ─────► sync ─────► cloud
94
+ forget Z ──────► local ─────► sync ─────► cloud
95
+ ```
96
+
97
+ ## 5. Conflict resolution
98
+
99
+ The cloud must not simply say "last write wins." Memorio tags every entry with:
100
+
101
+ - `confidence` (0–1, user/system trust in the value)
102
+ - `lastConfirmedAt` / `updatedAt` (epoch ms)
103
+ - `version` (monotonic per-key counter)
104
+ - `source` / `scope`
105
+
106
+ Remote conflicts are surfaced as `sync:'conflict'` rows via
107
+ `journal.pending()`; the provider's `resolve(op)` hint decides locally. Example:
108
+
109
+ ```
110
+ Laptop: language=Italian, confidence=0.92
111
+ Phone: language=English, confidence=0.61
112
+ → higher-confidence entry wins locally; the provider decides for shared scope.
113
+ ```
114
+
115
+ ## 6. Configuring a backend
116
+
117
+ Sync is **opt-in**. You supply an application-owned `provider` that knows how to
118
+ talk to your backend (REST, WebSocket, Supabase, a custom agent server, …).
119
+
120
+ ```ts
121
+ memorio.memory.configure({
122
+ namespace: 'user:123:device:abc', // tenant/user/device — partitions the journal
123
+ provider: {
124
+ push(ops) { return fetch('/api/sync', { method: 'POST', body: JSON.stringify(ops), headers: authHeaders }) }
125
+ pull(since) { return fetch(`/api/sync?since=${since}`).then(r => r.json()) }
126
+ resolve(op) { return op.confidence >= 0.8 ? 'local' : 'remote' }
127
+ },
128
+ auto: true // auto-replay on focus/online (default true)
129
+ })
130
+ ```
131
+
132
+ ```ts
133
+ interface SyncProvider {
134
+ push(ops: MemoryEntry[]): Promise<{ synced: string[]; conflicts?: string[]; error?: string }>
135
+ pull?(since?: number): Promise<MemoryEntry[]>
136
+ resolve?(op: MemoryEntry): Promise<'local' | 'remote' | 'merge'>
137
+ }
138
+ ```
139
+
140
+ `memorio.memory.ready` resolves once the local journal substrate is chosen.
141
+
142
+ ## 7. Security (NIST / OWASP / NSA posture)
143
+
144
+ - **Memorio never handles credentials.** No passwords, tokens, or API keys are
145
+ read from or stored by memorio. Authentication/authorization live in your
146
+ `provider`/backend (OWASP A01: Broken Access Control).
147
+ - **Namespace isolation.** The journal is keyed by `namespace:id` at the storage
148
+ layer; there is **no API** to enumerate or open another namespace's journal. A
149
+ client holding a forged/fake namespace simply sees its own (empty) journal.
150
+ - **No dynamic code.** Journal entries are strictly JSON-round-tripped,
151
+ size-capped (10 MB/entry), and never `eval`'d. The sql.js loader never
152
+ `import()`s a bare specifier that could be hijacked at build time.
153
+ - **Trust boundary:** memorio owns the local durable copy + operation log; the
154
+ provider/backend owns remote-side auth and conflict resolution. Memorio
155
+ surfaces `conflict`/`error` rows; it does not fabricate a winner.
156
+ - **Data-at-rest (NSA/CISA).** memorio's `store`/`idb`/`sqlite` snapshots are
157
+ **not encrypted**. If you persist user data server-side or ship it through your
158
+ backend, encrypt it server-side with keys you manage — memorio treats the local
159
+ store as untrusted-from-the-browser and does not attest its own integrity.
160
+
161
+ ## 8. Where data lives
162
+
163
+ | Substrate | API | Volatile? | Persistent? |
164
+ |---|---|---|---|
165
+ | in-memory `Proxy` | `state` | yes (per tab) | no |
166
+ | `localStorage` / Map | `store` | no | yes (browser) |
167
+ | `sessionStorage` / Map | `session` | no | per-tab (browser) |
168
+ | IndexedDB | `idb`, `memory` durable | no | yes |
169
+ | sql.js (WASM heap) | `sqlite` | **yes** | only with `persistence: true` (snapshot → `store`) |
170
+ | sync journal | `memory.journal` | no | yes (`store`) |
@@ -0,0 +1,158 @@
1
+ # Typed Stores - Memorio
2
+
3
+ > ✅ **Universal**: Works in Browser, Node.js, Deno, and Edge Workers
4
+
5
+ `memorio.typed<T>()` returns the global `state` proxy cast to a TypeScript type `T`, giving you **compile-time** type safety on every access and mutation.
6
+
7
+ It's a **zero-runtime-cost** wrapper: the returned object is the *exact same* Proxy as `globalThis.state`, just with TypeScript types applied via a generic.
8
+
9
+ ---
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import 'memorio'
15
+
16
+ interface AppState {
17
+ user: { name: string; age: number; email: string }
18
+ theme: 'light' | 'dark'
19
+ items: string[]
20
+ }
21
+
22
+ const app = memorio.typed<AppState>()
23
+
24
+ // Type-checked at compile time:
25
+ app.user = { name: 'Sara', age: 30, email: 'sara@test.com' }
26
+ app.theme = 'dark'
27
+
28
+ // TypeScript errors:
29
+ // app.user = { name: 42 } // age missing, name wrong type
30
+ // app.theme = 'purple' // not a valid literal
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Why use typed stores?
36
+
37
+ | Without typed | With `memorio.typed<T>()` |
38
+ |---|---|
39
+ | `state.user = { name: 42 }` — runs silently, bug at runtime | `app.user = { name: 42 }` — TypeScript error at compile time |
40
+ | No autocomplete on `state.user.email` | Full IntelliSense: properties, types, method suggestions |
41
+ | Rename `user` to `profile` — no compiler warning anywhere | Every `app.user` access flagged as an error |
42
+ | AI-generated code lacks guardrails | AI gets autocomplete and type feedback inline |
43
+
44
+ ---
45
+
46
+ ## Combine with Schema Validation
47
+
48
+ Typed stores catch type errors at compile time; schema validation catches invalid values at runtime. Together they form a **defense-in-depth** strategy:
49
+
50
+ ```typescript
51
+ import 'memorio'
52
+
53
+ interface ProfileState {
54
+ profile: { bio: string; avatar?: string }
55
+ }
56
+
57
+ const app = memorio.typed<ProfileState>()
58
+
59
+ memorio.registerSchema('profile', {
60
+ type: 'object',
61
+ required: ['bio'],
62
+ properties: {
63
+ bio: { type: 'string', min: 1 },
64
+ avatar: { type: 'string' }
65
+ }
66
+ })
67
+
68
+ app.profile = { bio: 'Developer', avatar: 'pic.png' } // ✅ type + schema pass
69
+ app.profile = { avatar: 'pic.png' } // ❌ TypeScript: bio missing
70
+ // ❌ Runtime: bio required
71
+ ```
72
+
73
+ See [Schema Validation](SCHEMA.md) for runtime validation details.
74
+
75
+ ---
76
+
77
+ ## Named import variant
78
+
79
+ `typed` is also available as a named export if you prefer explicit dependencies:
80
+
81
+ ```typescript
82
+ import { typed } from 'memorio'
83
+
84
+ const app = typed<AppState>()
85
+ ```
86
+
87
+ The `memorio` namespace object is the same — `import 'memorio'` is the recommended entry, named exports are an alternative.
88
+
89
+ ---
90
+
91
+ ## Full API
92
+
93
+ | Method | Parameters | Returns | Description |
94
+ |--------|-----------|---------|-------------|
95
+ | `memorio.typed<T>()` | Generic type `T` | `T` | Returns the global `state` proxy cast to `T` |
96
+
97
+ The returned object shares the same identity as `globalThis.state`:
98
+
99
+ ```typescript
100
+ const app = memorio.typed<AppState>()
101
+ console.debug(app === state) // true — same Proxy instance
102
+ ```
103
+
104
+ ---
105
+
106
+ ## React + typed stores
107
+
108
+ Pair with the `useObserver` hook for type-safe, reactive React components:
109
+
110
+ ```tsx
111
+ import 'memorio'
112
+ import { useReducer } from 'react'
113
+
114
+ interface AppState {
115
+ user: { name: string; age: number }
116
+ theme: 'light' | 'dark'
117
+ }
118
+
119
+ const app = memorio.typed<AppState>()
120
+
121
+ function UserProfile() {
122
+ const [, forceUpdate] = useReducer(x => x + 1, 0)
123
+
124
+ useObserver(forceUpdate, [state.user.name])
125
+
126
+ return (
127
+ <div>
128
+ <h1>{app.user.name}</h1>
129
+ <span>Theme: {app.theme}</span>
130
+ </div>
131
+ )
132
+ }
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Best Practices
138
+
139
+ 1. **Define your AppState at the root** of your app and import it everywhere:
140
+
141
+ ```typescript
142
+ // types/app-state.ts
143
+ export interface AppState {
144
+ user: { name: string; email: string }
145
+ theme: 'light' | 'dark'
146
+ }
147
+ ```
148
+
149
+ ```typescript
150
+ // anywhere in your app
151
+ import 'memorio'
152
+ import type { AppState } from '../types/app-state'
153
+ const app = memorio.typed<AppState>()
154
+ ```
155
+
156
+ 2. **Layer schema validation on top** for runtime safety, especially for data coming from APIs or user input.
157
+
158
+ 3. **Use alongside `memorio.help()`** to list available globals during development.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "memorio",
3
3
  "codeName": "memorio",
4
- "version": "4.7.1",
4
+ "version": "4.8.0",
5
5
  "description": "Memorio, State + Observer, Store and iDB for an easy life - Cross-platform compatible",
6
6
  "main": "./index.cjs",
7
7
  "browser": "./index.js",
@@ -34,8 +34,9 @@
34
34
  "reactive",
35
35
  "universal",
36
36
  "cross-platform",
37
- "node",
38
- "deno",
37
+ "sqlite",
38
+ "sql.js",
39
+ "wasm",
39
40
  "browser",
40
41
  "frontend"
41
42
  ],
@@ -52,15 +53,15 @@
52
53
  "markdown/**/*",
53
54
  "types/**/*",
54
55
  "COPYRIGHT.md",
55
- "index.d.ts",
56
- "index.cjs",
57
- "index.js",
58
- "LICENSE.md",
59
- "llms.txt",
60
56
  "README.md",
61
57
  "FUNDING.yml",
62
58
  "SECURITY.md",
63
- "SUMMARY.md"
59
+ "SUMMARY.md",
60
+ "LICENSE.md",
61
+ "index.d.ts",
62
+ "index.cjs",
63
+ "index.js",
64
+ "llms.txt"
64
65
  ],
65
66
  "publishConfig": {
66
67
  "access": "public"
@@ -77,6 +78,11 @@
77
78
  "overrides": {
78
79
  "esbuild": "^0.25.0"
79
80
  },
81
+ "peerDependenciesMeta": {
82
+ "sql.js": {
83
+ "optional": true
84
+ }
85
+ },
80
86
  "exports": {
81
87
  ".": {
82
88
  "import": "./index.js",
@@ -8,12 +8,16 @@
8
8
  * @see index.ts (runtime re-export)
9
9
  */
10
10
 
11
+ /// <reference path="./schema.d.ts" />
12
+ /// <reference path="./history.d.ts" />
13
+ /// <reference path="./inspect.d.ts" />
11
14
  /// <reference path="./memorio.d.ts" />
12
15
  /// <reference path="./state.d.ts" />
13
16
  /// <reference path="./store.d.ts" />
14
17
  /// <reference path="./session.d.ts" />
15
18
  /// <reference path="./cache.d.ts" />
16
19
  /// <reference path="./idb.d.ts" />
20
+ /// <reference path="./sqlite.d.ts" />
17
21
  /// <reference path="./observer.d.ts" />
18
22
  /// <reference path="./useObserver.d.ts" />
19
23
 
@@ -23,6 +27,7 @@ export const store: _store
23
27
  export const session: _session
24
28
  export const cache: _cache
25
29
  export const idb: _idb
30
+ export const sqlite: _sqlite
26
31
  export const observer: _observer
27
32
  export const useObserver: _useObserver
28
33
  export const dispatch: _dispatch
@@ -40,6 +45,28 @@ export const isolate: _memorio['isolate']
40
45
  export const message: (...args: unknown[]) => void
41
46
  export const help: () => void
42
47
 
48
+ export const registerSchema: (path: string, schema: any) => void
49
+ export const validate: (path: string, value: any) => { valid: boolean; errors?: string[] }
50
+ export const unregisterSchema: (path: string) => boolean
51
+ export const listSchemas: () => string[]
52
+ export const typed: <T extends Record<string, any>>() => T
53
+ export const snapshot: () => Record<string, any>
54
+ export const diff: (snap: Record<string, any>) => Array<{ path: string; oldValue: any; newValue: any }>
55
+ export const undo: () => any
56
+ export const redo: () => any
57
+ export const canUndo: () => boolean
58
+ export const canRedo: () => boolean
59
+ export const rollback: (snap: Record<string, any>) => void
60
+ export const trace: () => any[]
61
+ export const enableHistory: (enabled?: boolean) => void
62
+ export const clearHistory: () => void
63
+ export const clearRedo: () => void
64
+ export const stateKeys: () => string[]
65
+ export const pathExists: (path: string) => boolean
66
+ export const stateType: (path: string) => string
67
+ export const stateGet: (path: string) => any
68
+ export const stateSchema: () => Array<{ path: string; type: string; defined: boolean }>
69
+
43
70
  export const propertyName: (container: any, object: any) => string | null
44
71
  export const internal: {
45
72
  debug: boolean
@@ -0,0 +1,27 @@
1
+ /// MEMORIO HISTORY TYPES
2
+ /// Ambient declarations for the snapshot / undo / redo / trace API.
3
+
4
+ ///
5
+ // A single recorded state mutation.
6
+ ///
7
+ interface _mutationRecord {
8
+ /** Dotted state path (e.g. 'user.name'). */
9
+ path: string
10
+ /** 'set' or 'delete'. */
11
+ action: 'set' | 'delete'
12
+ /** The new value written (for 'set'), undefined for 'delete'. */
13
+ newValue: any
14
+ /** The previous value before the mutation. */
15
+ previousValue: any
16
+ /** Epoch timestamp (ms) when the mutation occurred. */
17
+ timestamp: number
18
+ }
19
+
20
+ ///
21
+ // Result of diffing a snapshot against current state.
22
+ ///
23
+ interface _diffEntry {
24
+ path: string
25
+ oldValue: any
26
+ newValue: any
27
+ }
@@ -0,0 +1,14 @@
1
+ /// MEMORIO INSPECT TYPES
2
+ /// Ambient declarations for the state introspection API.
3
+
4
+ ///
5
+ // Schema report entry describing a single state path.
6
+ ///
7
+ interface _stateSchemaEntry {
8
+ /** Dotted path relative to `state`. */
9
+ path: string
10
+ /** Runtime type: 'string', 'number', 'boolean', 'object', 'array', 'undefined'. */
11
+ type: string
12
+ /** Whether the path resolves to a defined value. */
13
+ defined: boolean
14
+ }
@@ -1,3 +1,18 @@
1
+ import type {
2
+ MemoryEntry,
3
+ MemorySync,
4
+ MemoryOperation,
5
+ SyncProvider,
6
+ SyncConfig,
7
+ SyncAck,
8
+ SyncDirection,
9
+ RememberOptions,
10
+ RecallOptions,
11
+ ContextOptions,
12
+ ContextEntry,
13
+ MemoryStats
14
+ } from './memory'
15
+
1
16
  /**
2
17
  * Environment capabilities
3
18
  */
@@ -60,10 +75,70 @@ interface _memorio {
60
75
  session: any
61
76
  cache: any
62
77
  }
63
- help?: () => void
78
+ help?: () => void
79
+ // Schema validation
80
+ registerSchema?: (path: string, schema: any) => void
81
+ validate?: (path: string, value: any) => { valid: boolean; errors?: string[] }
82
+ unregisterSchema?: (path: string) => boolean
83
+ listSchemas?: () => string[]
84
+ // Typed store
85
+ typed?: <T extends Record<string, any>>() => T
86
+ // History / snapshot / undo / redo / trace
87
+ snapshot?: () => Record<string, any>
88
+ diff?: (snap: Record<string, any>) => Array<{ path: string; oldValue: any; newValue: any }>
89
+ undo?: () => _mutationRecord | undefined
90
+ redo?: () => _mutationRecord | undefined
91
+ canUndo?: () => boolean
92
+ canRedo?: () => boolean
93
+ rollback?: (snap: Record<string, any>) => void
94
+ trace?: () => _mutationRecord[]
95
+ enableHistory?: (enabled?: boolean) => void
96
+ clearHistory?: () => void
97
+ clearRedo?: () => void
98
+ // Introspection
99
+ stateKeys?: () => string[]
100
+ pathExists?: (path: string) => boolean
101
+ stateType?: (path: string) => string
102
+ stateGet?: (path: string) => any
103
+ stateSchema?: () => Array<{ path: string; type: string; defined: boolean }>
104
+ memory?: MemoryAPI
105
+ }
106
+
107
+ interface MemoryAPI {
108
+ remember<T>(key: string, value: T, opts?: RememberOptions<T>): Promise<void>
109
+ recall<T>(query: string, opts?: RecallOptions): Promise<T | null>
110
+ update(key: string, value: any, opts?: Partial<RememberOptions>): Promise<void>
111
+ forget(key: string): Promise<void>
112
+ context(opts?: ContextOptions): Promise<ContextEntry[]>
113
+ stats(): Promise<MemoryStats>
114
+ forgetExpired(): Promise<number>
115
+ clear(): Promise<void>
116
+ /**
117
+ * Configure the optional cloud-sync layer. Supplying a `provider` (an
118
+ * application-owned object that knows how to talk to your backend) enables
119
+ * the local operation journal. The `namespace` (tenant/user/device)
120
+ * partitions the journal so contexts/tenants cannot read each other's
121
+ * operations. Memorio never handles credentials — auth lives in the
122
+ * provider/backend.
123
+ */
124
+ configure(opts: SyncConfig): MemoryAPI
125
+ /** Local operation journal. Writes are namespaced and durable (SQLite when
126
+ * available, else localStorage). Use `replay()` to push `pending()` entries
127
+ * to the configured provider. */
128
+ journal: MemoryJournal
129
+ }
130
+
131
+ interface MemoryJournal {
132
+ append(entry: MemoryEntry, operation: MemoryOperation): Promise<MemoryEntry>
133
+ pending(): Promise<MemoryEntry[]>
134
+ markSynced(ids: string[]): Promise<number>
135
+ get(id: string): Promise<MemoryEntry | null>
136
+ clear(): Promise<void>
137
+ replay(): Promise<SyncAck>
138
+ status(): Promise<'store'>
64
139
  }
65
140
 
66
- type memorio = _memorio
141
+ type memorio = _memorio
67
142
  declare var memorio: _memorio
68
143
 
69
144
  interface GlobalMemorio {
@@ -0,0 +1,127 @@
1
+ export type MemoryScope = 'hot' | 'session' | 'local' | 'durable'
2
+
3
+ export type MemoryType =
4
+ | 'fact'
5
+ | 'preference'
6
+ | 'decision'
7
+ | 'task'
8
+ | 'context'
9
+
10
+ export type MemoryStatus = 'active' | 'obsolete' | 'superseded'
11
+
12
+ /**
13
+ * Logical operations recorded in the local sync journal.
14
+ * The cloud side only ever sees operations — never a raw dump of a substrate.
15
+ */
16
+ export type MemoryOperation =
17
+ | 'remember'
18
+ | 'update'
19
+ | 'forget'
20
+ | 'expire'
21
+ | 'confirm'
22
+ | 'supersede'
23
+
24
+ /**
25
+ * Synchronization state of a journal entry against a remote/cloud backend.
26
+ */
27
+ export type MemorySync = 'pending' | 'synced' | 'conflict' | 'error'
28
+
29
+ export type SyncDirection = 'up' | 'down' | 'both'
30
+
31
+ /**
32
+ * Pluggable cloud/sync transport. Memorio never ships a credential flow:
33
+ * the application supplies a `provider` that knows how to talk to its backend
34
+ * (REST, WebSocket, Supabase, a custom agent server, ...). Memorio owns the
35
+ * local journal, the provider only moves operations.
36
+ */
37
+ export interface SyncProvider {
38
+ /** Pushes pending local operations; returns ids that were accepted */
39
+ push(ops: MemoryEntry[]): Promise<{ synced: string[]; conflicts?: string[]; error?: string }>
40
+ /** Optional pull of remote operations newer than `since` (epoch ms) */
41
+ pull?(since?: number): Promise<MemoryEntry[]>
42
+ /** Optional conflict resolution hint: 'local' | 'remote' | 'merge' */
43
+ resolve?(op: MemoryEntry): Promise<'local' | 'remote' | 'merge'>
44
+ }
45
+
46
+ export interface SyncConfig {
47
+ /** Sync provider implementation (application supplied) */
48
+ provider?: SyncProvider
49
+ /** Which direction to sync; 'both' is the default */
50
+ direction?: SyncDirection
51
+ /** Auto-sync on every local write? defaults to true */
52
+ auto?: boolean
53
+ }
54
+
55
+ export interface SyncAck {
56
+ synced: string[]
57
+ conflicts?: string[]
58
+ error?: string
59
+ }
60
+
61
+ export interface MemoryEntry<T = any> {
62
+ id: string
63
+ key: string
64
+ value: T
65
+ type: MemoryType
66
+ confidence: number
67
+ scope: MemoryScope
68
+ ttl?: number | null
69
+ tags: string[]
70
+ source?: string
71
+ status: MemoryStatus
72
+ createdAt: number
73
+ lastConfirmedAt: number
74
+ supersededId?: string | null
75
+ /** Monotonic per-key write counter, used for last-writer / conflict resolution */
76
+ version?: number
77
+ /** Epoch ms of the last mutation that touched this entry */
78
+ updatedAt?: number
79
+ /** Last logical operation that produced this entry */
80
+ operation?: MemoryOperation
81
+ /** Local-cloud sync state of the entry (undefined => not tracked for sync) */
82
+ sync?: MemorySync
83
+ }
84
+
85
+ export interface RememberOptions<T = any> {
86
+ id?: string
87
+ type?: MemoryType
88
+ confidence?: number
89
+ scope?: MemoryScope
90
+ ttl?: number | null
91
+ tags?: string | string[]
92
+ source?: string
93
+ }
94
+
95
+ export interface RecallOptions {
96
+ type?: MemoryType | MemoryType[]
97
+ tags?: string | string[]
98
+ minConfidence?: number
99
+ includeObsolete?: boolean
100
+ }
101
+
102
+ export interface ContextOptions {
103
+ tags?: string | string[]
104
+ types?: MemoryType | MemoryType[]
105
+ minConfidence?: number
106
+ maxEntries?: number
107
+ scopes?: MemoryScope[]
108
+ }
109
+
110
+ export interface ContextEntry {
111
+ key: string
112
+ value: any
113
+ type: MemoryType
114
+ confidence: number
115
+ scope: MemoryScope
116
+ tags: string[]
117
+ age: number
118
+ accessCount: number
119
+ lastAccessedAt: number
120
+ }
121
+
122
+ export interface MemoryStats {
123
+ total: number
124
+ byScope: Record<MemoryScope, number>
125
+ byType: Partial<Record<MemoryType, number>>
126
+ expired: number
127
+ }