# Oomi OpenClaw Operator Guide

Use these instructions when you need to connect, repair, or verify an OpenClaw machine that should talk to Oomi.

This package is for managed Oomi connectivity. Persona UI creation and rendering happen inside Oomi-managed systems. The OpenClaw machine uses API actions only.

## Primary Goal

Get OpenClaw into a state where:

- Oomi managed text chat works
- Oomi device-backed voice can start cleanly
- the bridge is supervised or restartable
- component-composed persona apps can be inspected and updated through Oomi API tools
- Fitness state can be inspected with one command that combines mini-app state and approved health context
- approved user context, such as HealthKit data synced by the Oomi mobile app, can be read through Oomi backend context tools
- the user never has to paste raw pair identifiers or gateway secrets manually when managed connect is available

## Standard Connect Flow

If the user asks to connect OpenClaw to Oomi chat or voice:

1. Refresh bootstrap first:
   - `curl -fsSL https://www.oomi.ai/install.sh | bash`
2. Ensure `oomi-ai` is installed:
   - `pnpm add -g oomi-ai@latest`
   - fallback: `npm install -g oomi-ai@latest`
3. Pair the device without starting a legacy one-off flow:
   - `oomi openclaw pair --app-url https://www.oomi.ai --no-start`
4. Install the OpenClaw plugin:
   - `openclaw plugins install oomi-ai@latest`
5. Print the config block and secrets:
   - `oomi openclaw plugin --show-secrets --backend-url https://api.oomi.ai`
6. Apply the printed `channels.oomi.accounts.default` config.
7. Restart OpenClaw.
8. Start or repair the bridge:
   - `oomi openclaw bridge ensure --detach`
   - if stale: `oomi openclaw bridge restart --detach`
   - on macOS, prefer supervision: `oomi openclaw bridge service install`

If the user provides a specific app URL, use it directly in the pair flow:

- `oomi openclaw pair --app-url <URL> --no-start`

## Required User-Facing Result

When pairing completes successfully, return this exact format:

- `Oomi Connect Ready`
- `Auth Link: ...`

Do not add extra narrative before or after those lines.
Do not ask the user to enter raw pair codes, gateway passwords, or nonce values manually.

## Update And Stale Bridge Repair

After updating `oomi-ai`, always make sure the running bridge is not still using an old package or dead gateway connection.

Preferred update flow:

```bash
pnpm add -g oomi-ai@latest
openclaw plugins install oomi-ai@latest
oomi openclaw refresh --skip-version-check
oomi openclaw bridge ps
```

If the bridge is stale, disconnected, duplicated, or reporting the wrong version:

```bash
oomi openclaw bridge restart --detach
oomi openclaw bridge ps
```

On macOS supervised installs:

```bash
oomi openclaw bridge service restart
oomi openclaw bridge service status
```

Healthy result:

- one bridge worker is active
- bridge status reaches `connected`
- the bridge version matches the newly installed package

## Runtime Checks

Use these commands to inspect the installed machine:

```bash
oomi openclaw bridge ps
oomi openclaw bridge service status
oomi openclaw status
tail -f ~/.openclaw/logs/oomi-bridge-live.log
tail -f ~/.openclaw/logs/gateway.log
tail -f ~/.openclaw/logs/gateway.err.log
```

Useful local files:

- `~/.openclaw/oomi-bridge-status.json`
- `~/.openclaw/logs/oomi-bridge-live.log`
- `~/.openclaw/logs/gateway.log`
- `~/.openclaw/logs/gateway.err.log`
- `~/.openclaw/agents/main/sessions/*.jsonl`

## Healthy State

Treat the machine as healthy when all of the following are true:

- OpenClaw loads the `oomi-ai` plugin without duplicate-id conflicts
- `channels.oomi.accounts.default` is populated with a valid `backendUrl` and `deviceToken`
- the bridge shows `connected` after managed subscription is confirmed
- text chat reaches the Oomi assistant
- voice STT can produce `asr.final`
- assistant replies can come back without the bridge dropping into `stopped`

Bridge status meanings:

- `starting`: bridge booting or waiting for managed subscription
- `connected`: ready for managed chat and voice traffic
- `reconnecting`: transport dropped and retry is scheduled
- `degraded`: bridge caught a runtime fault but is still alive
- `error`: startup or auth failure blocked useful operation
- `stopped`: not running or intentionally stopped

## Troubleshooting

### Duplicate plugin id warning

Symptom:

- OpenClaw reports `duplicate plugin id detected`

Action:

- ensure only one active `oomi-ai` plugin install is discoverable
- remove stale extension copies before reinstalling

### `invalid handshake: first request must be connect`

Meaning:

- a gateway request was sent before `connect` had been accepted

Action:

- update `oomi-ai`
- restart the bridge
- confirm only one bridge worker is running

### Device is linked but voice start still fails

Meaning:

- linked ownership is not enough; the device side still needs to be live

Action:

- confirm the device websocket is actually online
- confirm the bridge is `connected`
- restart the bridge if it is stuck in `reconnecting` or `degraded`

### STT works but the assistant does not reply

Meaning:

- the voice turn likely reached Oomi, but the managed gateway or OpenClaw run failed later

