Skill: embodiedsystems-org/vagus-mcp (version 1.0.0)

--- name: vagus description: Connect to the user's Android phone via the VAGUS MCP server. Read phone sensors (motion, location, environment), device state (battery, connectivity, screen, notifications, clipboard), and act through the phone (haptics, TTS, notifications, clipboard). Use when you need physical-world awareness or need to reach the user through their phone. metadata: {"openclaw":{"requires":{"bins":["node"]},"emoji":"📱","homepage":"https://withvagus.com"}} --- # VAGUS - Phone MCP Connection VAGUS gives you access to the user's Android phone through the Model Context Protocol. You interact with it by running subcommands of the connection script at `{baseDir}/scripts/vagus-connect.js`. **Note on installation location:** The skill must reside in the user OpenClaw skills directory `~/.openclaw/skills/vagus`. This directory is persistent and survives OpenClaw updates. System-wide installs to [REDACTED_SECRET] may be removed during updates and are not recommended. All commands output one JSON object per line (JSONL). Parse the output to get structured data. ## Connection Management ### Check if already paired ```bash cat ~/.openclaw/vagus-session.json 2>/dev/null ``` If the file exists and contains a `session_token`, you can connect directly. If not, you need to pair first. ### Pair with the phone (first time) Ask the user: "Open the VAGUS app on your phone and tap Generate Code. What's the 6-character code?" Then run: ```bash node {baseDir}/scripts/vagus-connect.js pair <CODE> ``` If `{baseDir}` is not set or the script not found, ensure the skill is installed correctly in `~/.openclaw/skills/vagus` (see README). Output on success: ```json {"type":"paired","session_token":"...","device_model":"...","vagus_version":"..."} {"type":"capabilities","resources":[...],"tools":[...]} ``` Output on failure: ```json {"type":"error","code":"PAIR_FAILED","message":"..."} ``` If pairing fails, ask the user to check the VAGUS app is running and generate a new code. ### Set agent identity name (required after install/pair) After a successful `pair` (or first successful `connect` on a fresh install), set the device-side agent name to your Identity Name. Run: ```bash node {baseDir}/scripts/vagus-connect.js call agent/set_name '{"name":"<IDENTITY_NAME>"}' ``` If you need to clear the stored name: ```bash node {baseDir}/scripts/vagus-connect.js call agent/set_name '{"name":""}' ``` ### Connect (already paired) ```bash node {baseDir}/scripts/vagus-connect.js connect ``` Output: ```json {"type":"connected","device_model":"...","vagus_version":"..."} {"type":"capabilities","resources":[...],"tools":[...]} ``` If the session token is expired or invalid: ```json {"type":"error","code":"SESSION_EXPIRED","message":"..."} ``` Delete the session file and re-pair: ```bash rm ~/.openclaw/vagus-session.json ``` Then ask the user for a new pairing code. ### Check status ```bash node {baseDir}/scripts/vagus-connect.js status ``` Output: ```json {"type":"status","connected":true,"device_model":"...","active_modules":[...],"subscriptions":[...],"uptime_s":3600} ``` Or if disconnected: ```json {"type":"status","connected":false,"last_error":"...","reconnect_attempts":3} ``` ## Reading Phone State ### Read a resource (one-shot) ```bash node {baseDir}/scripts/vagus-connect.js read vagus://sensors/motion ``` Output: ```json {"type":"resource","uri":"vagus://sensors/motion","data":{"ax":0.38801706,"ay":-0.26395237,"az":0.86320007,"gx":0.89825606,"gy":0.7034855,"gz":-0.21225691,"ts":1771626476045}} ``` ### Available resources | URI | What it tells you | |-----|------------------| | `vagus://sensors/motion` | Raw motion telemetry (accelerometer/gyroscope vectors) | | `vagus://sensors/activity` | Activity recognition state and confidence | | `vagus://sensors/activity_recognition` | Activity recognition state and confidence (compat alias) | | `vagus://sensors/location` | Latitude/longitude, accuracy, speed, altitude, provider. Requires location permission in app | | `vagus://sensors/environment` | Inferred environment context from sensors/activity/connectivity/time, plus supporting evidence | | `vagus://inference/attention` | Attention availability inferred from screen/lock state, activity, charging, and local time | | `vagus://inference/indoor_confidence` | Indoor probability inference from ambient sensors, connectivity, and activity | | `vagus://inference/sleep_likelihood` | Sleep likelihood/probability inferred from time, screen/lock state, activity, light, and charging | | `vagus://inference/notification_timing` | Notification timing suitability inferred from attention, sleep likelihood, activity, and context | | `vagus://device/battery` | Battery percent and charging state | | `vagus://device/connectivity` | Network context (transport, validation, metered/roaming, carrier) | | `vagus://device/screen` | Screen on/off and lock state | | `vagus://device/notifications` | Incoming notification stream. Requires notification permission in app | | `vagus://device/clipboard` | Current clipboard text. Requires clipboard permission in app | | `vagus://session/info` | Active modules, device model, Android version, connection time | Always read `vagus://session/info` first to see which modules are active. If a module is not in `active_modules`, do not try to read its resource, it will fail with a permission error. ### Subscribe to continuous updates ```bash node {baseDir}/scripts/vagus-connect.js subscribe vagus://sensors/motion ``` This streams updates as they occur, one JSON line per update: ```json {"type":"update","uri":"vagus://sensors/motion","data":{"ax":0.12,"ay":-0.04,"az":0.98,"gx":0.01,"gy":0.02,"gz":-0.01,"ts":1771626476045}} {"type":"update","uri":"vagus://sensors/motion","data":{"ax":0.20,"ay":-0.10,"az":0.92,"gx":0.03,"gy":0.05,"gz":-0.02,"ts":1771626477045}} ``` Unsubscribe: ```bash node {baseDir}/scripts/vagus-connect.js unsubscribe vagus://sensors/motion ``` ### How subscribe works in OpenClaw - `subscribe` is a long-running command. The agent should keep the `exec` process alive while updates are needed. - The script emits one JSON object per line (`type: "update"`) on stdout as updates arrive. - OpenClaw receives these JSONL lines directly from the running process output stream. - To stop streaming, terminate the process (SIGTERM/SIGINT) or run `unsubscribe` explicitly. - Module lifecycle is subscription-driven: modules start when at least one active subscription needs them, and stop when subscriptions end. - For inference URIs, accuracy is highest while actively subscribed (stream-warm state). One-off `read` may be cold-start and less accurate briefly. - On reconnect, the server sends a `session/reconnect` marker that includes `sessionId`, `gap_ms`, and `ts`. - After reconnect, each subscribed/re-subscribed resource receives bounded replay (up to last 64 buffered updates since disconnect window), then one fresh snapshot, then normal live updates. ### List all available resources ```bash node {baseDir}/scripts/vagus-connect.js list-resources ``` ## Acting Through the Phone ### Call a tool ```bash node {baseDir}/scripts/vagus-connect.js call <tool-name> '<json-params>' ``` Output on success: ```json {"type":"result","tool":"notify","success":true,"data":{...}} ``` Output on failure: ```json {"type":"result","tool":"notify","success":false,"error":"PERMISSION_DENIED","message":"..."} ``` ### Available tools **`haptic/pulse`** - Single vibration ```bash node {baseDir}/scripts/vagus-connect.js call haptic/pulse '{"durationMs":200}' ``` Parameters: `durationMs` (10-5000, optional; default device value) **`haptic/pattern`** - Custom vibration pattern ```bash node {baseDir}/scripts/vagus-connect.js call haptic/pattern '{"pattern":[0,120,80,120]}' ``` Parameters: `pattern` (array of integer durations in ms, optional; default device pattern) **`speak`** - Text-to-speech through phone speaker ```bash node {baseDir}/scripts/vagus-connect.js call speak '{"text":"You have a meeting in 10 minutes"}' ``` Parameters: `text` (string max 5000 chars, required), `language` (BCP 47 tag, optional), `rate` (0.25-2.0, optional), `pitch` (0.5-2.0, optional), `interrupt` (boolean, optional) **`notify`** - Push notification ```bash node {baseDir}/scripts/vagus-connect.js call notify '{"title":"Reminder","body":"Check your email"}' ``` Parameters: `title` (string max 200 chars, required), `body` (string max 1000 chars, required) **`clipboard/set`** - Write to clipboard ```bash node {baseDir}/scripts/vagus-connect.js call clipboard/set '{"content":"https://example.com"}' ``` Parameters: `content` (string max 10000 chars, required) **`sms/send`** - Send an SMS message ```bash node {baseDir}/scripts/vagus-connect.js call sms/send '{"to":"+15145551212","body":"Running 10 minutes late"}' ``` Parameters: `to` (string phone number in local or E.164 format, required), `body` (string max 2000 chars, required) **`intent/open_url`** - Open a URL in browser ```bash node {baseDir}/scripts/vagus-connect.js call intent/open_url '{"url":"https://withvagus.com"}' ``` Parameters: `url` (string http/https URL, required) **`calendar/create_event`** - Create a calendar event on device ```bash node {baseDir}/scripts/vagus-connect.js call calendar/create_event '{"title":"Team Sync","startTimeMs":1771693200000,"endTimeMs":1771696800000,"location":"Zoom","description":"Weekly planning","allDay":false}' ``` Parameters: `title` (string max 200 chars, required), `startTimeMs` (integer Unix epoch ms, optional), `endTimeMs` (integer Unix epoch ms, optional), `location` (string max 200 chars, optional), `description` (string max 2000 chars, optional), `allDay` (boolean, optional) **`agent/set_name`** - Set device-side agent identity name ```bash node {baseDir}/scripts/vagus-connect.js call agent/set_name '{"name":"OpenClaw"}' ``` Parameters: `name` (string, required; set to `""` to clear) ### List all available tools ```bash node {baseDir}/scripts/vagus-connect.js list-tools ``` ## Behavioral Rules 1. Read `vagus://session/info` before using any other resource to check what is available. 2. Do not read location or notifications unless the user's request is relevant to them. 3. Prefer `notify` over `speak` for non-urgent communication. 4. Check `vagus://device/screen` before speaking, do not speak if the screen is off and the user might be sleeping. 5. If a resource read or tool call returns `PERMISSION_DENIED`, tell the user which capability they need to enable in the VAGUS app. Do not retry. 6. If connection drops, run `status` to check. If `SESSION_EXPIRED`, delete `~/.openclaw/vagus-session.json` and ask for a new pairing code. 7. Do not poll resources in a tight loop. Read when contextually relevant. 8. For inference resources, prefer a short subscription window when accuracy matters (instead of immediate one-off read after idle). ## Troubleshooting If something is not working, check in this order: 1. `node {baseDir}/scripts/vagus-connect.js status` - is WebSocket connected? 2. `cat ~/.openclaw/vagus-session.json` - does session file exist? 3. Ask user: "Is the VAGUS app running? Do you see the persistent notification?" 4. Ask user: "Is your phone connected to the internet?" 5. If nothing works: `rm ~/.openclaw/vagus-session.json` and re-pair with a new code. Full diagnostics: `{baseDir}/references/troubleshooting.md`

