memorio 5.1.4 → 5.2.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/AGENTS.md +21 -12
- package/CHANGELOG.md +18 -6
- package/README.md +44 -7
- package/SECURITY-HARDENING.md +258 -0
- package/SECURITY.md +67 -3
- package/SUMMARY.md +55 -59
- package/adr/002-observer-semantics.md +1 -1
- package/adr/003-deep-mutation-semantics.md +1 -1
- package/adr/004-array-mutation-semantics.md +1 -1
- package/adr/010-logic-phase-0.md +42 -0
- package/adr/README.md +16 -11
- package/bin/cli.js +82 -60
- package/examples/acquired-knowledge.ts +174 -0
- package/examples/agent-memory-demo.ts +140 -0
- package/examples/sync.ts +90 -90
- package/examples/useObserver.tsx +2 -2
- package/global.cjs +1995 -124
- package/global.js +1990 -125
- package/index.cjs +1995 -124
- package/index.d.ts +1 -0
- package/index.js +1990 -125
- package/llms.txt +122 -4
- package/markdown/EXTENSION_VSCODE_DESIGN.md +410 -0
- package/markdown/LOGIC.md +100 -0
- package/markdown/MEMORY-ATTACHMENT.md +17 -10
- package/markdown/MEMORY.md +378 -15
- package/markdown/MEM_FORMAT.md +313 -0
- package/markdown/STATE.md +27 -3
- package/markdown/SYNC.md +18 -14
- package/markdown/TEMPORAL.md +297 -0
- package/markdown/USEOBSERVER.md +7 -4
- package/modules/redux.cjs +1159 -32
- package/modules/redux.cjs.map +1 -1
- package/modules/redux.js +1158 -32
- package/modules/redux.js.map +1 -1
- package/package.json +14 -5
- package/types/exports.d.ts +47 -3
- package/types/logic.d.ts +79 -0
- package/types/memorio.d.ts +60 -16
- package/types/memory.d.ts +118 -0
- package/types/session.d.ts +1 -4
- package/types/store.d.ts +1 -4
- package/types/temporal.d.ts +95 -0
- package/types/useObserver.d.ts +6 -10
- package/vsix/memorio.vsix +0 -0
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
> **Status:**
|
|
2
|
-
> **Date:** 2026-09-12
|
|
3
|
-
> **Scope**: API Reference
|
|
4
|
-
> **Standard**: Memorio API Specification v5
|
|
5
|
-
>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
> **Status:** Proposed (not yet implemented)
|
|
2
|
+
> **Date:** 2026-09-12
|
|
3
|
+
> **Scope**: API Reference
|
|
4
|
+
> **Standard**: Memorio API Specification v5
|
|
5
|
+
>
|
|
6
|
+
> **Not implemented.** The Node Attachment System described in this document is a
|
|
7
|
+
> proposed specification. None of the APIs below - `node.connect`, `node.attach`,
|
|
8
|
+
> `node.related`, `node.traverse`, `memory.connect`, `memory.disconnect` - are
|
|
9
|
+
> present in the current `MemoryAPI`. Additionally, `memory.remember()` returns
|
|
10
|
+
> `Promise<void>`, not a node object with relationship methods. This document is
|
|
11
|
+
> kept as a design reference until the feature is implemented.
|
|
12
|
+
>
|
|
13
|
+
---
|
|
14
|
+
# Node Attachment System (proposed)
|
|
15
|
+
|
|
16
|
+
`memorio.memory` is specified to extend with a **Node Attachment System** allowing memory elements to dynamically attach, reference, or connect to other memory elements at runtime.
|
|
10
17
|
|
|
11
18
|
This is an **extension**, not a replacement, of existing memory semantics.
|
|
12
19
|
|
|
@@ -92,4 +99,4 @@ Nodes serialize to IDs only - never recursively embedded graphs.
|
|
|
92
99
|
|
|
93
100
|
## Backward Compatibility
|
|
94
101
|
|
|
95
|
-
Existing APIs (`remember`, `
|
|
102
|
+
Existing APIs (`remember`, `recall`, `forget`, `context`) continue to work unchanged. A node-enabled memory entry remains usable as a normal memory entry.
|
package/markdown/MEMORY.md
CHANGED
|
@@ -1,14 +1,114 @@
|
|
|
1
|
-
> **Status:** Published
|
|
2
|
-
> **Date:** 2026-09-12
|
|
3
|
-
> **Scope**: API Reference
|
|
4
|
-
> **Standard**: Memorio API Specification v5
|
|
5
|
-
>
|
|
6
|
-
---
|
|
7
|
-
# Memory System
|
|
8
|
-
|
|
9
|
-
`memorio.memory` provides a semantic memory layer - a key/value store with type safety, TTL, confidence scoring, tagging, and scope-based persistence.
|
|
10
|
-
|
|
11
|
-
##
|
|
1
|
+
> **Status:** Published
|
|
2
|
+
> **Date:** 2026-09-12
|
|
3
|
+
> **Scope**: API Reference
|
|
4
|
+
> **Standard**: Memorio API Specification v5
|
|
5
|
+
>
|
|
6
|
+
---
|
|
7
|
+
# Memory System
|
|
8
|
+
|
|
9
|
+
`memorio.memory` provides a semantic memory layer - a key/value store with type safety, TTL, confidence scoring, tagging, and scope-based persistence.
|
|
10
|
+
|
|
11
|
+
## Acquired-experience discovery
|
|
12
|
+
|
|
13
|
+
`memory.discover()` finds a small, deterministic set of plausible **active acquired-experience candidates** from ordinary situation text. The caller does not need to know applicability keys or claim IDs.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
const result = await memory.discover('proxy target locking mutation')
|
|
17
|
+
|
|
18
|
+
for (const candidate of result.candidates) {
|
|
19
|
+
console.log(candidate.claim.id)
|
|
20
|
+
console.log(candidate.explanation.matchedTerms)
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The optional retrieval depth controls breadth, not a different memory store:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
await memory.discover('proxy locking', { depth: 'current' }) // at most 5 by default
|
|
28
|
+
await memory.discover('proxy locking', { depth: 'deep' }) // at most 25 by default
|
|
29
|
+
await memory.discover('proxy locking', { maxCandidates: 10 })
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Discovery is deliberately distinct from the existing acquired-context selector:
|
|
33
|
+
|
|
34
|
+
- **Discovery** uses normalized lexical overlap to find candidates. It can return zero candidates.
|
|
35
|
+
- **Applicability selection** (`memory.context({ acquired: ... })`) keeps exact structured matching and most-specific-wins behavior.
|
|
36
|
+
- **Context projection** (`memory.project()`) is a separate synchronous step that turns a Discovery result into bounded transferable context.
|
|
37
|
+
|
|
38
|
+
Normalization is local and deterministic: Unicode NFKC normalization, locale-stable lowercase conversion, punctuation/symbol separation, whitespace normalization, removal of duplicate terms, and omission of terms shorter than three characters. Stored claims are never rewritten.
|
|
39
|
+
|
|
40
|
+
Candidate ordering uses, in order: a direct normalized phrase match, the number of distinct matching query terms, the kinds of fields matched, acquisition recency, and stable claim ID. Recency only breaks relevance ties; old claims are not expired or excluded. Result explanations list the exact matched terms and fields and include the `.mem` source identifier when the repository provides one. These are discovery facts—not confidence, truth, trust, quality, authority, or applicability scores.
|
|
41
|
+
|
|
42
|
+
Only currently active claims are searched. Superseded targets, retracted targets, and retraction records are excluded using the established revision semantics. Discovery reads repository state and does not maintain a persistent index.
|
|
43
|
+
|
|
44
|
+
Security remains the repository/package boundary: Discovery does not ingest external memory or create trust. Existing decoding, validation, integrity, and configured repository rules still apply. A valid signature establishes authenticity only; it does not establish that a claim is trusted or true.
|
|
45
|
+
|
|
46
|
+
Known discovery limitation: this first strategy is lexical and structured, not semantic. Conceptually related statements with no shared discoverable term may produce zero candidates—for example, “state disappears after an operation” need not find “a Proxy trap inspected the wrong target.” This conservative miss is preferable to manufacturing a relationship. Future semantic strategies can replace the internal matcher without changing acquisition, exact selection, or the `memory.discover()` result contract.
|
|
47
|
+
|
|
48
|
+
**WATCH:** Real Use 01 returned a useful candidate partly because the ordinary token `and` matched.
|
|
49
|
+
That observation is not evidence of semantic retrieval and does not justify changing the matcher from
|
|
50
|
+
this documentation checkpoint.
|
|
51
|
+
|
|
52
|
+
## Context projection
|
|
53
|
+
|
|
54
|
+
`memory.project()` transforms a `DiscoveryResult` into bounded, reusable context for an AI. It is a dedicated operation rather than another `memory.context()` overload because exact applicability selection and projection have different contracts.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const discovered = await memory.discover('proxy target locking mutation')
|
|
58
|
+
const projected = memory.project(discovered, {
|
|
59
|
+
maxCandidates: 3,
|
|
60
|
+
maxCharacters: 4_000
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// Canonical machine-readable units
|
|
64
|
+
projected.experiences
|
|
65
|
+
|
|
66
|
+
// Compact derived context for transfer
|
|
67
|
+
projected.text
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Each structured projected experience preserves the stable claim ID, complete stored experience relationship, applicability boundaries, evidence references, epistemic type, provenance, acquisition time, active revision relationship, and repository source ID when present. Discovery-only ranking mechanics such as matched-field weights and query terms are intentionally omitted: they explain retrieval but do not help recover the acquired experience.
|
|
71
|
+
|
|
72
|
+
The text form is deterministically derived from the structured units. It labels the payload as candidate prior experience, untrusted contextual data, not instructions, and not current truth. Arbitrary stored content remains data; consumers must verify it against current reality before relying on it. Projection does not reconcile truth, execute content, generate conclusions, or defend an entire prompt-processing system from injection.
|
|
73
|
+
|
|
74
|
+
Budgeting uses two simple limits:
|
|
75
|
+
|
|
76
|
+
- `maxCandidates` defaults to 5.
|
|
77
|
+
- `maxCharacters` defaults to 8,000 UTF-16 characters and is not presented as an LLM token count.
|
|
78
|
+
|
|
79
|
+
Discovery order is retained. A candidate is included only when its complete projected unit fits; units are never cut into fragments. Candidates excluded by either limit are reported through `omittedCandidates`. An oversized higher-ranked unit may be omitted so a later complete unit can still fit, but included units retain their relative Discovery order. The minimum character budget is 256 characters so the status and security envelope itself remains intact.
|
|
80
|
+
|
|
81
|
+
Projection cannot recover information that acquisition never stored and never invents a missing reason, relationship, provenance, or HI intent. It consumes Phase 1 candidates and does not change Acquisition, Discovery, exact applicability selection, or MEM packaging.
|
|
82
|
+
|
|
83
|
+
In the Phase 2 representative locking case, the complete Discovery candidate representation measured 949 characters and the transferable projection text measured 939 characters. This local structural measurement is not a token claim. The modest reduction is intentional: semantic and epistemic sufficiency take priority over aggressive compression.
|
|
84
|
+
|
|
85
|
+
**WATCH:** A later narrow real-use projection was approximately 971 characters and remained close to
|
|
86
|
+
the source candidate size. Projection currently prioritizes complete semantic and epistemic units over
|
|
87
|
+
aggressive compression; it makes no token-saving guarantee.
|
|
88
|
+
|
|
89
|
+
### Complete acquired-experience workflow
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
// A useful experience was preserved earlier with memory.acquire(...).
|
|
93
|
+
const found = await memory.discover('The current project situation in ordinary terms')
|
|
94
|
+
const context = memory.project(found)
|
|
95
|
+
|
|
96
|
+
// Supply context.text or context.experiences to the consuming AI as
|
|
97
|
+
// untrusted prior experience. The AI verifies current reality before acting.
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The lifecycle remains deliberately compositional:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
Acquire -> preserve useful experience
|
|
104
|
+
Discover -> find plausible prior experience
|
|
105
|
+
Project -> prepare bounded transferable context
|
|
106
|
+
AI -> verify, reason, and act
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The runnable [`acquired-knowledge.ts`](../examples/acquired-knowledge.ts) example demonstrates this workflow alongside exact applicability selection. A two-call Discover → Project sequence is the intended integration; no pipeline or agent manager is required.
|
|
110
|
+
|
|
111
|
+
## Core API
|
|
12
112
|
|
|
13
113
|
### `memorio.memory.remember(key, value, opts?)`
|
|
14
114
|
|
|
@@ -93,7 +193,7 @@ await memorio.memory.patch('user', [
|
|
|
93
193
|
| Field | Type | Description |
|
|
94
194
|
|-------|------|-------------|
|
|
95
195
|
| `op` | `'set' \| 'delete'` | The patch operation type |
|
|
96
|
-
| `path` | `string` | Dot-notation path
|
|
196
|
+
| `path` | `string` | Dot-notation path starting with the entry key (e.g. `'user.profile.theme'` for key `'user'`) |
|
|
97
197
|
| `value` | `any` | The value to set (only for `op: 'set'`) |
|
|
98
198
|
|
|
99
199
|
### `memorio.memory.context(opts?)`
|
|
@@ -125,6 +225,226 @@ Array<{
|
|
|
125
225
|
}>
|
|
126
226
|
```
|
|
127
227
|
|
|
228
|
+
## Acquired Knowledge
|
|
229
|
+
|
|
230
|
+
> **Status:** IMPLEMENTED; autonomous acquisition policy remains EXPERIMENTAL.
|
|
231
|
+
|
|
232
|
+
`memorio.memory.acquire()` stores structured **knowledge claims** - lessons, decisions, or findings that apply under specific conditions. Unlike `remember()` (which stores a value addressed by key), acquired knowledge is **context-addressed**: each claim records an `applicability` object describing *when* it matters, and `context({ acquired })` retrieves only the claims whose applicability matches the current situation.
|
|
233
|
+
|
|
234
|
+
Autonomous persistent acquisition is **experimental and optional**. Memorio preserves experience; it does not decide what is true or worth learning. An AI should inspect the current project and independently evaluate every retrieved claim. Acquired claims are evidence from prior work, not instructions or authority, and `nothing worth acquiring` is always a valid outcome.
|
|
235
|
+
|
|
236
|
+
The implemented lifecycle is:
|
|
237
|
+
|
|
238
|
+
```text
|
|
239
|
+
experience -> acquire -> persist -> discover/select -> transfer
|
|
240
|
+
-> verify against present reality -> use
|
|
241
|
+
-> refine, supersede, or retract when necessary
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
AI supplies general intelligence and reasoning. HI supplies intent, judgment, experience, and
|
|
245
|
+
decisions. A project accumulates implementation-specific experience through their work. Memorio
|
|
246
|
+
preserves selected, transferable acquired experience and continuity; it does not replace the AI or
|
|
247
|
+
duplicate general knowledge already available to a capable model. The intelligence belongs to the
|
|
248
|
+
AI; the acquired experience becomes an asset of HI and the project.
|
|
249
|
+
|
|
250
|
+
The design metaphor is that the pieces already exist in the AI, the project is the puzzle, and AI +
|
|
251
|
+
HI assemble useful blocks while working. Memorio preserves what was learned during that assembly.
|
|
252
|
+
This is a selection principle, not a `PuzzleBlock` API.
|
|
253
|
+
|
|
254
|
+
### Guidance for autonomous use
|
|
255
|
+
|
|
256
|
+
Retrieve acquired context when beginning work in a describable project area, then verify relevant claims against current code, documentation, user direction, and test results. Consider acquisition after meaningful work only when the AI learned something non-obvious that a future session would otherwise have to rediscover and there is sufficient evidence to preserve it safely.
|
|
257
|
+
|
|
258
|
+
When missing knowledge belongs naturally to HI—such as project intent, rationale, desired behavior,
|
|
259
|
+
personal meaning, or whether an outcome matches the goal—the AI should ask HI instead of silently
|
|
260
|
+
inferring it. Preserve the answer with human-stated epistemic type and provenance when it becomes a
|
|
261
|
+
reusable acquired claim.
|
|
262
|
+
|
|
263
|
+
Current acquisition guidance is intentionally limited to:
|
|
264
|
+
|
|
265
|
+
- **Architecture experience:** non-obvious responsibilities, constraints, relationships, and why an architecture was chosen.
|
|
266
|
+
- **User technical decisions:** evidenced project decisions and rationale, without turning a single comment or acceptance into a permanent preference.
|
|
267
|
+
- **Documentation experience:** how documentation must be interpreted, including verified gaps or constraints, without copying the documentation itself.
|
|
268
|
+
- **Verified fix experience:** symptom, cause, change, verification, consequence, applicability, and remaining uncertainty. Preserve the lesson, not a stale assertion that the fixed bug still exists.
|
|
269
|
+
|
|
270
|
+
The practical test is whether experience created a useful connection worth recovering later. Preserve
|
|
271
|
+
relationships such as problem → cause → fix → verified consequence; decision → reason → boundary; or
|
|
272
|
+
assumption → evidence → correction. Do not use acquired knowledge as a dump for generic knowledge,
|
|
273
|
+
source-code or documentation copies, entire conversations, routine operations, test logs, or hidden
|
|
274
|
+
chain-of-thought.
|
|
275
|
+
|
|
276
|
+
Do not acquire repository facts that code, Git, or current documentation already express adequately. Keep these stages distinct in the claim content and evidence:
|
|
277
|
+
|
|
278
|
+
```text
|
|
279
|
+
observation -> what was directly encountered
|
|
280
|
+
inference -> the AI's interpretation, explicitly uncertain
|
|
281
|
+
verified -> what a named check or result demonstrated
|
|
282
|
+
experience -> the reusable why, consequence, or constraint selected for persistence
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
A practical claim can encode those distinctions in `knowledge`, for example `{ observation, inference, verifiedResult, experience, uncertainty }`. This is guidance for the existing JSON-valued field, not a new required schema. Use evidence references to identify the supporting review, test, user decision, or investigation. If reality has changed, ignore the claim or acquire a `refines`/`supersedes` revision; never force current reality to fit memory.
|
|
286
|
+
|
|
287
|
+
> **TODO - Autonomous Memory Safety:** Before autonomous acquisition is declared stable, decide what must never be acquired; how secrets, personal data, and unsafe content are excluded; when notification or approval is required; and how users inspect, reject, correct, forget, and assess the provenance, uncertainty, and staleness of acquired claims. The current API does not solve these policy questions.
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
await memorio.memory.acquire({
|
|
291
|
+
id: 'layout.aside-width',
|
|
292
|
+
knowledge: { guidance: 'Use the shared --aside-width token, not a fixed pixel value.' },
|
|
293
|
+
applies: { component: 'layout', element: 'aside' },
|
|
294
|
+
evidence: [{ id: 'review-17', source: 'design-review', observedAt: '2026-09-18T00:00:00Z' }]
|
|
295
|
+
})
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### `memorio.memory.acquire(opts)`
|
|
299
|
+
|
|
300
|
+
| Option | Type | Required | Description |
|
|
301
|
+
|--------|------|----------|-------------|
|
|
302
|
+
| `id` | `string` | Yes | Unique claim identifier (immutable; cannot be reused) |
|
|
303
|
+
| `knowledge` | `JsonValue` | Yes | The claim content - any JSON-serializable value |
|
|
304
|
+
| `applies` | `Record<string, JsonPrimitive>` | Yes | Applicability context - the condition under which this claim is relevant |
|
|
305
|
+
| `epistemicType` | `'observed' \| 'inferred' \| 'human-stated' \| 'verified'` | No | What kind of knowledge the claim represents |
|
|
306
|
+
| `provenance` | `{ source: string; detail?: string }` | No | Origin of the acquired claim |
|
|
307
|
+
| `acquiredAt` | `string` | No | Acquisition time; defaults to the current ISO timestamp |
|
|
308
|
+
| `evidence` | `EvidenceReference[]` | Yes (≥1) | Evidence references justifying the claim |
|
|
309
|
+
| `refines` | `string \| string[]` | No | Prior claim IDs this claim refines (more specific) |
|
|
310
|
+
| `supersedes` | `string \| string[]` | No | Prior claim IDs this claim replaces (mutually exclusive with `refines`) |
|
|
311
|
+
| `retracts` | `string \| string[]` | No | Prior claim IDs this claim withdraws (mutually exclusive with other revision kinds) |
|
|
312
|
+
| `reason` | `string` | No | Why the revision relationship exists |
|
|
313
|
+
|
|
314
|
+
**Evidence reference shape:**
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
interface EvidenceReference {
|
|
318
|
+
id: string // observed-at identifier (required, non-empty)
|
|
319
|
+
source?: string // where the observation came from
|
|
320
|
+
observedAt?: string // ISO timestamp of the observation
|
|
321
|
+
supports?: string // what proposition this evidence supports
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### `memorio.memory.context({ acquired })`
|
|
326
|
+
|
|
327
|
+
Retrieves knowledge claims whose `applicability` exactly matches the provided query context. Both a single context object and an array of contexts are accepted.
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
// Single context - retrieves claims applicable to this situation
|
|
331
|
+
const result = await memorio.memory.context({
|
|
332
|
+
acquired: { component: 'layout', element: 'aside' }
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
// Multiple contexts - each is queried independently
|
|
336
|
+
const result2 = await memorio.memory.context({
|
|
337
|
+
acquired: [
|
|
338
|
+
{ component: 'layout' },
|
|
339
|
+
{ component: 'layout', element: 'aside' }
|
|
340
|
+
]
|
|
341
|
+
})
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
```ts
|
|
346
|
+
{
|
|
347
|
+
knowledgeVersion: number // current persisted knowledge state version
|
|
348
|
+
claims: AcquiredClaim[] // matching claims, deduplicated
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Claim shape:**
|
|
353
|
+
```ts
|
|
354
|
+
interface AcquiredClaim {
|
|
355
|
+
id: string
|
|
356
|
+
knowledge: JsonValue // the stored claim content
|
|
357
|
+
applicability: Record<string, JsonPrimitive> // the condition this claim applies to
|
|
358
|
+
evidence: EvidenceReference[] // supporting evidence
|
|
359
|
+
epistemicType?: 'observed' | 'inferred' | 'human-stated' | 'verified'
|
|
360
|
+
provenance?: { source: string; detail?: string }
|
|
361
|
+
acquiredAt?: string
|
|
362
|
+
revision?: {
|
|
363
|
+
kind: 'refines' | 'supersedes' | 'retracts' // relationship type
|
|
364
|
+
claimIds: string[] // IDs of prior claims
|
|
365
|
+
reason?: string // why the revision exists
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Matching semantics
|
|
371
|
+
|
|
372
|
+
Selection follows two rules:
|
|
373
|
+
|
|
374
|
+
1. **All applicability keys must match.** A claim with `applicability: { component: 'layout', element: 'aside' }` only matches a query that provides *both* `component: 'layout'` *and* `element: 'aside'`.
|
|
375
|
+
2. **Most specific wins.** When multiple claims satisfy the same query, only those with the highest number of applicability keys are returned. Less-specific claims are excluded - they act as fallbacks only when no more-specific claim matches.
|
|
376
|
+
|
|
377
|
+
This lets you store a general rule and a specific override:
|
|
378
|
+
|
|
379
|
+
```ts
|
|
380
|
+
// General rule
|
|
381
|
+
await memorio.memory.acquire({
|
|
382
|
+
id: 'layout.general',
|
|
383
|
+
knowledge: 'Use layout tokens.',
|
|
384
|
+
applies: { component: 'layout' },
|
|
385
|
+
evidence: [{ id: 'e-general' }]
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
// Specific override
|
|
389
|
+
await memorio.memory.acquire({
|
|
390
|
+
id: 'layout.aside',
|
|
391
|
+
knowledge: 'Use aside layout tokens.',
|
|
392
|
+
applies: { component: 'layout', element: 'aside' },
|
|
393
|
+
evidence: [{ id: 'e-aside' }],
|
|
394
|
+
refines: 'layout.general'
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
const general = await memorio.memory.context({ acquired: { component: 'layout' } })
|
|
398
|
+
// general.claims → [{ id: 'layout.general', ... }]
|
|
399
|
+
|
|
400
|
+
const aside = await memorio.memory.context({ acquired: { component: 'layout', element: 'aside' } })
|
|
401
|
+
// aside.claims → [{ id: 'layout.aside', ... }] (more specific wins)
|
|
402
|
+
// aside.claims[0].revision → { kind: 'refines', claimIds: ['layout.general'] }
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Revision: supersede vs. refine
|
|
406
|
+
|
|
407
|
+
- **`supersedes`** - the new claim *replaces* the old one. Superseded claims are excluded from results; only the newer claim is returned for the same applicability context.
|
|
408
|
+
- **`refines`** - the new claim is *more specific* than the old one. Both remain active; the more specific claim wins when its applicability matches fully. The `revision` field on the returned claim records the relationship so a future AI can follow the reasoning chain.
|
|
409
|
+
- **`retracts`** - the new claim records withdrawal of prior claims. Both the retraction claim and the referenced claims are excluded from active selection, while history remains stored.
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
await memorio.memory.acquire({
|
|
413
|
+
id: 'layout.aside.width.v1',
|
|
414
|
+
knowledge: 'Width is 250px.',
|
|
415
|
+
applies: { component: 'layout', element: 'aside' },
|
|
416
|
+
evidence: [{ id: 'old-evidence' }]
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
await memorio.memory.acquire({
|
|
420
|
+
id: 'layout.aside.width.v2',
|
|
421
|
+
knowledge: 'Width is 300px (updated design token).',
|
|
422
|
+
applies: { component: 'layout', element: 'aside' },
|
|
423
|
+
evidence: [{ id: 'new-evidence' }],
|
|
424
|
+
supersedes: 'layout.aside.width.v1'
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
const result = await memorio.memory.context({ acquired: { component: 'layout', element: 'aside' } })
|
|
428
|
+
// result.claims → [{ id: 'layout.aside.width.v2', ... }] (v1 is excluded)
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Persistence
|
|
432
|
+
|
|
433
|
+
Acquired knowledge is persisted so it survives process restarts and is available to future sessions:
|
|
434
|
+
|
|
435
|
+
| Runtime | Storage |
|
|
436
|
+
|---------|---------|
|
|
437
|
+
| Node.js / Bun | `.memorio/project.mem` (standard ZIP package in the `.memorio` directory) |
|
|
438
|
+
| Browser / Edge | `store` (localStorage-backed key `__memorio_acquired_knowledge_state__`) |
|
|
439
|
+
|
|
440
|
+
The `.mem` file uses a versioned format: v1/v2 JSON remains readable and current writes are standard
|
|
441
|
+
ZIP packages containing logical `memorio.knowledge` v2 plus SHA-256 integrity metadata. See
|
|
442
|
+
[the format reference](./MEM_FORMAT.md). Because
|
|
443
|
+
acquisition persists across sessions, applications and autonomous agents should make that behavior
|
|
444
|
+
visible to users while it remains experimental. `memorio.memory.clear()` wipes all ordinary memories,
|
|
445
|
+
acquired knowledge, and the index; it is destructive and should not be used as routine example cleanup
|
|
446
|
+
or against a real project during tests.
|
|
447
|
+
|
|
128
448
|
### `memorio.memory.stats()`
|
|
129
449
|
|
|
130
450
|
Returns usage statistics:
|
|
@@ -144,7 +464,50 @@ Cleans all expired entries. Returns count removed.
|
|
|
144
464
|
|
|
145
465
|
### `memorio.memory.clear()`
|
|
146
466
|
|
|
147
|
-
Wipes all memories and the index.
|
|
467
|
+
Wipes all memories, acquired knowledge, and the index.
|
|
468
|
+
|
|
469
|
+
## Sync & Journal (optional)
|
|
470
|
+
|
|
471
|
+
`memorio.memory` includes an optional local-first sync layer. See [`SYNC.md`](./SYNC.md) for the
|
|
472
|
+
full guide.
|
|
473
|
+
|
|
474
|
+
### `memorio.memory.configure(opts)`
|
|
475
|
+
|
|
476
|
+
Enables the local operation journal and optionally registers a cloud-sync provider.
|
|
477
|
+
|
|
478
|
+
```ts
|
|
479
|
+
await memorio.memory.configure({
|
|
480
|
+
namespace: 'user:123:device:abc', // partitions the journal (tenant/user/device)
|
|
481
|
+
provider: { // application-owned transport
|
|
482
|
+
push(ops) { /* ... */ },
|
|
483
|
+
pull?(since) { /* ... */ },
|
|
484
|
+
resolveConflict?(local, remote) { /* ... */ }
|
|
485
|
+
},
|
|
486
|
+
auto: true // auto-replay journal on focus/online (default)
|
|
487
|
+
})
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
| Option | Type | Description |
|
|
491
|
+
|--------|------|-------------|
|
|
492
|
+
| `namespace` | `string` | Tenant/user/device identifier partitioning the journal |
|
|
493
|
+
| `provider` | `SyncProvider` | Application-owned object that pushes/pulls operations to your backend |
|
|
494
|
+
| `direction` | `'up' \| 'down' \| 'both'` | Sync direction (default `'both'`) |
|
|
495
|
+
| `auto` | `boolean` | Auto-replay journal on focus/online events (default `true`) |
|
|
496
|
+
|
|
497
|
+
### `memorio.memory.journal`
|
|
498
|
+
|
|
499
|
+
Local operation journal - a durable op log persisted on `store`. Only active when
|
|
500
|
+
sync has been configured via `configure()`.
|
|
501
|
+
|
|
502
|
+
| Method | Returns | Notes |
|
|
503
|
+
|--------|---------|-------|
|
|
504
|
+
| `append(entry, operation)` | `Promise<MemoryEntry>` | Records `remember \| update \| forget \| expire \| confirm \| supersede \| delete \| patch` |
|
|
505
|
+
| `pending()` | `Promise<MemoryEntry[]>` | Entries where `sync !== 'synced'`, namespace-scoped |
|
|
506
|
+
| `markSynced(ids)` | `Promise<number>` | Advances entries to `'synced'` |
|
|
507
|
+
| `get(id)` | `Promise<MemoryEntry \| null>` | Single entry, namespace-scoped |
|
|
508
|
+
| `clear()` | `Promise<void>` | Wipes the current namespace's journal only |
|
|
509
|
+
| `replay()` | `Promise<SyncAck>` | Pushes pending entries to the provider, marks synced, optional `pull` |
|
|
510
|
+
| `status()` | `Promise<'store'>` | The substrate in use (`'store'`) |
|
|
148
511
|
|
|
149
512
|
## Memory Entry Model
|
|
150
513
|
|
|
@@ -175,7 +538,7 @@ interface MemoryEntry<T = any> {
|
|
|
175
538
|
| `local` | `localStorage` | ✅ | ✅ | ~10MB |
|
|
176
539
|
| `durable` | `IndexedDB` | ✅ | ✅ | ~1GB+ |
|
|
177
540
|
|
|
178
|
-
Default scope
|
|
541
|
+
Default scope: `local` for values ≤1 MB, `durable` for values >1 MB. The `hot` and `session` scopes are only used when explicitly set via `opts.scope`.
|
|
179
542
|
|
|
180
543
|
## Memory Lifecycle
|
|
181
544
|
|
|
@@ -185,4 +548,4 @@ Default scope is `local` for values ≤100KB, `durable` for larger values.
|
|
|
185
548
|
| Entry with TTL expires | Status → `obsolete` (cleaned by `forgetExpired()`) |
|
|
186
549
|
| `recall()` expired entry | Returns `null` unless `includeObsolete: true` |
|
|
187
550
|
| `update()` | Creates superseded copy + updated active entry |
|
|
188
|
-
| `clear()` | Wipes all memories + index |
|
|
551
|
+
| `clear()` | Wipes all memories, acquired knowledge + index |
|