@onenomad/engram-mcp 1.1.0 → 2.0.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/README.md CHANGED
@@ -95,15 +95,15 @@ daily (2 days) --> short-term (14 days) --> long-term (90 days) --> archive
95
95
 
96
96
  Promotion isn't just about age. A memory moves to long-term if it's been recalled multiple times, has high importance, received "helpful" feedback, or is a procedural rule. Memories that keep getting recalled stay promoted. Memories that never get touched decay and eventually archive.
97
97
 
98
- **Scratch tier** is for exploratory, session-only notes that you may want to discard. Pass `tier: 'scratch'` to `memory_ingest` and the chunk is excluded from every consolidation path: no promotion, no merging, no decay-to-archive, no linking. After 24 hours scratch chunks are auto-purged. Use `memory_scratch_promote` to graduate one to short-term once you've decided it's worth keeping.
98
+ **Scratch tier** is for exploratory, session-only notes that you may want to discard. Pass `tier: 'scratch'` to `engram-ingest` and the chunk is excluded from every consolidation path: no promotion, no merging, no decay-to-archive, no linking. After 24 hours scratch chunks are auto-purged. Use `engram-scratch-promote` to graduate one to short-term once you've decided it's worth keeping.
99
99
 
100
100
  ### Memory Origin
101
101
 
102
102
  Every chunk carries an `origin` tag that distinguishes user-asserted memory from auto-derived memory:
103
103
 
104
- - **`user`** — written explicitly via `memory_ingest`. Treated as canonical user-territory: the consolidator never auto-merges, near-duplicate-deletes, or archives these. Importance still decays normally, but the content and lifecycle stay sacred.
105
- - **`extracted`** — pulled from a conversation by `memory_extract` or the Mem0 provider.
106
- - **`imported`** — bulk-loaded via `memory_import`.
104
+ - **`user`** — written explicitly via `engram-ingest`. Treated as canonical user-territory: the consolidator never auto-merges, near-duplicate-deletes, or archives these. Importance still decays normally, but the content and lifecycle stay sacred.
105
+ - **`extracted`** — pulled from a conversation by `engram-extract` or the Mem0 provider.
106
+ - **`imported`** — bulk-loaded via `engram-import`.
107
107
  - **`derived`** — produced by consolidation (e.g. episodic-to-semantic summaries).
108
108
 