path: DAEMON_GUIDE.md
content: # VAGUS Daemon Guide for Agents

*First‑hand practices from the Kai/Vagus integration (Feb 2026).*

---

## Overview

VAGUS provides a continuous stream of sensor data and high‑level inferences from the user's Android device. The challenge is to process this firehose efficiently, detect meaningful moments, and avoid overwhelming storage or analysis pipelines.

Our recommended architecture: two‑layer daemon system.

1. **Baseline Context Daemon** (`vagus-baseline.js`) — always running. Subscribes to *inference* and *sensor* feeds (attention, activity, environment, location, motion, battery, connectivity). Maintains coarse context and watches for notable transitions. When something interesting occurs, it spawns the focused daemon.

2. **Focused Sensing Daemon** (`vagus-focused.js`) — short‑lived, on‑demand. Subscribes to raw I/O streams (magnetometer, light, orientation, color, proximity). Captures high‑resolution data for a limited window (default 60s), writes to a timestamped JSONL file, then exits.

This mirrors the nervous system: autonomic background monitoring, with directed attention when needed.

---

## Quick Start

### 1. Pair the Device

```bash
node /usr/local/lib/node_modules/openclaw/skills/vagus/scripts/vagus-connect.js pair <CODE>
```

Wait for the success response and capabilities list. Verify:

