openvole 0.2.0 → 0.3.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 CHANGED
@@ -1,9 +1,17 @@
1
- # OpenVole
1
+ <p align="center">
2
+ <img src="assets/vole.png" alt="OpenVole" width="200">
3
+ </p>
2
4
 
3
- **Micro-agent core. The smallest possible thing that other useful things can be built on top of.**
5
+ <h1 align="center">OpenVole</h1>
4
6
 
5
- [![npm](https://img.shields.io/npm/v/openvole)](https://www.npmjs.com/package/openvole)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+ <p align="center">
8
+ <strong>Micro-agent core. The smallest possible thing that other useful things can be built on top of.</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/openvole"><img src="https://img.shields.io/npm/v/openvole" alt="npm"></a>
13
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
14
+ </p>
7
15
 
8
16
  ---
9
17
 
@@ -45,7 +53,7 @@ Edit `vole.config.json`:
45
53
  }
46
54
  ],
47
55
  "skills": [],
48
- "loop": { "maxIterations": 10, "logLevel": "info" }
56
+ "loop": { "maxIterations": 25, "compactThreshold": 50 }
49
57
  }
50
58
  ```
51
59
 
@@ -104,11 +112,12 @@ curl -fsSL https://raw.githubusercontent.com/openvole/openvole/main/presets/full
104
112
  [Brain Paw] [Channel] [Tools] [In-Process]
105
113
  Ollama Telegram Browser Compact
106
114
  Claude Slack Shell Memory
107
- OpenAI Discord MCP
108
- Gemini WhatsApp Email/GitHub/Calendar
115
+ OpenAI Discord MCP Session
116
+ Gemini WhatsApp Email/Resend/GitHub/Calendar
117
+ xAI
109
118
  ```
110
119
 
111
- 19 official Paws: 4 Brain, 4 Channel, 7 Tool, 4 Infrastructure.
120
+ 22 official Paws: 5 Brain, 4 Channel, 8 Tool, 4 Infrastructure.
112
121
 
113
122
  ## Core Concepts
114
123
 
@@ -162,6 +171,7 @@ The Brain is a Paw — the core is LLM-ignorant. Swap models by swapping Brain P
162
171
  - `@openvole/paw-claude` — Anthropic Claude models
163
172
  - `@openvole/paw-openai` — OpenAI models
164
173
  - `@openvole/paw-gemini` — Google Gemini models
174
+ - `@openvole/paw-xai` — xAI Grok models
165
175
 
166
176
  ## Features
167
177
 
@@ -182,22 +192,28 @@ The Brain is a Paw — the core is LLM-ignorant. Swap models by swapping Brain P
182
192
 
183
193
  ### Heartbeat
184
194
 
185
- Periodic wake-up — the Brain checks `HEARTBEAT.md` and decides what to do. No user input needed.
195
+ Periodic wake-up — the Brain checks `HEARTBEAT.md` and decides what to do. No user input needed. Uses cron expressions:
186
196
 
187
197
  ```json
188
- { "heartbeat": { "enabled": true, "intervalMinutes": 30 } }
198
+ { "heartbeat": { "enabled": true, "cron": "*/30 * * * *" } }
189
199
  ```
190
200
 
191
201
  ### Persistent Scheduling
192
202
 
193
- Brain-created schedules are stored in `.openvole/schedules.json` and survive restarts. The heartbeat is recreated from config on each startup.
203
+ Brain-created schedules use cron expressions and are stored in `.openvole/schedules.json`, surviving restarts. The heartbeat is recreated from config on each startup (intervalMinutes is auto-converted to cron).
204
+
205
+ ```
206
+ "0 13 * * *" — daily at 1 PM UTC
207
+ "*/30 * * * *" — every 30 minutes
208
+ "0 9 * * 1" — every Monday at 9 AM
209
+ ```
194
210
 
195
211
  ### Memory (Source-Isolated)
196
212
 
197
213
  Persistent memory with daily logs scoped by task source:
198
214
 
