@ai2070/memex 0.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.
Files changed (46) hide show
  1. package/.github/workflows/ci.yml +31 -0
  2. package/.github/workflows/release.yml +35 -0
  3. package/API.md +1078 -0
  4. package/LICENSE +190 -0
  5. package/README.md +574 -0
  6. package/package.json +30 -0
  7. package/src/bulk.ts +128 -0
  8. package/src/envelope.ts +52 -0
  9. package/src/errors.ts +27 -0
  10. package/src/graph.ts +15 -0
  11. package/src/helpers.ts +51 -0
  12. package/src/index.ts +142 -0
  13. package/src/integrity.ts +378 -0
  14. package/src/intent.ts +311 -0
  15. package/src/query.ts +357 -0
  16. package/src/reducer.ts +177 -0
  17. package/src/replay.ts +32 -0
  18. package/src/retrieval.ts +306 -0
  19. package/src/serialization.ts +34 -0
  20. package/src/stats.ts +62 -0
  21. package/src/task.ts +373 -0
  22. package/src/transplant.ts +488 -0
  23. package/src/types.ts +248 -0
  24. package/tests/bugfix-and-coverage.test.ts +958 -0
  25. package/tests/bugfix-holes.test.ts +856 -0
  26. package/tests/bulk.test.ts +256 -0
  27. package/tests/edge-cases-v2.test.ts +355 -0
  28. package/tests/edge-cases.test.ts +661 -0
  29. package/tests/envelope.test.ts +92 -0
  30. package/tests/graph.test.ts +41 -0
  31. package/tests/helpers.test.ts +120 -0
  32. package/tests/integrity.test.ts +371 -0
  33. package/tests/intent.test.ts +276 -0
  34. package/tests/query-advanced.test.ts +252 -0
  35. package/tests/query.test.ts +623 -0
  36. package/tests/reducer.test.ts +342 -0
  37. package/tests/replay.test.ts +145 -0
  38. package/tests/retrieval.test.ts +691 -0
  39. package/tests/serialization.test.ts +118 -0
  40. package/tests/setup.test.ts +7 -0
  41. package/tests/stats.test.ts +163 -0
  42. package/tests/task.test.ts +322 -0
  43. package/tests/transplant.test.ts +385 -0
  44. package/tests/types.test.ts +231 -0
  45. package/tsconfig.json +18 -0
  46. package/vitest.config.ts +7 -0
