dotmem 0.0.5 → 0.0.8

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/MEMORY.md ADDED
@@ -0,0 +1,606 @@
1
+ # dotmem Memory
2
+
3
+ > **Status:** Current API reference for dotmem 0.0.8.
4
+
5
+ dotmem has two distinct memory facilities on one `memory` object:
6
+
7
+ - **Application memory** stores typed key/value entries through `remember`, `recall`,
8
+ `update`, and `forget`.
9
+ - **Acquired experience** stores evidenced, applicability-scoped claims through
10
+ `acquire`, retrieves candidates with `discover`, and prepares bounded context with
11
+ `project`.
12
+
13
+ Acquired experience is prior evidence, never executable instructions or current truth.
14
+ Verify it against the current source, runtime, and direct evidence before acting on it.
15
+
16
+ ## Start
17
+
18
+ ```bash
19
+ npm install dotmem
20
+ npx dotmem init
21
+ ```
22
+
23
+ ```ts
24
+ import { memory } from 'dotmem'
25
+
26
+ await memory.acquire({
27
+ id: 'layout.grid.min-content',
28
+ knowledge: { cause: 'Inner margins contribute to min-content track sizing.' },
29
+ applicability: { area: 'layout', component: 'grid' },
30
+ evidence: [{ id: 'layout-regression', source: 'project-test' }],
31
+ epistemicType: 'verified'
32
+ })
33
+
34
+ const candidates = await memory.discover('Grid width changed after adding inner margins')
35
+ const context = memory.project(candidates, { maxCandidates: 3, maxCharacters: 4_000 })
36
+ ```
37
+
38
+ Initialization is lazy. `memory.init()` remains as a deprecated compatibility shim;
39
+ new code should simply call the operation it needs.
40
+
41
+ ## Deterministic AI activation
42
+
43
+ The activation phrases are `dotmem`, `.mem`, `load dotmem`, `Joy`, and `Jo`. A message that is exactly one
44
+ of them, ignoring case, surrounding whitespace, and trailing punctuation, activates dotmem once
45
+ for the session.
46
+
47
+ ### What are Joy and Jo?
48
+
49
+ **Joy** is the canonical AI identity/profile for dotmem — the operational role name that comes from
50
+ the profile, not from the runtime. **Jo** is an alternative shorthand for the same identity. They
51
+ represent the assistant's persistent identity in the dotmem system (canonical profile, decision-making
52
+ behavior, trust model, and relationship style).
53
+
54
+ The canonical profile (`core/profile/`) defines:
55
+ - **Identity & behavior** (`behavior.md`, `soul.md`) — character, relationship style, conversational behavior
56
+ - **Trust model** (`trust.md`) — epistemic standards, verification requirements
57
+ - **Decision framework** (`decision.md`) — how Jo evaluates and makes technical decisions
58
+ - **Curiosity & learning** (`curiosity.md`) — what Jo seeks to understand and remember
59
+ - **Language conventions** (`language.md`) — communication style
60
+
61
+ When activated via `Joy` or `Jo`, dotmem loads this canonical profile as part of the deterministic
62
+ discovery order. The profile is optional for basic memory operations but required for
63
+ identity-gated features (carrier handshake, authority binding).
64
+
65
+ ### Activation Behavior
66
+
67
+ Activation matching is **exact phrase only** (case-insensitive, trimmed, trailing punctuation ignored):
68
+ - ✅ `Joy`, `joy`, `Joy.`, ` Joy `, `Jo`, `jo`, `Jo!`
69
+ - ❌ `Hello Joy`, `Joyful`, `Joytron`, `My name is Jo`, `Joanna`
70
+
71
+ Ordinary mentions of a name (e.g., "Jo said...") do not trigger activation. The matcher requires
72
+ the entire message to be exactly one of the activation phrases.
73
+
74
+ ### Activation Procedure
75
+
76
+ When the user explicitly says one of them, an AI must:
77
+
78
+ 1. Resolve the user dotmem home with `dotmem.paths.resolveUserDotMemPath()`.
79
+ 2. Read `<dotmem-home>/DOTMEM.md`.
80
+ 3. Load applicable user-level acquired experience.
81
+ 4. Resolve the current project identity with `dotmem.paths.resolveProjectIdentity()`.
82
+ 5. Resolve the project's memory with `dotmem.paths.resolveUserProjectMemoryPath(projectId)`.
83
+ 6. Read only that resolved project memory.
84
+
85
+ Do not search the filesystem for `.mem` files. Deterministic resolution failure is a
86
+ stop condition.
87
+
88
+ ## Storage and routing
89
+
90
+ Every dotmem component obtains storage paths through one canonical resolver:
91
+ `resolveDotMemStorageRoot()` (also `dotmem.paths.resolveDotMemStorageRoot()`).
92
+ No component reconstructs paths on its own.
93
+
94
+ Resolution precedence:
95
+
96
+ 1. **Runtime override** — the `DOTMEM_STORAGE_ROOT` environment variable, for testing,
97
+ CI, containers, and temporary development environments. An override is never
98
+ persisted by itself.
99
+ 2. **User locator** — `dotmem.location.json` (location metadata only) inside the stable
100
+ user anchor.
101
+ 3. **Default user-scoped storage** — the user anchor itself: `<user-home>/.mem`
102
+ (the platform-neutral canonical form; see the OS examples below), resolved from the
103
+ current runtime.
104
+
105
+ The user anchor is resolved from the operating-system user at runtime. dotmem never
106
+ scans drive roots, the filesystem root, or unrelated directories to find storage. A
107
+ configured storage root that is missing, inaccessible, or invalid stops resolution with
108
+ an explicit error — no silent fallback, no autonomous search, no guessed path. A path is
109
+ reported only after it has been resolved or observed in the current runtime; otherwise
110
+ report the semantic location (user memory, project memory, temporary staging, canonical
111
+ storage, user anchor).
112
+
113
+ Storage relocation is user-controlled and verified:
114
+
115
+ ```bash
116
+ npx dotmem storage # semantic location + resolved storage root
117
+ npx dotmem storage --root # resolved storage root only (scriptable)
118
+ npx dotmem storage relocate <absolute-storage-dir> # migrate storage to an explicit absolute directory
119
+ npx dotmem storage restore # migrate storage back to the default location
120
+ ```
121
+
122
+ `relocate` copies the data, verifies it at the target, updates the locator, and only then
123
+ reports completion; the locator never points at a location whose data has not been
124
+ migrated and verified. The same verification applies to `restore` before the locator is
125
+ removed.
126
+
127
+ **Canonical form, platform-neutral.** The user anchor is written `<user-home>/.mem`
128
+ everywhere in this documentation. It resolves at runtime to the current OS user's home:
129
+
130
+ ```text
131
+ Example (OS-specific, illustrative only):
132
+ Windows: C:\Users\<user>\.mem
133
+ Linux: /home/<user>/.mem
134
+ macOS: /Users/<user>/.mem
135
+ ```
136
+
137
+ The runtime resolves the actual home directory from the OS/environment; no hardcoded
138
+ platform path is ever a default.
139
+
140
+ On Node.js and Bun, the writer selects a store from the top-level acquisition fields:
141
+
142
+ | Kind and scope | File |
143
+ | --- | --- |
144
+ | `experience`, `scope: 'project'` (default) | `$DOTMEM_HOME/projects/<project-id>.mem` |
145
+ | `experience`, `scope: 'user'` | `$DOTMEM_HOME/user.mem` |
146
+ | `agent` | `$DOTMEM_HOME/agent.mem` |
147
+ | `logic` | `$DOTMEM_HOME/logic.mem` |
148
+
149
+ `<project-id>` resolves in order from `DOTMEM_PROJECT_ID`, `package.json`'s `name`,
150
+ then the current directory's basename. Set `sourceProject` when writing project
151
+ experience for a project other than the current working directory.
152
+
153
+ `applicability` is flat JSON-primitive matching metadata. It is not routing. A claim
154
+ with `applicability.scope` but no top-level `scope` is rejected to avoid this mistake.
155
+ `kind: 'relationship'` is rejected by `acquire`; relationship graph writes belong to
156
+ the dedicated brain API.
157
+
158
+ Browser and edge environments use the existing `store`-backed acquired-knowledge
159
+ repository and never load Node filesystem code. The CLI is Node-only.
160
+
161
+ ## Acquisition safety
162
+
163
+ `memory.acquire()` validates JSON-safe claim data, sanitizes secret-looking keys and
164
+ values before persistence, and writes
165
+ atomically, and verifies that the claim was persisted. Sanitization replaces sensitive
166
+ content with a semantic marker such as `requiresToken: true`; it is not a secret vault,
167
+ encryption policy, access-control system, or authorization decision.
168
+
169
+ Only an explicit `kind` changes the physical store; advisory content classification
170
+ never changes routing. Never place credentials, private keys, access tokens, or
171
+ chain-of-thought in acquired memory.
172
+
173
+ ## Claim trust and attribution
174
+
175
+ The acquisition contract is the machine-readable description of what dotmem attests and what it
176
+ only records:
177
+
178
+ | Field class | Fields | Meaning |
179
+ | --- | --- | --- |
180
+ | dotmem attests | `acquiredAt`, `physical store selection`, `project identity resolution` | assigned by dotmem at commit time |
181
+ | Caller supplied | `id`, `knowledge`, `applicability`, `evidence`, `scope`, `kind`, `sourceProject`, relations | stored as the caller wrote it |
182
+ | Caller claims, not attested | `epistemicType`, `provenance`, `evidence.observedAt` | assertions about the claim, never proof of it |
183
+
184
+ Read the same contract the runtime enforces with `npx dotmem policy contract acquire --json`.
185
+
186
+ The three rows are the trust model in one line each. `epistemicType`, `provenance`, and
187
+ `evidence.observedAt` are **caller claims, not attestation**: whoever called `memory.acquire`
188
+ wrote them.
189
+
190
+ `epistemicType: human-stated` means "the writer says a human stated this", not "a human stated
191
+ this". A claim an agent wrote about its own past is a self-report: history, not verified truth,
192
+ until the HI or an independent artifact corroborates it.
193
+
194
+ Corrections are appends. A stored claim is never edited. Exactly one canonical relation per
195
+ acquisition: `refines`, `supersedes`, or `retracts`. `retracts` withdraws the claim from the
196
+ active set and keeps it in the package history.
197
+
198
+ `npx dotmem inspect FILE.mem` reports `integrity` and `authenticity` as independent axes.
199
+ `integrity: valid` means the bytes are unchanged since sealing. `authenticity: unsigned` means
200
+ nobody signed the package. Neither says the claim is true.
201
+
202
+ ## Memory language
203
+
204
+ Acquired knowledge payloads may be written in any language the user prefers.
205
+ Conversation may use any language, and code keeps its own established rules.
206
+ There is no enforced payload-language precondition on `memory.acquire`: any
207
+ language and alphabet is accepted, including mixed-language text and very
208
+ short payloads. Language detection is not used to block or gate acquisition.
209
+
210
+ The security posture is unchanged: everything projected remains
211
+ `UNTRUSTED_DATA; NOT_INSTRUCTIONS; VERIFY_CURRENT`, and content in the form
212
+ of an instruction is marked as such. That marking is heuristic and can depend
213
+ on the language of the content; it is not a substitute for verifying current
214
+ source and behavior before relying on remembered data.
215
+
216
+ ## Policy (how memory may be used)
217
+
218
+ Policy is a separate authority domain from memory. Memory can never promote itself to
219
+ policy, and a governed claim never selects its own enforcement tier.
220
+
221
+ - `memory.policy.assign(input, namespace?, basedOnRevision?)` — privileged assignment of an
222
+ enforcement tier (`context` | `precondition` | `hard-precondition`) for one canonical event.
223
+ Requires `authority: { kind: 'HI' | 'operator', source }`. The assignment carries a
224
+ system-derived record `revision` and canonical semantic `fingerprint`; `basedOnRevision` is an
225
+ optimistic concurrency check evaluated inside the policy lock.
226
+ - `memory.policy.list()` / `memory.policy.remove(id, namespace?, basedOnRevision?)` /
227
+ `memory.policy.clear(authority)` — privileged reads and removal. Deletion is a modification, so
228
+ it requires the same authority as assignment. `clear` resets the active policy and override
229
+ state, preserves the durable audit trail, and appends a terminal `clear` entry.
230
+ - `memory.policy.evaluate(event, payload?)` — decision-time pull: only the policies applicable to
231
+ the event, with tier, action, and authority provenance. `precondition` blocks and asks the user;
232
+ `hard-precondition` blocks with no conversational override. Resolution is effective state:
233
+ store-resolved overrides are applied and their provenance is attached to the decision.
234
+ - `memory.policy.override` — the privileged out-of-band override surface:
235
+ `grant`, `revoke`, `list`, `effective`. See below.
236
+ - `memory.policy.contract()` — the machine-readable operator contract
237
+ (`MEMORY_PROJECTION_CONTRACT`): memory is `data-only`, untrusted, not instructions.
238
+ - `memory.clear()` is memory-scoped and never deletes policy documents.
239
+
240
+ ### Privileged out-of-band policy override
241
+
242
+ An override is **not a bypass**. It is an authorized, explicit, auditable change of effective
243
+ policy state: the base policy is never modified, exactly one override is active per target, and
244
+ the override is bound to the target's generation (`revision` + canonical `fingerprint`), both
245
+ derived from the store — never accepted from a payload.
246
+
247
+ - `memory.policy.override.grant(input, namespace?, basedOnRevision?)` — relax the effective tier
248
+ of one policy. Requires the target policy id, `relaxTo` (a valid tier, `context` is the floor),
249
+ a bounded `expiresAt` (window cap `MAX_OVERRIDE_WINDOW_MS`, 7 days), an explicit `reason`, and
250
+ authority. Hierarchy: an operator cannot relax an HI-owned policy. A new grant atomically
251
+ supersedes the previous active override.
252
+ - `memory.policy.override.revoke(input, namespace?, basedOnRevision?)` — remove a relaxation and
253
+ restore the base policy. HI revokes any override; an operator revokes only operator-granted
254
+ overrides.
255
+ - `memory.policy.override.list(namespace?)` — every override with its resolved state
256
+ (`ACTIVE` | `EXPIRED` | `STALE` | `INERT`).
257
+ - `memory.policy.override.effective(event?, namespace?)` — effective state per policy: base tier,
258
+ effective tier, override state, and the provenance scar (override id, `relaxedFrom`, `by`,
259
+ `until`, `reason`).
260
+
261
+ Override resolution is store-only and namespace-local: an override lives in the same document as
262
+ the policy it targets, chat cannot invoke it, `memory.acquire` cannot create one, a payload
263
+ cannot self-request it, and retrieved text or tool output cannot author one. A changed policy
264
+ makes the override `STALE`, a deleted policy makes it `INERT`, and same-id recreation never
265
+ resurrects it. Expiry and revocation both restore the base policy.
266
+
267
+ Every mutation runs inside the cross-process policy namespace lock (exclusive create, owner
268
+ identity, bounded retry, audited stale-lock break, fail-closed when exclusive creation cannot be
269
+ verified) and is recorded in the durable in-document audit trail in the same atomic write. No
270
+ override is effective without a recoverable durable audit record.
271
+
272
+ ## One canonical acquisition boundary
273
+
274
+ Every path capable of producing durable memory crosses the same boundary
275
+ (`core/knowledge/acquisition-boundary.ts`) before persistence: sanitize, strip
276
+ non-authoritative fields and evaluate the `memory.acquire` policy against the
277
+ payload. `memory.acquire` and the staged path
278
+ (`memory.acquireStaged` → `commitStageFile`) both cross it, so direct and
279
+ staged writes cannot diverge in enforcement. The staged commit additionally
280
+ refuses a stage file that still contains secret shapes after the boundary
281
+ re-sanitization. Equivalence tests pin the two paths to identical outcomes.
282
+
283
+ Policy conditions are evaluated against the action payload, not merely discovered:
284
+
285
+ | Condition outcome | Meaning | `precondition` behavior |
286
+ | --- | --- | --- |
287
+ | `satisfied` | the payload deterministically meets the condition | allow |
288
+ | `violated` | the payload deterministically fails the condition | block and ask the user |
289
+ | `unresolved` | the condition cannot be established (no payload) | block and ask the user — never assumed satisfied |
290
+
291
+ `memory.acquire()` itself evaluates the policies applicable to the `memory.acquire` event against
292
+ the payload about to be written and blocks with `PolicyPreconditionViolationError` when a
293
+ precondition is violated or unresolved. Without any policy, acquisition behaves exactly as before.
294
+
295
+ CLI: `npx dotmem policy <assign|list|remove|clear|eval|contract>`;
296
+ `policy eval --event <event> [--payload <json>]` exits with code 2 when the event is blocked.
297
+ Privileged override commands: `policy override grant|revoke|list` and `policy effective`
298
+ (see `policy --help`). Canonical events so far: `artifact.write`, `memory.acquire`.
299
+
300
+ Policy documents live under `policy/operator.policy.json` and `policy/security.policy.json`
301
+ in the storage root, each carrying its records, overrides, and durable audit trail. Filesystem-level
302
+ tamper isolation and policy signing remain open work (see `docs/SECURITY.md`).
303
+
304
+ ## Retrieval semantics
305
+
306
+ - `memory.context({ acquired: query })` does exact applicability selection.
307
+ - `memory.discover(situation)` performs deterministic lexical and structured candidate
308
+ retrieval. It is not embedding or semantic-vector search.
309
+ - `memory.project(discovery)` returns bounded candidates and `text` marked as
310
+ untrusted prior context; it may omit candidates to meet its limits.
311
+
312
+ `DISCOVERED != APPLICABLE` and `PROJECTED != CURRENT FACT`.
313
+
314
+ `LABELLED != ATTESTED`: the epistemic label of a claim is written by the caller, never attested
315
+ by dotmem. See [Claim trust and attribution](#claim-trust-attribution-and-correction).
316
+
317
+ ## Public API
318
+
319
+ | API | Purpose |
320
+ | --- | --- |
321
+ | `init()` | Deprecated compatibility shim that prepares the runtime lazily. |
322
+ | `acquire(opts)` | Persist an evidenced acquired claim using scope/kind routing. |
323
+ | `discover(situation, options?)` | Find lexical/structured active candidates. |
324
+ | `project(discovery, options?)` | Produce bounded, untrusted AI context. |
325
+ | `context(opts)` | Exact acquired-context selection or ranked key/value retrieval. |
326
+ | `policy.assign/list/remove/clear/evaluate/contract` | Privileged policy authority surface; see the Policy section. |
327
+ | `policy.override.grant/revoke/list/effective` | Privileged out-of-band override surface; see the Policy section. |
328
+ | `remember`, `recall`, `update`, `forget`, `patch` | Application key/value memory lifecycle. |
329
+ | `stats`, `forgetExpired`, `clear` | Maintenance; `clear()` is destructive and memory-scoped. |
330
+ | `createAgentMemory(agentId)` (`dotmem/agent`) | Agent-facing memory API with logical namespace partitioning per agent. |
331
+
332
+ `AcquireOptions` includes `id`, `knowledge`, `applicability`, and `evidence`; optional
333
+ fields are `epistemicType`, `provenance`, revision links, `scope`, `kind`, and
334
+ `sourceProject`. See the checked-in type declaration at `types/memory.d.ts` for the
335
+ precise TypeScript contract.
336
+
337
+ `memory.context({ acquired })` returns `{ knowledgeVersion, claims: AcquiredClaim[] }`
338
+ (the low-level acquired-context selection). The CLI `npx dotmem context --json` goes
339
+ through `discover` + `project` and returns a `ContextProjection` envelope with
340
+ `totalCandidates`, `omittedCandidates`, `experiences`, and `text`. The two surfaces are
341
+ intentionally different: the API returns raw claims, the CLI returns bounded, projected
342
+ context. `totalCandidates` is present on the CLI projection envelope, not on the raw
343
+ `AcquiredContext` returned by `memory.context({ acquired })`.
344
+
345
+ ### Application Memory Scopes (remember/recall)
346
+
347
+ The `memory.remember()` and `memory.recall()` operations support four scopes with
348
+ different persistence characteristics per runtime:
349
+
350
+ | Scope | Browser Storage | Node/Bun Storage | Survives Browser Reload | Survives Node/Bun Restart | Default For |
351
+ |-------|-----------------|------------------|-------------------------|---------------------------|-------------|
352
+ | `hot` | `state` (memory) | `state` (memory) | No | No | Never default; explicit only |
353
+ | `session` | `sessionStorage` | In-memory `Map` | Yes (same tab) | No | Never default; explicit only |
354
+ | `local` | `localStorage` | In-memory `Map` | Yes | No | Values ≤ 1 MB |
355
+ | `durable` | IndexedDB (`idb`) | **Not available** | Yes | **No (browser only)** | Values > 1 MB |
356
+
357
+ > **Key distinction**: The `durable` scope uses IndexedDB and is **browser-only**. In Node.js and
358
+ > Bun, `store` and `session` fall back to in-memory `Map`s that do not survive a process restart.
359
+ > For cross-restart persistence in Node/Bun, use `memory.acquire()` which writes `.mem` package
360
+ > files to disk — this is the durable acquisition path, not the `memory.remember()` scope system.
361
+
362
+ Scope resolution defaults (when `opts.scope` is not provided):
363
+ - Values ≤ 1 MB → `local`
364
+ - Values > 1 MB → `durable` (browser) / `local` (Node/Bun, since `durable` is unavailable)
365
+
366
+ The `hot` and `session` scopes are only used when explicitly set via `opts.scope`.
367
+
368
+ #### Scope Behavior Summary
369
+
370
+ | Scope | Lifetime | Visibility | Owner/Namespace | Init | Clear |
371
+ |-------|----------|------------|-----------------|------|-------|
372
+ | `hot` | Process lifetime | Global `state` tree | Process | Lazy on first use | Process exit |
373
+ | `session` | Session lifetime | `sessionStorage` (browser) / in-memory `Map` (Node/Bun) | Browser tab / process | Lazy on first use | Tab close / process exit |
374
+ | `local` | Process lifetime (Node/Bun) or persistent (browser) | `localStorage` (browser) / in-memory `Map` (Node/Bun) | Origin / process | Lazy on first use | Manual `forget()` / `clear()` / browser clear |
375
+ | `durable` | Persistent (browser only) | **N/A** | Origin | Lazy on first use | Manual `forget()` / `clear()` |
376
+
377
+ > **Note**: `session` and `local` are behaviorally identical in Node/Bun (both use in-memory
378
+ > `Map`s). The distinction exists only in browser environments. This is a known limitation;
379
+ > no security or isolation boundary is implied by the scope names in Node/Bun.
380
+
381
+ ### Built-in seed claims
382
+
383
+ dotmem ships with a small set of built-in seed claims that describe the
384
+ acquisition logic itself. They are part of the product (`core/knowledge/logic-seed.ts`)
385
+ and are loaded into the logic knowledge store on every installation:
386
+
387
+ - `acquisition.experience-not-activity` — when to acquire (checkpoint principle)
388
+ - `acquisition.value-sources` — where useful memory comes from
389
+ - `acquisition.boundaries` — what to filter out, epistemic statuses
390
+ - `acquisition.project-specific` — project-specific experience candidates
391
+ - `relationship.continuity-pattern` — generalized AI-HI relationship pattern
392
+
393
+ They are marked as `source: built-in seed (dotmem acquisition logic seed)` in
394
+ `npx dotmem context --explain` output. They carry `epistemicType: inferred` and
395
+ `provenance.source: 'dotmem acquisition logic seed'`. They are read-only reference
396
+ material for the acquisition logic; they do not count as user-acquired experience.
397
+
398
+
399
+ The same memory is reachable from three surfaces: the CLI (`npx dotmem`), the MCP server
400
+ (`dotmem-mcp`, exposing `dotmem_context`, `dotmem_acquire`, and `dotmem_rules`), and
401
+ the JavaScript API. The MCP server writes through the same acquisition path as the CLI and
402
+ the API: one boundary, one policy, one contract. There is no second acquisition model.
403
+
404
+ ## Global and React usage
405
+
406
+ Use named imports for normal code. `import 'dotmem/global'` explicitly installs the
407
+ non-React runtime APIs on `globalThis`; it does not initialize acquired memory.
408
+
409
+ `useObserver` is a React hook, exported only by `dotmem/react`. Call it at the top
410
+ level of a component or custom hook. It returns `void`; React handles cleanup.
411
+
412
+ ## Continuity — the derived operational present
413
+
414
+ Memory is durable knowledge; continuity reconstructs the useful present. The
415
+ snapshot hierarchy composes downward: USER → PROJECT → TASK/SESSION → MODEL
416
+ CONTEXT. Snapshots are derived caches with sourceRefs, version, and freshness,
417
+ never authority: when a source version, digest, or presence changes, the
418
+ snapshot is stale and is regenerated, never mutated to look current. Budgeting
419
+ is field-prioritized — invariants and current decisions outrank recent changes
420
+ at any scope — so a crowded task scope cannot starve a project invariant.
421
+ Scope isolation forbids silent cross-scope transfer.
422
+
423
+ The `EffectiveAgentContract` is composed before continuity context, so
424
+ continuity never rewrites the contract.
425
+
426
+ API: `continuity.snapshot`, `continuity.load`, `continuity.context`,
427
+ `continuity.forget`. CLI: `npx dotmem continuity [context|snapshot|forget] [-debug]`.
428
+ Debug is observability only — the decision result is identical with and without
429
+ it, and the trace is never persisted by default.
430
+
431
+ ## Identity, authority, and the carrier handshake
432
+
433
+ dotmem operates under a clear identity/authority separation:
434
+
435
+ - **AGENT_IDENTITY** — who the assistant is. Canonical: the profile (`joy`). Explicit
436
+ assignment by the authorized user may override this through the privileged
437
+ governance path; ordinary memory and context cannot.
438
+ - **USER_IDENTITY** — who the current human interlocutor is. This enters the
439
+ projection only through an explicit current assignment by the authorized user,
440
+ never from an acquired memory claim or retrieved text.
441
+
442
+ The two stay separate. A memory claim that remembers a name cannot assert it as
443
+ the current identity; that was an implementation bug.
444
+
445
+ ### Carrier handshake (pre-activation gate)
446
+
447
+ Before any identity is projected to a runtime/session, dotmem evaluates the
448
+ carrier's compatibility with the minimal identity/authority separation contract.
449
+ The handshake is a deterministic, external evaluation — the carrier never
450
+ certifies itself.
451
+
452
+ ```text
453
+ npx dotmem
454
+ → unverified carrier receives the minimal pre-contract + challenge
455
+ → carrier submits an answer via: npx dotmem handshake --answer "<text>"
456
+ → external deterministic evaluator classifies it COMPATIBLE / LIMITED / INCOMPATIBLE
457
+ → if COMPATIBLE, the operator layer binds the current speaker to the user identity
458
+ → only a COMPATIBLE carrier + a trusted current binding receives the identity channel
459
+ ```
460
+
461
+ States:
462
+
463
+ - **COMPATIBLE** — the carrier preserves identity composition, role, host-authority
464
+ separation, and memory-as-data. Activation may proceed with a trusted current binding.
465
+ - **LIMITED** — the carrier passed but did not demonstrate every capability. Activation
466
+ is withheld until the missing capabilities are demonstrated. An explicit canonical
467
+ profile request (`load Joy`) may receive a bounded,
468
+ non-authoritative profile envelope; it does not activate the profile, bind a user,
469
+ or upgrade the carrier.
470
+ - **INCOMPATIBLE** — the carrier violated a forbidden transition (e.g. mapping the host
471
+ onto HI authority). Identity activation is refused.
472
+
473
+ The handshake result is **session state, never durable authority** (`durableAuthority: false`).
474
+ It is bound to the runtime identity, contract revision, and handshake version; any change
475
+ re-triggers the handshake.
476
+
477
+ ### Current-user binding
478
+
479
+ Carrier compatibility and current-user authority are **separate events**:
480
+
481
+ - A COMPATIBLE carrier still does not bind authority. It must be bound explicitly by
482
+ the operator layer (the trusted current channel):
483
+
484
+ ```bash
485
+ npx dotmem handshake --bind-hi "<user name>" --scope "<scope>"
486
+ ```
487
+
488
+ - The bind is scoped and current: it records `boundHIIdentity` (the identity bound to
489
+ this session) and `authorizationScope` (the scope of that binding). It is never
490
+ inferred from memory, history, files, or conversation.
491
+ - `--unbind` clears the current binding; the remembered identity stays informational.
492
+ - `--reset` invalidates the session state (recovery step 3); `--status` reports the
493
+ current session without evaluating anything.
494
+
495
+ ### Identity/runtime separation
496
+
497
+ Host, provider, runtime, and model names are implementation metadata:
498
+
499
+ - The operational role name (`Joy`) comes from the profile, not from the runtime.
500
+ - The canonical identity is never fused with runtime/model metadata into a hybrid name
501
+ (e.g. `Joy` or `HI`). dotmem detects and rejects such fusion
502
+ attempts.
503
+ - `knownHIIdentity` (the remembered user identity) is informational only and never
504
+ authenticates the current speaker. Only `boundHIIdentity` (set by a trusted current
505
+ binding) authorizes activation.
506
+
507
+ See [`SECURITY.md`](./SECURITY.md)
508
+
509
+ ## Boundaries
510
+
511
+ - `.mem` integrity and optional signatures do not make claims true or trustworthy.
512
+ - A claim's `epistemicType` and `provenance` are caller claims, not attestation.
513
+ - Namespaces and contexts are not authorization boundaries.
514
+ - Encryption is opt-in; key management belongs to the application.
515
+ - dotmem does not provide vector search, an IAM system, or secret management. Policy
516
+ authority separation for acquired experience is implemented (see Policy); OS-level
517
+ tamper isolation and policy signing remain open work.
518
+
519
+ ## MCP server (`dotmem-mcp`)
520
+
521
+ dotmem ships a stdio MCP server (`dotmem-mcp`) so an AI host and the terminal share the same store. It exposes read tools always, and write capabilities only when the operator opts in via `DOTMEM_MCP_WRITE=1`:
522
+
523
+ | Tool | Direction | Registered when | Use it when |
524
+ | --- | --- | --- | --- |
525
+ | `dotmem_context` | read | always | You need the acquired experience relevant to the current situation. |
526
+ | `dotmem_rules` | read | always | You need the iron rules that constrain every session. |
527
+ | `dotmem_acquire` | write | `DOTMEM_MCP_WRITE=1` | The work produced reusable, project-specific experience worth keeping. |
528
+ | `dotmem_continuity` | read | always | You need continuation status, summaries, open loops, or evaluation. |
529
+ | `dotmem_continuity` | write | `DOTMEM_MCP_WRITE=1` | You need to create checkpoints, resume, add/resolve open loops. |
530
+ | `dotmem_note_read` | read | always | You need to read a user note by id. |
531
+ | `dotmem_note_write` | write | `DOTMEM_MCP_WRITE=1` | You need to add or update a user note. |
532
+
533
+ Without `DOTMEM_MCP_WRITE=1`, `dotmem_acquire` is absent from `tools/list`: it does not appear, and calling it by name returns the transport's own "not found". The `dotmem_continuity` tool is always registered, but write actions (`checkpoint`, `add-open-loop`, `resolve-open-loop`) return an authorization error when `DOTMEM_MCP_WRITE=1` is not set. Read actions (`status`, `summaries`, `open-loops`, `evaluate`) remain available. The server never leaks the store path in error messages. `serverInfo.version` reports the package version, and `instructions` are built from boolean state only (write enabled / has experience), never from project-derived strings.
534
+
535
+ See [README.md](./README.md) for host registration examples (Kilo/OpenCode, Claude Desktop).
536
+
537
+ ### Continuity `evaluate` action
538
+
539
+ The `dotmem_continuity` `evaluate` action is read-only. It does not mutate
540
+ continuity state, create a checkpoint, or resume a conversation. It calls the
541
+ lifecycle monitor's evaluation of the configured trigger conditions and returns:
542
+
543
+ - `shouldCheckpoint`: whether a checkpoint is currently warranted
544
+ - `reasons`: the trigger reasons (for example threshold, milestone, decision,
545
+ incident, sessionEnd, or explicit), or an empty list when none apply
546
+ - `usage`: the current context usage (`tokens` and `characters`)
547
+
548
+ Input is optional at the MCP boundary (`conversationId`, `brainId`,
549
+ `tokenBudget`, `scope`, and `subject` may be supplied for surrounding context),
550
+ but `evaluate` itself takes no checkpoint options and performs no write. It is
551
+ distinct from `checkpoint`, `compact`, `resume`, and `verify`: it only reports
552
+ whether the current usage and trigger conditions warrant a checkpoint. A
553
+ positive result can precede an explicit `checkpoint` action; a negative result
554
+ does not prevent one.
555
+
556
+ ### Extra commands: proposed MCP exposure (no implementation yet)
557
+
558
+ The following CLI commands exist but are **not** exposed as MCP tools. This table records the proposal only; no command below becomes an MCP tool without a decision from Dario.
559
+
560
+ | Command | MCP Exposure | Risk |
561
+ | --- | --- | --- |
562
+ | `session` | none | Local session state; no need for remote exposure. |
563
+ | `loop` | none | Operator open loops; local management. |
564
+ | `vault` | none | Secrets: never exposed via MCP (untrusted channel). |
565
+ | `verify` | read-only | Freshness probes; read-only, low risk, but not high priority. |
566
+ | `guard` | none | Pre-execution policy evaluation: requires event context, not suitable for MCP. |
567
+ | `rule` / `rules` | read-only | Iron rules: already covered by `dotmem_rules`. |
568
+ | `violation` | none | Violation log: write-side, do not expose. |
569
+ | `violation clear` | none | Log deletion: destructive privileged operation. |
570
+ | `policy assign/remove/clear` | none | Policy assignments: require HI/operator authority; never via MCP. |
571
+ | `policy override grant/revoke` | none | Privileged override: requires explicit authority and audit. |
572
+ | `install-extension` | removed | Extension removed from the project (see point 4). |
573
+
574
+ ### CLI command reference
575
+
576
+ | Command | Purpose | Example | Type |
577
+ | --- | --- | --- | --- |
578
+ | `context "<situation>"` | Project the acquired experience for the given situation | `npx dotmem context "css grid layout" --json --claims-only` | read |
579
+ | `init` | Create the project anchor in user storage | `npx dotmem init` | write |
580
+ | `acquire` | Acquire a durable claim from JSON on stdin | `cat claim.json \| npx dotmem acquire` | write |
581
+ | `learn "<text>"` | Acquisition from shorthand text | `npx dotmem learn "CSS rule" --id "css.1"` | write |
582
+ | `inspect FILE.mem` | Validate and decode a `.mem` package | `npx dotmem inspect user.mem` | read |
583
+ | `continuity` | Compose USER+PROJECT+TASK continuity | `npx dotmem continuity context` | read |
584
+ | `handshake` | Carrier compatibility gate and authority binding | `npx dotmem handshake --answer "..."` | privileged |
585
+ | `storage` | Show/relocate/restore the storage root | `npx dotmem storage --root` | read/write |
586
+ | `policy` | Manage policy assignments and contracts | `npx dotmem policy list` | privileged |
587
+ | `doctor [--json] [--fix]` | Duplicate and integrity audit | `npx dotmem doctor` | read |
588
+ | `session [show\|doing]` | Current task state of the session | `npx dotmem session doing "task"` | read/write |
589
+ | `loop <subcommand>` | Manage open loops (add\|list\|close\|show) | `npx dotmem loop add lp-1 "task"` | write |
590
+ | `vault <subcommand>` | Local handle→value vault (put\|resolve\|list\|forget) | `npx dotmem vault put val --handle h1` | write |
591
+ | `verify` | Freshness probes on claims | `npx dotmem verify` | read |
592
+ | `guard <event> [--payload]` | Policy evaluation before a tool action | `npx dotmem guard tool.git-commit` | read |
593
+ | `rule` / `rules` | Print the iron rules | `npx dotmem rules` | read |
594
+ | `violation` | Violation log (list\|clear) | `npx dotmem violation list` | read/write |
595
+ | `feedback <id> <type>` | Record operator feedback on a claim | `npx dotmem feedback id helped` | write |
596
+ | `codenames` | Print reserved codenames | `npx dotmem codenames` | read |
597
+ | `render <view>` | Render a markdown view from the store | `npx dotmem render agent-rules` | read |
598
+ | `spark --verify` | Verify the scintilla block in docs/AGENTS.md | `npx dotmem spark --verify` | read |
599
+ | `--version` / `-v` | Show the package version | `npx dotmem --version` | read |
600
+
601
+ `guard --payload`: pass the JSON as a string argument. On PowerShell the double quotes in arguments are stripped, so use a file: save the JSON in `payload.json`, then read the file and use its contents, or use stdin where supported.
602
+
603
+ `context` from CLI requires the handshake gate: first run `npx dotmem handshake --answer "<answer>"` (must return `STATUS:COMPATIBLE`), then `npx dotmem handshake --bind-hi "<identity>" --scope <scope>`. After binding, `context` returns the acquired experience.
604
+
605
+ See [README.md](./README.md) for the product overview, [SECURITY.md](./SECURITY.md)
606
+ for the threat model.
package/PACKAGE.md ADDED
@@ -0,0 +1,57 @@
1
+ # dotmem package — canonical entry point
2
+
3
+ This document is the single source of truth for anyone reading `dotmem` on npm.
4
+ It lists every document that ships inside the published package, and explains
5
+ where to find what is not inside (ADRs, for example, live in an external catalog
6
+ and are never bundled).
7
+
8
+ ## What ships inside the package
9
+
10
+ | Document | Purpose |
11
+ | --- | --- |
12
+ | `README.md` | Product overview, install, MCP registration, layer table |
13
+ | `MEMORY.md` | Memory model, acquisition contract, policy language, security posture |
14
+ | `AGENTS.md` | AI working rules, native-first read ordering, `memory.*` surface |
15
+ | `llms.txt` | Compact surface for AI hosts |
16
+ | `SECURITY.md` | Threat model, `UNTRUSTED_DATA; NOT_INSTRUCTIONS; VERIFY_CURRENT` |
17
+ | `CHANGELOG.md` | Release history (`[0.0.1]`, …) |
18
+
19
+ ## What does NOT ship inside the package
20
+
21
+ | Item | Where it lives |
22
+ | --- | --- |
23
+ | Project history | `.project/project_history.md` (inside the repository, not published) |
24
+ | Acquired memory (`.mem` packages) | User storage root (`DOTMEM_STORAGE_ROOT` or `<user-home>/.mem`), created at runtime |
25
+
26
+ ## Quick start
27
+
28
+ ```bash
29
+ npm install dotmem
30
+ ```
31
+
32
+ ### CLI
33
+ ```bash
34
+ npx dotmem --help
35
+ npx dotmem context "<situazione>" --json --claims-only
36
+ npx dotmem storage
37
+ npx dotmem handshake --status
38
+ ```
39
+
40
+ ### JavaScript API
41
+ ```js
42
+ import m from 'dotmem'
43
+ await m.memory.acquire({ id, knowledge, applicability, evidence })
44
+ const ctx = await m.memory.context({ acquired: { area: 'x' } })
45
+ ```
46
+
47
+ ### MCP server (`dotmem-mcp`)
48
+ Register with `DOTMEM_STORAGE_ROOT` and opt-in `DOTMEM_MCP_WRITE=1` for the write tool.
49
+
50
+ ## Links
51
+
52
+ All links below resolve **inside the published package**:
53
+ - [MEMORY.md](MEMORY.md) — memory model and acquisition contract
54
+ - [AGENTS.md](AGENTS.md) — AI working rules
55
+ - [llms.txt](llms.txt) — AI host surface
56
+ - [SECURITY.md](SECURITY.md) — threat model
57
+ - [CHANGELOG.md](CHANGELOG.md) — release history