199
215
  ```
200
- .openvole/memory/
216
+ .openvole/paws/paw-memory/
201
217
  ├── MEMORY.md # Shared long-term facts
202
218
  ├── user/ # CLI session logs
203
219
  ├── paw/ # Telegram/Slack logs
@@ -206,11 +222,47 @@ Persistent memory with daily logs scoped by task source:
206
222
 
207
223
  ### Sessions
208
224
 
209
- Conversation continuity across messages. Auto-expiring transcripts per session ID.
225
+ Conversation continuity across messages. Auto-expiring transcripts per session ID. Session data lives in `.openvole/paws/paw-session/`, organized by session ID (e.g., `cli:default/`, `telegram:123/`).
210
226
 
211
227
  ### MCP Support
212
228
 
213
- Bridge 1000+ MCP servers into the tool registry via `paw-mcp`. MCP tools appear alongside Paw tools — the Brain doesn't know the difference.
229
+ Bridge 1000+ community MCP servers into the tool registry via `paw-mcp`. MCP tools appear alongside Paw tools — the Brain doesn't know the difference.
230
+
231
+ - MCP tools are **auto-discovered at runtime** as MCP servers connect
232
+ - **Late tool registration** — tools appear after the engine starts, not at boot
233
+ - MCP config lives in `.openvole/paws/paw-mcp/servers.json` (not in the installed package)
234
+
235
+ Example `.openvole/paws/paw-mcp/servers.json`:
236
+
237
+ ```json
238
+ {
239
+ "servers": [
240
+ {
241
+ "name": "resend",
242
+ "command": "npx",
243
+ "args": ["-y", "resend-mcp"],
244
+ "env": { "RESEND_API_KEY": "$RESEND_API_KEY" }
245
+ }
246
+ ]
247
+ }
248
+ ```
249
+
250
+ ### Late Tool Registration
251
+
252
+ Any Paw can discover and register tools at runtime using the `register_tools` mechanism — not just MCP. Tools registered this way appear in the tool registry like any other tool. This is a generic capability of the engine, not an MCP-specific feature.
253
+
254
+ ### Local Paw Config
255
+
256
+ Each Paw has its own local config directory at `.openvole/paws/<name>/`. The installed npm package stays immutable — all user configuration lives in the local paw directory.
257
+
258
+ ```
259
+ .openvole/paws/
260
+ ├── paw-memory/ ← memory data (MEMORY.md, daily logs)
261
+ ├── paw-session/ ← session transcripts
262
+ └── paw-mcp/ ← MCP config (servers.json)
263
+ ```
264
+
265
+ Example: `paw-mcp` reads its `servers.json` from `.openvole/paws/paw-mcp/`, not from `node_modules/`.
214
266
 
215
267
  ### Rate Limiting
216
268
 
@@ -230,12 +282,12 @@ Customize agent behavior with optional markdown files in `.openvole/`:
230
282
 
231
283
  | File | Purpose |
232
284
  |------|---------|
233
- | `BRAIN.md` | Custom system prompt (overrides default) |
285
+ | `BRAIN.md` | Custom system prompt — **overrides the default system prompt entirely** |
234
286
  | `SOUL.md` | Agent personality and tone |
235
287
  | `USER.md` | User profile and preferences |
236
288
  | `AGENT.md` | Operating rules and constraints |
237
289
 
238
- All Brain Paws (Ollama, Claude, OpenAI, Gemini) load these on startup.
290
+ All Brain Paws (Ollama, Claude, OpenAI, Gemini, xAI) load these on startup.
239
291
 
240
292
  ### Workspace
241
293
 
@@ -262,11 +314,11 @@ When context grows too large, `paw-compact` summarizes old messages while preser
262
314
 
263
315
  Real-time web UI at `localhost:3001` — paws, tools, skills, tasks, schedules, live events.
264
316
 
265
- ## Official Paws (19)
317
+ ## Official Paws (22)
266
318
 
267
319
  All paws live in [PawHub](https://github.com/openvole/pawhub) and are installed via npm.
268
320
 
269
- ### Brain (4)
321
+ ### Brain (5)
270
322
 
271
323
  | Paw | Purpose |
272
324
  |-----|---------|
@@ -274,6 +326,7 @@ All paws live in [PawHub](https://github.com/openvole/pawhub) and are installed
274
326
  | `paw-claude` | Anthropic Claude models |
275
327
  | `paw-openai` | OpenAI models |
276
328
  | `paw-gemini` | Google Gemini models |
329
+ | `paw-xai` | xAI Grok models |
277
330
 
278
331
  ### Channel (4)
279
332
 
@@ -284,7 +337,7 @@ All paws live in [PawHub](https://github.com/openvole/pawhub) and are installed
284
337
  | `paw-discord` | Discord bot channel |
285
338
  | `paw-whatsapp` | WhatsApp bot channel |
286
339
 
287
- ### Tool (7)
340
+ ### Tool (8)
288
341
 
289
342
  | Paw | Purpose |
290
343
  |-----|---------|
@@ -293,6 +346,7 @@ All paws live in [PawHub](https://github.com/openvole/pawhub) and are installed
293
346
  | `paw-filesystem` | File system operations |
294
347
  | `paw-mcp` | MCP server bridge |
295
348
  | `paw-email` | Email sending |
349
+ | `paw-resend` | Email via Resend API |
296
350
  | `paw-github` | GitHub integration |
297
351
  | `paw-calendar` | Calendar integration |
298
352
 
@@ -353,8 +407,8 @@ Single `vole.config.json` — plain JSON, no imports:
353
407
  "brain": "@openvole/paw-ollama",
354
408
  "paws": ["@openvole/paw-ollama", "@openvole/paw-memory"],
355
409
  "skills": ["clawhub/summarize"],
356
- "loop": { "maxIterations": 10, "logLevel": "info" },
357
- "heartbeat": { "enabled": false, "intervalMinutes": 30 },
410
+ "loop": { "maxIterations": 25, "compactThreshold": 50 },
411
+ "heartbeat": { "enabled": false, "cron": "*/30 * * * *" },
358
412
  "toolProfiles": { "paw": { "deny": ["shell_exec"] } }
359
413
  }
360
414
  ```
