jasper-recall 0.5.2 → 0.5.4
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.
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Auto-recall: inject relevant memories before agent processing
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { execSync } from 'child_process';
|
|
14
14
|
import * as path from 'path';
|
|
15
15
|
import * as os from 'os';
|
|
16
16
|
|
|
@@ -55,16 +55,12 @@ function runRecall(query: string, options: { limit?: number; json?: boolean; pub
|
|
|
55
55
|
|
|
56
56
|
const recallPath = path.join(BIN_PATH, 'recall');
|
|
57
57
|
try {
|
|
58
|
-
return
|
|
58
|
+
return execSync(`${recallPath} ${args.join(' ')}`, { encoding: 'utf8', timeout: 30000 });
|
|
59
59
|
} catch (err: any) {
|
|
60
60
|
throw new Error(`Recall failed: ${err.message}`);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
function getSimilarity(result: any): number {
|
|
65
|
-
return typeof result?.similarity === 'number' ? result.similarity : result?.score ?? 0;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
64
|
export default function register(api: PluginApi) {
|
|
69
65
|
const cfg = api.config.plugins?.entries?.['jasper-recall']?.config ?? {};
|
|
70
66
|
|
|
@@ -85,18 +81,70 @@ export default function register(api: PluginApi) {
|
|
|
85
81
|
// ============================================================================
|
|
86
82
|
|
|
87
83
|
if (autoRecall) {
|
|
88
|
-
api.on('before_agent_start', async (event: {
|
|
84
|
+
api.on('before_agent_start', async (event: {
|
|
85
|
+
prompt?: string;
|
|
86
|
+
senderId?: string;
|
|
87
|
+
source?: string;
|
|
88
|
+
isNewSession?: boolean;
|
|
89
|
+
messageCount?: number;
|
|
90
|
+
context?: { messages?: any[] };
|
|
91
|
+
}) => {
|
|
89
92
|
// Skip if no prompt or too short
|
|
90
93
|
if (!event.prompt || event.prompt.length < 10) {
|
|
91
94
|
return;
|
|
92
95
|
}
|
|
93
96
|
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
const prompt = event.prompt;
|
|
98
|
+
|
|
99
|
+
// Detect fresh session (after /new or first message)
|
|
100
|
+
const isFreshSession = event.isNewSession ||
|
|
101
|
+
event.messageCount === 0 ||
|
|
102
|
+
event.messageCount === 1 ||
|
|
103
|
+
(event.context?.messages?.length ?? 0) <= 1;
|
|
104
|
+
|
|
105
|
+
// Skip heartbeats and system prompts
|
|
106
|
+
if (prompt.startsWith('HEARTBEAT') ||
|
|
107
|
+
prompt.startsWith('Read HEARTBEAT.md') ||
|
|
108
|
+
prompt.includes('NO_REPLY') ||
|
|
109
|
+
prompt.includes('HEARTBEAT_OK')) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Skip agent-to-agent messages (cron jobs, workers, spawned agents)
|
|
114
|
+
if (event.source?.startsWith('cron:') ||
|
|
115
|
+
event.source?.startsWith('agent:') ||
|
|
116
|
+
event.source?.startsWith('spawn:') ||
|
|
117
|
+
event.source === 'sessions_send' ||
|
|
118
|
+
event.senderId?.startsWith('agent:') ||
|
|
119
|
+
event.senderId?.startsWith('worker-')) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Skip common automated patterns
|
|
124
|
+
if (prompt.startsWith('Agent-to-agent') ||
|
|
125
|
+
prompt.startsWith('📋 PR Review') ||
|
|
126
|
+
prompt.startsWith('🤖 Codex Watch') ||
|
|
127
|
+
prompt.startsWith('ANNOUNCE_')) {
|
|
96
128
|
return;
|
|
97
129
|
}
|
|
98
130
|
|
|
99
131
|
try {
|
|
132
|
+
let prependParts: string[] = [];
|
|
133
|
+
|
|
134
|
+
// If fresh session, remind agent to read identity files
|
|
135
|
+
if (isFreshSession) {
|
|
136
|
+
api.logger.info('[jasper-recall] Fresh session detected - adding identity reminder');
|
|
137
|
+
prependParts.push(`<session-init>
|
|
138
|
+
🔄 **Fresh session detected.** Before responding, read your identity files:
|
|
139
|
+
\`\`\`bash
|
|
140
|
+
cat ~/.openclaw/workspace/IDENTITY.md
|
|
141
|
+
cat ~/.openclaw/workspace/SOUL.md
|
|
142
|
+
cat ~/.openclaw/workspace/USER.md
|
|
143
|
+
\`\`\`
|
|
144
|
+
This ensures you maintain your persona and context after session reset.
|
|
145
|
+
</session-init>`);
|
|
146
|
+
}
|
|
147
|
+
|
|
100
148
|
const results = runRecall(event.prompt, {
|
|
101
149
|
limit: 3,
|
|
102
150
|
json: true,
|
|
@@ -106,25 +154,45 @@ export default function register(api: PluginApi) {
|
|
|
106
154
|
const parsed = JSON.parse(results);
|
|
107
155
|
|
|
108
156
|
// Filter by minimum score
|
|
109
|
-
const relevant = parsed.filter((r: any) =>
|
|
157
|
+
const relevant = parsed.filter((r: any) => r.score >= minScore);
|
|
110
158
|
|
|
111
|
-
if (relevant.length
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
159
|
+
if (relevant.length > 0) {
|
|
160
|
+
// Format memories for context injection
|
|
161
|
+
const memoryContext = relevant
|
|
162
|
+
.map((r: any) => `- [${r.source || 'memory'}] ${r.content.slice(0, 500)}${r.content.length > 500 ? '...' : ''}`)
|
|
163
|
+
.join('\n');
|
|
115
164
|
|
|
116
|
-
|
|
117
|
-
const memoryContext = relevant
|
|
118
|
-
.map((r: any) => `- [${r.source || 'memory'}] ${r.content.slice(0, 500)}${r.content.length > 500 ? '...' : ''}`)
|
|
119
|
-
.join('\n');
|
|
165
|
+
api.logger.info(`[jasper-recall] Auto-injecting ${relevant.length} memories into context`);
|
|
120
166
|
|
|
121
|
-
|
|
167
|
+
prependParts.push(`<relevant-memories>
|
|
168
|
+
The following memories may be relevant to this conversation:
|
|
169
|
+
${memoryContext}
|
|
170
|
+
</relevant-memories>`);
|
|
171
|
+
} else {
|
|
172
|
+
api.logger.debug?.('[jasper-recall] No relevant memories found for auto-recall');
|
|
173
|
+
}
|
|
122
174
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
175
|
+
if (prependParts.length > 0) {
|
|
176
|
+
return {
|
|
177
|
+
prependContext: prependParts.join('\n\n'),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
126
180
|
} catch (err: any) {
|
|
127
181
|
api.logger.warn(`[jasper-recall] Auto-recall failed: ${err.message}`);
|
|
182
|
+
|
|
183
|
+
// Still inject identity reminder on fresh session even if recall fails
|
|
184
|
+
if (isFreshSession) {
|
|
185
|
+
return {
|
|
186
|
+
prependContext: `<session-init>
|
|
187
|
+
🔄 **Fresh session detected.** Before responding, read your identity files:
|
|
188
|
+
\`\`\`bash
|
|
189
|
+
cat ~/.openclaw/workspace/IDENTITY.md
|
|
190
|
+
cat ~/.openclaw/workspace/SOUL.md
|
|
191
|
+
cat ~/.openclaw/workspace/USER.md
|
|
192
|
+
\`\`\`
|
|
193
|
+
</session-init>`,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
128
196
|
}
|
|
129
197
|
});
|
|
130
198
|
}
|
|
@@ -168,7 +236,7 @@ export default function register(api: PluginApi) {
|
|
|
168
236
|
} else {
|
|
169
237
|
for (const result of parsed) {
|
|
170
238
|
formatted += `### ${result.source || 'Memory'}\n`;
|
|
171
|
-
formatted += `**
|
|
239
|
+
formatted += `**Score:** ${(result.score * 100).toFixed(1)}%\n\n`;
|
|
172
240
|
formatted += `${result.content}\n\n---\n\n`;
|
|
173
241
|
}
|
|
174
242
|
}
|
|
@@ -227,6 +295,99 @@ export default function register(api: PluginApi) {
|
|
|
227
295
|
},
|
|
228
296
|
});
|
|
229
297
|
|
|
298
|
+
// ============================================================================
|
|
299
|
+
// Command: /jasper-recall setup
|
|
300
|
+
// ============================================================================
|
|
301
|
+
|
|
302
|
+
api.registerCommand({
|
|
303
|
+
name: 'jasper-recall',
|
|
304
|
+
description: 'Jasper Recall setup and status',
|
|
305
|
+
acceptsArgs: true,
|
|
306
|
+
requireAuth: true,
|
|
307
|
+
handler: async (ctx: { args?: string }) => {
|
|
308
|
+
const subcommand = ctx.args?.trim().toLowerCase();
|
|
309
|
+
|
|
310
|
+
if (subcommand === 'setup') {
|
|
311
|
+
return {
|
|
312
|
+
text: `🧠 **Jasper Recall Setup**
|
|
313
|
+
|
|
314
|
+
**Current status:**
|
|
315
|
+
- Auto-recall: ${autoRecall ? '✅ Enabled' : '❌ Disabled'}
|
|
316
|
+
- Public-only mode: ${publicOnly ? '✅ Yes' : '❌ No (full access)'}
|
|
317
|
+
- Default limit: ${defaultLimit} results
|
|
318
|
+
- Min score: ${minScore}
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
<agent-action required="true">
|
|
323
|
+
**CREATE THESE CRON JOBS NOW:**
|
|
324
|
+
|
|
325
|
+
1. **Index memories every 6 hours:**
|
|
326
|
+
- Name: "jasper-recall-index"
|
|
327
|
+
- Schedule: \`{ "kind": "cron", "expr": "0 */6 * * *" }\`
|
|
328
|
+
- Payload: \`{ "kind": "systemEvent", "text": "Run index-digests to update jasper-recall ChromaDB index" }\`
|
|
329
|
+
- Session target: "main"
|
|
330
|
+
|
|
331
|
+
2. **Digest sessions daily at 4am:**
|
|
332
|
+
- Name: "jasper-recall-digest"
|
|
333
|
+
- Schedule: \`{ "kind": "cron", "expr": "0 4 * * *" }\`
|
|
334
|
+
- Payload: \`{ "kind": "systemEvent", "text": "Run digest-sessions to extract summaries from recent session logs into memory" }\`
|
|
335
|
+
- Session target: "main"
|
|
336
|
+
|
|
337
|
+
Use the \`cron\` tool with \`action: "add"\` to create both jobs.
|
|
338
|
+
</agent-action>`,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (subcommand === 'status') {
|
|
343
|
+
try {
|
|
344
|
+
// Check if ChromaDB is accessible
|
|
345
|
+
const testResult = runRecall('test query', { limit: 1, json: true, publicOnly });
|
|
346
|
+
const parsed = JSON.parse(testResult);
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
text: `🧠 **Jasper Recall Status**
|
|
350
|
+
|
|
351
|
+
**Plugin:** ✅ Active
|
|
352
|
+
**Auto-recall:** ${autoRecall ? '✅ Enabled' : '❌ Disabled'}
|
|
353
|
+
**ChromaDB:** ✅ Connected
|
|
354
|
+
**Index size:** ${parsed.length > 0 ? 'Has data' : 'Empty or minimal'}
|
|
355
|
+
|
|
356
|
+
**Config:**
|
|
357
|
+
- Public-only: ${publicOnly}
|
|
358
|
+
- Default limit: ${defaultLimit}
|
|
359
|
+
- Min score: ${minScore}`,
|
|
360
|
+
};
|
|
361
|
+
} catch (err: any) {
|
|
362
|
+
return {
|
|
363
|
+
text: `🧠 **Jasper Recall Status**
|
|
364
|
+
|
|
365
|
+
**Plugin:** ✅ Active
|
|
366
|
+
**Auto-recall:** ${autoRecall ? '✅ Enabled' : '❌ Disabled'}
|
|
367
|
+
**ChromaDB:** ❌ Error - ${err.message}
|
|
368
|
+
|
|
369
|
+
Run \`npx jasper-recall setup\` to install dependencies.`,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Default: show help
|
|
375
|
+
return {
|
|
376
|
+
text: `🧠 **Jasper Recall**
|
|
377
|
+
|
|
378
|
+
**Commands:**
|
|
379
|
+
- \`/jasper-recall setup\` — Setup instructions & cron jobs
|
|
380
|
+
- \`/jasper-recall status\` — Check plugin status
|
|
381
|
+
- \`/recall <query>\` — Search memory
|
|
382
|
+
- \`/index\` — Re-index memory files
|
|
383
|
+
|
|
384
|
+
**CLI:**
|
|
385
|
+
- \`npx jasper-recall setup\` — Install Python dependencies
|
|
386
|
+
- \`npx jasper-recall doctor\` — Health check`,
|
|
387
|
+
};
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
|
|
230
391
|
// ============================================================================
|
|
231
392
|
// RPC Methods
|
|
232
393
|
// ============================================================================
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jasper-recall",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"description": "Local RAG system for AI agent memory using ChromaDB and sentence-transformers",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"openclaw": {
|
|
10
10
|
"extensions": [
|
|
11
|
-
"./extensions/
|
|
11
|
+
"./extensions/jasper-recall/index.ts"
|
|
12
12
|
]
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"scripts/",
|
|
43
43
|
"src/",
|
|
44
44
|
"extensions/",
|
|
45
|
+
"openclaw.plugin.json",
|
|
45
46
|
"SKILL.md",
|
|
46
47
|
"README.md"
|
|
47
48
|
]
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
# Jasper Recall - OpenClaw Plugin
|
|
2
|
-
|
|
3
|
-
Semantic search over indexed memory using ChromaDB. Automatically injects relevant context before agent processing.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **`recall` tool** — Manual semantic search over memory
|
|
8
|
-
- **`/recall` command** — Quick lookups from chat
|
|
9
|
-
- **`/index` command** — Re-index memory files
|
|
10
|
-
- **Auto-recall** — Automatically inject relevant memories before processing
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## Auto-Recall (The Magic ✨)
|
|
15
|
-
|
|
16
|
-
When `autoRecall` is enabled, jasper-recall hooks into the agent lifecycle and automatically searches your memory before every message is processed.
|
|
17
|
-
|
|
18
|
-
### How It Works
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
22
|
-
│ 1. Message arrives from user │
|
|
23
|
-
│ 2. before_agent_start hook fires │
|
|
24
|
-
│ 3. jasper-recall searches ChromaDB with message as query │
|
|
25
|
-
│ 4. Results filtered by minScore (default: 30%) │
|
|
26
|
-
│ 5. Relevant memories injected via prependContext │
|
|
27
|
-
│ 6. Agent sees memories + original message │
|
|
28
|
-
│ 7. Agent responds with full context │
|
|
29
|
-
└─────────────────────────────────────────────────────────────┘
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### What Gets Injected
|
|
33
|
-
|
|
34
|
-
```xml
|
|
35
|
-
<relevant-memories>
|
|
36
|
-
The following memories may be relevant to this conversation:
|
|
37
|
-
- [memory/2026-02-05.md] Worker orchestration decisions...
|
|
38
|
-
- [MEMORY.md] Git workflow: feature → develop → main...
|
|
39
|
-
- [memory/sops/codex-integration-sop.md] Codex Cloud sync...
|
|
40
|
-
</relevant-memories>
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### What's Skipped
|
|
44
|
-
|
|
45
|
-
Auto-recall won't run for:
|
|
46
|
-
- Heartbeat polls (`HEARTBEAT...`)
|
|
47
|
-
- System prompts containing `NO_REPLY`
|
|
48
|
-
- Messages shorter than 10 characters
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## Configuration
|
|
53
|
-
|
|
54
|
-
In `openclaw.json`:
|
|
55
|
-
|
|
56
|
-
```json
|
|
57
|
-
{
|
|
58
|
-
"plugins": {
|
|
59
|
-
"entries": {
|
|
60
|
-
"jasper-recall": {
|
|
61
|
-
"enabled": true,
|
|
62
|
-
"config": {
|
|
63
|
-
"autoRecall": true,
|
|
64
|
-
"minScore": 0.3,
|
|
65
|
-
"defaultLimit": 5,
|
|
66
|
-
"publicOnly": false
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### Options
|
|
75
|
-
|
|
76
|
-
| Option | Type | Default | Description |
|
|
77
|
-
|--------|------|---------|-------------|
|
|
78
|
-
| `enabled` | boolean | `true` | Enable/disable plugin |
|
|
79
|
-
| `autoRecall` | boolean | `false` | Auto-inject memories before processing |
|
|
80
|
-
| `minScore` | number | `0.3` | Minimum similarity score (0-1) for auto-recall |
|
|
81
|
-
| `defaultLimit` | number | `5` | Default number of results |
|
|
82
|
-
| `publicOnly` | boolean | `false` | Only search public memory (sandboxed agents) |
|
|
83
|
-
|
|
84
|
-
### Score Tuning
|
|
85
|
-
|
|
86
|
-
- `minScore: 0.3` — Include loosely related memories (more context, may include noise)
|
|
87
|
-
- `minScore: 0.5` — Only moderately relevant (balanced)
|
|
88
|
-
- `minScore: 0.7` — Only highly relevant (precise, may miss useful context)
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
## Tools
|
|
93
|
-
|
|
94
|
-
### `recall`
|
|
95
|
-
|
|
96
|
-
Manual semantic search over memory.
|
|
97
|
-
|
|
98
|
-
**Parameters:**
|
|
99
|
-
- `query` (string, required): Natural language search query
|
|
100
|
-
- `limit` (number, optional): Max results (default: 5)
|
|
101
|
-
|
|
102
|
-
**Example:**
|
|
103
|
-
```
|
|
104
|
-
recall query="what did we decide about the API design" limit=3
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
**Returns:** Formatted markdown with matching memories, scores, and sources.
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
|
-
## Commands
|
|
112
|
-
|
|
113
|
-
### `/recall <query>`
|
|
114
|
-
|
|
115
|
-
Quick memory search from chat.
|
|
116
|
-
|
|
117
|
-
```
|
|
118
|
-
/recall worker orchestration decisions
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### `/index`
|
|
122
|
-
|
|
123
|
-
Re-index memory files into ChromaDB. Run after updating notes.
|
|
124
|
-
|
|
125
|
-
```
|
|
126
|
-
/index
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
|
-
## RPC Methods
|
|
132
|
-
|
|
133
|
-
For external integrations:
|
|
134
|
-
|
|
135
|
-
### `recall.search`
|
|
136
|
-
|
|
137
|
-
```json
|
|
138
|
-
{ "query": "search terms", "limit": 5 }
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### `recall.index`
|
|
142
|
-
|
|
143
|
-
Re-index memory files (no params).
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
## Requirements
|
|
148
|
-
|
|
149
|
-
- `recall` command in `~/.local/bin/`
|
|
150
|
-
- ChromaDB index at `~/.openclaw/chroma-db`
|
|
151
|
-
- Python venv at `~/.openclaw/rag-env`
|
|
152
|
-
|
|
153
|
-
## Installation
|
|
154
|
-
|
|
155
|
-
```bash
|
|
156
|
-
npx jasper-recall setup
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
This sets up:
|
|
160
|
-
1. Python venv with ChromaDB + sentence-transformers
|
|
161
|
-
2. `recall`, `index-digests`, `digest-sessions` scripts
|
|
162
|
-
3. Initial index of memory files
|
|
163
|
-
|
|
164
|
-
---
|
|
165
|
-
|
|
166
|
-
## When Auto-Recall Helps
|
|
167
|
-
|
|
168
|
-
✅ **Great for:**
|
|
169
|
-
- Questions about past decisions ("what did we decide about X?")
|
|
170
|
-
- Following up on previous work ("where were we with the worker setup?")
|
|
171
|
-
- Context about people, preferences, projects
|
|
172
|
-
- Finding SOPs and procedures
|
|
173
|
-
|
|
174
|
-
⚠️ **Less useful for:**
|
|
175
|
-
- Brand new topics with no memory
|
|
176
|
-
- Simple commands ("list files")
|
|
177
|
-
- Real-time data (weather, time)
|
|
178
|
-
|
|
179
|
-
---
|
|
180
|
-
|
|
181
|
-
## Sandboxed Agents
|
|
182
|
-
|
|
183
|
-
For agents processing untrusted input, use `publicOnly`:
|
|
184
|
-
|
|
185
|
-
```json
|
|
186
|
-
{
|
|
187
|
-
"jasper-recall": {
|
|
188
|
-
"config": {
|
|
189
|
-
"publicOnly": true,
|
|
190
|
-
"autoRecall": true
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
This restricts searches to `memory/shared/` and public-tagged content, preventing leakage of private memories.
|
|
197
|
-
|
|
198
|
-
---
|
|
199
|
-
|
|
200
|
-
## Links
|
|
201
|
-
|
|
202
|
-
- **GitHub**: https://github.com/E-x-O-Entertainment-Studios-Inc/jasper-recall
|
|
203
|
-
- **npm**: `npx jasper-recall setup`
|
|
204
|
-
- **ClawHub**: `clawhub install jasper-recall`
|
|
@@ -1,417 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Jasper Recall OpenClaw Plugin
|
|
3
|
-
*
|
|
4
|
-
* Semantic search over indexed memory using ChromaDB.
|
|
5
|
-
* "Remember everything. Recall what matters."
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - `recall` tool for manual searches
|
|
9
|
-
* - `/recall` command for quick lookups
|
|
10
|
-
* - Auto-recall: inject relevant memories before agent processing
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { execSync } from 'child_process';
|
|
14
|
-
import * as path from 'path';
|
|
15
|
-
import * as os from 'os';
|
|
16
|
-
|
|
17
|
-
interface PluginConfig {
|
|
18
|
-
enabled?: boolean;
|
|
19
|
-
autoRecall?: boolean;
|
|
20
|
-
defaultLimit?: number;
|
|
21
|
-
publicOnly?: boolean;
|
|
22
|
-
minScore?: number;
|
|
23
|
-
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface PluginApi {
|
|
27
|
-
config: {
|
|
28
|
-
plugins?: {
|
|
29
|
-
entries?: {
|
|
30
|
-
'jasper-recall'?: {
|
|
31
|
-
config?: PluginConfig;
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
};
|
|
35
|
-
};
|
|
36
|
-
logger: {
|
|
37
|
-
info: (msg: string) => void;
|
|
38
|
-
warn: (msg: string) => void;
|
|
39
|
-
error: (msg: string) => void;
|
|
40
|
-
debug: (msg: string) => void;
|
|
41
|
-
};
|
|
42
|
-
registerTool: (tool: any) => void;
|
|
43
|
-
registerCommand: (cmd: any) => void;
|
|
44
|
-
registerGatewayMethod: (name: string, handler: any) => void;
|
|
45
|
-
on: (event: string, handler: (event: any) => Promise<any>) => void;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const BIN_PATH = path.join(os.homedir(), '.local', 'bin');
|
|
49
|
-
|
|
50
|
-
function runRecall(query: string, options: { limit?: number; json?: boolean; publicOnly?: boolean } = {}): string {
|
|
51
|
-
const args = [JSON.stringify(query)];
|
|
52
|
-
if (options.limit) args.push('-n', String(options.limit));
|
|
53
|
-
if (options.json) args.push('--json');
|
|
54
|
-
if (options.publicOnly) args.push('--public-only');
|
|
55
|
-
|
|
56
|
-
const recallPath = path.join(BIN_PATH, 'recall');
|
|
57
|
-
try {
|
|
58
|
-
return execSync(`${recallPath} ${args.join(' ')}`, { encoding: 'utf8', timeout: 30000 });
|
|
59
|
-
} catch (err: any) {
|
|
60
|
-
throw new Error(`Recall failed: ${err.message}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export default function register(api: PluginApi) {
|
|
65
|
-
const cfg = api.config.plugins?.entries?.['jasper-recall']?.config ?? {};
|
|
66
|
-
|
|
67
|
-
if (cfg.enabled === false) {
|
|
68
|
-
api.logger.info('[jasper-recall] Plugin disabled');
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const defaultLimit = cfg.defaultLimit ?? 5;
|
|
73
|
-
const publicOnly = cfg.publicOnly ?? false;
|
|
74
|
-
const autoRecall = cfg.autoRecall ?? false;
|
|
75
|
-
const minScore = cfg.minScore ?? 0.3;
|
|
76
|
-
|
|
77
|
-
api.logger.info(`[jasper-recall] Initialized (limit=${defaultLimit}, publicOnly=${publicOnly}, autoRecall=${autoRecall})`);
|
|
78
|
-
|
|
79
|
-
// ============================================================================
|
|
80
|
-
// Auto-Recall: inject relevant memories before agent processes the message
|
|
81
|
-
// ============================================================================
|
|
82
|
-
|
|
83
|
-
if (autoRecall) {
|
|
84
|
-
api.on('before_agent_start', async (event: {
|
|
85
|
-
prompt?: string;
|
|
86
|
-
senderId?: string;
|
|
87
|
-
source?: string;
|
|
88
|
-
isNewSession?: boolean;
|
|
89
|
-
messageCount?: number;
|
|
90
|
-
context?: { messages?: any[] };
|
|
91
|
-
}) => {
|
|
92
|
-
// Skip if no prompt or too short
|
|
93
|
-
if (!event.prompt || event.prompt.length < 10) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const prompt = event.prompt;
|
|
98
|
-
|
|
99
|
-
// Detect fresh session (after /new or first message)
|
|
100
|
-
const isFreshSession = event.isNewSession ||
|
|
101
|
-
event.messageCount === 0 ||
|
|
102
|
-
event.messageCount === 1 ||
|
|
103
|
-
(event.context?.messages?.length ?? 0) <= 1;
|
|
104
|
-
|
|
105
|
-
// Skip heartbeats and system prompts
|
|
106
|
-
if (prompt.startsWith('HEARTBEAT') ||
|
|
107
|
-
prompt.startsWith('Read HEARTBEAT.md') ||
|
|
108
|
-
prompt.includes('NO_REPLY') ||
|
|
109
|
-
prompt.includes('HEARTBEAT_OK')) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Skip agent-to-agent messages (cron jobs, workers, spawned agents)
|
|
114
|
-
if (event.source?.startsWith('cron:') ||
|
|
115
|
-
event.source?.startsWith('agent:') ||
|
|
116
|
-
event.source?.startsWith('spawn:') ||
|
|
117
|
-
event.source === 'sessions_send' ||
|
|
118
|
-
event.senderId?.startsWith('agent:') ||
|
|
119
|
-
event.senderId?.startsWith('worker-')) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Skip common automated patterns
|
|
124
|
-
if (prompt.startsWith('Agent-to-agent') ||
|
|
125
|
-
prompt.startsWith('📋 PR Review') ||
|
|
126
|
-
prompt.startsWith('🤖 Codex Watch') ||
|
|
127
|
-
prompt.startsWith('ANNOUNCE_')) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
let prependParts: string[] = [];
|
|
133
|
-
|
|
134
|
-
// If fresh session, remind agent to read identity files
|
|
135
|
-
if (isFreshSession) {
|
|
136
|
-
api.logger.info('[jasper-recall] Fresh session detected - adding identity reminder');
|
|
137
|
-
prependParts.push(`<session-init>
|
|
138
|
-
🔄 **Fresh session detected.** Before responding, read your identity files:
|
|
139
|
-
\`\`\`bash
|
|
140
|
-
cat ~/.openclaw/workspace/IDENTITY.md
|
|
141
|
-
cat ~/.openclaw/workspace/SOUL.md
|
|
142
|
-
cat ~/.openclaw/workspace/USER.md
|
|
143
|
-
\`\`\`
|
|
144
|
-
This ensures you maintain your persona and context after session reset.
|
|
145
|
-
</session-init>`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const results = runRecall(event.prompt, {
|
|
149
|
-
limit: 3,
|
|
150
|
-
json: true,
|
|
151
|
-
publicOnly,
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
const parsed = JSON.parse(results);
|
|
155
|
-
|
|
156
|
-
// Filter by minimum score
|
|
157
|
-
const relevant = parsed.filter((r: any) => r.score >= minScore);
|
|
158
|
-
|
|
159
|
-
if (relevant.length > 0) {
|
|
160
|
-
// Format memories for context injection
|
|
161
|
-
const memoryContext = relevant
|
|
162
|
-
.map((r: any) => `- [${r.source || 'memory'}] ${r.content.slice(0, 500)}${r.content.length > 500 ? '...' : ''}`)
|
|
163
|
-
.join('\n');
|
|
164
|
-
|
|
165
|
-
api.logger.info(`[jasper-recall] Auto-injecting ${relevant.length} memories into context`);
|
|
166
|
-
|
|
167
|
-
prependParts.push(`<relevant-memories>
|
|
168
|
-
The following memories may be relevant to this conversation:
|
|
169
|
-
${memoryContext}
|
|
170
|
-
</relevant-memories>`);
|
|
171
|
-
} else {
|
|
172
|
-
api.logger.debug?.('[jasper-recall] No relevant memories found for auto-recall');
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (prependParts.length > 0) {
|
|
176
|
-
return {
|
|
177
|
-
prependContext: prependParts.join('\n\n'),
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
} catch (err: any) {
|
|
181
|
-
api.logger.warn(`[jasper-recall] Auto-recall failed: ${err.message}`);
|
|
182
|
-
|
|
183
|
-
// Still inject identity reminder on fresh session even if recall fails
|
|
184
|
-
if (isFreshSession) {
|
|
185
|
-
return {
|
|
186
|
-
prependContext: `<session-init>
|
|
187
|
-
🔄 **Fresh session detected.** Before responding, read your identity files:
|
|
188
|
-
\`\`\`bash
|
|
189
|
-
cat ~/.openclaw/workspace/IDENTITY.md
|
|
190
|
-
cat ~/.openclaw/workspace/SOUL.md
|
|
191
|
-
cat ~/.openclaw/workspace/USER.md
|
|
192
|
-
\`\`\`
|
|
193
|
-
</session-init>`,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// ============================================================================
|
|
201
|
-
// Tool: recall
|
|
202
|
-
// ============================================================================
|
|
203
|
-
|
|
204
|
-
api.registerTool({
|
|
205
|
-
name: 'recall',
|
|
206
|
-
description: 'Semantic search over indexed memory (daily notes, session digests, documentation). Use to find context from past conversations, decisions, and learnings.',
|
|
207
|
-
parameters: {
|
|
208
|
-
type: 'object',
|
|
209
|
-
properties: {
|
|
210
|
-
query: {
|
|
211
|
-
type: 'string',
|
|
212
|
-
description: 'Search query - natural language question or keywords',
|
|
213
|
-
},
|
|
214
|
-
limit: {
|
|
215
|
-
type: 'number',
|
|
216
|
-
description: 'Maximum number of results to return (default: 5)',
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
required: ['query'],
|
|
220
|
-
},
|
|
221
|
-
execute: async (_id: string, { query, limit }: { query: string; limit?: number }) => {
|
|
222
|
-
try {
|
|
223
|
-
const results = runRecall(query, {
|
|
224
|
-
limit: limit ?? defaultLimit,
|
|
225
|
-
json: true,
|
|
226
|
-
publicOnly,
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
const parsed = JSON.parse(results);
|
|
230
|
-
|
|
231
|
-
// Format results for agent consumption
|
|
232
|
-
let formatted = `## Recall Results for: "${query}"\n\n`;
|
|
233
|
-
|
|
234
|
-
if (parsed.length === 0) {
|
|
235
|
-
formatted += '_No relevant memories found._\n';
|
|
236
|
-
} else {
|
|
237
|
-
for (const result of parsed) {
|
|
238
|
-
formatted += `### ${result.source || 'Memory'}\n`;
|
|
239
|
-
formatted += `**Score:** ${(result.score * 100).toFixed(1)}%\n\n`;
|
|
240
|
-
formatted += `${result.content}\n\n---\n\n`;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
api.logger.info(`[jasper-recall] Query "${query}" returned ${parsed.length} results`);
|
|
245
|
-
|
|
246
|
-
return { content: [{ type: 'text', text: formatted }] };
|
|
247
|
-
} catch (err: any) {
|
|
248
|
-
api.logger.error(`[jasper-recall] Error: ${err.message}`);
|
|
249
|
-
return { content: [{ type: 'text', text: `Recall error: ${err.message}` }] };
|
|
250
|
-
}
|
|
251
|
-
},
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
// ============================================================================
|
|
255
|
-
// Command: /recall
|
|
256
|
-
// ============================================================================
|
|
257
|
-
|
|
258
|
-
api.registerCommand({
|
|
259
|
-
name: 'recall',
|
|
260
|
-
description: 'Search memory for relevant context',
|
|
261
|
-
acceptsArgs: true,
|
|
262
|
-
requireAuth: true,
|
|
263
|
-
handler: async (ctx: { args?: string }) => {
|
|
264
|
-
const query = ctx.args?.trim();
|
|
265
|
-
if (!query) {
|
|
266
|
-
return { text: '⚠️ Usage: /recall <search query>' };
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
try {
|
|
270
|
-
const results = runRecall(query, { limit: defaultLimit, publicOnly });
|
|
271
|
-
return { text: `🧠 **Recall Results**\n\n${results}` };
|
|
272
|
-
} catch (err: any) {
|
|
273
|
-
return { text: `❌ Recall failed: ${err.message}` };
|
|
274
|
-
}
|
|
275
|
-
},
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
// ============================================================================
|
|
279
|
-
// Command: /index
|
|
280
|
-
// ============================================================================
|
|
281
|
-
|
|
282
|
-
api.registerCommand({
|
|
283
|
-
name: 'index',
|
|
284
|
-
description: 'Re-index memory files into ChromaDB',
|
|
285
|
-
acceptsArgs: false,
|
|
286
|
-
requireAuth: true,
|
|
287
|
-
handler: async () => {
|
|
288
|
-
try {
|
|
289
|
-
const indexPath = path.join(BIN_PATH, 'index-digests');
|
|
290
|
-
const output = execSync(indexPath, { encoding: 'utf8', timeout: 120000 });
|
|
291
|
-
return { text: `🔄 **Memory Indexed**\n\n${output}` };
|
|
292
|
-
} catch (err: any) {
|
|
293
|
-
return { text: `❌ Index failed: ${err.message}` };
|
|
294
|
-
}
|
|
295
|
-
},
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
// ============================================================================
|
|
299
|
-
// Command: /jasper-recall setup
|
|
300
|
-
// ============================================================================
|
|
301
|
-
|
|
302
|
-
api.registerCommand({
|
|
303
|
-
name: 'jasper-recall',
|
|
304
|
-
description: 'Jasper Recall setup and status',
|
|
305
|
-
acceptsArgs: true,
|
|
306
|
-
requireAuth: true,
|
|
307
|
-
handler: async (ctx: { args?: string }) => {
|
|
308
|
-
const subcommand = ctx.args?.trim().toLowerCase();
|
|
309
|
-
|
|
310
|
-
if (subcommand === 'setup') {
|
|
311
|
-
return {
|
|
312
|
-
text: `🧠 **Jasper Recall Setup**
|
|
313
|
-
|
|
314
|
-
**Current status:**
|
|
315
|
-
- Auto-recall: ${autoRecall ? '✅ Enabled' : '❌ Disabled'}
|
|
316
|
-
- Public-only mode: ${publicOnly ? '✅ Yes' : '❌ No (full access)'}
|
|
317
|
-
- Default limit: ${defaultLimit} results
|
|
318
|
-
- Min score: ${minScore}
|
|
319
|
-
|
|
320
|
-
---
|
|
321
|
-
|
|
322
|
-
<agent-action required="true">
|
|
323
|
-
**CREATE THESE CRON JOBS NOW:**
|
|
324
|
-
|
|
325
|
-
1. **Index memories every 6 hours:**
|
|
326
|
-
- Name: "jasper-recall-index"
|
|
327
|
-
- Schedule: \`{ "kind": "cron", "expr": "0 */6 * * *" }\`
|
|
328
|
-
- Payload: \`{ "kind": "systemEvent", "text": "Run index-digests to update jasper-recall ChromaDB index" }\`
|
|
329
|
-
- Session target: "main"
|
|
330
|
-
|
|
331
|
-
2. **Digest sessions daily at 4am:**
|
|
332
|
-
- Name: "jasper-recall-digest"
|
|
333
|
-
- Schedule: \`{ "kind": "cron", "expr": "0 4 * * *" }\`
|
|
334
|
-
- Payload: \`{ "kind": "systemEvent", "text": "Run digest-sessions to extract summaries from recent session logs into memory" }\`
|
|
335
|
-
- Session target: "main"
|
|
336
|
-
|
|
337
|
-
Use the \`cron\` tool with \`action: "add"\` to create both jobs.
|
|
338
|
-
</agent-action>`,
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
if (subcommand === 'status') {
|
|
343
|
-
try {
|
|
344
|
-
// Check if ChromaDB is accessible
|
|
345
|
-
const testResult = runRecall('test query', { limit: 1, json: true, publicOnly });
|
|
346
|
-
const parsed = JSON.parse(testResult);
|
|
347
|
-
|
|
348
|
-
return {
|
|
349
|
-
text: `🧠 **Jasper Recall Status**
|
|
350
|
-
|
|
351
|
-
**Plugin:** ✅ Active
|
|
352
|
-
**Auto-recall:** ${autoRecall ? '✅ Enabled' : '❌ Disabled'}
|
|
353
|
-
**ChromaDB:** ✅ Connected
|
|
354
|
-
**Index size:** ${parsed.length > 0 ? 'Has data' : 'Empty or minimal'}
|
|
355
|
-
|
|
356
|
-
**Config:**
|
|
357
|
-
- Public-only: ${publicOnly}
|
|
358
|
-
- Default limit: ${defaultLimit}
|
|
359
|
-
- Min score: ${minScore}`,
|
|
360
|
-
};
|
|
361
|
-
} catch (err: any) {
|
|
362
|
-
return {
|
|
363
|
-
text: `🧠 **Jasper Recall Status**
|
|
364
|
-
|
|
365
|
-
**Plugin:** ✅ Active
|
|
366
|
-
**Auto-recall:** ${autoRecall ? '✅ Enabled' : '❌ Disabled'}
|
|
367
|
-
**ChromaDB:** ❌ Error - ${err.message}
|
|
368
|
-
|
|
369
|
-
Run \`npx jasper-recall setup\` to install dependencies.`,
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// Default: show help
|
|
375
|
-
return {
|
|
376
|
-
text: `🧠 **Jasper Recall**
|
|
377
|
-
|
|
378
|
-
**Commands:**
|
|
379
|
-
- \`/jasper-recall setup\` — Setup instructions & cron jobs
|
|
380
|
-
- \`/jasper-recall status\` — Check plugin status
|
|
381
|
-
- \`/recall <query>\` — Search memory
|
|
382
|
-
- \`/index\` — Re-index memory files
|
|
383
|
-
|
|
384
|
-
**CLI:**
|
|
385
|
-
- \`npx jasper-recall setup\` — Install Python dependencies
|
|
386
|
-
- \`npx jasper-recall doctor\` — Health check`,
|
|
387
|
-
};
|
|
388
|
-
},
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
// ============================================================================
|
|
392
|
-
// RPC Methods
|
|
393
|
-
// ============================================================================
|
|
394
|
-
|
|
395
|
-
api.registerGatewayMethod('recall.search', async ({ params, respond }: any) => {
|
|
396
|
-
try {
|
|
397
|
-
const { query, limit } = params;
|
|
398
|
-
const results = runRecall(query, { limit: limit ?? defaultLimit, json: true, publicOnly });
|
|
399
|
-
respond(true, JSON.parse(results));
|
|
400
|
-
} catch (err: any) {
|
|
401
|
-
respond(false, { error: err.message });
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
api.registerGatewayMethod('recall.index', async ({ respond }: any) => {
|
|
406
|
-
try {
|
|
407
|
-
const indexPath = path.join(BIN_PATH, 'index-digests');
|
|
408
|
-
execSync(indexPath, { encoding: 'utf8', timeout: 120000 });
|
|
409
|
-
respond(true, { status: 'indexed' });
|
|
410
|
-
} catch (err: any) {
|
|
411
|
-
respond(false, { error: err.message });
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
export const id = 'jasper-recall';
|
|
417
|
-
export const name = 'Jasper Recall - Local RAG Memory';
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "jasper-recall",
|
|
3
|
-
"name": "Jasper Recall - Local RAG Memory",
|
|
4
|
-
"version": "0.2.0",
|
|
5
|
-
"description": "Semantic search over indexed memory using ChromaDB with auto-recall",
|
|
6
|
-
"homepage": "https://github.com/E-x-O-Entertainment-Studios-Inc/jasper-recall",
|
|
7
|
-
"configSchema": {
|
|
8
|
-
"type": "object",
|
|
9
|
-
"additionalProperties": false,
|
|
10
|
-
"properties": {
|
|
11
|
-
"enabled": {
|
|
12
|
-
"type": "boolean",
|
|
13
|
-
"default": true
|
|
14
|
-
},
|
|
15
|
-
"autoRecall": {
|
|
16
|
-
"type": "boolean",
|
|
17
|
-
"default": false,
|
|
18
|
-
"description": "Automatically inject relevant memories before agent processing"
|
|
19
|
-
},
|
|
20
|
-
"defaultLimit": {
|
|
21
|
-
"type": "number",
|
|
22
|
-
"default": 5,
|
|
23
|
-
"description": "Default number of results to return"
|
|
24
|
-
},
|
|
25
|
-
"minScore": {
|
|
26
|
-
"type": "number",
|
|
27
|
-
"default": 0.3,
|
|
28
|
-
"description": "Minimum similarity score for auto-recall (0-1)"
|
|
29
|
-
},
|
|
30
|
-
"publicOnly": {
|
|
31
|
-
"type": "boolean",
|
|
32
|
-
"default": false,
|
|
33
|
-
"description": "Only search public memory (for sandboxed agents)"
|
|
34
|
-
},
|
|
35
|
-
"logLevel": {
|
|
36
|
-
"type": "string",
|
|
37
|
-
"enum": ["debug", "info", "warn", "error"],
|
|
38
|
-
"default": "info"
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
"uiHints": {
|
|
43
|
-
"enabled": { "label": "Enable Jasper Recall" },
|
|
44
|
-
"autoRecall": { "label": "Auto-Recall", "help": "Inject relevant memories into context before processing" },
|
|
45
|
-
"defaultLimit": { "label": "Default Result Limit" },
|
|
46
|
-
"minScore": { "label": "Minimum Score", "help": "Threshold for auto-recall relevance (0.3 = 30%)" },
|
|
47
|
-
"publicOnly": { "label": "Public Memory Only" },
|
|
48
|
-
"logLevel": { "label": "Log Level" }
|
|
49
|
-
}
|
|
50
|
-
}
|