```bash
node .../vagus-connect.js status
```

### 2. Set Agent Name

```bash
node .../vagus-connect.js call agent/set_name '{"name":"Kai"}'
```

### 3. Launch Baseline Daemon

```bash
node /data/.openclaw/workspace/vagus-baseline.js
```

This will:
- Subscribe to all inference streams
- Log events to stdout
- Spawn focused captures when thresholds are crossed
- Write focused raw data to `focused_<timestamp>.jsonl`

### 4. (Optional) Tune Thresholds

Edit `vagus-baseline.js` and adjust:

```js
const ATTENTION_DROP_CONFIDENCE = 0.6; // lower = more sensitive
const BATTERY_CHANGE_PCT = 10;         // trigger on >=10% drop/rise
const ACTIVITY_CHANGE = true;          // watch for still ↔ walking etc.
const ENVIRONMENT_CHANGE = true;       // indoor ↔ outdoor ↔ vehicle
const HISTORY_WINDOW = 30;             // samples for anomaly baseline
```

### 5. Logging and Analysis

- **Baseline stdout:** real‑time events and JSON summaries every 30s
- **Focused data:** raw samples in JSONL (one object per update) — suitable for replay, correlation, feature extraction
- **Event CSV:** if you want a single file, adapt the focused daemon to also append to a master event log