109
109
  The split mirrors the journal pattern in [Persona](https://github.com/OneNomad-LLC/persona-mcp): a clean ownership boundary between what the user said and what the system inferred. If you want auto-extracted memories to lose to your hand-written ones in a near-duplicate fight, this is what makes that happen.
@@ -162,7 +162,7 @@ If a memory gets marked irrelevant 3+ times out of the last 5 recalls, its impor
162
162
 
163
163
  ### Knowledge Graph Auto-Population
164
164
 
165
- When a memory is ingested, the system heuristically extracts entity-relationship triples and adds them to the knowledge graph automatically. It detects 12 relationship types including `works-at`, `uses`, `depends-on`, `prefers`, `chose`, `located-in`, and more. This means the knowledge graph grows passively as memories accumulate, without needing explicit `memory_kg_add` calls for every fact.
165
+ When a memory is ingested, the system heuristically extracts entity-relationship triples and adds them to the knowledge graph automatically. It detects 12 relationship types including `works-at`, `uses`, `depends-on`, `prefers`, `chose`, `located-in`, and more. This means the knowledge graph grows passively as memories accumulate, without needing explicit `engram-kg-add` calls for every fact.
166
166
 
167
167
  ### Governance Middleware
168
168
 
@@ -192,11 +192,11 @@ During consolidation, the system also scans for near-duplicates using cosine sim
192
192
 
193
193
  Context compaction is irreversible, and if the window fills completely before compaction runs the user has to abandon the chat. Engram treats this as a first-class failure mode and ships three tools that mechanize the fix:
194
194
 
195
- - `memory_handoff_write` persists a structured "where we left off" snapshot to `handoffs/YYYY-MM-DD_HH-MM-SS.{json,md}` — currentTask, completed, nextSteps, openQuestions, file references, decisions, and free-form notes. The JSON half is for programmatic resume; the markdown half is for humans.
196
- - `memory_handoff_read` loads the latest handoff (or a specific one by stamp). Agents call it at session start to pick up from exactly where the previous session stopped.
197
- - `memory_context_pressure` is a self-nudge: the agent reports its own pressure level (`ok`/`warm`/`hot`/`critical`) and gets back a deterministic action plan — when to save, when to write the handoff, when to compact early rather than riding the window to the edge. Passing `phaseBoundary=true` (task complete, pivoting focus, finishing a subsystem) overrides level and forces a proactive compact; the reasoning is that pivots thrash Anthropic's 5-minute prompt cache anyway, so eating that miss at the boundary is effectively free and avoids carrying the verbose tool output of the finished phase into the next one.
195
+ - `engram-handoff-write` persists a structured "where we left off" snapshot to `handoffs/YYYY-MM-DD_HH-MM-SS.{json,md}` — currentTask, completed, nextSteps, openQuestions, file references, decisions, and free-form notes. The JSON half is for programmatic resume; the markdown half is for humans.
196
+ - `engram-handoff-read` loads the latest handoff (or a specific one by stamp). Agents call it at session start to pick up from exactly where the previous session stopped.
197
+ - `engram-context-pressure` is a self-nudge: the agent reports its own pressure level (`ok`/`warm`/`hot`/`critical`) and gets back a deterministic action plan — when to save, when to write the handoff, when to compact early rather than riding the window to the edge. Passing `phaseBoundary=true` (task complete, pivoting focus, finishing a subsystem) overrides level and forces a proactive compact; the reasoning is that pivots thrash Anthropic's 5-minute prompt cache anyway, so eating that miss at the boundary is effectively free and avoids carrying the verbose tool output of the finished phase into the next one.
198
198
 
199
- The bundled `engram_precompact_hook.sh` makes the write mandatory: it **blocks** compaction until `memory_handoff_write` has been called with `reason=compact`. Save constantly, compact at natural phase boundaries, and the next session starts with a full picture regardless of what happened in the previous one.
199
+ The bundled `engram_precompact_hook.sh` makes the write mandatory: it **blocks** compaction until `engram-handoff-write` has been called with `reason=compact`. Save constantly, compact at natural phase boundaries, and the next session starts with a full picture regardless of what happened in the previous one.
200
200
 
201
201
  ## Compatibility
202
202
 
@@ -389,57 +389,57 @@ For shared/cloud deployments where many users share one Engram process, Engram a
389
389
 
390
390
  ## Tools
391
391
 
392
- The MCP server exposes 20 tools across six groups. Several earlier tools (`memory_format`, `memory_check_duplicate`, `memory_extract_rules`, `memory_taxonomy`, `memory_kg_stats`) were folded into their parent tools in 1.0.0-beta.6 — pass the relevant flag or mode to the parent instead. 1.0.0-beta.8 added the Handoff tools for cross-session continuity. 1.0.0 adds the memory origin field (user vs derived), the scratch tier, and `memory_scratch_promote`.
392
+ The MCP server exposes 20 tools across six groups. Several earlier tools (`engram-format`, `engram-check-duplicate`, `engram-extract-rules`, `engram-taxonomy`, `engram-kg-stats`) were folded into their parent tools in 1.0.0-beta.6 — pass the relevant flag or mode to the parent instead. 1.0.0-beta.8 added the Handoff tools for cross-session continuity. 1.0.0 adds the memory origin field (user vs derived), the scratch tier, and `engram-scratch-promote`.
393
393
 
394
394
  ### Core Memory
395
395
 
396
396
  | Tool | What it does |
397
397
  |------|-------------|
398
- | `memory_search` | Hybrid ANN + keyword search with spreading activation. Supports a formatted output mode for prompt injection (replaces the old `memory_format`). |
399
- | `memory_ingest` | Write-ahead log: immediately persist a memory before responding. Runs duplicate detection inline (replaces `memory_check_duplicate`). Defaults `origin='user'` since explicit ingest is user-asserted; pass `tier: 'scratch'` for session-only notes. |
400
- | `memory_scratch_promote` | Graduate a scratch-tier memory to short-term so it survives the 24h auto-purge and enters the normal consolidation lifecycle. |
401
- | `memory_extract` | Extract memories from a conversation (LLM or heuristic). Rules-only mode replaces the old `memory_extract_rules`. |
402
- | `memory_maintain` | Run consolidation (decay, promote, link, merge, self-organize). Auto-describes unnamed memories, generates cross-links, and syncs the Persona procedural bridge when both servers are running. |
403
- | `memory_rules` | Show active procedural rules |
404
- | `memory_outcome` | Record recall feedback (helpful/corrected/irrelevant) |
405
- | `memory_session` | Manage session state (hot RAM scratchpad) |
406
- | `memory_stats` | Memory statistics by tier, layer, type. Includes KG stats, domain/topic taxonomy, and Persona bridge status (replaces `memory_kg_stats` and `memory_taxonomy`). |
398
+ | `engram-search` | Hybrid ANN + keyword search with spreading activation. Supports a formatted output mode for prompt injection (replaces the old `engram-format`). |
399
+ | `engram-ingest` | Write-ahead log: immediately persist a memory before responding. Runs duplicate detection inline (replaces `engram-check-duplicate`). Defaults `origin='user'` since explicit ingest is user-asserted; pass `tier: 'scratch'` for session-only notes. |
400
+ | `engram-scratch-promote` | Graduate a scratch-tier memory to short-term so it survives the 24h auto-purge and enters the normal consolidation lifecycle. |
401
+ | `engram-extract` | Extract memories from a conversation (LLM or heuristic). Rules-only mode replaces the old `engram-extract-rules`. |
402
+ | `engram-maintain` | Run consolidation (decay, promote, link, merge, self-organize). Auto-describes unnamed memories, generates cross-links, and syncs the Persona procedural bridge when both servers are running. |
403
+ | `engram-rules` | Show active procedural rules |
404
+ | `engram-outcome` | Record recall feedback (helpful/corrected/irrelevant) |
405
+ | `engram-session` | Manage session state (hot RAM scratchpad) |
406
+ | `engram-stats` | Memory statistics by tier, layer, type. Includes KG stats, domain/topic taxonomy, and Persona bridge status (replaces `engram-kg-stats` and `engram-taxonomy`). |
407
407
 
408
408
  ### Knowledge Graph
409
409
 
410
410
  | Tool | What it does |
411
411
  |------|-------------|
412
- | `memory_kg_add` | Add a subject-predicate-object triple |
413
- | `memory_kg_query` | Query triples with optional filters |
414
- | `memory_kg_invalidate` | Mark a fact as no longer valid |
415
- | `memory_kg_timeline` | Get chronological history of an entity |
412
+ | `engram-kg-add` | Add a subject-predicate-object triple |
413
+ | `engram-kg-query` | Query triples with optional filters |
414
+ | `engram-kg-invalidate` | Mark a fact as no longer valid |
415
+ | `engram-kg-timeline` | Get chronological history of an entity |
416
416
 
417
417
  ### Diary
418
418
 
419
419
  | Tool | What it does |
420
420
  |------|-------------|
421
- | `memory_diary_write` | Write a session diary entry |
422
- | `memory_diary_read` | Read diary entries by date or range |
421
+ | `engram-diary-write` | Write a session diary entry |
422
+ | `engram-diary-read` | Read diary entries by date or range |
423
423
 
424
424
  ### Handoff (cross-session continuity)
425
425
 
426
426
  | Tool | What it does |
427
427
  |------|-------------|
428
- | `memory_handoff_write` | Structured "where we left off" snapshot — currentTask, completed, nextSteps, openQuestions, fileRefs, decisions, notes. Written before compaction or session end so a fresh session can resume without re-explanation. |
429
- | `memory_handoff_read` | Load the latest handoff (or one by stamp; `list=true` for recent stamps). Call at session start to pick up where the prior session left off. |
430
- | `memory_context_pressure` | Self-assess context window pressure (`ok`/`warm`/`hot`/`critical`) and receive a deterministic action plan — when to save memories, when to write a handoff, when to invoke `/compact`. Pass `phaseBoundary=true` at natural task/phase boundaries to force a proactive compact regardless of level (pivots thrash the cache anyway — compacting at the boundary is a free lunch). |
428
+ | `engram-handoff-write` | Structured "where we left off" snapshot — currentTask, completed, nextSteps, openQuestions, fileRefs, decisions, notes. Written before compaction or session end so a fresh session can resume without re-explanation. |
429
+ | `engram-handoff-read` | Load the latest handoff (or one by stamp; `list=true` for recent stamps). Call at session start to pick up where the prior session left off. |
430
+ | `engram-context-pressure` | Self-assess context window pressure (`ok`/`warm`/`hot`/`critical`) and receive a deterministic action plan — when to save memories, when to write a handoff, when to invoke `/compact`. Pass `phaseBoundary=true` at natural task/phase boundaries to force a proactive compact regardless of level (pivots thrash the cache anyway — compacting at the boundary is a free lunch). |
431
431
 
432
432
  ### Governance
433
433
 
434
434
  | Tool | What it does |
435
435
  |------|-------------|
436
- | `memory_govern` | Run governance checks: contradiction detection (vector + heuristic + LLM), semantic drift monitoring, and memory poisoning detection. All advisory — flags issues without auto-deleting. |
436
+ | `engram-govern` | Run governance checks: contradiction detection (vector + heuristic + LLM), semantic drift monitoring, and memory poisoning detection. All advisory — flags issues without auto-deleting. |
437
437
 
438
438
  ### Import
439
439
 
440
440
  | Tool | What it does |
441
441
  |------|-------------|
442
- | `memory_import` | Bulk import from Claude Code JSONL, ChatGPT JSON, or plain text |
442
+ | `engram-import` | Bulk import from Claude Code JSONL, ChatGPT JSON, or plain text |
443
443
 
444
444
  ## Slash Commands
445
445
 
@@ -611,7 +611,7 @@ Runtime: ~1–3 min at default settings.
611
611
 
612
612
  ### Query latency
613
613
 
614
- Loads N synthetic chunks (default 10,000), runs M queries (default 1,000) sequentially, and reports p50 / p95 / p99 latency per query bucket (`short` keyword queries, `medium` single-sentence questions, `long` multi-clause questions). Wall-clock is measured around the full `search()` call — the same path `memory_search` hits at the MCP boundary.
614
+ Loads N synthetic chunks (default 10,000), runs M queries (default 1,000) sequentially, and reports p50 / p95 / p99 latency per query bucket (`short` keyword queries, `medium` single-sentence questions, `long` multi-clause questions). Wall-clock is measured around the full `search()` call — the same path `engram-search` hits at the MCP boundary.
615
615
 
616
616
  ```bash
617
617
  npm run bench:latency
@@ -668,7 +668,7 @@ When both servers are running, they coordinate through three mechanisms:
668
668
 
669
669
  1. **Emotion-weighted memory importance.** Engram calls `persona_state` during ingestion to get the current emotional valence and arousal. High-arousal negative emotions boost memory importance by up to 30%. A frustrated correction gets remembered more strongly than a neutral fact.
670
670
 
671
- 2. **Cognitive-load-gated search.** When Persona detects cognitive overload, Engram's `memory_search` receives the load signal and returns only the top 3 high-importance memories instead of the full result set. Less noise when you're already overwhelmed.
671
+ 2. **Cognitive-load-gated search.** When Persona detects cognitive overload, Engram's `engram-search` receives the load signal and returns only the top 3 high-importance memories instead of the full result set. Less noise when you're already overwhelmed.
672
672
 
673
673
  3. **Procedural bridge.** Engram's learned rules (from corrections and instructions) and Persona's applied evolution proposals sync through a shared bridge file at `~/.claude/procedural-bridge.json`. Engram rules become Persona proposals. Persona's applied proposals reinforce or create Engram rules. The bridge auto-syncs during `persona_consolidate`.
674
674
 
@@ -1,68 +1,107 @@
1
- /**
2
- * Device-code login + logout against pyre-web.
3
- *
4
- * Flow:
5
- * 1. POST /api/auth/device-code → user_code, device_code, verification_url, expires_in, interval.
6
- * 2. Print the URL + code, best-effort open the browser.
7
- * 3. Poll /api/auth/device-code/poll until approved / denied / expired / timeout.
8
- * 4. On approval, write ~/.pyre/credentials.json. The api_url written
9
- * to disk is the server-returned canonical URL from the poll
10
- * response, NOT the one the user typed at login time. Server is
11
- * the source of truth -- it may normalise / redirect / hand back
12
- * a different storage endpoint than the login endpoint.
13
- *
14
- * No hardcoded URLs. The CLI requires the user to supply the server
15
- * URL at login (positional arg, --server flag, or PYRE_API_URL env).
16
- * Shipping prod is "users point at prod when they log in."
17
- *
18
- * Everything except the final success/failure line goes to stderr. The
19
- * URL + code block goes to stdout so a caller piping our output to a
20
- * file gets only the actionable bits.
21
- */
22
- import { credentialsPath } from './credentials.js';
23
- export interface LoginOptions {
24
- /**
25
- * pyre-web base URL. Required. Caller (the CLI) is responsible for
26
- * resolving this from positional arg / --server flag / PYRE_API_URL
27
- * env var and refusing to call runLogin() without one. runLogin
28
- * itself does NOT look at process.env — keeps the function
29
- * testable and the policy ("server URL required") visible at the
30
- * caller.
31
- */
32
- apiUrl: string;
33
- /** Override hostname (tests). */
34
- deviceName?: string;
35
- /** Override the writes-to-disk target (tests). */
36
- credentialsFile?: string;
37
- /** Override the "open in browser" hook (tests). */
38
- openBrowser?: (url: string) => void;
39
- /** Override fetch (tests). */
40
- fetchImpl?: typeof fetch;
41
- /** Override sleep (tests). */
42
- sleep?: (ms: number) => Promise<void>;
43
- /** Override Date.now (tests). */
44
- now?: () => number;
45
- }
46
- /**
47
- * Resolve a user-supplied server URL from CLI arg / flag / env, in
48
- * that precedence. Returns null when none of the three sources gave
49
- * us a URL caller is expected to print the spec'd error message
50
- * and exit 1.
51
- */
52
- export declare function resolveServerUrl(opts: {
53
- positional?: string;
54
- flag?: string;
55
- }): string | null;
56
- /**
57
- * Run the login flow end-to-end. Returns 0 on success, non-zero on
58
- * failure. Prints user-visible messages to stdout/stderr as documented
59
- * in the deliverable spec.
60
- */
61
- export declare function runLogin(opts: LoginOptions): Promise<number>;
62
- /**
63
- * Idempotent logout — exits 0 whether or not the file existed.
64
- */
65
- export declare function runLogout(opts?: {
66
- credentialsFile?: string;
67
- }): number;
68
- export { credentialsPath };
1
+ /**
2
+ * Device-code login + logout against pyre-web.
3
+ *
4
+ * Flow:
5
+ * 1. POST /api/auth/device-code → user_code, device_code, verification_url, expires_in, interval.
6
+ * 2. Print the URL + code, best-effort open the browser.
7
+ * 3. Poll /api/auth/device-code/poll until approved / denied / expired / timeout.
8
+ * 4. On approval, write ~/.pyre/credentials.json. The api_url written
9
+ * to disk is the server-returned canonical URL from the poll
10
+ * response, NOT the one the user typed at login time. Server is
11
+ * the source of truth -- it may normalise / redirect / hand back
12
+ * a different storage endpoint than the login endpoint.
13
+ *
14
+ * No hardcoded URLs. The CLI requires the user to supply the server
15
+ * URL at login (positional arg, --server flag, or PYRE_API_URL env).
16
+ * Shipping prod is "users point at prod when they log in."
17
+ *
18
+ * Everything except the final success/failure line goes to stderr. The
19
+ * URL + code block goes to stdout so a caller piping our output to a
20
+ * file gets only the actionable bits.
21
+ */
22
+ import { credentialsPath, type Credentials } from './credentials.js';
23
+ export interface DeviceCodeStart {
24
+ user_code: string;
25
+ device_code: string;
26
+ verification_url: string;
27
+ expires_in: number;
28
+ interval: number;
29
+ }
30
+ export type DeviceCodePoll = {
31
+ status: 'pending';
32
+ } | {
33
+ status: 'approved';
34
+ api_url: string;
35
+ api_key: string;
36
+ label: string;
37
+ scopes: string[];
38
+ } | {
39
+ status: 'denied';
40
+ } | {
41
+ status: 'expired';
42
+ };
43
+ export interface LoginOptions {
44
+ /**
45
+ * pyre-web base URL. Required. Caller (the CLI) is responsible for
46
+ * resolving this from positional arg / --server flag / PYRE_API_URL
47
+ * env var and refusing to call runLogin() without one. runLogin
48
+ * itself does NOT look at process.env keeps the function
49
+ * testable and the policy ("server URL required") visible at the
50
+ * caller.
51
+ */
52
+ apiUrl: string;
53
+ /** Override hostname (tests). */
54
+ deviceName?: string;
55
+ /** Override the writes-to-disk target (tests). */
56
+ credentialsFile?: string;
57
+ /** Override the "open in browser" hook (tests). */
58
+ openBrowser?: (url: string) => void;
59
+ /** Override fetch (tests). */
60
+ fetchImpl?: typeof fetch;
61
+ /** Override sleep (tests). */
62
+ sleep?: (ms: number) => Promise<void>;
63
+ /** Override Date.now (tests). */
64
+ now?: () => number;
65
+ }
66
+ /**
67
+ * Resolve a user-supplied server URL from CLI arg / flag / env, in
68
+ * that precedence. Returns null when none of the three sources gave
69
+ * us a URL — caller is expected to print the spec'd error message
70
+ * and exit 1.
71
+ */
72
+ export declare function resolveServerUrl(opts: {
73
+ positional?: string;
74
+ flag?: string;
75
+ }): string | null;
76
+ /**
77
+ * Start a device-code pairing. Retries up to 3 times with 1s/2s/4s
78
+ * backoff on transient network failures before giving up.
79
+ */
80
+ export declare function startDeviceCode(fetchImpl: typeof fetch, apiUrl: string, deviceName: string, sleep: (ms: number) => Promise<void>): Promise<DeviceCodeStart>;
81
+ /**
82
+ * Single poll of /api/auth/device-code/poll. Normalises HTTP 410 to
83
+ * the `expired` status the rest of the codebase already handles.
84
+ * Other non-2xx responses are surfaced as thrown errors so callers
85
+ * (CLI's loop, MCP tool) can decide whether to retry or give up.
86
+ */
87
+ export declare function pollDeviceCode(fetchImpl: typeof fetch, apiUrl: string, deviceCode: string): Promise<DeviceCodePoll>;
88
+ /**
89
+ * Build a Credentials object from an approved poll response. Centralises
90
+ * the shape used by both the CLI and the MCP tool path.
91
+ */
92
+ export declare function credentialsFromApproval(approved: Extract<DeviceCodePoll, {
93
+ status: 'approved';
94
+ }>): Credentials;
95
+ /**
96
+ * Run the login flow end-to-end. Returns 0 on success, non-zero on
97
+ * failure. Prints user-visible messages to stdout/stderr as documented
98
+ * in the deliverable spec.
99
+ */
100
+ export declare function runLogin(opts: LoginOptions): Promise<number>;
101
+ /**
102
+ * Idempotent logout — exits 0 whether or not the file existed.
103
+ */
104
+ export declare function runLogout(opts?: {
105
+ credentialsFile?: string;
106
+ }): number;
107
+ export { credentialsPath };