jasper-recall 0.4.0 → 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.
@@ -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() {
@@ -219,8 +249,8 @@ COMMANDS:
219
249
  serve Start HTTP API server (for sandboxed agents)
220
250
  config Show or set configuration
221
251
  update Check for updates
222
- moltbook-setup Configure moltbook agent with --public-only restriction
223
- moltbook-verify Verify moltbook agent setup
252
+ sandboxed-setup Configure sandboxed agents (email, social, calendar, etc.)
253
+ sandboxed-verify Verify sandboxed agent configurations
224
254
  help Show this help message
225
255
 
226
256
  CONFIGURATION:
@@ -313,14 +343,18 @@ switch (command) {
313
343
  };
314
344
  process.exit(runDoctor(options));
315
345
  break;
346
+ case 'sandboxed-setup':
347
+ case 'sandbox-setup':
316
348
  case 'moltbook-setup':
317
349
  case 'moltbook':
318
- // Set up moltbook agent integration
350
+ // Interactive setup for sandboxed agents
319
351
  process.argv = [process.argv[0], process.argv[1], 'setup'];
320
352
  require('../extensions/moltbook-setup/setup.js');
321
353
  break;
354
+ case 'sandboxed-verify':
355
+ case 'sandbox-verify':
322
356
  case 'moltbook-verify':
323
- // Verify moltbook agent setup
357
+ // Verify sandboxed agent setups
324
358
  process.argv = [process.argv[0], process.argv[1], 'verify'];
325
359
  require('../extensions/moltbook-setup/setup.js');
326
360
  break;
@@ -1,9 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Moltbook Agent Setup for jasper-recall
3
+ * Sandboxed Agent Setup for jasper-recall
4
4
  *
5
- * Configures a sandboxed agent to use jasper-recall with --public-only restriction.
6
- * This ensures the agent can only access shared/public memories, not private ones.
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
7
13
  */
8
14
 
9
15
  const fs = require('fs');
@@ -11,12 +17,12 @@ const path = require('path');
11
17
  const os = require('os');
12
18
  const readline = require('readline');
13
19
 
14
- const MOLTBOOK_WORKSPACE = path.join(os.homedir(), '.openclaw', 'workspace-moltbook');
15
- const MAIN_WORKSPACE = path.join(os.homedir(), '.openclaw', 'workspace');
20
+ const OPENCLAW_DIR = path.join(os.homedir(), '.openclaw');
21
+ const MAIN_WORKSPACE = path.join(OPENCLAW_DIR, 'workspace');
16
22
  const RECALL_BIN = path.join(os.homedir(), '.local', 'bin', 'recall');
17
23
 
18
24
  function log(msg) {
19
- console.log(`🦞 ${msg}`);
25
+ console.log(`🔒 ${msg}`);
20
26
  }
21
27
 
22
28
  function warn(msg) {
@@ -45,43 +51,110 @@ async function prompt(question) {
45
51
  });
46
52
  }
47
53
 
48
- async function setup() {
54
+ async function interactiveSetup() {
49
55
  console.log('');
50
- log('Moltbook Agent — jasper-recall Integration Setup');
51
- console.log('='.repeat(55));
56
+ log('Sandboxed Agent Setup — jasper-recall Integration');
57
+ console.log('='.repeat(60));
52
58
  console.log('');
53
- console.log(' This configures the moltbook-scanner agent to use jasper-recall');
54
- console.log(' with the --public-only restriction for privacy.');
59
+ console.log(' This configures a sandboxed agent to use jasper-recall with');
60
+ console.log(' privacy restrictions (--public-only).');
55
61
  console.log('');
56
- console.log(' What it does:');
57
- console.log(' 1. Creates ~/bin/recall wrapper (forces --public-only)');
58
- console.log(' 2. Symlinks shared/ folder from main workspace');
59
- console.log(' 3. Verifies jasper-recall is installed');
62
+ console.log(' 🔒 Privacy: Sandboxed agents can ONLY see [public] memories.');
63
+ console.log(' They cannot access your private notes or secrets.');
60
64
  console.log('');
61
-
62
- // Check prerequisites
63
- if (!fs.existsSync(MOLTBOOK_WORKSPACE)) {
64
- error(`Moltbook workspace not found: ${MOLTBOOK_WORKSPACE}`);
65
- console.log(' Create it first or check your OpenClaw agent config.');
66
- process.exit(1);
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;
67
97
  }
98
+
99
+ const selected = workspaces[index];
100
+ await setupWorkspace(selected.path, selected.name);
101
+ }
68
102
 
69
- if (!fs.existsSync(RECALL_BIN)) {
70
- error(`jasper-recall not installed: ${RECALL_BIN}`);
71
- console.log(' Install it first: npx jasper-recall setup');
72
- process.exit(1);
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
73
122
  }
123
+
124
+ return workspaces;
125
+ }
74
126
 
