jasper-recall 0.3.6 → 0.4.1

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/SKILL.md CHANGED
@@ -197,6 +197,32 @@ recall "product info" --public-only
197
197
  recall "product info"
198
198
  ```
199
199
 
200
+ ### Moltbook Agent Setup (v0.4.0+)
201
+
202
+ For the moltbook-scanner (or any sandboxed agent), use the built-in setup:
203
+
204
+ ```bash
205
+ # Configure sandboxed agent with --public-only restriction
206
+ npx jasper-recall moltbook-setup
207
+
208
+ # Verify the setup is correct
209
+ npx jasper-recall moltbook-verify
210
+ ```
211
+
212
+ This creates:
213
+ - `~/bin/recall` — Wrapper that forces `--public-only` flag
214
+ - `shared/` — Symlink to main workspace's shared memory
215
+
216
+ The sandboxed agent can then use:
217
+ ```bash
218
+ ~/bin/recall "query" # Automatically restricted to public memories
219
+ ```
220
+
221
+ **Privacy model:**
222
+ 1. Main agent tags memories as `[public]` or `[private]` in daily notes
223
+ 2. `sync-shared` extracts `[public]` content to `memory/shared/`
224
+ 3. Sandboxed agents can ONLY search the `shared` collection
225
+
200
226
  ### Privacy Workflow
201
227
 
202
228
  ```bash
@@ -198,6 +198,36 @@ function setup() {
198
198
  console.log(' 1. index-digests # Index your memory files');
199
199
  console.log(' 2. recall "query" # Search your memory');
200
200
  console.log(' 3. digest-sessions # Process session logs');
201
+ console.log('');
202
+
203
+ // Check for sandboxed agents
204
+ const sandboxedWorkspaces = findSandboxedWorkspaces();
205
+ if (sandboxedWorkspaces.length > 0) {
206
+ console.log('📦 Sandboxed agents detected:');
207
+ sandboxedWorkspaces.forEach(ws => console.log(` - ${ws}`));
208
+ console.log('');
209
+ console.log(' Configure them with: npx jasper-recall sandboxed-setup');
210
+ console.log(' (gives them --public-only access to shared memories)');
211
+ console.log('');
212
+ }
213
+ }
214
+
215
+ function findSandboxedWorkspaces() {
216
+ const openclawDir = path.join(os.homedir(), '.openclaw');
217
+ const workspaces = [];
218
+
219
+ try {
220
+ const entries = fs.readdirSync(openclawDir);
221
+ for (const entry of entries) {
222
+ if (entry.startsWith('workspace-') && entry !== 'workspace') {
223
+ workspaces.push(entry.replace('workspace-', ''));
224
+ }
225
+ }
226
+ } catch (e) {
227
+ // Directory doesn't exist
228
+ }
229
+
230
+ return workspaces;
201
231
  }
202
232
 