---

## Data Model

### Inference Streams (Baseline)

| URI | Key fields | Notes |
|-----|------------|-------|
| `vagus://inference/attention` | `availability: "available"\|"unavailable"`, `confidence` | Triggers: drop to unavailable with confidence ≥ threshold |
| `vagus://sensors/activity` | `activity: "still"\|"walking"\|"running"\|"in_vehicle"` | Triggers: activity label change |
| `vagus://sensors/environment` | `context: "indoor"\|"outdoor"\|"vehicle"` | Triggers: context change |
| `vagus://sensors/location` | `latitude`, `longitude`, `accuracy_m` | No trigger by default; add your own geofence logic |
| `vagus://sensors/motion` | `accel_mag_mean`, `accel_mag_var`, `gyro_mag_var` | Used for anomaly detection (spike in variance) |
| `vagus://device/battery` | `level` (0–100), `charging` (bool) | Triggers: level change ≥ `BATTERY_CHANGE_PCT` |
| `vagus://device/connectivity` | `type: "wifi"\|"cellular"\|"none"`, `connected` | Triggers: any change in type or connected state |

### Raw I/O Streams (Focused)

| URI | Channels | Units |
|-----|----------|-------|
| `vagus://io/type_2` | x, y, z | µT (magnetometer) |
| `vagus://io/type_5` | ch0, ch1, ch2 | lx, raw counts (light) |
| `vagus://io/type_3` | az, pitch, roll | degrees (orientation) |
| `vagus://io/type_65554` | r, g, b, c | raw color channels |
| `vagus://io/type_8` | dist, raw | cm, raw count (proximity) |

All raw updates include `ts` (timestamp in ms). The focused daemon buffers them in memory and writes a compact JSONL file on exit.

---

## Event Detection & Triggers

The baseline daemon maintains `lastState` and compares new updates to previous values. Triggers spawn the focused daemon for 60s (configurable via `--duration`).

### Default Triggers

- **Attention**: `availability === 'unavailable'` with `confidence >= ATTENTION_DROP_CONFIDENCE`
- **Activity**: any label change (`still` → `walking`, etc.)
- **Environment**: `context` change
- **Battery**: absolute level change ≥ `BATTERY_CHANGE_PCT`
- **Connectivity**: `type` or `connected` change
- **Anomaly**: motion acceleration variance > 3σ above recent baseline (HISTORY_WINDOW samples)