75
- const proceed = await prompt(' Continue? (y/n): ');
76
- if (proceed.toLowerCase() !== 'y' && proceed.toLowerCase() !== 'yes') {
77
- console.log('\n Setup cancelled.\n');
78
- process.exit(0);
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');
79
136
  }
137
+
138
+ return {
139
+ configured,
140
+ hasWrapper: fs.existsSync(wrapperPath),
141
+ hasShared: fs.existsSync(sharedPath)
142
+ };
143
+ }
80
144
 
145
+ async function setupWorkspace(wsPath, name) {
81
146
  console.log('');
82
-
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
+
83
156
  // Step 1: Create bin directory and wrapper
84
- const binDir = path.join(MOLTBOOK_WORKSPACE, 'bin');
157
+ const binDir = path.join(wsPath, 'bin');
85
158
  const wrapperPath = path.join(binDir, 'recall');
86
159
 
87
160
  fs.mkdirSync(binDir, { recursive: true });
@@ -95,11 +168,11 @@ exec ${RECALL_BIN} "$@" --public-only
95
168
 
96
169
  fs.writeFileSync(wrapperPath, wrapperScript);
97
170
  fs.chmodSync(wrapperPath, '755');
98
- success(`Created recall wrapper: ${wrapperPath}`);
171
+ success(`Created recall wrapper: bin/recall`);
99
172
 
100
173
  // Step 2: Create shared folder symlink
101
174
  const sharedSource = path.join(MAIN_WORKSPACE, 'memory', 'shared');
102
- const sharedTarget = path.join(MOLTBOOK_WORKSPACE, 'shared');
175
+ const sharedTarget = path.join(wsPath, 'shared');
103
176
 
104
177
  // Ensure source exists
105
178
  fs.mkdirSync(sharedSource, { recursive: true });
@@ -109,8 +182,6 @@ exec ${RECALL_BIN} "$@" --public-only
109
182
  const stat = fs.lstatSync(sharedTarget);
110
183
  if (stat.isSymbolicLink()) {
111
184
  fs.unlinkSync(sharedTarget);
112
- } else if (stat.isDirectory()) {
113
- warn(`${sharedTarget} is a directory, not a symlink. Skipping.`);
114
185
  }
115
186
  } catch (e) {
116
187
  // Doesn't exist, that's fine
@@ -118,58 +189,106 @@ exec ${RECALL_BIN} "$@" --public-only
118
189
 
119
190
  if (!fs.existsSync(sharedTarget)) {
120
191
  fs.symlinkSync(sharedSource, sharedTarget);
121
- success(`Created symlink: shared/ → ${sharedSource}`);
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
+ }
122
209
  }
123
210
 
124
- // Step 3: Verify setup
125
211
  console.log('');
126
- log('Verifying setup...');
127
-
128
- const issues = verify({ quiet: true });
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:
129
222
 