package/README.md ADDED
@@ -0,0 +1,574 @@
1
+ # MemEX — Structured Memory for AI Agents
2
+
3
+ Multi-session continuity for AI systems.
4
+
5
+ MemEX stores beliefs, evidence, conflicts, and updates -- not just retrieved text. It gives agents a continuous belief state across sessions instead of fragmented chat logs.
6
+
7
+ ## The Problem
8
+
9
+ Every chat session starts from scratch. Memory systems try to fix this by appending text and summarizing when it gets long. But that loses:
10
+
11
+ - **Why** something is believed (provenance)
12
+ - **How much** to trust it (authority, conviction)
13
+ - **What conflicts** with it (contradictions)
14
+ - **Whether** it's still relevant (decay)
15
+ - **Where** it came from (source attribution)
16
+
17
+ The result: agents that can retrieve old text but can't reason about what they know.
18
+
19
+ ## What MemEX Does
20
+
21
+ MemEX is a typed, scored, provenance-tracked graph. Each memory item carries:
22
+
23
+ - A **kind** -- what it is (observation, assertion, hypothesis, derivation, simulation, policy, trait)
24
+ - A **source_kind** -- how it got here (user-stated, observed, inferred, imported)
25
+ - Three **scores** -- authority (trust), conviction (author confidence), importance (attention priority)
26
+ - **Parents** -- what items it was derived from, forming provenance chains
27
+ - **Edges** -- typed relationships to other items (supports, contradicts, supersedes, alias)
28
+
29
+ This means the system can:
30
+
31
+ - Carry forward beliefs across sessions, not just text
32
+ - Track what was observed vs inferred vs assumed
33
+ - Surface contradictions instead of silently overwriting
34
+ - Explain *why* it believes something (provenance tree)
35
+ - Decay stale context while preserving stable knowledge
36
+ - Recognize that two observations refer to the same entity
37
+
38
+ ## Where MemEX Fits
39
+
40
+ MemEX is the structured memory layer in a larger stack. It doesn't replace your other tools -- it gives them something better to read from and write to.
41
+
42
+ ```
43
+ ┌─────────────────────────────────────────────────┐
44
+ │ Agent / App │
45
+ │ │
46
+ │ ┌──────────┐ ┌──────────┐ ┌───────────────┐ │
47
+ │ │ Chat │ │ Working │ │ Cognition │ │
48
+ │ │ Window │ │ Memory │ │ Layer │ │
49
+ │ │ (sliding) │ │(scratch) │ │ (thinking) │ │
50
+ │ └────┬─────┘ └────┬─────┘ └──────┬────────┘ │
51
+ │ │ │ │ │
52
+ │ └──────────────┼───────────────┘ │
53
+ │ │ │
54
+ │ ┌───────▼────────┐ │
55
+ │ │ MemEX │ │
56
+ │ │ (this library) │ │
57
+ │ └───────┬────────┘ │
58
+ │ │ │
59
+ │ ┌────────────┼────────────┐ │
60
+ │ │ │ │ │
61
+ │ ┌─────▼─────┐ ┌───▼───┐ ┌─────▼─────┐ │
62
+ │ │ Vector │ │ Text │ │ Event │ │
63
+ │ │ Search │ │ Search│ │ Store │ │
64
+ │ └───────────┘ └───────┘ └───────────┘ │
65
+ └─────────────────────────────────────────────────┘
66
+ ```
67
+
68
+ ### How the pieces connect
69
+
70
+ **Chat window (sliding context)** -- the current conversation. As messages flow, the agent extracts observations, assertions, and preferences and writes them to MemEX. The chat window is ephemeral; MemEX is where things persist.
71
+
72
+ **Working memory (scratchpad)** -- short-lived, high-importance items the agent is actively reasoning about. These live in MemEX with `kind: "hypothesis"` or `kind: "assumption"` and high `importance`. After processing, their importance decays and they settle into long-term memory.
73
+
74
+ **Vector / text search** -- MemEX stores structured items, not embeddings. Search tools subscribe to MemEX lifecycle events and maintain their own indexes. Search indexes are derived from MemEX, not the other way around.
75
+
76
+ **Cognition layer** -- uses `getScoredItems` and `smartRetrieve` to build its thinking queue. Writes back inferred items, resolved contradictions, and updated scores. The agent prioritizes thinking using authority, conviction, and importance.
77
+
78
+ **Event store** -- the append-only command log. MemEX emits lifecycle events that get persisted. On restart, `replayFromEnvelopes` rebuilds the graph from the log.
79
+
80
+ MemEX is the system of record. The library itself is pure TypeScript with a single runtime dependency (`uuidv7`). Storage, search, and bus integration belong in the service layer above.
81
+
82
+ ### What changes in agent behavior
83
+
84
+ Without MemEX, an agent:
85
+ - Forgets between sessions, or retrieves flat text with no trust signal
86
+ - Can't tell if something was observed, inferred, or assumed
87
+ - Silently overwrites old beliefs with new ones
88
+ - Can't explain why it believes something
89
+ - Treats everything as equally important
90
+
91
+ With MemEX, an agent:
92
+ - Carries forward a structured belief state across sessions
93
+ - Knows the difference between an observation and a hypothesis
94
+ - Surfaces contradictions instead of hiding them
95
+ - Can trace any belief back to its evidence chain
96
+ - Prioritizes what to think about based on importance and uncertainty
97
+ - Lets stale context fade while stable knowledge persists
98
+
99
+ ## Install
100
+
101
+ ```bash
102
+ npm install @ai2070/memex
103
+ ```
104
+
105
+ ## Quick Start
106
+
107
+ ```ts
108
+ import {
109
+ createGraphState,
110
+ createMemoryItem,
111
+ applyCommand,
112
+ getItems,
113
+ getScoredItems,
114
+ smartRetrieve,
115
+ } from "@ai2070/memex";
116
+
117
+ // create an empty graph
118
+ let state = createGraphState();
119
+
120
+ // add an observation
121
+ const obs = createMemoryItem({
122
+ scope: "user:laz/general",
123
+ kind: "observation",
124
+ content: { key: "login_count", value: 42 },
125
+ author: "agent:monitor",
126
+ source_kind: "observed",
127
+ authority: 0.9,
128
+ importance: 0.7,
129
+ });
130
+
131
+ const result = applyCommand(state, { type: "memory.create", item: obs });
132
+ state = result.state;
133
+
134
+ // add a hypothesis derived from the observation
135
+ const hyp = createMemoryItem({
136
+ scope: "user:laz/general",
137
+ kind: "hypothesis",
138
+ content: { key: "is_power_user", value: true },
139
+ author: "agent:reasoner",
140
+ source_kind: "agent_inferred",
141
+ parents: [obs.id],
142
+ authority: 0.4,
143
+ conviction: 0.7,
144
+ importance: 0.8,
145
+ });
146
+
147
+ state = applyCommand(state, { type: "memory.create", item: hyp }).state;
148
+
149
+ // query with filters
150
+ const recent = getItems(state, {
151
+ or: [{ kind: "observation" }, { kind: "assertion" }],
152
+ range: { authority: { min: 0.5 } },
153
+ created: { after: Date.now() - 86400000 },
154
+ });
155
+
156
+ // scored retrieval with time decay
157
+ const ranked = getScoredItems(
158
+ state,
159
+ {
160
+ authority: 0.5,
161
+ conviction: 0.3,
162
+ importance: 0.2,
163
+ decay: { rate: 0.1, interval: "day", type: "exponential" },
164
+ },
165
+ { pre: { scope: "user:laz/general" }, limit: 10 },
166
+ );
167
+
168
+ // smart retrieval: decay + contradiction surfacing + diversity + budget
169
+ const context = smartRetrieve(state, {
170
+ budget: 4096,
171
+ costFn: (item) => JSON.stringify(item.content).length,
172
+ weights: {
173
+ authority: 0.5,
174
+ importance: 0.5,
175
+ decay: { rate: 0.1, interval: "day", type: "exponential" },
176
+ },
177
+ filter: { scope: "user:laz/general" },
178
+ contradictions: "surface",
179
+ diversity: { author_penalty: 0.3 },
180
+ });
181
+ ```
182
+
183
+ ## Core Concepts
184
+
185
+ ### Memory Items
186
+
187
+ Not everything is a "fact." A `MemoryItem` can be an observation, an assertion, an assumption, a hypothesis, a derivation, a simulation, a policy, or a trait. The `kind` field says what it *is*; the `source_kind` field says how it *got here*.
188
+
189
+ ### Three Scores
190
+
191
+ | Score | Question | Range |
192
+ |-------|----------|-------|
193
+ | `authority` | How much should the system trust this? | 0..1 |
194
+ | `conviction` | How sure was the author? | 0..1 |
195
+ | `importance` | How much attention does this need right now? (salience) | 0..1 |
196
+
197
+ These are orthogonal. A hypothesis can be high-importance (matters a lot) but low-authority (not yet verified).
198
+
199
+ ### Time Decay
200
+
201
+ Scores decay over time at query time -- the stored values are not mutated. Configure decay per query:
202
+
203
+ ```ts
204
+ { rate: 0.1, interval: "day", type: "exponential" }
205
+ ```
206
+
207
+ Three types: **exponential** (smooth curve, never zero), **linear** (straight to zero), **step** (drops at interval boundaries). You can also filter out items that have decayed below a threshold.
208
+
209
+ ### Provenance
210
+
211
+ Items can declare **parents** -- the items they were derived or inferred from. This creates provenance chains that let the system explain *why* it believes something:
212
+
213
+ ```ts
214
+ getSupportSet(state, claimId)
215
+ // -> [claim, parent1, parent2, grandparent1] -- everything that justifies this claim
216
+ ```
217
+
218
+ If a parent is retracted, `getStaleItems` finds orphaned children. `cascadeRetract` removes the entire dependency chain.
219
+
220
+ ### Contradictions
221
+
222
+ When two items conflict, they can be linked with a `CONTRADICTS` edge. At retrieval time:
223
+
224
+ - `contradictions: "filter"` -- keep the higher-scoring side (clean context)
225
+ - `contradictions: "surface"` -- keep both, flagged with `contradicted_by` (agent reasoning)
226
+
227
+ Contradictions can be resolved: `resolveContradiction` creates a `SUPERSEDES` edge and lowers the loser's authority.
228
+
229
+ ### Identity
230
+
231
+ Two observations of the same entity can be aliased: `markAlias` creates bidirectional `ALIAS` edges. `getAliasGroup` returns the full identity group via transitive closure.
232
+
233
+ ### Edges
234
+
235
+ Typed relationships between items:
236
+
237
+ | Edge | Meaning |
238
+ |------|---------|
239
+ | `DERIVED_FROM` | Relationship discovered after creation |
240
+ | `CONTRADICTS` | Two items assert conflicting things |
241
+ | `SUPPORTS` | Evidence for another item |
242
+ | `ABOUT` | References another item |
243
+ | `SUPERSEDES` | Replaces another item (conflict resolution) |
244
+ | `ALIAS` | Same entity, different observations |
245
+
246
+ ### Events
247
+
248
+ Three categories, all under `namespace: "memory"`:
249
+
250
+ - **Commands** (imperative): `memory.create`, `memory.update`, `memory.retract`, `edge.create`, `edge.update`, `edge.retract`
251
+ - **Lifecycle** (past tense): `memory.created`, `memory.updated`, `memory.retracted`, `edge.created`, `edge.updated`, `edge.retracted`
252
+ - **State**: `state.memory`, `state.edge`
253
+
254
+ Commands go in, lifecycle events come out of the reducer, state events are full snapshots for downstream consumers.
255
+
256
+ ### Immutability
257
+
258
+ `applyCommand` never mutates input state. It returns a new `GraphState` and an array of lifecycle events. History is in the append-only event log; `GraphState` is always the latest snapshot.
259
+
260
+ ## Three Graphs
261
+
262
+ MemEX contains three logical graphs in one package. Use what you need:
263
+
264
+ | Graph | Purpose | Core type | Namespace |
265
+ |-------|---------|-----------|-----------|
266
+ | **Memory** | Epistemic state -- beliefs, evidence, contradictions | `MemoryItem` | `"memory"` |
267
+ | **Intent** | Goals and objectives | `Intent` | `"intent"` |
268
+ | **Task** | Units of work tied to intents | `Task` | `"task"` |
269
+
270
+ All three follow the same pattern: commands → reducer → lifecycle events. They cross-reference by ID:
271
+
272
+ ```ts
273
+ // intent links to memory items that motivated it
274
+ const intent = createIntent({ label: "find_kati", root_memory_ids: [obs.id], ... });
275
+
276
+ // task links to its parent intent and memory items it consumes/produces
277
+ const task = createTask({ intent_id: intent.id, input_memory_ids: [obs.id], ... });
278
+
279
+ // after task completes, memory items link back
280
+ createMemoryItem({ ..., meta: { creation_intent_id: intent.id, creation_task_id: task.id } });
281
+ ```
282
+
283
+ ## The Loop
284
+
285
+ The three graphs form a continuous cycle:
286
+
287
+ ```
288
+ ┌─────────────────────────────────────────┐
289
+ │ │
290
+ ▼ │
291
+ Memory ──────► Intent ──────► Task ──────────┘
292
+ (belief) (direction) (execution)
293
+ │ │ │
294
+ │ something │ spawns │ produces
295
+ │ important │ actionable │ new memory
296
+ │ or uncertain │ steps │ (results,
297
+ │ appears │ │ failures,
298
+ │ │ │ observations)
299
+ └───────────────┘ │
300
+ updates belief │
301
+ state with new ◄──────────┘
302
+ evidence
303
+ ```
304
+
305
+ 1. **Memory produces intents** — an important or uncertain item surfaces, triggering a goal
306
+ 2. **Intents spawn tasks** — the goal breaks into actionable steps
307
+ 3. **Tasks produce new memory** — results, observations, and failures write back as memory items
308
+ 4. **Memory updates belief state** — new evidence resolves contradictions, reinforces or decays existing beliefs
309
+
310
+ Most AI systems mix these together: goals hidden in prompts, tasks implicit in code, memory as text blobs. MemEX separates them:
311
+
312
+ | Layer | Responsibility |
313
+ |-------|---------------|
314
+ | Memory | What is believed |
315
+ | Intent | What is wanted |
316
+ | Task | What is done |
317
+
318
+ Each layer has its own types, commands, reducer, and query — but they reference each other by ID and share the same event envelope pattern. The separation is what makes the loop auditable: you can trace any belief back to the task that produced it, the intent that motivated it, and the evidence it was based on.
319
+
320
+ ## Cognitive Transfer
321
+
322
+ The three graphs together form a complete cognitive state that can be serialized, transferred, and resumed by another agent.
323
+
324
+ ```
325
+ Agent A → Agent B:
326
+
327
+ Memory export (what I know)
328
+ + Intent export (what I want)
329
+ + Task export (what I've tried, what worked, what failed)
330
+ = Complete cognitive state transferred
331
+ ```
332
+
333
+ This isn't just data migration. The receiving agent inherits:
334
+
335
+ - **Context** — the belief state (observations, hypotheses, contradictions)
336
+ - **Direction** — active goals and their priorities
337
+ - **Progress** — which approaches were tried, which failed, which are still running
338
+
339
+ The agent picks up where the other left off. It doesn't re-derive context from scratch. It doesn't retry failed approaches. It continues.
340
+
341
+ ### The vector model
342
+
343
+ Think of the three graphs as a cognitive vector:
344
+
345
+ | Component | Role | Analogy |
346
+ |-----------|------|---------|
347
+ | **Memory** | Origin | Starting point in state space — what is known |
348
+ | **Intent** | Magnitude | How much energy is allocated — priority and importance |
349
+ | **Task** | Direction | Which approaches have been tried — path through solution space |
350
+
351
+ Transferring cognition between agents is transferring this vector. The receiving agent starts from the same origin (memory), pursues the same goals with the same energy (intent), and avoids the same dead ends (task history).
352
+
353
+ This is what `exportSlice` / `importSlice` enables at the library level. The transport layer (network, bus, file) is outside the library; MemEX provides the serializable structure.
354
+
355
+ ## Features
356
+
357
+ **Memory graph:**
358
+ - Full query algebra: `and`, `or`, `not`, `range`, `ids`, `scope_prefix`, `parents` (includes/count), `meta` (dot-path), `meta_has`, `created` (time range), `decay` (freshness filter)
359
+ - Multi-sort with tiebreakers (authority, conviction, importance, recency)
360
+ - Configurable time decay: exponential, linear, or step -- applied at query time, not stored
361
+ - Scored retrieval with pre/post filters, min_score threshold, and decay
362
+ - Smart retrieval: contradiction-aware packing + diversity penalties + budget limits
363
+ - Budget-aware retrieval (greedy knapsack by score/cost)
364
+ - Provenance trees and minimal support sets (`getSupportTree`, `getSupportSet`)
365
+ - Temporal sort and time-based importance decay
366
+ - Bulk transforms with conditional update/retract (`applyMany`)
367
+ - Conflict detection and resolution (`CONTRADICTS` / `SUPERSEDES`)
368
+ - Staleness detection and cascade retraction
369
+ - Identity resolution (transitive `ALIAS` groups)
370
+ - Serialization (`toJSON` / `fromJSON` / `stringify` / `parse`)
371
+ - Graph stats (counts by kind, author, scope, edge kind)
372
+ - Event envelope wrapping for bus integration
373
+ - Command log replay for state reconstruction
374
+
375
+ **Intent graph:**
376
+ - Status machine: active ↔ paused → completed / cancelled
377
+ - Query by owner, status, priority, linked memory items
378
+ - Invalid transitions throw typed errors
379
+
380
+ **Task graph:**
381
+ - Status machine: pending → running → completed / failed, with retry support (failed → running)
382
+ - Links to parent intent, input/output memory items, agent assignment
383
+ - Query by intent, action, status, agent, linked memory items
384
+
385
+ **Transplant (export / import):**
386
+ - Export a self-contained slice by walking provenance chains, aliases, related intents/tasks
387
+ - Import into another graph instance — default: skip existing ids, append-only
388
+ - Optional shallow compare to detect conflicts, optional re-id to mint new ids on conflict
389
+ - JSON-serializable slices for migration, sub-agent isolation, cloning, and backup
390
+
391
+ ## Multi-Agent & Crew Orchestration
392
+
393
+ MemEX supports multi-agent systems where each agent works on a segment of the graph. No separate memory stores per agent — one graph, segmented by conventions.
394
+
395
+ ### Soft isolation (shared graph, scoped views)
396
+
397
+ Each agent reads and writes to the shared graph, filtered by `meta.agent_id` and `scope`:
398
+
399
+ ```ts
400
+ // agent:researcher only sees its own observations
401
+ const myMemories = getItems(state, {
402
+ meta: { agent_id: "agent:researcher" },
403
+ });
404
+
405
+ // agent:analyst sees everything in a project scope
406
+ const projectMemories = getItems(state, {
407
+ scope_prefix: "project:cyberdeck/",
408
+ });
409
+
410
+ // orchestrator sees all agents' work, ranked by importance
411
+ const ranked = getScoredItems(state,
412
+ { authority: 0.5, importance: 0.5 },
413
+ { pre: { scope_prefix: "project:cyberdeck/" } },
414
+ );
415
+ ```
416
+
417
+ Agents write with their own `author` and `meta.agent_id`. The orchestrator can query across all agents, compare their findings, and resolve contradictions.
418
+
419
+ ### Hard isolation (exported slices)
420
+
421
+ For risky operations or external sandboxes, export a slice for the sub-agent to work on independently:
422
+
423
+ ```ts
424
+ // give the sub-agent a slice of the graph
425
+ const slice = exportSlice(memState, intentState, taskState, {
426
+ memory_ids: relevantIds,
427
+ include_parents: true,
428
+ include_related_tasks: true,
429
+ });
430
+
431
+ // sub-agent works on its own copy...
432
+ // ...then merge results back
433
+ const { memState: updated, report } = importSlice(
434
+ memState, intentState, taskState,
435
+ subAgentSlice,
436
+ );
437
+ // report.created -> what the sub-agent added
438
+ // existing items untouched (append-only)
439
+ ```
440
+
441
+ ### Crew patterns
442
+
443
+ | Pattern | How |
444
+ |---------|-----|
445
+ | Shared workspace | All agents write to the same scope, filter by `meta.agent_id` to see own work |
446
+ | Pipeline | Agent A's `output_memory_ids` on a task become agent B's `input_memory_ids` |
447
+ | Review | Agent B reads agent A's items, creates `SUPPORTS` / `CONTRADICTS` edges |
448
+ | Delegation | Orchestrator creates an intent, assigns tasks to specific agents via `task.agent_id` |
449
+ | Sandbox | Export slice → sub-agent mutates copy → import results back |
450
+
451
+ ### What the `author` and `meta` fields enable
452
+
453
+ ```ts
454
+ // who wrote this?
455
+ item.author // "agent:researcher"
456
+
457
+ // which agent instance?
458
+ item.meta.agent_id // "agent:researcher-v2"
459
+
460
+ // which session?
461
+ item.meta.session_id // "session-abc"
462
+
463
+ // which crew run?
464
+ item.meta.crew_id // "crew:investigation-42"
465
+
466
+ // which intent spawned this?
467
+ item.meta.creation_intent_id // "i1"
468
+ ```
469
+
470
+ All of these are queryable via `meta` and `meta_has` filters. The graph is one shared structure; segmentation is just queries.
471
+
472
+ ## Dynamic Resolution
473
+
474
+ MemEX supports different levels of detail at every stage of the memory lifecycle:
475
+
476
+ | Stage | Low resolution | High resolution |
477
+ |-------|---------------|----------------|
478
+ | **Retrieval** | High-authority items only, no inferred, fast | Include hypotheses, simulations, full provenance chains |
479
+ | **Thinking** | Direct facts + deterministic derivations | Multi-hop reasoning, contradiction surfacing, support tree traversal |
480
+ | **Insertion** | Store summaries, mark details as low-importance | Store atomic events with full `DERIVED_FROM` chains |
481
+
482
+ Resolution is controlled through the same primitives -- filters, score weights, and decay:
483
+
484
+ ```ts
485
+ // low resolution: only trusted, recent items
486
+ getItems(state, {
487
+ range: { authority: { min: 0.7 } },
488
+ not: { or: [{ kind: "hypothesis" }, { kind: "simulation" }] },
489
+ decay: { config: { rate: 0.3, interval: "day", type: "exponential" }, min: 0.5 },
490
+ });
491
+
492
+ // high resolution: everything, scored and ranked
493
+ smartRetrieve(state, {
494
+ budget: 8192,
495
+ costFn: (item) => JSON.stringify(item.content).length,
496
+ weights: { authority: 0.3, conviction: 0.3, importance: 0.4 },
497
+ contradictions: "surface",
498
+ });
499
+ ```
500
+
501
+ The agent decides resolution based on the task. A routine action uses low resolution. A decision with consequences uses high resolution. The same graph serves both -- no separate "fast" and "deep" memory stores.
502
+
503
+ ### Thinking Budget from Scores
504
+
505
+ The three scores can drive the thinking budget itself. Items that are important but uncertain deserve more processing. Items that have been processed should have their importance reduced.
506
+
507
+ ```text
508
+ thinking_priority = importance * (1 - authority)
509
+ ```
510
+
511
+ An item with `importance: 0.9` and `authority: 0.3` gets priority `0.63` -- high attention, uncertain, worth reasoning about. An item with `importance: 0.9` and `authority: 0.95` gets priority `0.045` -- important but already trusted, just use it.
512
+
513
+ After the agent processes an item, reduce its importance:
514
+
515
+ ```ts
516
+ applyCommand(state, {
517
+ type: "memory.update",
518
+ item_id: processedItem.id,
519
+ partial: { importance: processedItem.importance * 0.3 },
520
+ author: "system:thinker",
521
+ reason: "processed",
522
+ });
523
+ ```
524
+
525
+ This creates a natural attention cycle: new items arrive with high importance, get processed, importance drops, and they fade into long-term memory unless re-activated. Items that are never processed accumulate and eventually surface through importance-weighted queries.
526
+
527
+ The cognition layer above can use `getScoredItems` with importance-heavy weights to build its thinking queue, and `decayImportance` to age out items that were never worth processing.
528
+
529
+ ## Choosing Parameters
530
+
531
+ The library provides knobs. Here's how to think about turning them.
532
+
533
+ ### Decay
534
+
535
+ | Scenario | Recommendation |
536
+ |----------|---------------|
537
+ | Chat context, ephemeral state | Fast decay: `{ rate: 0.3, interval: "hour", type: "linear" }` |
538
+ | Project knowledge, working memory | Moderate decay: `{ rate: 0.1, interval: "day", type: "exponential" }` |
539
+ | Policies, traits, identity | No decay — these don't become less true over time |
540
+ | Mixed graph | Use the `decay` filter to exclude stale items, but don't decay items with `kind: "policy"` or `kind: "trait"` — filter them in separately with `or` |
541
+
542
+ ### Diversity penalties
543
+
544
+ | Scenario | Recommendation |
545
+ |----------|---------------|
546
+ | Exploration ("what do we know?") | High `author_penalty` (0.3-0.5) — spread across sources |
547
+ | Verification ("is this true?") | Low or zero `author_penalty` — you *want* correlated evidence |
548
+ | Summarization | Moderate `parent_penalty` (0.2-0.3) — avoid redundant derivations |
549
+ | Debugging / audit | Zero penalties — show everything |
550
+
551
+ ### Score weights
552
+
553
+ | Scenario | Weights |
554
+ |----------|---------|
555
+ | High-trust retrieval | `{ authority: 0.8, importance: 0.2 }` |
556
+ | Attention-driven (what needs processing?) | `{ importance: 0.8, authority: 0.2 }` |
557
+ | Agent self-evaluation | `{ conviction: 0.5, authority: 0.5 }` |
558
+ | Balanced | `{ authority: 0.4, conviction: 0.3, importance: 0.3 }` |
559
+
560
+ ### Contradiction handling
561
+
562
+ | Scenario | Mode |
563
+ |----------|------|
564
+ | User-facing context (clean, no confusion) | `contradictions: "filter"` |
565
+ | Agent reasoning (needs to see disagreement) | `contradictions: "surface"` |
566
+ | Audit / debugging | Neither — use `getContradictions()` directly |
567
+
568
+ These are starting points, not prescriptions. Calibrate based on your use case.
569
+
570
+ See [API.md](./API.md) for the full API reference.
571
+
572
+ ## License
573
+
574
+ Apache 2.0 -- see [LICENSE](./LICENSE).
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@ai2070/memex",
3
+ "version": "0.9.0",
4
+ "description": "MemEX memory layer — graph of memories and edges over an append-only event log",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "test": "vitest run",
17
+ "test:watch": "vitest",
18
+ "prettier": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
19
+ "prettier:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\""
20
+ },
21
+ "license": "Apache-2.0",
22
+ "dependencies": {
23
+ "uuidv7": "^1.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "prettier": "^3.8.2",
27
+ "typescript": "^5.9.0",
28
+ "vitest": "^4.0.0"
29
+ }
30
+ }