@plur-ai/core 0.2.1 → 0.2.3
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 +74 -129
- package/dist/index.d.ts +12 -1
- package/dist/index.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,65 +1,66 @@
|
|
|
1
1
|
# @plur-ai/core
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The engine behind [PLUR](https://plur.ai) — persistent memory for AI agents.
|
|
4
|
+
|
|
5
|
+
You correct your agent on Monday. On Tuesday, it makes the same mistake. PLUR fixes this. Corrections, preferences, and conventions persist across sessions. Your data stays on your disk as plain YAML. Search runs locally with zero API calls.
|
|
6
|
+
|
|
7
|
+
The result: **Haiku with PLUR memory outperforms Opus without it** — 2.6x better on tool routing, at 10x less cost. The bottleneck isn't model intelligence. It's context.
|
|
8
|
+
|
|
9
|
+
## Why @plur-ai/core
|
|
10
|
+
|
|
11
|
+
This is the engine that powers everything. Use it directly when you're building your own agent framework or want programmatic control over memory. If you just want to add memory to Claude Code or Cursor, use [`@plur-ai/mcp`](https://www.npmjs.com/package/@plur-ai/mcp) instead — it wraps this package as MCP tools.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
4
14
|
|
|
5
15
|
```bash
|
|
6
16
|
npm install @plur-ai/core
|
|
7
17
|
```
|
|
8
18
|
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
9
21
|
```typescript
|
|
10
22
|
import { Plur } from '@plur-ai/core'
|
|
11
23
|
|
|
12
24
|
const plur = new Plur()
|
|
13
25
|
|
|
14
|
-
//
|
|
26
|
+
// Your agent gets corrected — save it
|
|
15
27
|
plur.learn('toEqual() in Vitest is strict — use toMatchObject() for partial matching', {
|
|
16
28
|
type: 'behavioral',
|
|
17
29
|
scope: 'project:my-app',
|
|
18
30
|
domain: 'dev/testing'
|
|
19
31
|
})
|
|
20
32
|
|
|
21
|
-
//
|
|
33
|
+
// Next session: recall what was learned (hybrid search, zero cost)
|
|
22
34
|
const results = await plur.recallHybrid('vitest assertion matching')
|
|
23
35
|
|
|
24
|
-
//
|
|
25
|
-
const { directives, consider,
|
|
36
|
+
// Or inject the best engrams into a system prompt, within a token budget
|
|
37
|
+
const { directives, consider, tokens_used } = plur.inject('Write tests for the user service', {
|
|
26
38
|
scope: 'project:my-app',
|
|
27
39
|
budget: 2000
|
|
28
40
|
})
|
|
29
41
|
|
|
30
|
-
//
|
|
42
|
+
// Rate what was useful — the system improves over time
|
|
31
43
|
plur.feedback(results[0].id, 'positive')
|
|
32
44
|
|
|
33
|
-
// Sync across machines
|
|
45
|
+
// Sync across machines via git
|
|
34
46
|
plur.sync('git@github.com:you/plur-memory.git')
|
|
35
47
|
```
|
|
36
48
|
|
|
37
|
-
##
|
|
49
|
+
## How it works
|
|
38
50
|
|
|
39
|
-
|
|
51
|
+
Knowledge is stored as **engrams** — small assertions that strengthen with use and decay when irrelevant, modeled on how human memory works (ACT-R activation). The system gets better over time, not just bigger.
|
|
40
52
|
|
|
41
|
-
```typescript
|
|
42
|
-
const plur = new Plur({ path: '/custom/storage/path' })
|
|
43
53
|
```
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
### `learn(statement, context?)`
|
|
48
|
-
|
|
49
|
-
Create an engram. Detects conflicts with existing engrams in the same scope.
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
plur.learn('Always run lint before committing', {
|
|
53
|
-
type: 'behavioral', // behavioral | architectural | procedural | terminological
|
|
54
|
-
scope: 'project:myapp', // namespace for filtering
|
|
55
|
-
domain: 'software.git', // dot-separated domain tag
|
|
56
|
-
source: 'user-correction',
|
|
57
|
-
})
|
|
54
|
+
You correct your agent → engram created → YAML on your disk
|
|
55
|
+
Next session starts → relevant ones injected → agent remembers
|
|
56
|
+
You rate the result → engram strengthens → quality improves
|
|
58
57
|
```
|
|
59
58
|
|
|
60
|
-
|
|
59
|
+
Search is fully local: BM25 over enriched text + BGE-small-en-v1.5 embeddings + Reciprocal Rank Fusion. **86.7% on LongMemEval** — on par with cloud solutions that charge per query.
|
|
61
60
|
|
|
62
|
-
|
|
61
|
+
## Search modes
|
|
62
|
+
|
|
63
|
+
Five modes, from fastest to most accurate:
|
|
63
64
|
|
|
64
65
|
| Method | Speed | API calls | Best for |
|
|
65
66
|
|--------|-------|-----------|----------|
|
|
@@ -67,110 +68,34 @@ Five search modes, from fastest to most accurate:
|
|
|
67
68
|
| `recallSemantic(query)` | ~200ms | None | Meaning-based search (local embeddings) |
|
|
68
69
|
| `recallHybrid(query)` | ~200ms | None | **Best default** — BM25 + embeddings via RRF |
|
|
69
70
|
| `recallAsync(query, { llm })` | ~1s | 1 LLM call | LLM-assisted semantic filtering |
|
|
70
|
-
| `recallExpanded(query, { llm })` | ~3s | 3-5 LLM calls | Query expansion
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
### `feedback(id, signal)`
|
|
95
|
-
|
|
96
|
-
Rate an engram's usefulness. Trains injection relevance over time.
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
plur.feedback('ENG-001', 'positive') // +0.05 retrieval strength
|
|
100
|
-
plur.feedback('ENG-002', 'negative') // -0.10 retrieval strength
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### `forget(id, reason?)`
|
|
104
|
-
|
|
105
|
-
Retire an engram. History preserved, excluded from recall and injection.
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
plur.forget('ENG-001', 'API changed')
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### `sync(remote?)`
|
|
112
|
-
|
|
113
|
-
Git-based sync across machines. Initializes on first call, commits + push/pull on subsequent calls.
|
|
114
|
-
|
|
115
|
-
```typescript
|
|
116
|
-
// First time — init repo and push
|
|
117
|
-
plur.sync('git@github.com:you/plur-memory.git')
|
|
118
|
-
|
|
119
|
-
// Later — commit, pull, push
|
|
120
|
-
plur.sync()
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
// Check sync status (no changes made)
|
|
125
|
-
const status = plur.syncStatus()
|
|
126
|
-
// { initialized, remote, dirty, branch, ahead, behind }
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### `capture(summary, context?)` / `timeline(query?)`
|
|
130
|
-
|
|
131
|
-
Episodic memory — record what happened, query the timeline.
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
plur.capture('Deployed v2.0 to production', {
|
|
135
|
-
agent: 'claude-code',
|
|
136
|
-
session_id: 'abc123',
|
|
137
|
-
tags: ['deploy'],
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
const episodes = plur.timeline({ since: new Date('2025-01-01'), agent: 'claude-code' })
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### `ingest(content, options?)`
|
|
144
|
-
|
|
145
|
-
Extract engram candidates from text using pattern matching.
|
|
146
|
-
|
|
147
|
-
```typescript
|
|
148
|
-
const candidates = plur.ingest(markdownContent, {
|
|
149
|
-
extract_only: true, // preview without saving
|
|
150
|
-
scope: 'project:myapp',
|
|
151
|
-
source: 'docs/architecture.md',
|
|
152
|
-
})
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### `installPack(source)` / `exportPack(...)` / `listPacks()`
|
|
156
|
-
|
|
157
|
-
Share engram collections between agents or users.
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
plur.installPack('/path/to/pack-directory')
|
|
161
|
-
const packs = plur.listPacks()
|
|
162
|
-
plur.exportPack(engrams, './output', { name: 'my-pack', version: '1.0.0' })
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### `status()`
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
const { engram_count, episode_count, pack_count, storage_root, config } = plur.status()
|
|
169
|
-
```
|
|
71
|
+
| `recallExpanded(query, { llm })` | ~3s | 3-5 LLM calls | Query expansion for exhaustive retrieval |
|
|
72
|
+
|
|
73
|
+
## Full API
|
|
74
|
+
|
|
75
|
+
| Method | What it does |
|
|
76
|
+
|--------|-------------|
|
|
77
|
+
| `learn(statement, context?)` | Store an engram (correction, preference, convention, decision) |
|
|
78
|
+
| `recall(query, options?)` | BM25 keyword search — instant, zero cost |
|
|
79
|
+
| `recallHybrid(query, options?)` | BM25 + embeddings merged via RRF — best default |
|
|
80
|
+
| `recallSemantic(query, options?)` | Embedding-only search — meaning over keywords |
|
|
81
|
+
| `recallAsync(query, { llm })` | LLM-assisted semantic filtering |
|
|
82
|
+
| `recallExpanded(query, { llm })` | Query expansion + hybrid + RRF merge |
|
|
83
|
+
| `inject(task, options?)` | Select engrams for a task within a token budget |
|
|
84
|
+
| `feedback(id, signal)` | Rate an engram — trains injection relevance over time |
|
|
85
|
+
| `forget(id, reason?)` | Retire an engram (history preserved) |
|
|
86
|
+
| `sync(remote?)` | Git-based sync across machines |
|
|
87
|
+
| `syncStatus()` | Check sync state without making changes |
|
|
88
|
+
| `capture(summary, context?)` | Record a session event to the episodic timeline |
|
|
89
|
+
| `timeline(query?)` | Query past episodes by time, agent, or search |
|
|
90
|
+
| `ingest(content, options?)` | Extract engram candidates from text via pattern matching |
|
|
91
|
+
| `installPack(source)` | Install a shareable engram pack |
|
|
92
|
+
| `exportPack(engrams, dir, manifest)` | Export engrams as a shareable pack |
|
|
93
|
+
| `listPacks()` | List installed packs |
|
|
94
|
+
| `status()` | System health — counts, storage root, config |
|
|
170
95
|
|
|
171
96
|
## Storage
|
|
172
97
|
|
|
173
|
-
Everything is plain YAML. Open it, read it, edit it.
|
|
98
|
+
Everything is plain YAML. Open it, read it, edit it, version it.
|
|
174
99
|
|
|
175
100
|
```
|
|
176
101
|
~/.plur/
|
|
@@ -181,6 +106,26 @@ Everything is plain YAML. Open it, read it, edit it.
|
|
|
181
106
|
└── packs/ # installed engram packs
|
|
182
107
|
```
|
|
183
108
|
|
|
109
|
+
Override the location with `PLUR_PATH` env var or `new Plur({ path: '...' })`.
|
|
110
|
+
|
|
111
|
+
## Benchmark
|
|
112
|
+
|
|
113
|
+
| Metric | Score |
|
|
114
|
+
|--------|-------|
|
|
115
|
+
| LongMemEval overall | **86.7%** |
|
|
116
|
+
| Hit@10 (retrieval) | 93.3% |
|
|
117
|
+
| A/B win rate vs no memory | 89% |
|
|
118
|
+
| House rules accuracy | 100% |
|
|
119
|
+
|
|
120
|
+
[Full methodology →](https://plur.ai/benchmark.html)
|
|
121
|
+
|
|
122
|
+
## Related packages
|
|
123
|
+
|
|
124
|
+
| Package | For |
|
|
125
|
+
|---------|-----|
|
|
126
|
+
| [`@plur-ai/mcp`](https://www.npmjs.com/package/@plur-ai/mcp) | Claude Code, Cursor, Windsurf (MCP server) |
|
|
127
|
+
| [`@plur-ai/claw`](https://www.npmjs.com/package/@plur-ai/claw) | OpenClaw (automatic memory plugin) |
|
|
128
|
+
|
|
184
129
|
## License
|
|
185
130
|
|
|
186
|
-
Apache-2.0
|
|
131
|
+
Apache-2.0 · [GitHub](https://github.com/plur-ai/plur) · [plur.ai](https://plur.ai)
|
package/dist/index.d.ts
CHANGED
|
@@ -684,6 +684,17 @@ interface TimelineQuery {
|
|
|
684
684
|
/** Build searchable text from all engram fields */
|
|
685
685
|
declare function engramSearchText(engram: Engram): string;
|
|
686
686
|
|
|
687
|
+
interface PlurPaths {
|
|
688
|
+
root: string;
|
|
689
|
+
engrams: string;
|
|
690
|
+
episodes: string;
|
|
691
|
+
candidates: string;
|
|
692
|
+
packs: string;
|
|
693
|
+
exchange: string;
|
|
694
|
+
config: string;
|
|
695
|
+
}
|
|
696
|
+
declare function detectPlurStorage(explicitPath?: string): PlurPaths;
|
|
697
|
+
|
|
687
698
|
/**
|
|
688
699
|
* Non-blocking version check against npm registry.
|
|
689
700
|
* Caches result in memory — one fetch per process lifetime.
|
|
@@ -795,4 +806,4 @@ declare class Plur {
|
|
|
795
806
|
status(): StatusResult;
|
|
796
807
|
}
|
|
797
808
|
|
|
798
|
-
export { type Association, type CaptureContext, type Engram, type Episode, type IngestCandidate, type IngestOptions, type InjectOptions, type InjectionResult, type KnowledgeAnchor, type LearnContext, type LlmFunction, type PackManifest, Plur, type PlurConfig, type RecallOptions, type StatusResult, type SyncResult, type SyncStatus, type TimelineQuery, type VersionCheckResult, checkForUpdate, clearVersionCache, engramSearchText, getCachedUpdateCheck };
|
|
809
|
+
export { type Association, type CaptureContext, type Engram, type Episode, type IngestCandidate, type IngestOptions, type InjectOptions, type InjectionResult, type KnowledgeAnchor, type LearnContext, type LlmFunction, type PackManifest, Plur, type PlurConfig, type PlurPaths, type RecallOptions, type StatusResult, type SyncResult, type SyncStatus, type TimelineQuery, type VersionCheckResult, checkForUpdate, clearVersionCache, detectPlurStorage, engramSearchText, getCachedUpdateCheck };
|
package/dist/index.js
CHANGED