shabti 2.6.0 → 2.7.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 +139 -41
- package/package.json +1 -1
- package/src/a2a/server.js +3 -3
- package/src/commands/delete.js +20 -0
- package/src/commands/import.js +1 -1
- package/src/core/engine.js +1 -1
- package/src/index.js +2 -0
- package/src/mcp/server.js +2 -2
- package/src/repl/index.js +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Agent Memory OS — semantic memory for AI agents.
|
|
4
4
|
|
|
5
|
-
A Rust-powered memory engine with Node.js CLI that provides semantic search, deduplication, time-decay scoring, and graph-based memory linking for AI agents.
|
|
5
|
+
A Rust-powered memory engine with Node.js CLI that provides semantic search, deduplication, time-decay scoring, and graph-based memory linking for AI agents. Integrates with Claude Code, Cursor, and other tools via MCP and A2A protocols.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -30,11 +30,14 @@ shabti config setup --check
|
|
|
30
30
|
# Store a memory
|
|
31
31
|
shabti store "Rust is a systems programming language"
|
|
32
32
|
|
|
33
|
+
# Store with namespace and TTL (auto-expires after 1 hour)
|
|
34
|
+
shabti store "temp note" --namespace work --ttl 3600
|
|
35
|
+
|
|
33
36
|
# Search memories
|
|
34
37
|
shabti search "systems programming"
|
|
35
38
|
|
|
36
|
-
# Search with score explanation
|
|
37
|
-
shabti search "programming" --explain
|
|
39
|
+
# Search with score explanation and graph expansion
|
|
40
|
+
shabti search "programming" --explain --follow-links 2
|
|
38
41
|
|
|
39
42
|
# Check engine status
|
|
40
43
|
shabti status
|
|
@@ -42,14 +45,20 @@ shabti status
|
|
|
42
45
|
|
|
43
46
|
## Commands
|
|
44
47
|
|
|
45
|
-
| Command | Description
|
|
46
|
-
| ----------------- |
|
|
47
|
-
| `store <content>` | Store a memory entry
|
|
48
|
-
| `search <query>` | Search memory entries
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
51
|
-
| `
|
|
52
|
-
| `
|
|
48
|
+
| Command | Description |
|
|
49
|
+
| ----------------- | ----------------------------------- |
|
|
50
|
+
| `store <content>` | Store a memory entry |
|
|
51
|
+
| `search <query>` | Search memory entries |
|
|
52
|
+
| `delete <id>` | Delete a memory entry by ID |
|
|
53
|
+
| `status` | Show engine status |
|
|
54
|
+
| `export` | Export memories as JSONL |
|
|
55
|
+
| `import <file>` | Import memories from JSONL |
|
|
56
|
+
| `snapshot` | Manage storage snapshots |
|
|
57
|
+
| `config` | Manage configuration |
|
|
58
|
+
| `gc` | Garbage collect expired entries |
|
|
59
|
+
| `a2a` | Start A2A protocol server |
|
|
60
|
+
| `chat` | Interactive chat with OpenAI |
|
|
61
|
+
| `mcp-config` | Print MCP server configuration JSON |
|
|
53
62
|
|
|
54
63
|
### store
|
|
55
64
|
|
|
@@ -57,6 +66,7 @@ shabti status
|
|
|
57
66
|
shabti store "Tokyo is the capital of Japan"
|
|
58
67
|
shabti store "meeting at 3pm" --namespace work
|
|
59
68
|
shabti store "buy groceries" --tags "todo,personal"
|
|
69
|
+
shabti store "temporary note" --ttl 3600 # expires in 1 hour
|
|
60
70
|
```
|
|
61
71
|
|
|
62
72
|
### search
|
|
@@ -70,6 +80,28 @@ shabti search "recent events" --min-score 0.5
|
|
|
70
80
|
shabti search "query" --json # JSON output
|
|
71
81
|
```
|
|
72
82
|
|
|
83
|
+
### delete
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
shabti delete <uuid>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### export / import
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Export all entries to stdout
|
|
93
|
+
shabti export
|
|
94
|
+
|
|
95
|
+
# Export to file with namespace filter
|
|
96
|
+
shabti export --namespace work --output backup.jsonl
|
|
97
|
+
|
|
98
|
+
# Import from JSONL file
|
|
99
|
+
shabti import backup.jsonl
|
|
100
|
+
|
|
101
|
+
# Import with namespace override and dry-run
|
|
102
|
+
shabti import data.jsonl --namespace imported --dry-run
|
|
103
|
+
```
|
|
104
|
+
|
|
73
105
|
### snapshot
|
|
74
106
|
|
|
75
107
|
```bash
|
|
@@ -88,13 +120,18 @@ shabti config setup # show Qdrant setup instructions
|
|
|
88
120
|
shabti config setup --check # test Qdrant connection
|
|
89
121
|
```
|
|
90
122
|
|
|
123
|
+
### gc
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
shabti gc # remove entries past their TTL
|
|
127
|
+
```
|
|
128
|
+
|
|
91
129
|
## Interactive Mode (REPL)
|
|
92
130
|
|
|
93
131
|
Run `shabti` with no arguments in a terminal to enter interactive mode:
|
|
94
132
|
|
|
95
133
|
```
|
|
96
134
|
$ shabti
|
|
97
|
-
shabti v2.0.0
|
|
98
135
|
|
|
99
136
|
[info] Memory engine connected (/remember, /recall available)
|
|
100
137
|
[info] Interactive mode — model: gpt-4o-mini
|
|
@@ -119,14 +156,14 @@ Requires `OPENAI_API_KEY` in `.env` for chat functionality. Memory commands (`/r
|
|
|
119
156
|
|
|
120
157
|
## MCP Server
|
|
121
158
|
|
|
122
|
-
|
|
159
|
+
Shabti includes an MCP (Model Context Protocol) server for integration with Claude Code, Cursor, and other MCP-compatible tools.
|
|
123
160
|
|
|
124
161
|
```bash
|
|
125
162
|
# Start the MCP server (stdio transport)
|
|
126
163
|
npx shabti-mcp
|
|
127
164
|
```
|
|
128
165
|
|
|
129
|
-
### Claude Code configuration
|
|
166
|
+
### Claude Code / Cursor configuration
|
|
130
167
|
|
|
131
168
|
Generate the MCP settings JSON:
|
|
132
169
|
|
|
@@ -147,23 +184,57 @@ Or manually add to your MCP settings:
|
|
|
147
184
|
}
|
|
148
185
|
```
|
|
149
186
|
|
|
150
|
-
###
|
|
187
|
+
### MCP Tools
|
|
151
188
|
|
|
152
|
-
| Tool | Description
|
|
153
|
-
| --------------- |
|
|
154
|
-
| `memory_store` | Store a memory entry
|
|
155
|
-
| `memory_search` | Search memories by semantic similarity
|
|
156
|
-
| `memory_delete` | Delete a memory entry by ID
|
|
157
|
-
| `memory_list` | List recent memory entries
|
|
158
|
-
| `
|
|
189
|
+
| Tool | Description |
|
|
190
|
+
| --------------- | ----------------------------------------------------------- |
|
|
191
|
+
| `memory_store` | Store a memory entry with optional namespace, tags, and TTL |
|
|
192
|
+
| `memory_search` | Search memories by semantic similarity |
|
|
193
|
+
| `memory_delete` | Delete a memory entry by ID |
|
|
194
|
+
| `memory_list` | List recent memory entries |
|
|
195
|
+
| `memory_export` | Export entries as JSONL |
|
|
196
|
+
| `memory_gc` | Garbage collect expired entries |
|
|
197
|
+
| `memory_status` | Get engine status |
|
|
159
198
|
|
|
160
|
-
###
|
|
199
|
+
### MCP Resources
|
|
161
200
|
|
|
162
201
|
| URI | Description |
|
|
163
202
|
| ----------------- | ---------------------------- |
|
|
164
203
|
| `shabti://status` | Engine status and statistics |
|
|
165
204
|
| `shabti://config` | Current configuration |
|
|
166
205
|
|
|
206
|
+
## A2A Protocol Server
|
|
207
|
+
|
|
208
|
+
Shabti supports Google's [Agent-to-Agent (A2A) protocol](https://google.github.io/A2A/) v0.3.0 for inter-agent communication.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
# Start the A2A server (default port 3000)
|
|
212
|
+
shabti a2a
|
|
213
|
+
|
|
214
|
+
# Start on a custom port
|
|
215
|
+
shabti a2a --port 4000
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Agent Card
|
|
219
|
+
|
|
220
|
+
Discoverable at `GET /.well-known/agent-card.json`.
|
|
221
|
+
|
|
222
|
+
### Skills
|
|
223
|
+
|
|
224
|
+
| Skill | Description |
|
|
225
|
+
| --------------- | ------------------------------------ |
|
|
226
|
+
| `memory_store` | Store a memory entry via A2A message |
|
|
227
|
+
| `memory_search` | Semantic search via A2A message |
|
|
228
|
+
| `memory_status` | Engine status via A2A message |
|
|
229
|
+
|
|
230
|
+
### JSON-RPC Methods
|
|
231
|
+
|
|
232
|
+
| Method | Description |
|
|
233
|
+
| -------------- | -------------------------------- |
|
|
234
|
+
| `message/send` | Send a message to invoke a skill |
|
|
235
|
+
| `tasks/get` | Retrieve task status and results |
|
|
236
|
+
| `tasks/cancel` | Cancel a running task |
|
|
237
|
+
|
|
167
238
|
## Node.js API
|
|
168
239
|
|
|
169
240
|
```javascript
|
|
@@ -176,7 +247,11 @@ const engine = new ShabtiEngine({
|
|
|
176
247
|
});
|
|
177
248
|
|
|
178
249
|
// Store
|
|
179
|
-
await engine.store("Rust is a systems programming language", {
|
|
250
|
+
await engine.store("Rust is a systems programming language", {
|
|
251
|
+
namespace: "tech",
|
|
252
|
+
tags: ["rust", "programming"],
|
|
253
|
+
ttlSeconds: 86400, // expires in 24 hours
|
|
254
|
+
});
|
|
180
255
|
|
|
181
256
|
// Search
|
|
182
257
|
const results = await engine.executeQuery({
|
|
@@ -187,29 +262,52 @@ const results = await engine.executeQuery({
|
|
|
187
262
|
|
|
188
263
|
for (const r of results) {
|
|
189
264
|
console.log(`${r.score.toFixed(4)} ${r.content}`);
|
|
190
|
-
if (r.explanation) {
|
|
191
|
-
console.log(
|
|
192
|
-
` sim=${r.explanation.semanticSimilarity.toFixed(3)} ` +
|
|
193
|
-
`decay=${r.explanation.timeDecayFactor.toFixed(3)} ` +
|
|
194
|
-
`boost=${r.explanation.accessBoostFactor.toFixed(3)}`,
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
265
|
}
|
|
198
266
|
|
|
267
|
+
// Export
|
|
268
|
+
const entries = engine.listEntries({ namespace: "tech", limit: 100 });
|
|
269
|
+
|
|
270
|
+
// Garbage collect
|
|
271
|
+
const removed = await engine.gc();
|
|
272
|
+
|
|
199
273
|
await engine.shutdown();
|
|
200
274
|
```
|
|
201
275
|
|
|
276
|
+
## Configuration
|
|
277
|
+
|
|
278
|
+
### Environment Variables
|
|
279
|
+
|
|
280
|
+
| Variable | Description | Default |
|
|
281
|
+
| ------------------- | ----------------------------------------------------- | ----------------------- |
|
|
282
|
+
| `SHABTI_QDRANT_URL` | Override Qdrant URL | `http://localhost:6334` |
|
|
283
|
+
| `SHABTI_LOG_LEVEL` | Log level: `debug`, `info`, `warn`, `error`, `silent` | `info` |
|
|
284
|
+
| `SHABTI_LOG_JSON` | Set to `1` for JSON-formatted log output | — |
|
|
285
|
+
| `SHABTI_A2A_PORT` | A2A server port (standalone mode) | `3000` |
|
|
286
|
+
| `OPENAI_API_KEY` | Required for chat/REPL mode | — |
|
|
287
|
+
|
|
288
|
+
### Config File
|
|
289
|
+
|
|
290
|
+
Stored at `~/.shabti/config.json`. Manage via `shabti config` commands.
|
|
291
|
+
|
|
202
292
|
## Architecture
|
|
203
293
|
|
|
204
294
|
```
|
|
205
295
|
shabti (npm CLI + Node.js API)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
296
|
+
├── src/commands/ 12 CLI commands (Commander.js)
|
|
297
|
+
├── src/repl/ Interactive REPL with slash commands
|
|
298
|
+
├── src/mcp/ MCP server (stdio, 7 tools, 2 resources)
|
|
299
|
+
├── src/a2a/ A2A server (HTTP, JSON-RPC 2.0)
|
|
300
|
+
├── src/core/ Engine factory, session, retry logic
|
|
301
|
+
├── src/utils/ Style, validation, structured logger
|
|
302
|
+
└── native.cjs NAPI-RS bridge
|
|
303
|
+
└── crates/
|
|
304
|
+
├── shabti-engine Orchestration, store/search/gc
|
|
305
|
+
├── shabti-core Data models, scoring, dedup, query DSL
|
|
306
|
+
├── shabti-embedding fastembed-rs (MultilingualE5Small, 384-dim)
|
|
307
|
+
├── shabti-index Qdrant vector DB client
|
|
308
|
+
├── shabti-storage Append-only log, event store, snapshots
|
|
309
|
+
├── shabti-graph k-NN memory link graph
|
|
310
|
+
└── shabti-napi NAPI-RS bindings
|
|
213
311
|
```
|
|
214
312
|
|
|
215
313
|
## Benchmarks
|
|
@@ -227,10 +325,10 @@ See [BENCHMARKS.md](BENCHMARKS.md) for detailed results.
|
|
|
227
325
|
```bash
|
|
228
326
|
npm install # install JS dependencies
|
|
229
327
|
cargo build # build Rust workspace
|
|
230
|
-
npm test # run JS tests (vitest)
|
|
231
|
-
cargo test # run Rust tests
|
|
328
|
+
npm test # run JS tests (vitest) — 129 tests
|
|
329
|
+
cargo test # run Rust tests — 26 test modules
|
|
232
330
|
npm run lint # eslint
|
|
233
|
-
|
|
331
|
+
npm run ci # lint + format check + audit + coverage
|
|
234
332
|
```
|
|
235
333
|
|
|
236
334
|
## License
|
package/package.json
CHANGED
package/src/a2a/server.js
CHANGED
|
@@ -325,7 +325,7 @@ export function startA2AServer(port = 3000) {
|
|
|
325
325
|
let body;
|
|
326
326
|
try {
|
|
327
327
|
body = await readBody(req);
|
|
328
|
-
} catch {
|
|
328
|
+
} catch (_) {
|
|
329
329
|
res.writeHead(400);
|
|
330
330
|
return res.end();
|
|
331
331
|
}
|
|
@@ -333,7 +333,7 @@ export function startA2AServer(port = 3000) {
|
|
|
333
333
|
let rpc;
|
|
334
334
|
try {
|
|
335
335
|
rpc = JSON.parse(body);
|
|
336
|
-
} catch {
|
|
336
|
+
} catch (_) {
|
|
337
337
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
338
338
|
return res.end(
|
|
339
339
|
JSON.stringify({
|
|
@@ -376,7 +376,7 @@ export function startA2AServer(port = 3000) {
|
|
|
376
376
|
if (engine && engine.shutdown) {
|
|
377
377
|
try {
|
|
378
378
|
await engine.shutdown();
|
|
379
|
-
} catch {
|
|
379
|
+
} catch (_) {
|
|
380
380
|
// best-effort
|
|
381
381
|
}
|
|
382
382
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createEngine } from "../core/engine.js";
|
|
2
|
+
import { success, error } from "../utils/style.js";
|
|
3
|
+
|
|
4
|
+
export function registerDelete(program) {
|
|
5
|
+
program
|
|
6
|
+
.command("delete")
|
|
7
|
+
.description("Delete a memory entry by ID")
|
|
8
|
+
.argument("<id>", "UUID of the memory entry to delete")
|
|
9
|
+
.action(async (id) => {
|
|
10
|
+
try {
|
|
11
|
+
const engine = createEngine();
|
|
12
|
+
await engine.delete(id);
|
|
13
|
+
success(`Deleted: ${id}`);
|
|
14
|
+
await engine.shutdown();
|
|
15
|
+
} catch (err) {
|
|
16
|
+
error(err.message);
|
|
17
|
+
process.exitCode = 1;
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
package/src/commands/import.js
CHANGED
package/src/core/engine.js
CHANGED
package/src/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import { registerStatus } from "./commands/status.js";
|
|
|
15
15
|
import { registerExport } from "./commands/export.js";
|
|
16
16
|
import { registerImport } from "./commands/import.js";
|
|
17
17
|
import { registerStore } from "./commands/store.js";
|
|
18
|
+
import { registerDelete } from "./commands/delete.js";
|
|
18
19
|
|
|
19
20
|
const { version } = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
20
21
|
|
|
@@ -84,6 +85,7 @@ function buildProgram() {
|
|
|
84
85
|
registerExport(program);
|
|
85
86
|
registerImport(program);
|
|
86
87
|
registerStore(program);
|
|
88
|
+
registerDelete(program);
|
|
87
89
|
|
|
88
90
|
program
|
|
89
91
|
.command("gc")
|
package/src/mcp/server.js
CHANGED
|
@@ -428,7 +428,7 @@ async function handleRequest(line) {
|
|
|
428
428
|
let req;
|
|
429
429
|
try {
|
|
430
430
|
req = JSON.parse(line);
|
|
431
|
-
} catch {
|
|
431
|
+
} catch (_) {
|
|
432
432
|
return respondError(null, -32700, "Parse error");
|
|
433
433
|
}
|
|
434
434
|
|
|
@@ -465,7 +465,7 @@ async function shutdown() {
|
|
|
465
465
|
if (engine && engine.shutdown) {
|
|
466
466
|
try {
|
|
467
467
|
await engine.shutdown();
|
|
468
|
-
} catch {
|
|
468
|
+
} catch (_) {
|
|
469
469
|
// best-effort
|
|
470
470
|
}
|
|
471
471
|
}
|
package/src/repl/index.js
CHANGED
|
@@ -26,7 +26,7 @@ export async function launchRepl() {
|
|
|
26
26
|
info(
|
|
27
27
|
`Memory engine connected (${chalk.cyan("/remember")}, ${chalk.cyan("/recall")} available)`,
|
|
28
28
|
);
|
|
29
|
-
} catch {
|
|
29
|
+
} catch (_) {
|
|
30
30
|
warn("Memory engine not available. Chat-only mode (start Qdrant to enable memory).");
|
|
31
31
|
}
|
|
32
32
|
|