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