203
233
  function showHelp() {
@@ -209,17 +239,19 @@ USAGE:
209
239
  npx jasper-recall <command>
210
240
 
211
241
  COMMANDS:
212
- setup Install dependencies and CLI scripts
213
- doctor Run system health check
214
- Flags: --fix (auto-repair issues), --dry-run (verbose output)
215
- recall Search your memory (alias for the recall command)
216
- index Index memory files (alias for index-digests)
217
- digest Process session logs (alias for digest-sessions)
218
- summarize Compress old entries to save tokens (alias for summarize-old)
219
- serve Start HTTP API server (for sandboxed agents)
220
- config Show or set configuration
221
- update Check for updates
222
- help Show this help message
242
+ setup Install dependencies and CLI scripts
243
+ doctor Run system health check
244
+ Flags: --fix (auto-repair issues), --dry-run (verbose output)
245
+ recall Search your memory (alias for the recall command)
246
+ index Index memory files (alias for index-digests)
247
+ digest Process session logs (alias for digest-sessions)
248
+ summarize Compress old entries to save tokens (alias for summarize-old)
249
+ serve Start HTTP API server (for sandboxed agents)
250
+ config Show or set configuration
251
+ update Check for updates
252
+ sandboxed-setup Configure sandboxed agents (email, social, calendar, etc.)
253
+ sandboxed-verify Verify sandboxed agent configurations
254
+ help Show this help message
223
255
 
224
256
  CONFIGURATION:
225
257
  Config file: ~/.jasper-recall/config.json
@@ -311,6 +343,21 @@ switch (command) {
311
343
  };
312
344
  process.exit(runDoctor(options));
313
345
  break;
346
+ case 'sandboxed-setup':
347
+ case 'sandbox-setup':
348
+ case 'moltbook-setup':
349
+ case 'moltbook':
350
+ // Interactive setup for sandboxed agents
351
+ process.argv = [process.argv[0], process.argv[1], 'setup'];
352
+ require('../extensions/moltbook-setup/setup.js');
353
+ break;
354
+ case 'sandboxed-verify':
355
+ case 'sandbox-verify':
356
+ case 'moltbook-verify':
357
+ // Verify sandboxed agent setups
358
+ process.argv = [process.argv[0], process.argv[1], 'verify'];
359
+ require('../extensions/moltbook-setup/setup.js');
360
+ break;
314
361
  case 'config':
315
362
  // Configuration management
316
363
  const config = require('./config');
@@ -0,0 +1,433 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Sandboxed Agent Setup for jasper-recall
4
+ *
5
+ * Configures sandboxed agents to use jasper-recall with --public-only restriction.
6
+ * This ensures agents can only access shared/public memories, not private ones.
7
+ *
8
+ * Use cases:
9
+ * - Moltbook scanner (social media engagement)
10
+ * - Email agent (inbox management)
11
+ * - Calendar agent (scheduling)
12
+ * - Any agent that shouldn't see private memories
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const os = require('os');
18
+ const readline = require('readline');
19
+
20
+ const OPENCLAW_DIR = path.join(os.homedir(), '.openclaw');
21
+ const MAIN_WORKSPACE = path.join(OPENCLAW_DIR, 'workspace');
22
+ const RECALL_BIN = path.join(os.homedir(), '.local', 'bin', 'recall');
23
+
24
+ function log(msg) {
25
+ console.log(`🔒 ${msg}`);
26
+ }
27
+
28
+ function warn(msg) {
29
+ console.log(`⚠️ ${msg}`);
30
+ }
31
+
32
+ function error(msg) {
33
+ console.error(`❌ ${msg}`);
34
+ }
35
+
36
+ function success(msg) {
37
+ console.log(`✅ ${msg}`);
38
+ }
39
+
40
+ async function prompt(question) {
41
+ const rl = readline.createInterface({
42
+ input: process.stdin,
43
+ output: process.stdout
44
+ });
45
+
46
+ return new Promise(resolve => {
47
+ rl.question(question, answer => {
48
+ rl.close();
49
+ resolve(answer.trim());
50
+ });
51
+ });
52
+ }
53
+
54
+ async function interactiveSetup() {
55
+ console.log('');
56
+ log('Sandboxed Agent Setup — jasper-recall Integration');
57
+ console.log('='.repeat(60));
58
+ console.log('');
59
+ console.log(' This configures a sandboxed agent to use jasper-recall with');
60
+ console.log(' privacy restrictions (--public-only).');
61
+ console.log('');
62
+ console.log(' 🔒 Privacy: Sandboxed agents can ONLY see [public] memories.');
63
+ console.log(' They cannot access your private notes or secrets.');
64
+ console.log('');
65
+
66
+ // List existing workspaces
67
+ const workspaces = findAgentWorkspaces();
68
+
69
+ if (workspaces.length === 0) {
70
+ console.log(' No sandboxed agent workspaces found.');
71
+ console.log('');
72
+ console.log(' To create one, add an agent to your openclaw.json:');
73
+ console.log('');
74
+ showAgentExample();
75
+ return;
76
+ }
77
+
78
+ console.log(' Found agent workspaces:');
79
+ workspaces.forEach((ws, i) => {
80
+ const status = checkWorkspaceStatus(ws.path);
81
+ const statusIcon = status.configured ? '✅' : '⚪';
82
+ console.log(` ${i + 1}. ${statusIcon} ${ws.name} (${ws.path})`);
83
+ });
84
+ console.log('');
85
+
86
+ const choice = await prompt(' Configure which agent? (number, or "skip" to exit): ');
87
+
88
+ if (choice.toLowerCase() === 'skip' || choice === '') {
89
+ console.log('\n Skipped.\n');
90
+ return;
91
+ }
92
+
93
+ const index = parseInt(choice, 10) - 1;
94
+ if (isNaN(index) || index < 0 || index >= workspaces.length) {
95
+ error('Invalid selection');
96
+ return;
97
+ }
98
+
99
+ const selected = workspaces[index];
100
+ await setupWorkspace(selected.path, selected.name);
101
+ }
102
+
103
+ function findAgentWorkspaces() {
104
+ const workspaces = [];
105
+
106
+ // Look for workspace-* directories
107
+ try {
108
+ const entries = fs.readdirSync(OPENCLAW_DIR);
109
+ for (const entry of entries) {
110
+ if (entry.startsWith('workspace-') && entry !== 'workspace') {
111
+ const wsPath = path.join(OPENCLAW_DIR, entry);
112
+ if (fs.statSync(wsPath).isDirectory()) {
113
+ workspaces.push({
114
+ name: entry.replace('workspace-', ''),
115
+ path: wsPath
116
+ });
117
+ }
118
+ }
119
+ }
120
+ } catch (e) {
121
+ // Directory doesn't exist or not readable
122
+ }
123
+
124
+ return workspaces;
125
+ }
126
+
127
+ function checkWorkspaceStatus(wsPath) {
128
+ const wrapperPath = path.join(wsPath, 'bin', 'recall');
129
+ const sharedPath = path.join(wsPath, 'shared');
130
+
131
+ let configured = false;
132
+
133
+ if (fs.existsSync(wrapperPath)) {
134
+ const content = fs.readFileSync(wrapperPath, 'utf8');
135
+ configured = content.includes('--public-only');
136
+ }
137
+
138
+ return {
139
+ configured,
140
+ hasWrapper: fs.existsSync(wrapperPath),
141
+ hasShared: fs.existsSync(sharedPath)
142
+ };
143
+ }
144
+
145
+ async function setupWorkspace(wsPath, name) {
146
+ console.log('');
147
+ log(`Configuring ${name}...`);
148
+
149
+ // Check prerequisites
150
+ if (!fs.existsSync(RECALL_BIN)) {
151
+ error(`jasper-recall not installed: ${RECALL_BIN}`);
152
+ console.log(' Run the main setup first: npx jasper-recall setup');
153
+ return;
154
+ }
155
+
156
+ // Step 1: Create bin directory and wrapper
157
+ const binDir = path.join(wsPath, 'bin');
158
+ const wrapperPath = path.join(binDir, 'recall');
159
+
160
+ fs.mkdirSync(binDir, { recursive: true });
161
+
162
+ const wrapperScript = `#!/bin/bash
163
+ # Sandboxed recall wrapper - forces --public-only for privacy
164
+ # This agent can ONLY access shared/public memory
165
+
166
+ exec ${RECALL_BIN} "$@" --public-only
167
+ `;
168
+
169
+ fs.writeFileSync(wrapperPath, wrapperScript);
170
+ fs.chmodSync(wrapperPath, '755');
171
+ success(`Created recall wrapper: bin/recall`);
172
+
173
+ // Step 2: Create shared folder symlink
174
+ const sharedSource = path.join(MAIN_WORKSPACE, 'memory', 'shared');
175
+ const sharedTarget = path.join(wsPath, 'shared');
176
+
177
+ // Ensure source exists
178
+ fs.mkdirSync(sharedSource, { recursive: true });
179
+
180
+ // Remove existing symlink/dir if needed
181
+ try {
182
+ const stat = fs.lstatSync(sharedTarget);
183
+ if (stat.isSymbolicLink()) {
184
+ fs.unlinkSync(sharedTarget);
185
+ }
186
+ } catch (e) {
187
+ // Doesn't exist, that's fine
188
+ }
189
+
190
+ if (!fs.existsSync(sharedTarget)) {
191
+ fs.symlinkSync(sharedSource, sharedTarget);
192
+ success(`Created symlink: shared/ → main workspace`);
193
+ }
194
+
195
+ // Step 3: Check if AGENTS.md exists and suggest update
196
+ const agentsMd = path.join(wsPath, 'AGENTS.md');
197
+ if (fs.existsSync(agentsMd)) {
198
+ const content = fs.readFileSync(agentsMd, 'utf8');
199
+ if (!content.includes('public-only') && !content.includes('--public-only')) {
200
+ warn('Consider adding recall restrictions to AGENTS.md');
201
+ console.log('');
202
+ console.log(' Suggested addition:');
203
+ console.log(' ```');
204
+ console.log(' ## Memory Access');
205
+ console.log(' Use `~/bin/recall "query"` for memory search.');
206
+ console.log(' This wrapper enforces --public-only (you cannot see private memories).');
207
+ console.log(' ```');
208
+ }
209
+ }
210
+
211
+ console.log('');
212
+ success(`${name} configured!`);
213
+ console.log('');
214
+ console.log(' The agent can now use:');
215
+ console.log(` ~/bin/recall "query" — searches public memories only`);
216
+ console.log(` shared/ — symlink to shared memory folder`);
217
+ console.log('');
218
+ }
219
+
220
+ function showAgentExample() {
221
+ console.log(` Example openclaw.json agent config:
222
+
223
+ {
224
+ "agents": {
225
+ "list": [
226
+ {
227
+ "id": "email-agent",
228
+ "workspace": "~/.openclaw/workspace-email",
229
+ "model": { "primary": "anthropic/claude-sonnet-4-5" },
230
+ "sandbox": {
231
+ "mode": "all",
232
+ "workspaceAccess": "rw"
233
+ },
234
+ "tools": {
235
+ "profile": "minimal",
236
+ "allow": ["read", "write", "exec", "web_fetch"]
237
+ }
238
+ }
239
+ ]
240
+ }
241
+ }
242
+
243
+ Common sandboxed agent use cases:
244
+
245
+ 📧 Email Agent
246
+ - Checks inbox, drafts replies, summarizes threads
247
+ - Sandbox: Only email API access, no filesystem
248
+ - Memory: Sees [public] context about projects
249
+
250
+ 📱 Social Agent (Moltbook, Twitter, etc.)
251
+ - Monitors feeds, engages with posts
252
+ - Sandbox: Only that platform's API
253
+ - Memory: Sees [public] product info for authentic engagement
254
+
255
+ 📅 Calendar Agent
256
+ - Manages scheduling, sends reminders
257
+ - Sandbox: Only calendar API
258
+ - Memory: Sees [public] project timelines
259
+
260
+ 🔍 Research Agent
261
+ - Web searches, summarizes articles
262
+ - Sandbox: Read-only web access
263
+ - Memory: Sees [public] research context
264
+
265
+ After creating the workspace, run:
266
+ npx jasper-recall sandboxed-setup
267
+ `);
268
+ }
269
+
270
+ function verify(wsPath, options = {}) {
271
+ const { quiet = false } = options;
272
+ const issues = [];
273
+
274
+ if (!quiet) {
275
+ console.log('');
276
+ log('Verifying sandboxed agent setup...');
277
+ console.log('');
278
+ }
279
+
280
+ // Check 1: Workspace exists
281
+ if (!fs.existsSync(wsPath)) {
282
+ issues.push(`Workspace missing: ${wsPath}`);
283
+ } else if (!quiet) {
284
+ success(`Workspace exists`);
285
+ }
286
+
287
+ // Check 2: Recall wrapper exists and is correct
288
+ const wrapperPath = path.join(wsPath, 'bin', 'recall');
289
+ if (!fs.existsSync(wrapperPath)) {
290
+ issues.push(`Recall wrapper missing: bin/recall`);
291
+ } else {
292
+ const content = fs.readFileSync(wrapperPath, 'utf8');
293
+ if (!content.includes('--public-only')) {
294
+ issues.push('Recall wrapper missing --public-only flag!');
295
+ } else if (!quiet) {
296
+ success('Recall wrapper has --public-only restriction');
297
+ }
298
+ }
299
+
300
+ // Check 3: Shared folder is a symlink
301
+ const sharedPath = path.join(wsPath, 'shared');
302
+ try {
303
+ const stat = fs.lstatSync(sharedPath);
304
+ if (!stat.isSymbolicLink()) {
305
+ issues.push(`shared/ is not a symlink`);
306
+ } else if (!quiet) {
307
+ const target = fs.readlinkSync(sharedPath);
308
+ success(`shared/ → ${target}`);
309
+ }
310
+ } catch (e) {
311
+ issues.push(`shared/ folder missing`);
312
+ }
313
+
314
+ // Check 4: jasper-recall is installed
315
+ if (!fs.existsSync(RECALL_BIN)) {
316
+ issues.push(`jasper-recall not installed`);
317
+ } else if (!quiet) {
318
+ success(`jasper-recall installed`);
319
+ }
320
+
321
+ if (!quiet) {
322
+ console.log('');
323
+ if (issues.length === 0) {
324
+ success('All checks passed!');
325
+ } else {
326
+ warn(`Found ${issues.length} issue(s):`);
327
+ issues.forEach(issue => console.log(` ❌ ${issue}`));
328
+ console.log('');
329
+ console.log(' Run setup to fix: npx jasper-recall sandboxed-setup');
330
+ }
331
+ console.log('');
332
+ }
333
+
334
+ return issues;
335
+ }
336
+
337
+ async function verifyInteractive() {
338
+ const workspaces = findAgentWorkspaces();
339
+
340
+ if (workspaces.length === 0) {
341
+ console.log('');
342
+ warn('No sandboxed agent workspaces found.');
343
+ console.log('');
344
+ return;
345
+ }
346
+
347
+ console.log('');
348
+ log('Sandboxed Agent Verification');
349
+ console.log('='.repeat(60));
350
+
351
+ for (const ws of workspaces) {
352
+ console.log('');
353
+ console.log(` 📁 ${ws.name}`);
354
+ const issues = verify(ws.path, { quiet: true });
355
+ if (issues.length === 0) {
356
+ console.log(` ✅ Properly configured`);
357
+ } else {
358
+ console.log(` ⚠️ ${issues.length} issue(s):`);
359
+ issues.forEach(issue => console.log(` - ${issue}`));
360
+ }
361
+ }
362
+
363
+ console.log('');
364
+ }
365
+
366
+ function showHelp() {
367
+ console.log(`
368
+ Sandboxed Agent Setup — jasper-recall Integration
369
+
370
+ USAGE:
371
+ npx jasper-recall sandboxed-setup Interactive setup for any sandboxed agent
372
+ npx jasper-recall sandboxed-verify Check all sandboxed agents
373
+ npx jasper-recall moltbook-setup (alias) Setup for moltbook specifically
374
+ npx jasper-recall moltbook-verify (alias) Verify moltbook specifically
375
+
376
+ WHAT IT DOES:
377
+ Configures sandboxed agents to use jasper-recall with privacy restrictions.
378
+ Agents can only access [public] tagged memories, not private ones.
379
+
380
+ COMPONENTS CREATED:
381
+ bin/recall Wrapper script that forces --public-only flag
382
+ shared/ Symlink to main workspace's shared memory folder
383
+
384
+ USE CASES:
385
+ 📧 Email Agent — inbox management, drafts, summaries
386
+ 📱 Social Agent — moltbook, twitter engagement
387
+ 📅 Calendar Agent — scheduling, reminders
388
+ 🔍 Research Agent — web searches, article summaries
389
+
390
+ PRIVACY MODEL:
391
+ 1. Main agent tags memories as [public] or [private] in daily notes
392
+ 2. sync-shared extracts [public] content to memory/shared/
393
+ 3. Sandboxed agents can ONLY search the shared collection
394
+
395
+ EXAMPLE:
396
+ # Main agent daily note
397
+ ## 2026-02-11 [public] - Shipped jasper-recall v0.4.0
398
+ New sandboxed agent setup feature.
399
+
400
+ ## 2026-02-11 [private] - Personal context
401
+ This stays private, sandboxed agents can't see it.
402
+ `);
403
+ }
404
+
405
+ // Main
406
+ const command = process.argv[2];
407
+
408
+ switch (command) {
409
+ case 'setup':
410
+ case 'install':
411
+ interactiveSetup().catch(err => {
412
+ error(err.message);
413
+ process.exit(1);
414
+ });
415
+ break;
416
+ case 'verify':
417
+ case 'check':
418
+ verifyInteractive().catch(err => {
419
+ error(err.message);
420
+ process.exit(1);
421
+ });
422
+ break;
423
+ case 'help':
424
+ case '--help':
425
+ case '-h':
426
+ case undefined:
427
+ showHelp();
428
+ break;
429
+ default:
430
+ error(`Unknown command: ${command}`);
431
+ showHelp();
432
+ process.exit(1);
433
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jasper-recall",
3
- "version": "0.3.6",
3
+ "version": "0.4.1",
4
4
  "description": "Local RAG system for AI agent memory using ChromaDB and sentence-transformers",
5
5
  "main": "src/index.js",
6
6
  "bin": {