@@ -367,6 +421,29 @@ OpenVole loads [OpenClaw](https://openclaw.ai) skills natively — same `SKILL.m
367
421
  npx vole clawhub install summarize
368
422
  ```
369
423
 
424
+ ## .openvole Directory Structure
425
+
426
+ ```
427
+ .openvole/
428
+ ├── paws/
429
+ │ ├── paw-memory/ ← memory data
430
+ │ │ ├── MEMORY.md
431
+ │ │ └── user/, paw/, heartbeat/
432
+ │ ├── paw-session/ ← session transcripts
433
+ │ │ └── cli:default/, telegram:123/
434
+ │ └── paw-mcp/ ← MCP config
435
+ │ └── servers.json
436
+ ├── workspace/ ← agent scratch space
437
+ ├── skills/ ← local and clawhub skills
438
+ ├── vault.json ← encrypted key-value store
439
+ ├── schedules.json ← persistent cron schedules
440
+ ├── BRAIN.md ← custom system prompt
441
+ ├── SOUL.md ← agent personality
442
+ ├── USER.md ← user profile
443
+ ├── AGENT.md ← operating rules
444
+ └── HEARTBEAT.md ← recurring job definitions
445
+ ```
446
+
370
447
  ## Philosophy
371
448
 
372
449
  > **If it connects to something, it's a Paw.**
package/dist/cli.js CHANGED
@@ -575,11 +575,13 @@ var init_scheduler = __esm({
575
575
  setTickHandler(handler) {
576
576
  this.tickHandler = handler;
577
577
  }
578
- /** Load schedule data from disk without starting cron jobs (for read-only access) */
578
+ /** Load schedule data from disk without starting cron jobs (for read-only access). Never persists. */
579
579
  async loadFromDisk() {
580
580
  if (!this.savePath) return;
581
+ const savedPath = this.savePath;
582
+ this.savePath = void 0;
581
583
  try {
582
- const raw = await fs3.readFile(this.savePath, "utf-8");
584
+ const raw = await fs3.readFile(savedPath, "utf-8");
583
585
  const persisted = JSON.parse(raw);
584
586
  for (const s of persisted) {
585
587
  const job = new Cron(s.cron, { timezone: "UTC", paused: true }, () => {
@@ -594,6 +596,7 @@ var init_scheduler = __esm({
594
596
  }
595
597
  } catch {
596
598
  }
599
+ this.savePath = savedPath;
597
600
  }
598
601
  /** Load persisted schedules from disk and restart their jobs */
599
602
  async restore() {
@@ -2654,6 +2657,20 @@ var PawRegistry = class {
2654
2657
  const { type } = params;
2655
2658
  return this.handleQuery(type);
2656
2659
  });
2660
+ transport.onRequest("register_tools", async (params) => {
2661
+ const { tools } = params;
2662
+ if (tools && tools.length > 0) {
2663
+ const toolDefs = tools.map((t) => ({
2664
+ name: t.name,
2665
+ description: t.description,
2666
+ parameters: {},
2667
+ execute: async (toolParams) => this.executeRemoteTool(pawName, t.name, toolParams)
2668
+ }));
2669
+ this.toolRegistry.register(pawName, toolDefs, false);
2670
+ logger12.info(`Paw "${pawName}" late-registered ${tools.length} tool(s)`);
2671
+ }
2672
+ return { ok: true };
2673
+ });
2657
2674
  transport.onRequest("create_task", async (params) => {
2658
2675
  const { input, source, sessionId, metadata } = params;
2659
2676
  if (!this.taskQueue) {
@@ -2736,6 +2753,18 @@ var PawRegistry = class {
2736
2753
  if (registration.hooks?.bootstrap) {
2737
2754
  this.bootstrapPaws.push(pawName);
2738
2755
  }
2756
+ if (registration.hooks?.perceive) {
2757
+ const config = this.paws.get(pawName)?.config;
2758
+ const hookConfig = config?.hooks?.perceive;
2759
+ const hasTools = (registration.tools?.length ?? 0) > 0;
2760
+ this.perceiveHooks.push({
2761
+ pawName,
2762
+ order: hookConfig?.order ?? 100,
2763
+ pipeline: hookConfig?.pipeline ?? true,
2764
+ hasTools
2765
+ });
2766
+ this.perceiveHooks.sort((a, b) => a.order - b.order);
2767
+ }
2739
2768
  if (registration.hooks?.observe) {
2740
2769
  this.observeHookPaws.push(pawName);
2741
2770
  }
@@ -3107,11 +3136,12 @@ async function initProject(projectRoot) {
3107
3136
  await fs10.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
3108
3137
  await fs10.mkdir(path11.join(projectRoot, ".openvole", "skills"), { recursive: true });
3109
3138
  await fs10.mkdir(path11.join(projectRoot, ".openvole", "skills", "clawhub"), { recursive: true });
3110
- await fs10.mkdir(path11.join(projectRoot, ".openvole", "memory"), { recursive: true });
3111
- await fs10.mkdir(path11.join(projectRoot, ".openvole", "sessions"), { recursive: true });
3112
3139
  await fs10.mkdir(path11.join(projectRoot, ".openvole", "workspace"), { recursive: true });
3140
+ await fs10.mkdir(path11.join(projectRoot, ".openvole", "paws", "paw-memory"), { recursive: true });
3141
+ await fs10.mkdir(path11.join(projectRoot, ".openvole", "paws", "paw-session"), { recursive: true });
3142
+ await fs10.mkdir(path11.join(projectRoot, ".openvole", "paws", "paw-mcp"), { recursive: true });
3113
3143
  await fs10.writeFile(
3114
- path11.join(projectRoot, ".openvole", "memory", "MEMORY.md"),
3144
+ path11.join(projectRoot, ".openvole", "paws", "paw-memory", "MEMORY.md"),
3115
3145
  "# Memory\n\nLong-term memory for the agent. Store important facts, user preferences, and decisions here.\n",
3116
3146
  "utf-8"
3117
3147
  );
@@ -3156,10 +3186,12 @@ async function initProject(projectRoot) {
3156
3186
  }
3157
3187
  logger15.info("Created vole.config.json");
3158
3188
  logger15.info("Created .openvole/");
3159
- logger15.info(" skills/ \u2014 local and ClawHub skills");
3160
- logger15.info(" memory/ \u2014 agent memory (MEMORY.md + daily logs)");
3161
- logger15.info(" sessions/ \u2014 session transcripts");
3162
- logger15.info("Created HEARTBEAT.md");
3189
+ logger15.info(" skills/ \u2014 local and ClawHub skills");
3190
+ logger15.info(" workspace/ \u2014 agent scratch space");
3191
+ logger15.info(" paws/paw-memory/ \u2014 agent memory (MEMORY.md + daily logs)");
3192
+ logger15.info(" paws/paw-session/ \u2014 session transcripts");
3193
+ logger15.info(" paws/paw-mcp/ \u2014 MCP server config");
3194
+ logger15.info("Created identity files (BRAIN.md, SOUL.md, USER.md, AGENT.md, HEARTBEAT.md)");
3163
3195
  logger15.info("Created .env");
3164
3196
  logger15.info("");
3165
3197
  logger15.info("Next: install paws and start");