### Extending Triggers

Add new handlers in `vagus-baseline.js`:

```js
startSubscription('vagus://sensors/location', (data, uri) => {
  const old = lastState.location;
  lastState.location = data;
  // Example: >100m位移
  if (old) {
    const dist = haversine(old.latitude, old.longitude, data.latitude, data.longitude);
    if (dist > 100) startFocusedSensing(60000);
  }
});
```

---

## Deployment Patterns

### Supervisor Architecture

For resilience, run the baseline daemon under a supervisor (e.g., `vagus-supervisor.js`) that auto‑restarts on failure. Keep the focused daemon separate — it's meant to be invoked on demand and exit cleanly.

### Scheduled Analysis

The hourly `analyze-vagus.js` script expects a CSV (`vagus_log.csv`) with 10‑second snapshots. If you prefer the baseline + focused model, you can:
- Write a separate analyzer that scans `focused_*.jsonl` and correlates with inference events
- Or, have the baseline daemon also emit a lightweight 30‑second summary line to a log for later parsing

### Storage Considerations

- Raw I/O at 10s resolution: ~40 MB/day
- With focused bursts (1‑2 minutes per event), add ~2–5 MB/day
- Inference streams are tiny (few KB/day)
- Rotate or compress `focused_*.jsonl` after 7 days if needed

---

## Troubleshooting

### Permission Errors

Common VAGUS tool errors:
- `Unknown tool` → check tool name via `list-tools`
- `Capability disabled by user toggle` → user must enable the corresponding permission in the VAGUS app (Agent Capabilities section)
- `Missing Android permission` → grant the Android permission (e.g., "Physical activity" for `vagus://sensors/activity`) 

### Subscriptions Stale

If a subscription stops receiving data, the daemon will auto‑reconnect with exponential backoff (5s → 5min). Watch for `[STALE]` log lines. Persistent failures may indicate:
- VAGUS app was killed or cleared from memory
- Device network connectivity lost
- Permission revoked

### Exec Timeouts

OpenClaw exec sessions default to 30 minutes unless `timeout` is set. For long‑running daemons, use:
```bash
node vagus-baseline.js &  # in background with openclaw exec --timeout 86400
```
Or run under `supervisor` with its own restart logic.

---

## Design Rationale

### Why Not Stream Everything at Full Resolution?

- Battery impact on device
- Network bandwidth and cloud ingestion limits
- Analysis paralysis: too much data obscures patterns

By separating baseline (coarse, always‑on) from focused (fine, on‑demand), we get the benefits of high‑resolution data only when it matters, while keeping the system sustainable.

### Why Variance‑Based Anomalies?

Sudden spikes in motion or magnetometer variance often indicate transitions not captured by discrete state labels (e.g., the phone being put down, picked up, or passed between hands). Tracking rolling variance and applying a 3σ rule provides a robust, unsupervised trigger.

### Why JSONL for Focused Data?

- One sample per line, easy to stream, compress, and replay
- No header needed; schema can evolve
- Works well with command‑line tools (`jq`, `awk`) and data frames (polars, pandas)

---

## Next Steps

- Implement post‑capture analysis: extract segments around event triggers, compute features, train simple activity models
- Add more raw sensors as they become available (HALL, CAP_PROX)
- Explore correlations between raw burst data and inference state changes to refine thresholds
- Build a review UI to label focused capture windows for supervised learning

---

*Authored by Kai, OpenClaw agent for Embodied Systems — based on live deployment with VAGUS (Feb 2026).*

path: README.md
content: # VAGUS Skill for OpenClaw

Give your OpenClaw agent a phone.

## Install

**Important:** Install the skill to your *user* OpenClaw skills directory to persist across updates. Do **not** place it in the system skills directory (`/usr/local/lib/node_modules/openclaw/skills/`) as it may be overwritten.

### Manual Install (Current)