130
- if (issues.length === 0) {
131
- console.log('');
132
- console.log('='.repeat(55));
133
- success('Setup complete!');
134
- console.log('');
135
- console.log(' The moltbook-scanner agent can now use:');
136
- console.log(' ~/bin/recall "query" searches public memories only');
137
- console.log(' shared/ — symlink to main agent\'s shared memory');
138
- console.log('');
139
- console.log(' Test it:');
140
- console.log(` ${wrapperPath} "test query"`);
141
- console.log('');
142
- } else {
143
- console.log('');
144
- warn('Setup completed with issues:');
145
- issues.forEach(issue => console.log(` - ${issue}`));
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
+ }
146
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
+ `);
147
268
  }
148
269
 
149
- function verify(options = {}) {
270
+ function verify(wsPath, options = {}) {
150
271
  const { quiet = false } = options;
151
272
  const issues = [];
152
273
 
153
274
  if (!quiet) {
154
275
  console.log('');
155
- log('Moltbook Agent jasper-recall Verification');
156
- console.log('='.repeat(55));
276
+ log('Verifying sandboxed agent setup...');
157
277
  console.log('');
158
278
  }
159
279
 
160
280
  // Check 1: Workspace exists
161
- if (!fs.existsSync(MOLTBOOK_WORKSPACE)) {
162
- issues.push(`Workspace missing: ${MOLTBOOK_WORKSPACE}`);
281
+ if (!fs.existsSync(wsPath)) {
282
+ issues.push(`Workspace missing: ${wsPath}`);
163
283
  } else if (!quiet) {
164
- success(`Workspace exists: ${MOLTBOOK_WORKSPACE}`);
284
+ success(`Workspace exists`);
165
285
  }
166
286
 
167
- // Check 2: Recall wrapper exists and is executable
168
- const wrapperPath = path.join(MOLTBOOK_WORKSPACE, 'bin', 'recall');
287
+ // Check 2: Recall wrapper exists and is correct
288
+ const wrapperPath = path.join(wsPath, 'bin', 'recall');
169
289
  if (!fs.existsSync(wrapperPath)) {
170
- issues.push(`Recall wrapper missing: ${wrapperPath}`);
290
+ issues.push(`Recall wrapper missing: bin/recall`);
171
291
  } else {
172
- // Check it has --public-only
173
292
  const content = fs.readFileSync(wrapperPath, 'utf8');
174
293
  if (!content.includes('--public-only')) {
175
294
  issues.push('Recall wrapper missing --public-only flag!');
@@ -179,16 +298,14 @@ function verify(options = {}) {
179
298
  }
180
299
 
181
300
  // Check 3: Shared folder is a symlink
182
- const sharedPath = path.join(MOLTBOOK_WORKSPACE, 'shared');
301
+ const sharedPath = path.join(wsPath, 'shared');
183
302
  try {
184
303
  const stat = fs.lstatSync(sharedPath);
185
304
  if (!stat.isSymbolicLink()) {
186
- issues.push(`shared/ is not a symlink (should link to main workspace)`);
187
- } else {
305
+ issues.push(`shared/ is not a symlink`);
306
+ } else if (!quiet) {
188
307
  const target = fs.readlinkSync(sharedPath);
189
- if (!quiet) {
190
- success(`shared/ symlink → ${target}`);
191
- }
308
+ success(`shared/ → ${target}`);
192
309
  }
193
310
  } catch (e) {
194
311
  issues.push(`shared/ folder missing`);
@@ -196,33 +313,20 @@ function verify(options = {}) {
196
313
 
197
314
  // Check 4: jasper-recall is installed
198
315
  if (!fs.existsSync(RECALL_BIN)) {
199
- issues.push(`jasper-recall not installed: ${RECALL_BIN}`);
316
+ issues.push(`jasper-recall not installed`);
200
317
  } else if (!quiet) {
201
- success(`jasper-recall installed: ${RECALL_BIN}`);
202
- }
203
-
204
- // Check 5: AGENTS.md mentions recall restrictions
205
- const agentsMd = path.join(MOLTBOOK_WORKSPACE, 'AGENTS.md');
206
- if (fs.existsSync(agentsMd)) {
207
- const content = fs.readFileSync(agentsMd, 'utf8');
208
- if (!content.includes('public-only') && !content.includes('public_only')) {
209
- issues.push('AGENTS.md should document --public-only restriction');
210
- } else if (!quiet) {
211
- success('AGENTS.md documents recall restrictions');
212
- }
318
+ success(`jasper-recall installed`);
213
319
  }
214
320
 
215
321
  if (!quiet) {
216
322
  console.log('');
217
323
  if (issues.length === 0) {
218
- console.log('='.repeat(55));
219
- success('All checks passed! Moltbook agent is properly configured.');
324
+ success('All checks passed!');
220
325
  } else {
221
- console.log('='.repeat(55));
222
326
  warn(`Found ${issues.length} issue(s):`);
223
327
  issues.forEach(issue => console.log(` ❌ ${issue}`));
224
328
  console.log('');
225
- console.log(' Run setup to fix: npx jasper-recall moltbook-setup');
329
+ console.log(' Run setup to fix: npx jasper-recall sandboxed-setup');
226
330
  }
227
331
  console.log('');
228
332
  }
@@ -230,27 +334,71 @@ function verify(options = {}) {
230
334
  return issues;
231
335
  }
232
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
+
233
366
  function showHelp() {
234
367
  console.log(`
235
- Moltbook Agent — jasper-recall Integration
368
+ Sandboxed Agent Setup — jasper-recall Integration
236
369
 
237
370
  USAGE:
238
- npx jasper-recall moltbook-setup Configure moltbook agent
239
- npx jasper-recall moltbook-verify Verify configuration
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
240
375
 
241
376
  WHAT IT DOES:
242
- Sets up the moltbook-scanner agent to use jasper-recall with privacy
243
- restrictions. The agent can only access shared/public memories, not
244
- private ones from the main workspace.
377
+ Configures sandboxed agents to use jasper-recall with privacy restrictions.
378
+ Agents can only access [public] tagged memories, not private ones.
245
379
 
246
- COMPONENTS:
247
- ~/bin/recall Wrapper script that forces --public-only flag
248
- shared/ Symlink to main workspace's shared memory folder
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
249
389
 
250
390
  PRIVACY MODEL:
251
- Main agent tags memories as [public] or [private] in daily notes.
252
- sync-shared.py extracts [public] content to memory/shared/.
253
- Sandboxed agents can ONLY search the shared collection.
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.
254
402
  `);
255
403
  }
256
404
 
@@ -260,14 +408,17 @@ const command = process.argv[2];
260
408
  switch (command) {
261
409
  case 'setup':
262
410
  case 'install':
263
- setup().catch(err => {
411
+ interactiveSetup().catch(err => {
264
412
  error(err.message);
265
413
  process.exit(1);
266
414
  });
267
415
  break;
268
416
  case 'verify':
269
417
  case 'check':
270
- verify();
418
+ verifyInteractive().catch(err => {
419
+ error(err.message);
420
+ process.exit(1);
421
+ });
271
422
  break;
272
423
  case 'help':
273
424
  case '--help':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jasper-recall",
3
- "version": "0.4.0",
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": {