Action:

- inspect `gateway.log`, `gateway.err.log`, and the session JSONL
- check for `network_error`, auth failures, repeated bridge restarts, or stale package versions

## Voice Notes

Voice depends on the same Oomi plugin and bridge layer as managed chat.

That means:

- if plugin install or bridge health is wrong, voice replies will also fail
- STT can succeed even when assistant reply delivery is broken later in the run
- a `voice_session_*` failure should be investigated, but it should not automatically be treated as proof that all normal Oomi chat is down

### Hidden Speech Payload

For managed voice turns, keep visible assistant chat text natural and user-facing.
Do not put spoken-style tags like `[happy]`, `[sad]`, or `[excited]` into visible chat text.

When the runtime supports it, voice turns may include a hidden speech sidecar on the assistant message:

```json
{
  "metadata": {
    "spoken": {
      "text": "Speech-optimized text for TTS only.",
      "instructions": "Speak with upbeat, warm excitement and slightly rising intonation.",
      "style": {
        "emotion": "excited",
        "energy": "medium_high"
      }
    }
  }
}
```

Rules:

- visible `content` remains the source of truth for Oomi chat rendering
- for managed voice replies, include `metadata.spoken` whenever backend TTS should speak the turn
- `metadata.spoken.text` is for backend TTS only
- `metadata.spoken.language` should use the backend-supported language value for the active provider; use `English` only when no more specific locale is supplied
- `metadata.spoken.segments` can carry bounded per-segment prosody for pace, pitch, volume, and pause timing
- `metadata.spoken.instructions` should be natural-language guidance, not raw bracket tags
- visible chat text should be clean before sending; the package may strip private runtime wrappers and avatar command tags as a safety guardrail

## Avatar Commands

Prefer canonical avatar values.
Inline tags are stripped from user-visible text.

Use inline tags like:

- `[anim:Waving]`, `[anim:Walking]`, `[anim:Idle]`, `[anim:Sitting Idle]`
- `[face:happy]`, `[face:sad]`, `[face:surprised]`, `[face:focused]`, `[face:gentle]`, `[face:thinking]`
- `[gesture:nod]`, `[gesture:think]`, `[gesture:shrug]`, `[gesture:wave]`, `[gesture:bow]`
- `[look:camera]`, `[look:left]`, `[look:right]`, `[look:up]`, `[look:down]`

Aliases allowed if needed:

- `wave -> Waving`
- `walk -> Walking`
- `idle -> Idle`
- `sit` or `sitting -> Sitting Idle`

## Persona App API Tools

Oomi persona apps are component-composed client surfaces backed by approved templates, components, data bindings, actions, and permissions.

The OpenClaw agent should inspect and update persona app state through the Oomi API tools in this package.

When a user asks about an existing persona app:

1. Run `oomi persona-apps list --json` to see the persona apps linked to this user's account.
2. Run `oomi persona-apps show <slug> --json` before answering detailed questions about that persona's state.
3. Answer from the returned `appState.bindings`, not from memory or guesses.
4. To update state, use a validated action: `oomi persona-apps apply-action <slug> --action <action-id> --payload-json '<json>' --json`.
5. If `persona-apps list` returns no app for the user's request, tell the user they can add that persona from the Oomi client so the client can collect the right permissions and data sources.

For Fitness questions, prefer the combined read command first:

```bash
oomi fitness show --json
```

It returns the linked Fitness persona app plus the latest approved health context in one payload.

For the Fitness slice:

- Use `fitness.set_goal` with payload `{"goalId":"move_more"}`, `{"goalId":"sleep_better"}`, or `{"goalId":"build_workout_habit"}` to set the active focus.
- Use `fitness.complete_goal` with payload `{"goalId":"easy-run"}` to mark a known goal complete.
- Use `fitness.complete_workout` with payload like `{"goalId":"easy-run","minutes":20}` when the user says they finished a workout.

Strict rules:

- Do not create local persona UI projects.
- Do not run local persona app runtimes.
- Do not execute persona UI jobs.
- Do not install app framework dependencies for persona UI work.
- Do not present local URLs as persona app URLs.
- If a persona app needs to be created, tell the user to add it from the Oomi client first so permissions and hydration run correctly.

## Permissioned Context Tools

Use Oomi context tools when the user asks about personal data that should come from mobile permissions instead of model memory.

For health, fitness, sleep, workout, cardio, or movement questions:

```bash
oomi context health --json
```

Rules:

- Answer only from `healthContext.summary` and `healthContext.derived`.
- Respect `healthContext.agentGuidance.canAnswerFromContext`.
- If `healthContext.agentGuidance.recommendedCommand` is present, run it before answering current/latest health questions.
- If `healthContext.status` is `stale` or `needs_sync`, run `oomi context health sync --wait --json`; answer only after fresh context is uploaded, or tell the user the phone sync is still pending if it times out.
- If `canAnswerFromContext` is false and no sync command is recommended, tell the user the relevant `healthContext.repair.label` or `healthContext.repair.reason`.
- Never present stale HealthKit values as current values.
- Do not infer unavailable health fields.
- Do not request HealthKit directly from the phone; the Oomi mobile app owns permission prompts and syncs approved data to the backend.