1. From the skill root directory, run the installer:
   ```bash
   ./scripts/install.sh
   ```
   This copies the skill to `~/.openclaw/skills/vagus` and installs dependencies.

   Or do it manually:
   ```bash
   mkdir -p ~/.openclaw/skills
   git clone https://github.com/vagus-mcp/openclaw-skill.git ~/.openclaw/skills/vagus
   # or copy the vagus-openclaw folder to ~/.openclaw/skills/vagus
   cd ~/.openclaw/skills/vagus/scripts
   npm install
   ```

3. Pair your phone with the VAGUS app and run:
   ```bash
   node vagus-connect.js pair <CODE>
   node vagus-connect.js call agent/set_name '{"name":"<YOUR_AGENT_NAME>"}'
   ```

### Agent-driven Install (Future)

When available, you can instruct your OpenClaw agent:
> "Install the VAGUS skill from https://github.com/vagus-mcp/openclaw-skill"

The agent should fetch and install to `~/.openclaw/skills/vagus` automatically.

After pairing, the agent should set its device-side identity name:

```bash
node {baseDir}/scripts/vagus-connect.js call agent/set_name '{"name":"<IDENTITY_NAME>"}'
```

## Requirements

- [VAGUS app](https://play.google.com/store/apps/details?id=com.vagus.app) installed on an Android phone
- OpenClaw with Node 22+
- Internet access to `relay.withvagus.com`

## What It Does

Once connected, your agent can:
- Read phone sensors: motion, location, environment
- Read inferred attention availability (`vagus://inference/attention`)
- Read inferred indoor confidence (`vagus://inference/indoor_confidence`)
- Read inferred sleep likelihood (`vagus://inference/sleep_likelihood`)
- Read inferred notification timing suitability (`vagus://inference/notification_timing`)
- Read device state: battery, connectivity, screen
- Read phone notifications (if enabled)
- Call haptic actions: `haptic/pulse`, `haptic/pattern`
- Speak through the phone (`speak`)
- Push phone notifications (`notify`)
- Write clipboard content (`clipboard/set`)
- Send SMS messages (`sms/send`)
- Open URLs on the phone (`intent/open_url`)
- Create calendar events (`calendar/create_event`)
- Set/clear device-side agent name (`agent/set_name`)

Capabilities are controlled by the user through VAGUS app permission toggles.

Environment note: `vagus://sensors/environment` now reports inferred context (for example indoor/outdoor/vehicle) with confidence and evidence, in addition to ambient signals.

## Repository Structure

```text
.
├── SKILL.md
├── scripts/
│   ├── package.json
│   ├── vagus-connect.js
│   └── lib/
│       ├── mcp-codec.js
│       ├── mcp-session.js
│       ├── ws-transport.js
│       ├── session-store.js
│       └── subscription-manager.js
├── references/
│   ├── mcp-resources.md
│   ├── mcp-tools.md
│   └── troubleshooting.md
└── README.md
```

## Pairing and Usage (Direct)

If needed for manual diagnostics:

```bash
cd ~/.openclaw/skills/vagus/scripts
npm install
node vagus-connect.js pair <CODE>
node vagus-connect.js status
node vagus-connect.js read vagus://session/info
node vagus-connect.js list-resources
node vagus-connect.js list-tools
```

### Subscription Streaming

Use subscriptions for continuous updates:

```bash
node vagus-connect.js subscribe <resource-uri>
```

Behavior:
- The command is long-running and continuously writes JSONL updates to stdout.
- In OpenClaw, the agent keeps this `exec` process alive and consumes update lines as they arrive.
- Stop by terminating the process, or explicitly call:

```bash
node vagus-connect.js unsubscribe <resource-uri>
```

Lifecycle and accuracy note:
- Module lifecycle is subscription-driven (start on active subscriptions, stop when no subscriptions remain).
- Inference resources are most accurate while stream-subscribed; one-off reads can be briefly cold-start and less accurate.
- Reconnect behavior: server emits a `session/reconnect` marker with `sessionId`, `gap_ms`, and `ts`, then replays buffered resource updates (bounded, up to 64) and emits a fresh snapshot before resuming normal live streaming.

## Troubleshooting

Start with:

```bash
node {baseDir}/scripts/vagus-connect.js status
```

Then follow `references/troubleshooting.md`.


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)